From 9e0338c4b0c2728e16d09d87e3150fdece8f1fcd Mon Sep 17 00:00:00 2001 From: Rowan Seymour Date: Mon, 20 May 2024 17:32:37 -0500 Subject: [PATCH] Use std library errors --- assets/static/flow.go | 5 +- assets/static/source.go | 10 ++-- cmd/classify/main.go | 3 +- cmd/docgen/completion/completion.go | 6 +- cmd/docgen/docs/base.go | 10 ++-- cmd/docgen/docs/editor_support.go | 4 +- cmd/docgen/docs/html.go | 22 ++++--- cmd/docgen/docs/html_renderers.go | 58 +++++++++---------- cmd/flowrunner/main.go | 6 +- cmd/flowxgettext/main.go | 5 +- cmd/transferairtime/main.go | 3 +- contactql/error.go | 10 +--- contactql/error_test.go | 5 +- contactql/es/sort.go | 3 +- envs/dates.go | 8 +-- excellent/base_test.go | 4 +- excellent/errors.go | 3 +- excellent/types/base_test.go | 8 +-- excellent/types/date_test.go | 4 +- excellent/types/datetime_test.go | 4 +- excellent/types/error_test.go | 4 +- excellent/types/json.go | 3 +- excellent/types/number.go | 5 +- excellent/types/object.go | 2 +- excellent/types/time_test.go | 4 +- flows/actions/base.go | 3 +- flows/actions/base_test.go | 6 +- flows/actions/call_resthook.go | 5 +- flows/actions/call_webhook.go | 4 +- flows/actions/enter_flow.go | 6 +- flows/actions/remove_contact_groups.go | 6 +- flows/actions/send_email.go | 4 +- flows/actions/transfer_airtime.go | 4 +- flows/contact.go | 7 +-- flows/definition/exit.go | 6 +- flows/definition/flow.go | 8 +-- flows/definition/legacy/definition.go | 20 +++---- .../legacy/expressions/functions.go | 6 +- flows/definition/legacy/utils.go | 3 +- flows/definition/migrations/base.go | 10 ++-- flows/definition/migrations/primitives.go | 3 +- flows/definition/node.go | 20 +++---- flows/engine/assets_test.go | 3 +- flows/engine/services.go | 4 +- flows/engine/session.go | 35 ++++++----- flows/engine/sprint_test.go | 3 +- flows/events/base.go | 5 +- flows/events/msg_wait.go | 5 +- flows/inputs/base.go | 5 +- flows/modifiers/base.go | 6 +- flows/resumes/base.go | 9 ++- flows/resumes/base_test.go | 4 +- flows/routers/base.go | 15 ++--- flows/routers/base_test.go | 4 +- flows/routers/category.go | 6 +- flows/routers/switch.go | 10 ++-- flows/routers/waits/base.go | 5 +- flows/routers/waits/hints/base.go | 5 +- flows/routers/waits/msg.go | 5 +- flows/runs/run.go | 12 ++-- flows/triggers/base.go | 14 ++--- flows/triggers/base_test.go | 3 +- flows/triggers/ticket.go | 4 +- go.mod | 3 +- go.sum | 6 +- services/airtime/dtone/service.go | 14 ++--- test/engine.go | 4 +- test/runner_test.go | 14 ++--- test/session.go | 29 +++++----- test/utils.go | 12 ++++ utils/smtpx/client.go | 4 +- utils/smtpx/mock.go | 4 +- utils/smtpx/retries_test.go | 3 +- utils/validator.go | 3 +- 74 files changed, 267 insertions(+), 311 deletions(-) diff --git a/assets/static/flow.go b/assets/static/flow.go index 15e119c6a..f6731625a 100644 --- a/assets/static/flow.go +++ b/assets/static/flow.go @@ -2,12 +2,11 @@ package static import ( "encoding/json" + "errors" + "github.com/buger/jsonparser" "github.com/nyaruka/gocommon/jsonx" "github.com/nyaruka/goflow/assets" - - "github.com/buger/jsonparser" - "github.com/pkg/errors" ) // Flow is a JSON serializable implementation of a flow asset diff --git a/assets/static/source.go b/assets/static/source.go index 1a9860aaf..0888215ba 100644 --- a/assets/static/source.go +++ b/assets/static/source.go @@ -3,13 +3,13 @@ package static import ( "encoding/json" + "fmt" "os" "strings" "github.com/nyaruka/goflow/assets" "github.com/nyaruka/goflow/envs" "github.com/nyaruka/goflow/utils" - "github.com/pkg/errors" ) // StaticSource is an asset source which loads assets from a static JSON file @@ -40,7 +40,7 @@ func NewEmptySource() *StaticSource { func NewSource(data json.RawMessage) (*StaticSource, error) { s := &StaticSource{} if err := utils.UnmarshalAndValidate(data, &s.s); err != nil { - return nil, errors.Wrap(err, "unable to read assets") + return nil, fmt.Errorf("unable to read assets: %w", err) } return s, nil } @@ -49,7 +49,7 @@ func NewSource(data json.RawMessage) (*StaticSource, error) { func LoadSource(path string) (*StaticSource, error) { data, err := os.ReadFile(path) if err != nil { - return nil, errors.Wrapf(err, "error reading file '%s'", path) + return nil, fmt.Errorf("error reading file '%s': %w", path, err) } return NewSource(data) } @@ -88,7 +88,7 @@ func (s *StaticSource) FlowByUUID(uuid assets.FlowUUID) (assets.Flow, error) { return flow, nil } } - return nil, errors.Errorf("no such flow with UUID '%s'", uuid) + return nil, fmt.Errorf("no such flow with UUID '%s'", uuid) } // Flow returns the flow asset with the given UUID @@ -98,7 +98,7 @@ func (s *StaticSource) FlowByName(name string) (assets.Flow, error) { return flow, nil } } - return nil, errors.Errorf("no such flow with name '%s'", name) + return nil, fmt.Errorf("no such flow with name '%s'", name) } // Globals returns all global assets diff --git a/cmd/classify/main.go b/cmd/classify/main.go index 6286b5c19..b9b8b202f 100644 --- a/cmd/classify/main.go +++ b/cmd/classify/main.go @@ -11,7 +11,6 @@ import ( "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/services/classification/luis" "github.com/nyaruka/goflow/services/classification/wit" - "github.com/pkg/errors" ) const usage = `usage: classify [flags] ` @@ -72,7 +71,7 @@ func classify(svcs map[string]flows.ClassificationService, input string) (map[st for t, s := range svcs { c, err := s.Classify(nil, input, log.Log) if err != nil { - return nil, log.Logs, errors.Wrapf(err, "error classifying with %s", t) + return nil, log.Logs, fmt.Errorf("error classifying with %s: %w", t, err) } res[t] = c } diff --git a/cmd/docgen/completion/completion.go b/cmd/docgen/completion/completion.go index 76fc3d1cd..8f6076225 100644 --- a/cmd/docgen/completion/completion.go +++ b/cmd/docgen/completion/completion.go @@ -2,8 +2,6 @@ package completion import ( "fmt" - - "github.com/pkg/errors" ) // types available in root of context even without a session @@ -47,13 +45,13 @@ func (c *Completion) Validate() error { for _, t := range c.Types { for _, ref := range t.TypeRefs() { if !knownTypes[ref] { - return errors.Errorf("context type %s references unknown type %s", t.Name(), ref) + return fmt.Errorf("context type %s references unknown type %s", t.Name(), ref) } } } for _, p := range c.Root { if !knownTypes[p.Type] { - return errors.Errorf("context root references unknown type %s", p.Type) + return fmt.Errorf("context root references unknown type %s", p.Type) } } return nil diff --git a/cmd/docgen/docs/base.go b/cmd/docgen/docs/base.go index efc7191e8..c9026e396 100644 --- a/cmd/docgen/docs/base.go +++ b/cmd/docgen/docs/base.go @@ -8,8 +8,6 @@ import ( "github.com/nyaruka/gocommon/dates" "github.com/nyaruka/goflow/utils/po" - - "github.com/pkg/errors" ) const ( @@ -40,7 +38,7 @@ func Generate(baseDir, outputDir, localeDir string) error { // extract all documented items from the source code taggedItems, err := FindAllTaggedItems(baseDir) if err != nil { - return errors.Wrap(err, "error extracting tagged items") + return fmt.Errorf("error extracting tagged items: %w", err) } for k, v := range taggedItems { @@ -72,7 +70,7 @@ func Generate(baseDir, outputDir, localeDir string) error { // and merge with existing locale files if err := locales.Update(poDomain, pot); err != nil { - return errors.Wrap(err, "error updating locale files") + return fmt.Errorf("error updating locale files: %w", err) } return nil @@ -92,7 +90,7 @@ func generateForLang(baseDir, outputDir string, items map[string][]*TaggedItem, // load PO file for translations po, err := locales.Load(locale, poDomain) if err != nil { - return errors.Wrapf(err, "error loading PO file for '%s'", locale) + return fmt.Errorf("error loading PO file for '%s': %w", locale, err) } // create gettext function which will keep track of all unique message IDs @@ -108,7 +106,7 @@ func generateForLang(baseDir, outputDir string, items map[string][]*TaggedItem, fmt.Printf("Invoking generator: %s...\n", g.Name()) if err := g.Generate(baseDir, genDir, items, gettext); err != nil { - return errors.Wrapf(err, "error invoking generator %s", g.Name()) + return fmt.Errorf("error invoking generator %s: %w", g.Name(), err) } } diff --git a/cmd/docgen/docs/editor_support.go b/cmd/docgen/docs/editor_support.go index a95154ec9..c2b3a07f9 100644 --- a/cmd/docgen/docs/editor_support.go +++ b/cmd/docgen/docs/editor_support.go @@ -10,8 +10,6 @@ import ( "github.com/nyaruka/gocommon/jsonx" "github.com/nyaruka/gocommon/urns" "github.com/nyaruka/goflow/cmd/docgen/completion" - - "github.com/pkg/errors" ) func init() { @@ -90,7 +88,7 @@ func (g *editorSupportGenerator) buildContextCompletion(items map[string][]*Tagg for i, propDesc := range item.examples { prop := completion.ParseProperty(propDesc) if prop == nil { - return nil, errors.Errorf("invalid format for property description \"%s\"", propDesc) + return nil, fmt.Errorf("invalid format for property description \"%s\"", propDesc) } prop.Help = gettext(prop.Help) properties[i] = prop diff --git a/cmd/docgen/docs/html.go b/cmd/docgen/docs/html.go index 8026ff912..07530b4d9 100644 --- a/cmd/docgen/docs/html.go +++ b/cmd/docgen/docs/html.go @@ -16,8 +16,6 @@ import ( "github.com/nyaruka/goflow/envs" "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/test" - - "github.com/pkg/errors" ) func init() { @@ -63,7 +61,7 @@ func (g *htmlDocsGenerator) Name() string { func (g *htmlDocsGenerator) Generate(baseDir, outputDir string, items map[string][]*TaggedItem, gettext func(string) string) error { if err := renderTemplateDocs(baseDir, outputDir, items); err != nil { - return errors.Wrap(err, "error rendering templates") + return fmt.Errorf("error rendering templates: %w", err) } // copy static resources to docs dir @@ -71,7 +69,7 @@ func (g *htmlDocsGenerator) Generate(baseDir, outputDir string, items map[string src := path.Join(baseDir, templateDir, res) dst := path.Join(outputDir, res) if err := copyFile(src, dst); err != nil { - return errors.Wrap(err, "error copying resource") + return fmt.Errorf("error copying resource: %w", err) } fmt.Printf(" > Copied %s > %s\n", src, dst) } @@ -82,7 +80,7 @@ func renderTemplateDocs(baseDir string, outputDir string, items map[string][]*Ta // render items as context for the main doc templates context, err := buildTemplateContext(items) if err != nil { - return errors.Wrap(err, "error building docs context") + return fmt.Errorf("error building docs context: %w", err) } // to post-process templates to resolve links between templates @@ -102,14 +100,14 @@ func renderTemplateDocs(baseDir string, outputDir string, items map[string][]*Ta htmlPath := path.Join(outputDir, template.Path[0:len(template.Path)-3]+".html") if err := renderTemplate(templatePath, renderedPath, context, linkResolver, linkTargets); err != nil { - return errors.Wrapf(err, "error rendering template %s", templatePath) + return fmt.Errorf("error rendering template %s: %w", templatePath, err) } htmlTemplate := path.Join(baseDir, "cmd/docgen/templates/template.html") htmlContext := map[string]string{"title": template.Title} if err := renderHTML(renderedPath, htmlPath, htmlTemplate, template.TOC, htmlContext); err != nil { - return errors.Wrapf(err, "error rendering HTML from %s to %s", renderedPath, htmlPath) + return fmt.Errorf("error rendering HTML from %s to %s: %w", renderedPath, htmlPath, err) } fmt.Printf(" > Rendered %s > %s > %s\n", templatePath, renderedPath, htmlPath) @@ -123,12 +121,12 @@ func renderTemplate(src, dst string, context map[string]string, resolver urlReso // generate our complete docs docTpl, err := template.ParseFiles(src) if err != nil { - return errors.Wrap(err, "error reading template file") + return fmt.Errorf("error reading template file: %w", err) } output := &strings.Builder{} if err := docTpl.Execute(output, context); err != nil { - return errors.Wrap(err, "error executing template") + return fmt.Errorf("error executing template: %w", err) } processed := resolveLinks(output.String(), resolver, linkTargets) @@ -177,7 +175,7 @@ func createLinkResolver(items map[string][]*TaggedItem) (urlResolver, map[string return func(tag string, val string) (string, error) { linkTpl := typeTemplates[tag] if linkTpl == "" { - return "", errors.Errorf("no link template for type %s", tag) + return "", fmt.Errorf("no link template for type %s", tag) } return fmt.Sprintf(linkTpl, val), nil }, linkTargets @@ -224,12 +222,12 @@ func buildTemplateContext(items map[string][]*TaggedItem) (map[string]string, er session, _, err := test.CreateTestSession(server.URL, envs.RedactionPolicyNone) if err != nil { - return nil, errors.Wrap(err, "error creating example session") + return nil, fmt.Errorf("error creating example session: %w", err) } voiceSession, _, err := test.CreateTestVoiceSession(server.URL) if err != nil { - return nil, errors.Wrap(err, "error creating example session") + return nil, fmt.Errorf("error creating example session: %w", err) } context := make(map[string]string) diff --git a/cmd/docgen/docs/html_renderers.go b/cmd/docgen/docs/html_renderers.go index 40754c883..c5e8e14fd 100644 --- a/cmd/docgen/docs/html_renderers.go +++ b/cmd/docgen/docs/html_renderers.go @@ -18,8 +18,6 @@ import ( "github.com/nyaruka/goflow/flows/triggers" "github.com/nyaruka/goflow/test" "github.com/nyaruka/goflow/utils" - - "github.com/pkg/errors" ) var dynamicContextTypes = []string{"fields", "globals", "results", "urns"} @@ -50,7 +48,7 @@ func createItemListContextFunc(tag string, renderer renderFunc) ContextFunc { for _, item := range items[tag] { if err := renderer(buffer, item, session, voiceSession); err != nil { - return nil, errors.Wrapf(err, "error rendering %s:%s", item.tagName, item.tagValue) + return nil, fmt.Errorf("error rendering %s:%s: %w", item.tagName, item.tagValue, err) } } @@ -60,12 +58,12 @@ func createItemListContextFunc(tag string, renderer renderFunc) ContextFunc { func renderAssetDoc(output *strings.Builder, item *TaggedItem, session flows.Session, voiceSession flows.Session) error { if len(item.examples) == 0 { - return errors.Errorf("no examples found for asset item %s/%s", item.tagValue, item.typeName) + return fmt.Errorf("no examples found for asset item %s/%s", item.tagValue, item.typeName) } marshaled, err := jsonx.MarshalPretty(json.RawMessage(strings.Join(item.examples, "\n"))) if err != nil { - return errors.Wrap(err, "unable to marshal example") + return fmt.Errorf("unable to marshal example: %w", err) } // try to load example as part of a static asset source @@ -78,7 +76,7 @@ func renderAssetDoc(output *strings.Builder, item *TaggedItem, session flows.Ses _, err = static.NewSource([]byte(assetSet)) if err != nil { - return errors.Wrap(err, "unable to load example into asset source") + return fmt.Errorf("unable to load example into asset source: %w", err) } output.WriteString(renderItemTitle(item)) @@ -94,7 +92,7 @@ func renderAssetDoc(output *strings.Builder, item *TaggedItem, session flows.Ses func renderTypeDoc(output *strings.Builder, item *TaggedItem, session flows.Session, voiceSession flows.Session) error { if len(item.examples) == 0 { - return errors.Errorf("no examples found for type %s/%s", item.tagValue, item.typeName) + return fmt.Errorf("no examples found for type %s/%s", item.tagValue, item.typeName) } // check the examples @@ -117,7 +115,7 @@ func renderTypeDoc(output *strings.Builder, item *TaggedItem, session flows.Sess func renderOperatorDoc(output *strings.Builder, item *TaggedItem, session flows.Session, voiceSession flows.Session) error { if len(item.examples) == 0 { - return errors.Errorf("no examples found for operator %s/%s", item.tagValue, item.typeName) + return fmt.Errorf("no examples found for operator %s/%s", item.tagValue, item.typeName) } // check the examples @@ -150,7 +148,7 @@ func renderContextDoc(output *strings.Builder, item *TaggedItem, session flows.S for _, propDesc := range item.examples { prop := completion.ParseProperty(propDesc) if prop == nil { - return errors.Errorf("invalid format for property description \"%s\"", propDesc) + return fmt.Errorf("invalid format for property description \"%s\"", propDesc) } if prop.Key == "__default__" { defaultProp = prop @@ -186,7 +184,7 @@ func renderRootContext(items map[string][]*TaggedItem, session flows.Session, vo for _, propDesc := range root.examples { prop := completion.ParseProperty(propDesc) if prop == nil { - return nil, errors.Errorf("invalid format for property description \"%s\"", propDesc) + return nil, fmt.Errorf("invalid format for property description \"%s\"", propDesc) } properties = append(properties, prop) } @@ -211,13 +209,13 @@ func renderPropertyType(p *completion.Property) string { func renderFunctionDoc(output *strings.Builder, item *TaggedItem, session flows.Session, voiceSession flows.Session) error { if len(item.examples) == 0 { - return errors.Errorf("no examples found for function %s", item.tagValue) + return fmt.Errorf("no examples found for function %s", item.tagValue) } // check the function name is a registered function _, exists := functions.XFUNCTIONS[item.tagValue] if !exists { - return errors.Errorf("docstring function tag %s isn't a registered function", item.tagValue) + return fmt.Errorf("docstring function tag %s isn't a registered function", item.tagValue) } // check the examples @@ -243,18 +241,18 @@ func renderEventDoc(output *strings.Builder, item *TaggedItem, session flows.Ses exampleJSON := []byte(strings.Join(item.examples, "\n")) event, err := events.ReadEvent(exampleJSON) if err != nil { - return errors.Wrap(err, "unable to read event") + return fmt.Errorf("unable to read event: %w", err) } // validate it err = utils.Validate(event) if err != nil { - return errors.Wrap(err, "unable to validate example") + return fmt.Errorf("unable to validate example: %w", err) } exampleJSON, err = jsonx.MarshalPretty(event) if err != nil { - return errors.Wrap(err, "unable to marshal example") + return fmt.Errorf("unable to marshal example: %w", err) } output.WriteString(renderItemTitle(item)) @@ -277,24 +275,24 @@ func renderActionDoc(output *strings.Builder, item *TaggedItem, session flows.Se exampleJSON := []byte(strings.Join(item.examples, "\n")) action, err := actions.ReadAction(exampleJSON) if err != nil { - return errors.Wrap(err, "unable to read action") + return fmt.Errorf("unable to read action: %w", err) } // validate it err = utils.Validate(action) if err != nil { - return errors.Wrap(err, "unable to validate example") + return fmt.Errorf("unable to validate example: %w", err) } exampleJSON, err = jsonx.MarshalPretty(action) if err != nil { - return errors.Wrap(err, "unable to marshal example") + return fmt.Errorf("unable to marshal example: %w", err) } // get the events created by this action events, err := eventsForAction(action, session, voiceSession) if err != nil { - return errors.Wrap(err, "error running action") + return fmt.Errorf("error running action: %w", err) } output.WriteString(renderItemTitle(item)) @@ -323,18 +321,18 @@ func renderTriggerDoc(output *strings.Builder, item *TaggedItem, session flows.S exampleJSON := json.RawMessage(strings.Join(item.examples, "\n")) trigger, err := triggers.ReadTrigger(session.Assets(), exampleJSON, assets.PanicOnMissing) if err != nil { - return errors.Wrap(err, "unable to read trigger") + return fmt.Errorf("unable to read trigger: %w", err) } // validate it err = utils.Validate(trigger) if err != nil { - return errors.Wrap(err, "unable to validate example") + return fmt.Errorf("unable to validate example: %w", err) } exampleJSON, err = jsonx.MarshalPretty(trigger) if err != nil { - return errors.Wrap(err, "unable to marshal example") + return fmt.Errorf("unable to marshal example: %w", err) } output.WriteString(renderItemTitle(item)) @@ -353,17 +351,17 @@ func renderResumeDoc(output *strings.Builder, item *TaggedItem, session flows.Se exampleJSON := json.RawMessage(strings.Join(item.examples, "\n")) resume, err := resumes.ReadResume(session.Assets(), exampleJSON, assets.PanicOnMissing) if err != nil { - return errors.Wrap(err, "unable to read resume") + return fmt.Errorf("unable to read resume: %w", err) } // validate it if err := utils.Validate(resume); err != nil { - return errors.Wrap(err, "unable to validate example") + return fmt.Errorf("unable to validate example: %w", err) } exampleJSON, err = jsonx.MarshalPretty(resume) if err != nil { - return errors.Wrap(err, "unable to marshal example") + return fmt.Errorf("unable to marshal example: %w", err) } output.WriteString(renderItemTitle(item)) @@ -392,7 +390,7 @@ func renderItemTitle(item *TaggedItem) string { func checkExample(session flows.Session, line string) error { pieces := strings.Split(line, "→") if len(pieces) != 2 { - return errors.Errorf("unparseable example: %s", line) + return fmt.Errorf("unparseable example: %s", line) } example := strings.TrimSpace(pieces[0]) @@ -405,14 +403,14 @@ func checkExample(session flows.Session, line string) error { if expected == "ERROR" { if ok { - return errors.Errorf("expected example '%s' to error but it didn't", strconv.Quote(example)) + return fmt.Errorf("expected example '%s' to error but it didn't", strconv.Quote(example)) } } else { if !ok { - return errors.Errorf("unexpected error from example '%s': %s", strconv.Quote(example), log.Error()) + return fmt.Errorf("unexpected error from example '%s': %s", strconv.Quote(example), log.Error()) } if val != expected { - return errors.Errorf("expected %s from example: %s, but got %s", strconv.Quote(expected), strconv.Quote(example), strconv.Quote(val)) + return fmt.Errorf("expected %s from example: %s, but got %s", strconv.Quote(expected), strconv.Quote(example), strconv.Quote(val)) } } @@ -456,7 +454,7 @@ func eventsForAction(action flows.Action, msgSession flows.Session, voiceSession // action examples aren't supposed to generate error events - if they have, something went wrong if event.Type() == events.TypeError { errEvent := event.(*events.ErrorEvent) - return nil, errors.Errorf("error event generated: %s", errEvent.Text) + return nil, fmt.Errorf("error event generated: %s", errEvent.Text) } eventJSON[i], err = jsonx.MarshalPretty(event) diff --git a/cmd/flowrunner/main.go b/cmd/flowrunner/main.go index 684b6df9c..bcbdcc79f 100644 --- a/cmd/flowrunner/main.go +++ b/cmd/flowrunner/main.go @@ -3,6 +3,7 @@ package main import ( "bufio" "encoding/json" + "errors" "flag" "fmt" "io" @@ -28,7 +29,6 @@ import ( "github.com/nyaruka/goflow/services/classification/wit" "github.com/nyaruka/goflow/services/webhooks" "github.com/nyaruka/goflow/utils" - "github.com/pkg/errors" ) const contactJSON = `{ @@ -107,7 +107,7 @@ func createEngine(witToken string) flows.Engine { func RunFlow(eng flows.Engine, assetsPath string, flowUUID assets.FlowUUID, initialMsg string, contactLang i18n.Language, in io.Reader, out io.Writer) (*Repro, error) { assetsJSON, err := os.ReadFile(assetsPath) if err != nil { - return nil, errors.Wrapf(err, "error reading assets file '%s'", assetsPath) + return nil, fmt.Errorf("error reading assets file '%s': %w", assetsPath, err) } // if user didn't provide a flow UUID, look for the UUID of the first flow @@ -126,7 +126,7 @@ func RunFlow(eng flows.Engine, assetsPath string, flowUUID assets.FlowUUID, init sa, err := engine.NewSessionAssets(envs.NewBuilder().Build(), source, nil) if err != nil { - return nil, errors.Wrap(err, "error parsing assets") + return nil, fmt.Errorf("error parsing assets: %w", err) } flow, err := sa.Flows().Get(assets.FlowUUID(flowUUID)) diff --git a/cmd/flowxgettext/main.go b/cmd/flowxgettext/main.go index 92184d5c5..56ed9722d 100644 --- a/cmd/flowxgettext/main.go +++ b/cmd/flowxgettext/main.go @@ -13,7 +13,6 @@ import ( "github.com/nyaruka/goflow/flows/definition" "github.com/nyaruka/goflow/flows/definition/migrations" "github.com/nyaruka/goflow/flows/translation" - "github.com/pkg/errors" ) const usage = `usage: flowxgettext [flags] ...` @@ -65,7 +64,7 @@ func loadFlows(paths []string) ([]flows.Flow, error) { for _, path := range paths { fileJSON, err := os.ReadFile(path) if err != nil { - return nil, errors.Wrapf(err, "error reading flow file '%s'", path) + return nil, fmt.Errorf("error reading flow file '%s': %w", path, err) } var flowDefs []json.RawMessage @@ -84,7 +83,7 @@ func loadFlows(paths []string) ([]flows.Flow, error) { for _, flowDef := range flowDefs { flow, err := definition.ReadFlow(flowDef, &migrations.Config{BaseMediaURL: "http://temba.io"}) if err != nil { - return nil, errors.Wrapf(err, "error reading flow '%s'", path) + return nil, fmt.Errorf("error reading flow '%s': %w", path, err) } flows = append(flows, flow) } diff --git a/cmd/transferairtime/main.go b/cmd/transferairtime/main.go index f7787e54f..9ff593b1c 100644 --- a/cmd/transferairtime/main.go +++ b/cmd/transferairtime/main.go @@ -15,7 +15,6 @@ import ( "github.com/nyaruka/goflow/flows/engine" "github.com/nyaruka/goflow/flows/triggers" "github.com/nyaruka/goflow/services/airtime/dtone" - "github.com/pkg/errors" "github.com/shopspring/decimal" ) @@ -107,7 +106,7 @@ func transferAirtime(destination urns.URN, amount decimal.Decimal, currency stri sa, err := engine.NewSessionAssets(env, source, nil) if err != nil { - return errors.Wrap(err, "error parsing assets") + return fmt.Errorf("error parsing assets: %w", err) } eng := engine.NewBuilder().WithAirtimeServiceFactory(svcFactory).Build() diff --git a/contactql/error.go b/contactql/error.go index abf40571a..ecbe9b414 100644 --- a/contactql/error.go +++ b/contactql/error.go @@ -1,10 +1,10 @@ package contactql import ( + "errors" "fmt" "github.com/nyaruka/goflow/utils" - "github.com/pkg/errors" ) // error codes with values included in extra @@ -63,12 +63,8 @@ func (e *QueryError) Extra() map[string]string { // IsQueryError is a utility to determine if the cause of an error was a query error func IsQueryError(err error) (bool, error) { - switch cause := errors.Cause(err).(type) { - case *QueryError: - return true, cause - default: - return false, nil - } + var qErr *QueryError + return errors.As(err, &qErr), qErr } var _ utils.RichError = (*QueryError)(nil) diff --git a/contactql/error_test.go b/contactql/error_test.go index b03b17b82..58cb8e135 100644 --- a/contactql/error_test.go +++ b/contactql/error_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/nyaruka/goflow/contactql" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) @@ -14,12 +13,12 @@ func TestQueryError(t *testing.T) { assert.Equal(t, "Bad Query", e.Error()) - e1 := errors.Wrap(errors.Wrap(e, "wrapped once"), "wrapped twice") + e1 := fmt.Errorf("wrapped twice: %w", fmt.Errorf("wrapped once: %w", e)) isQueryError, cause := contactql.IsQueryError(e1) assert.True(t, isQueryError) assert.Equal(t, e, cause) - e2 := errors.Wrap(errors.Wrap(fmt.Errorf("not a query error"), "wrapped once"), "wrapped twice") + e2 := fmt.Errorf("wrapped twice: %w", fmt.Errorf("wrapped once: %w", fmt.Errorf("not a query error"))) isQueryError, cause = contactql.IsQueryError(e2) assert.False(t, isQueryError) assert.Nil(t, cause) diff --git a/contactql/es/sort.go b/contactql/es/sort.go index d23856e82..a3d10029c 100644 --- a/contactql/es/sort.go +++ b/contactql/es/sort.go @@ -7,7 +7,6 @@ import ( "github.com/nyaruka/gocommon/elastic" "github.com/nyaruka/goflow/assets" "github.com/nyaruka/goflow/contactql" - "github.com/pkg/errors" ) // ToElasticSort returns the elastic sort for the passed in sort by string @@ -40,7 +39,7 @@ func ToElasticSort(sortBy string, resolver contactql.Resolver) (elastic.Sort, er // we are sorting by a custom field field := resolver.ResolveField(property) if field == nil { - return nil, errors.Errorf("no such field with key: %s", property) + return nil, fmt.Errorf("no such field with key: %s", property) } var key string diff --git a/envs/dates.go b/envs/dates.go index f89e49649..d6091d59f 100644 --- a/envs/dates.go +++ b/envs/dates.go @@ -1,6 +1,7 @@ package envs import ( + "fmt" "math" "regexp" "strconv" @@ -10,7 +11,6 @@ import ( "github.com/go-playground/validator/v10" "github.com/nyaruka/gocommon/dates" "github.com/nyaruka/goflow/utils" - "github.com/pkg/errors" ) func init() { @@ -103,7 +103,7 @@ func dateFromFormats(currentYear int, pattern *regexp.Regexp, d int, m int, y in return dates.NewDate(year, month, day), remainder, nil } - return dates.ZeroDate, str, errors.Errorf("string '%s' couldn't be parsed as a date", str) + return dates.ZeroDate, str, fmt.Errorf("string '%s' couldn't be parsed as a date", str) } // DateTimeFromString returns a datetime constructed from the passed in string, or an error if we @@ -149,7 +149,7 @@ func DateFromString(env Environment, str string) (dates.Date, error) { func TimeFromString(str string) (dates.TimeOfDay, error) { hasTime, timeOfDay := parseTime(str) if !hasTime { - return dates.ZeroTimeOfDay, errors.Errorf("string '%s' couldn't be parsed as a time", str) + return dates.ZeroTimeOfDay, fmt.Errorf("string '%s' couldn't be parsed as a time", str) } return timeOfDay, nil } @@ -175,7 +175,7 @@ func parseDate(env Environment, str string) (dates.Date, string, error) { return dateFromFormats(currentYear, patternMonthDayYear, 2, 1, 3, str) } - return dates.ZeroDate, "", errors.Errorf("unknown date format: %s", env.DateFormat()) + return dates.ZeroDate, "", fmt.Errorf("unknown date format: %s", env.DateFormat()) } func parseTime(str string) (bool, dates.TimeOfDay) { diff --git a/excellent/base_test.go b/excellent/base_test.go index 94596efea..9909e9fb2 100644 --- a/excellent/base_test.go +++ b/excellent/base_test.go @@ -1,6 +1,7 @@ package excellent_test import ( + "fmt" "strings" "testing" @@ -9,7 +10,6 @@ import ( "github.com/nyaruka/goflow/excellent/functions" "github.com/nyaruka/goflow/excellent/types" "github.com/nyaruka/goflow/test" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) @@ -229,7 +229,7 @@ func TestEvaluateTemplate(t *testing.T) { "missing": nil, }), "func": functions.Lookup("upper"), - "err": types.NewXError(errors.Errorf("an error")), + "err": types.NewXError(fmt.Errorf("an error")), "object1": types.NewXObject(map[string]types.XValue{ "__default__": types.NewXText("123"), "foo": types.NewXNumberFromInt(123), diff --git a/excellent/errors.go b/excellent/errors.go index 1c66ad633..a5f6b20b2 100644 --- a/excellent/errors.go +++ b/excellent/errors.go @@ -5,7 +5,6 @@ import ( "strings" "github.com/antlr4-go/antlr/v4" - "github.com/pkg/errors" ) // TemplateError is an error which occurs during evaluation of an expression @@ -72,5 +71,5 @@ func (l *ErrorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol lineOfError := lines[line-1] contextOfError := lineOfError[column:min(column+10, len(lineOfError))] - l.errors = append(l.errors, errors.Errorf("syntax error at %s", contextOfError)) + l.errors = append(l.errors, fmt.Errorf("syntax error at %s", contextOfError)) } diff --git a/excellent/types/base_test.go b/excellent/types/base_test.go index e93c4fe4f..f666e8049 100644 --- a/excellent/types/base_test.go +++ b/excellent/types/base_test.go @@ -1,6 +1,7 @@ package types_test import ( + "fmt" "testing" "time" @@ -8,7 +9,6 @@ import ( "github.com/nyaruka/gocommon/jsonx" "github.com/nyaruka/goflow/envs" "github.com/nyaruka/goflow/excellent/types" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -152,7 +152,7 @@ func TestXValue(t *testing.T) { formatted: "1", asBool: true, }, { - value: types.NewXError(errors.Errorf("it failed")), // once an error, always an error + value: types.NewXError(fmt.Errorf("it failed")), // once an error, always an error marshaled: `null`, rendered: "", formatted: "", @@ -232,8 +232,8 @@ func TestEquals(t *testing.T) { false, // different value }, - {types.NewXError(errors.Errorf("Error")), types.NewXError(errors.Errorf("Error")), true}, - {types.NewXError(errors.Errorf("Error")), types.XDateTimeZero, false}, + {types.NewXError(fmt.Errorf("Error")), types.NewXError(fmt.Errorf("Error")), true}, + {types.NewXError(fmt.Errorf("Error")), types.XDateTimeZero, false}, {types.NewXText("bob"), types.NewXText("bob"), true}, {types.NewXText("bob"), types.NewXText("abc"), false}, diff --git a/excellent/types/date_test.go b/excellent/types/date_test.go index 7b12459dc..281736445 100644 --- a/excellent/types/date_test.go +++ b/excellent/types/date_test.go @@ -1,13 +1,13 @@ package types_test import ( + "fmt" "testing" "time" "github.com/nyaruka/gocommon/dates" "github.com/nyaruka/goflow/envs" "github.com/nyaruka/goflow/excellent/types" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) @@ -54,7 +54,7 @@ func TestToXDate(t *testing.T) { hasError bool }{ {nil, types.XDateZero, true}, - {types.NewXError(errors.Errorf("Error")), types.XDateZero, true}, + {types.NewXError(fmt.Errorf("Error")), types.XDateZero, true}, {types.NewXNumberFromInt(123), types.XDateZero, true}, {types.NewXText("2018-01-20"), types.NewXDate(dates.NewDate(2018, 1, 20)), false}, {types.NewXDate(dates.NewDate(2018, 4, 19)), types.NewXDate(dates.NewDate(2018, 4, 19)), false}, diff --git a/excellent/types/datetime_test.go b/excellent/types/datetime_test.go index a8e048f7f..fc581a432 100644 --- a/excellent/types/datetime_test.go +++ b/excellent/types/datetime_test.go @@ -1,6 +1,7 @@ package types_test import ( + "fmt" "testing" "time" @@ -8,7 +9,6 @@ import ( "github.com/nyaruka/gocommon/jsonx" "github.com/nyaruka/goflow/envs" "github.com/nyaruka/goflow/excellent/types" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) @@ -82,7 +82,7 @@ func TestToXDateTime(t *testing.T) { hasError bool }{ {nil, types.XDateTimeZero, true}, - {types.NewXError(errors.Errorf("Error")), types.XDateTimeZero, true}, + {types.NewXError(fmt.Errorf("Error")), types.XDateTimeZero, true}, {types.NewXNumberFromInt(123), types.XDateTimeZero, true}, {types.NewXText("2018-06-05"), types.NewXDateTime(time.Date(2018, 6, 5, 0, 0, 0, 0, time.UTC)), false}, {types.NewXText("wha?"), types.XDateTimeZero, true}, diff --git a/excellent/types/error_test.go b/excellent/types/error_test.go index bbb30dc3b..453219ebe 100644 --- a/excellent/types/error_test.go +++ b/excellent/types/error_test.go @@ -1,19 +1,19 @@ package types_test import ( + "fmt" "testing" "github.com/nyaruka/gocommon/jsonx" "github.com/nyaruka/goflow/envs" "github.com/nyaruka/goflow/excellent/types" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) func TestXError(t *testing.T) { env := envs.NewBuilder().Build() - err1 := types.NewXError(errors.Errorf("I failed")) + err1 := types.NewXError(fmt.Errorf("I failed")) assert.Equal(t, "error", err1.Describe()) assert.Equal(t, "I failed", err1.Render()) assert.Equal(t, "", err1.Format(env)) diff --git a/excellent/types/json.go b/excellent/types/json.go index 7d1dd596e..0de80d089 100644 --- a/excellent/types/json.go +++ b/excellent/types/json.go @@ -6,7 +6,6 @@ import ( "github.com/buger/jsonparser" "github.com/nyaruka/gocommon/jsonx" - "github.com/pkg/errors" "github.com/shopspring/decimal" ) @@ -53,7 +52,7 @@ func jsonTypeToXValue(data []byte, valType jsonparser.ValueType) XValue { return jsonToObject(data) } - return NewXError(errors.Errorf("unknown JSON parsing error")) + return NewXError(fmt.Errorf("unknown JSON parsing error")) } func jsonToObject(data []byte) *XObject { diff --git a/excellent/types/number.go b/excellent/types/number.go index 829ad9097..daf7ed81d 100644 --- a/excellent/types/number.go +++ b/excellent/types/number.go @@ -1,13 +1,14 @@ package types import ( + "errors" + "fmt" "math" "regexp" "strings" "github.com/nyaruka/gocommon/jsonx" "github.com/nyaruka/goflow/envs" - "github.com/pkg/errors" "github.com/shopspring/decimal" ) @@ -51,7 +52,7 @@ func NewXNumberFromInt64(value int64) *XNumber { func RequireXNumberFromString(value string) *XNumber { num, err := newXNumberFromString(value) if err != nil { - panic(errors.Wrapf(err, "error parsing '%s' as number", value)) + panic(fmt.Errorf("error parsing '%s' as number: %w", value, err)) } return num } diff --git a/excellent/types/object.go b/excellent/types/object.go index a3dc4ddb5..ca2055c84 100644 --- a/excellent/types/object.go +++ b/excellent/types/object.go @@ -2,6 +2,7 @@ package types import ( "encoding/json" + "errors" "fmt" "sort" "strings" @@ -9,7 +10,6 @@ import ( "github.com/nyaruka/gocommon/jsonx" "github.com/nyaruka/goflow/envs" "github.com/nyaruka/goflow/utils" - "github.com/pkg/errors" ) const serializeDefaultAs = "__default__" diff --git a/excellent/types/time_test.go b/excellent/types/time_test.go index e44f5587b..ee58257b6 100644 --- a/excellent/types/time_test.go +++ b/excellent/types/time_test.go @@ -1,6 +1,7 @@ package types_test import ( + "fmt" "testing" "time" @@ -8,7 +9,6 @@ import ( "github.com/nyaruka/gocommon/jsonx" "github.com/nyaruka/goflow/envs" "github.com/nyaruka/goflow/excellent/types" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) @@ -50,7 +50,7 @@ func TestToXTime(t *testing.T) { hasError bool }{ {nil, types.XTimeZero, true}, - {types.NewXError(errors.Errorf("Error")), types.XTimeZero, true}, + {types.NewXError(fmt.Errorf("Error")), types.XTimeZero, true}, {types.NewXNumberFromInt(123), types.XTimeZero, true}, {types.NewXNumberFromInt(23), types.NewXTime(dates.NewTimeOfDay(23, 0, 0, 0)), false}, {types.NewXNumberFromInt(24), types.XTimeZero, false}, diff --git a/flows/actions/base.go b/flows/actions/base.go index 6b807b152..fb6e8b107 100644 --- a/flows/actions/base.go +++ b/flows/actions/base.go @@ -17,7 +17,6 @@ import ( "github.com/nyaruka/goflow/flows/events" "github.com/nyaruka/goflow/flows/modifiers" "github.com/nyaruka/goflow/utils" - "github.com/pkg/errors" ) // max number of bytes to be saved to extra on a result @@ -385,7 +384,7 @@ func ReadAction(data json.RawMessage) (flows.Action, error) { f := registeredTypes[typeName] if f == nil { - return nil, errors.Errorf("unknown type: '%s'", typeName) + return nil, fmt.Errorf("unknown type: '%s'", typeName) } action := f() diff --git a/flows/actions/base_test.go b/flows/actions/base_test.go index 33b1c73bd..2dafca7a0 100644 --- a/flows/actions/base_test.go +++ b/flows/actions/base_test.go @@ -2,6 +2,7 @@ package actions_test import ( "encoding/json" + "errors" "fmt" "net/http" "os" @@ -31,7 +32,6 @@ import ( "github.com/nyaruka/goflow/test" "github.com/nyaruka/goflow/utils" "github.com/nyaruka/goflow/utils/smtpx" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -155,7 +155,7 @@ func testActionType(t *testing.T, assetsJSON json.RawMessage, typeName string) { // now try to read the flow, and if we expect a read error, check that flow, err := sa.Flows().Get(flowUUID) if tc.ReadError != "" { - rootErr := errors.Cause(err) + rootErr := test.RootError(err) assert.EqualError(t, rootErr, tc.ReadError, "read error mismatch in %s", testName) continue } else { @@ -227,7 +227,7 @@ func testActionType(t *testing.T, assetsJSON json.RawMessage, typeName string) { if c.Type() == "wit" { return wit.NewService(http.DefaultClient, nil, c, "123456789"), nil } - return nil, errors.Errorf("no classification service available for %s", c.Reference()) + return nil, fmt.Errorf("no classification service available for %s", c.Reference()) }). WithAirtimeServiceFactory(func(flows.SessionAssets) (flows.AirtimeService, error) { return dtone.NewService(http.DefaultClient, nil, "nyaruka", "123456789"), nil diff --git a/flows/actions/call_resthook.go b/flows/actions/call_resthook.go index 52527c5ab..c1f59c67c 100644 --- a/flows/actions/call_resthook.go +++ b/flows/actions/call_resthook.go @@ -2,13 +2,12 @@ package actions import ( "encoding/json" + "fmt" "net/http" "strings" "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/flows/events" - - "github.com/pkg/errors" ) // ResthookPayload is the POST payload used by resthooks @@ -93,7 +92,7 @@ func (a *CallResthookAction) Execute(run flows.Run, step flows.Step, logModifier // check the payload is valid JSON - it ends up in the session so needs to be valid if !json.Valid([]byte(payload)) { - return errors.Errorf("resthook payload evaluation produced invalid JSON: %s", payload) + return fmt.Errorf("resthook payload evaluation produced invalid JSON: %s", payload) } // regardless of what subscriber calls we make, we need to record the payload that would be sent diff --git a/flows/actions/call_webhook.go b/flows/actions/call_webhook.go index 89de8be77..ca35d9294 100644 --- a/flows/actions/call_webhook.go +++ b/flows/actions/call_webhook.go @@ -1,6 +1,7 @@ package actions import ( + "fmt" "net/http" "net/url" "strings" @@ -9,7 +10,6 @@ import ( "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/flows/events" - "github.com/pkg/errors" "golang.org/x/net/http/httpguts" ) @@ -75,7 +75,7 @@ func NewCallWebhook(uuid flows.ActionUUID, method string, url string, headers ma func (a *CallWebhookAction) Validate() error { for key := range a.Headers { if !httpguts.ValidHeaderFieldName(key) { - return errors.Errorf("header '%s' is not a valid HTTP header", key) + return fmt.Errorf("header '%s' is not a valid HTTP header", key) } } diff --git a/flows/actions/enter_flow.go b/flows/actions/enter_flow.go index d260fa389..f0743be36 100644 --- a/flows/actions/enter_flow.go +++ b/flows/actions/enter_flow.go @@ -1,11 +1,11 @@ package actions import ( + "fmt" + "github.com/nyaruka/goflow/assets" "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/flows/events" - - "github.com/pkg/errors" ) func init() { @@ -55,7 +55,7 @@ func (a *EnterFlowAction) Execute(run flows.Run, step flows.Step, logModifier fl } if run.Session().Type() != flow.Type() { - a.fail(run, errors.Errorf("can't enter %s of type %s from type %s", flow.Reference(false), flow.Type(), run.Session().Type()), logEvent) + a.fail(run, fmt.Errorf("can't enter %s of type %s from type %s", flow.Reference(false), flow.Type(), run.Session().Type()), logEvent) return nil } diff --git a/flows/actions/remove_contact_groups.go b/flows/actions/remove_contact_groups.go index 9ed50b787..3756e4da3 100644 --- a/flows/actions/remove_contact_groups.go +++ b/flows/actions/remove_contact_groups.go @@ -1,11 +1,11 @@ package actions import ( + "fmt" + "github.com/nyaruka/goflow/assets" "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/flows/modifiers" - - "github.com/pkg/errors" ) func init() { @@ -49,7 +49,7 @@ func NewRemoveContactGroups(uuid flows.ActionUUID, groups []*assets.GroupReferen // Validate validates our action is valid func (a *RemoveContactGroupsAction) Validate() error { if a.AllGroups && len(a.Groups) > 0 { - return errors.Errorf("can't specify specific groups when all_groups=true") + return fmt.Errorf("can't specify specific groups when all_groups=true") } return nil } diff --git a/flows/actions/send_email.go b/flows/actions/send_email.go index b59380913..b69ce5765 100644 --- a/flows/actions/send_email.go +++ b/flows/actions/send_email.go @@ -1,13 +1,13 @@ package actions import ( + "fmt" "regexp" "strings" "github.com/nyaruka/gocommon/uuids" "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/flows/events" - "github.com/pkg/errors" ) func init() { @@ -99,7 +99,7 @@ func (a *SendEmailAction) Execute(run flows.Run, step flows.Step, logModifier fl err = svc.Send(evaluatedAddresses, evaluatedSubject, evaluatedBody) if err != nil { - logEvent(events.NewError(errors.Wrap(err, "unable to send email"))) + logEvent(events.NewError(fmt.Errorf("unable to send email: %w", err))) } else { logEvent(events.NewEmailSent(evaluatedAddresses, evaluatedSubject, evaluatedBody)) } diff --git a/flows/actions/transfer_airtime.go b/flows/actions/transfer_airtime.go index be3a41c82..d6e21ce6f 100644 --- a/flows/actions/transfer_airtime.go +++ b/flows/actions/transfer_airtime.go @@ -1,11 +1,11 @@ package actions import ( + "errors" + "github.com/nyaruka/gocommon/urns" "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/flows/events" - "github.com/pkg/errors" - "github.com/shopspring/decimal" ) diff --git a/flows/contact.go b/flows/contact.go index 4a68ebeb7..bc7c272c4 100644 --- a/flows/contact.go +++ b/flows/contact.go @@ -17,7 +17,6 @@ import ( "github.com/nyaruka/goflow/envs" "github.com/nyaruka/goflow/excellent/types" "github.com/nyaruka/goflow/utils" - "github.com/pkg/errors" "github.com/shopspring/decimal" ) @@ -602,7 +601,7 @@ func ReadContact(sa SessionAssets, data json.RawMessage, missing assets.MissingC var err error if err := utils.UnmarshalAndValidate(data, &envelope); err != nil { - return nil, errors.Wrap(err, "unable to read contact") + return nil, fmt.Errorf("unable to read contact: %w", err) } c := &Contact{ @@ -631,7 +630,7 @@ func ReadContact(sa SessionAssets, data json.RawMessage, missing assets.MissingC c.urns = make(URNList, 0) } else { if c.urns, err = ReadURNList(sa, envelope.URNs, missing); err != nil { - return nil, errors.Wrap(err, "error reading urns") + return nil, fmt.Errorf("error reading urns: %w", err) } } @@ -641,7 +640,7 @@ func ReadContact(sa SessionAssets, data json.RawMessage, missing assets.MissingC if envelope.Ticket != nil { c.ticket, err = ReadTicket(sa, envelope.Ticket, missing) if err != nil { - return nil, errors.Wrap(err, "unable to read ticket") + return nil, fmt.Errorf("unable to read ticket: %w", err) } } diff --git a/flows/definition/exit.go b/flows/definition/exit.go index 2f3585e91..bde81246b 100644 --- a/flows/definition/exit.go +++ b/flows/definition/exit.go @@ -1,12 +1,12 @@ package definition import ( + "fmt" + "github.com/nyaruka/gocommon/jsonx" "github.com/nyaruka/gocommon/uuids" "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/utils" - - "github.com/pkg/errors" ) type exit struct { @@ -39,7 +39,7 @@ func (e *exit) UnmarshalJSON(data []byte) error { envelope := &exitEnvelope{} if err := utils.UnmarshalAndValidate(data, envelope); err != nil { - return errors.Wrap(err, "unable to read exit") + return fmt.Errorf("unable to read exit: %w", err) } e.uuid = envelope.UUID diff --git a/flows/definition/flow.go b/flows/definition/flow.go index 7814c81c5..9c228e801 100644 --- a/flows/definition/flow.go +++ b/flows/definition/flow.go @@ -2,6 +2,7 @@ package definition import ( "encoding/json" + "fmt" "github.com/Masterminds/semver" "github.com/nyaruka/gocommon/i18n" @@ -15,7 +16,6 @@ import ( "github.com/nyaruka/goflow/flows/inspect" "github.com/nyaruka/goflow/flows/inspect/issues" "github.com/nyaruka/goflow/utils" - "github.com/pkg/errors" ) // CurrentSpecVersion is the flow spec version supported by this library @@ -96,12 +96,12 @@ func (f *flow) validate() error { for _, node := range f.nodes { uuidAlreadySeen := seenUUIDs[uuids.UUID(node.UUID())] if uuidAlreadySeen { - return errors.Errorf("node UUID %s isn't unique", node.UUID()) + return fmt.Errorf("node UUID %s isn't unique", node.UUID()) } seenUUIDs[uuids.UUID(node.UUID())] = true if err := node.Validate(f, seenUUIDs); err != nil { - return errors.Wrapf(err, "invalid node[uuid=%s]", node.UUID()) + return fmt.Errorf("invalid node[uuid=%s]: %w", node.UUID(), err) } } @@ -345,7 +345,7 @@ func readFlow(data json.RawMessage, mc *migrations.Config, a assets.Flow) (flows jsonx.Unmarshal(data, header) if !IsVersionSupported(header.SpecVersion) { - return nil, errors.Errorf("spec version %s is newer than this library (%s)", header.SpecVersion, CurrentSpecVersion) + return nil, fmt.Errorf("spec version %s is newer than this library (%s)", header.SpecVersion, CurrentSpecVersion) } e := &flowEnvelope{} diff --git a/flows/definition/legacy/definition.go b/flows/definition/legacy/definition.go index 2d727c792..4f87d52c3 100644 --- a/flows/definition/legacy/definition.go +++ b/flows/definition/legacy/definition.go @@ -8,6 +8,7 @@ import ( "strconv" "strings" + "github.com/buger/jsonparser" "github.com/nyaruka/gocommon/i18n" "github.com/nyaruka/gocommon/jsonx" "github.com/nyaruka/gocommon/urns" @@ -16,9 +17,6 @@ import ( "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/flows/definition/legacy/expressions" "github.com/nyaruka/goflow/utils" - - "github.com/buger/jsonparser" - "github.com/pkg/errors" "github.com/shopspring/decimal" ) @@ -507,7 +505,7 @@ func migrateAction(baseLanguage i18n.Language, a Action, localization migratedLo return newPlayAudioAction(a.UUID, migratedAudioURL), nil default: - return nil, errors.Errorf("unable to migrate legacy action type: %s", a.Type) + return nil, fmt.Errorf("unable to migrate legacy action type: %s", a.Type) } } @@ -706,7 +704,7 @@ func migrateRuleSet(lang i18n.Language, r RuleSet, validDests map[uuids.UUID]boo // check if we already have a configuration for this currency existingAmount, alreadyDefined := currencyAmounts[countryCfg.CurrencyCode] if alreadyDefined && existingAmount != countryCfg.Amount { - return nil, "", nil, errors.Errorf("unable to migrate airtime ruleset with different amounts in same currency") + return nil, "", nil, fmt.Errorf("unable to migrate airtime ruleset with different amounts in same currency") } currencyAmounts[countryCfg.CurrencyCode] = countryCfg.Amount @@ -721,7 +719,7 @@ func migrateRuleSet(lang i18n.Language, r RuleSet, validDests map[uuids.UUID]boo uiType = UINodeTypeSplitByAirtime default: - return nil, "", nil, errors.Errorf("unrecognized ruleset type: %s", r.Type) + return nil, "", nil, fmt.Errorf("unrecognized ruleset type: %s", r.Type) } return newNode(r.UUID, newActions, router, exits), uiType, uiConfig, nil @@ -967,7 +965,7 @@ func migrateRule(baseLanguage i18n.Language, r Rule, category migratedCategory, arguments = []string{migratedDistrict, migratedState} default: - return nil, nil, errors.Errorf("migration of '%s' tests not supported", r.Test.Type) + return nil, nil, fmt.Errorf("migration of '%s' tests not supported", r.Test.Type) } return newCase(caseUUID, newType, arguments, category.UUID()), caseUI, err @@ -981,7 +979,7 @@ func migrateActionSet(lang i18n.Language, a ActionSet, validDests map[uuids.UUID for i := range a.Actions { action, err := migrateAction(lang, a.Actions[i], localization, baseMediaURL) if err != nil { - return nil, errors.Wrapf(err, "error migrating action[type=%s]", a.Actions[i].Type) + return nil, fmt.Errorf("error migrating action[type=%s]: %w", a.Actions[i].Type, err) } actions[i] = action } @@ -1028,7 +1026,7 @@ func migrateNodes(f *Flow, baseMediaURL string) ([]migratedNode, map[uuids.UUID] for i, actionSet := range f.ActionSets { node, err := migrateActionSet(f.BaseLanguage, actionSet, validDestinations, localization, baseMediaURL) if err != nil { - return nil, nil, nil, errors.Wrapf(err, "error migrating action_set[uuid=%s]", actionSet.UUID) + return nil, nil, nil, fmt.Errorf("error migrating action_set[uuid=%s]: %w", actionSet.UUID, err) } nodes[i] = node nodeUIs[node.UUID()] = NewNodeUI(UINodeTypeActionSet, actionSet.X, actionSet.Y, nil) @@ -1037,7 +1035,7 @@ func migrateNodes(f *Flow, baseMediaURL string) ([]migratedNode, map[uuids.UUID] for i, ruleSet := range f.RuleSets { node, uiType, uiNodeConfig, err := migrateRuleSet(f.BaseLanguage, ruleSet, validDestinations, localization) if err != nil { - return nil, nil, nil, errors.Wrapf(err, "error migrating rule_set[uuid=%s]", ruleSet.UUID) + return nil, nil, nil, fmt.Errorf("error migrating rule_set[uuid=%s]: %w", ruleSet.UUID, err) } nodes[len(f.ActionSets)+i] = node nodeUIs[node.UUID()] = NewNodeUI(uiType, ruleSet.X, ruleSet.Y, uiNodeConfig) @@ -1132,7 +1130,7 @@ func IsPossibleDefinition(data json.RawMessage) bool { func MigrateDefinition(data json.RawMessage, baseMediaURL string) (json.RawMessage, error) { legacyFlow, err := readLegacyFlow(data) if err != nil { - return nil, errors.Wrap(err, "unable to read legacy flow") + return nil, fmt.Errorf("unable to read legacy flow: %w", err) } return legacyFlow.Migrate(baseMediaURL) diff --git a/flows/definition/legacy/expressions/functions.go b/flows/definition/legacy/expressions/functions.go index beb4e2304..306acdfd5 100644 --- a/flows/definition/legacy/expressions/functions.go +++ b/flows/definition/legacy/expressions/functions.go @@ -4,8 +4,6 @@ import ( "fmt" "strconv" "strings" - - "github.com/pkg/errors" ) // migrates a parameter value in an legacy expression @@ -33,7 +31,7 @@ func asTemplate(template string) callMigrator { return func(funcName string, params []string) (string, error) { numParamPlaceholders := strings.Count(template, "%s") + strings.Count(template, "%v") if numParamPlaceholders > len(params) { - return "", errors.Errorf("expecting %d params whilst migrating call to %s but got %d", numParamPlaceholders, funcName, len(params)) + return "", fmt.Errorf("expecting %d params whilst migrating call to %s but got %d", numParamPlaceholders, funcName, len(params)) } paramsAsInterfaces := make([]any, len(params)) @@ -61,7 +59,7 @@ func asParamMigrators(newName string, paramMigrators ...paramMigrator) callMigra func asParamMigratorsWithDefaults(newName string, defaults []string, paramMigrators ...paramMigrator) callMigrator { return func(funcName string, oldParams []string) (string, error) { if len(oldParams) > len(paramMigrators) { - return "", errors.Errorf("don't know how to migrate call to %s with %d parameters", funcName, len(oldParams)) + return "", fmt.Errorf("don't know how to migrate call to %s with %d parameters", funcName, len(oldParams)) } newParams := make([]string, max(len(oldParams), len(defaults))) diff --git a/flows/definition/legacy/utils.go b/flows/definition/legacy/utils.go index b382dd407..fb3af19b2 100644 --- a/flows/definition/legacy/utils.go +++ b/flows/definition/legacy/utils.go @@ -8,7 +8,6 @@ import ( "github.com/nyaruka/gocommon/i18n" "github.com/nyaruka/gocommon/jsonx" "github.com/nyaruka/goflow/utils" - "github.com/pkg/errors" ) // Translations is an inline translation map used for localization @@ -86,7 +85,7 @@ func (s *StringOrNumber) UnmarshalJSON(data []byte) error { // data is JSON number *s = StringOrNumber(data) } else { - return errors.Errorf("expected string or number, not %s", string(c)) + return fmt.Errorf("expected string or number, not %s", string(c)) } return nil } diff --git a/flows/definition/migrations/base.go b/flows/definition/migrations/base.go index 68acd9540..2ea843043 100644 --- a/flows/definition/migrations/base.go +++ b/flows/definition/migrations/base.go @@ -5,14 +5,12 @@ import ( "sort" "strings" + "github.com/Masterminds/semver" "github.com/nyaruka/gocommon/jsonx" "github.com/nyaruka/gocommon/uuids" "github.com/nyaruka/goflow/assets" "github.com/nyaruka/goflow/flows/definition/legacy" "github.com/nyaruka/goflow/utils" - - "github.com/Masterminds/semver" - "github.com/pkg/errors" ) // MigrationFunc is a function that can migrate a flow definition from one version to another @@ -62,7 +60,7 @@ func MigrateToVersion(data []byte, to *semver.Version, cfg *Config) ([]byte, err var err error data, err = legacy.MigrateDefinition(data, cfg.BaseMediaURL) if err != nil { - return nil, errors.Wrap(err, "error migrating what appears to be a legacy definition") + return nil, fmt.Errorf("error migrating what appears to be a legacy definition: %w", err) } } @@ -71,7 +69,7 @@ func MigrateToVersion(data []byte, to *semver.Version, cfg *Config) ([]byte, err } if err != nil { - return nil, errors.Wrap(err, "unable to read flow header") + return nil, fmt.Errorf("unable to read flow header: %w", err) } return migrate(data, header.SpecVersion, to, cfg) @@ -103,7 +101,7 @@ func migrate(data []byte, from *semver.Version, to *semver.Version, cfg *Config) flow, err = registered[version](flow, cfg) if err != nil { - return nil, errors.Wrapf(err, "unable to migrate to version %s", version.String()) + return nil, fmt.Errorf("unable to migrate to version %s: %w", version.String(), err) } flow["spec_version"] = version.String() diff --git a/flows/definition/migrations/primitives.go b/flows/definition/migrations/primitives.go index d6cd32d63..84093a9f1 100644 --- a/flows/definition/migrations/primitives.go +++ b/flows/definition/migrations/primitives.go @@ -1,10 +1,11 @@ package migrations import ( + "errors" + "github.com/nyaruka/gocommon/i18n" "github.com/nyaruka/gocommon/jsonx" "github.com/nyaruka/gocommon/uuids" - "github.com/pkg/errors" ) // Flow holds a flow definition diff --git a/flows/definition/node.go b/flows/definition/node.go index 6c6a82c96..d5b653bb2 100644 --- a/flows/definition/node.go +++ b/flows/definition/node.go @@ -2,6 +2,7 @@ package definition import ( "encoding/json" + "fmt" "github.com/nyaruka/gocommon/i18n" "github.com/nyaruka/gocommon/jsonx" @@ -12,7 +13,6 @@ import ( "github.com/nyaruka/goflow/flows/inspect" "github.com/nyaruka/goflow/flows/routers" "github.com/nyaruka/goflow/utils" - "github.com/pkg/errors" ) type node struct { @@ -43,24 +43,24 @@ func (n *node) Validate(flow flows.Flow, seenUUIDs map[uuids.UUID]bool) error { // check that this action is valid for this flow type if !flow.Type().Allows(action) { - return errors.Errorf("action type '%s' is not allowed in a flow of type '%s'", action.Type(), flow.Type()) + return fmt.Errorf("action type '%s' is not allowed in a flow of type '%s'", action.Type(), flow.Type()) } uuidAlreadySeen := seenUUIDs[uuids.UUID(action.UUID())] if uuidAlreadySeen { - return errors.Errorf("action UUID %s isn't unique", action.UUID()) + return fmt.Errorf("action UUID %s isn't unique", action.UUID()) } seenUUIDs[uuids.UUID(action.UUID())] = true if err := action.Validate(); err != nil { - return errors.Wrapf(err, "invalid action[uuid=%s, type=%s]", action.UUID(), action.Type()) + return fmt.Errorf("invalid action[uuid=%s, type=%s]: %w", action.UUID(), action.Type(), err) } } // check the router if there is one if n.Router() != nil { if err := n.Router().Validate(flow, n.Exits()); err != nil { - return errors.Wrap(err, "invalid router") + return fmt.Errorf("invalid router: %w", err) } } @@ -68,12 +68,12 @@ func (n *node) Validate(flow flows.Flow, seenUUIDs map[uuids.UUID]bool) error { for _, exit := range n.Exits() { uuidAlreadySeen := seenUUIDs[uuids.UUID(exit.UUID())] if uuidAlreadySeen { - return errors.Errorf("exit UUID %s isn't unique", exit.UUID()) + return fmt.Errorf("exit UUID %s isn't unique", exit.UUID()) } seenUUIDs[uuids.UUID(exit.UUID())] = true if exit.DestinationUUID() != "" && flow.GetNode(exit.DestinationUUID()) == nil { - return errors.Errorf("destination %s of exit[uuid=%s] isn't a known node", exit.DestinationUUID(), exit.UUID()) + return fmt.Errorf("destination %s of exit[uuid=%s] isn't a known node", exit.DestinationUUID(), exit.UUID()) } } @@ -152,7 +152,7 @@ func (n *node) UnmarshalJSON(data []byte) error { e := &nodeEnvelope{} err := utils.UnmarshalAndValidate(data, e) if err != nil { - return errors.Wrap(err, "unable to read node") + return fmt.Errorf("unable to read node: %w", err) } n.uuid = e.UUID @@ -161,7 +161,7 @@ func (n *node) UnmarshalJSON(data []byte) error { if e.Router != nil { n.router, err = routers.ReadRouter(e.Router) if err != nil { - return errors.Wrap(err, "unable to read router") + return fmt.Errorf("unable to read router: %w", err) } } @@ -170,7 +170,7 @@ func (n *node) UnmarshalJSON(data []byte) error { for i := range e.Actions { n.actions[i], err = actions.ReadAction(e.Actions[i]) if err != nil { - return errors.Wrap(err, "unable to read action") + return fmt.Errorf("unable to read action: %w", err) } } diff --git a/flows/engine/assets_test.go b/flows/engine/assets_test.go index 77260060e..8413e252f 100644 --- a/flows/engine/assets_test.go +++ b/flows/engine/assets_test.go @@ -9,7 +9,6 @@ import ( "github.com/nyaruka/goflow/envs" "github.com/nyaruka/goflow/flows/engine" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -153,7 +152,7 @@ type testSource struct { func (s *testSource) err(t string) error { if t == s.currentErrType { - return errors.Errorf("unable to load %s assets", t) + return fmt.Errorf("unable to load %s assets", t) } return nil } diff --git a/flows/engine/services.go b/flows/engine/services.go index 10b604524..36d426b97 100644 --- a/flows/engine/services.go +++ b/flows/engine/services.go @@ -1,9 +1,9 @@ package engine import ( - "github.com/nyaruka/goflow/flows" + "errors" - "github.com/pkg/errors" + "github.com/nyaruka/goflow/flows" ) // EmailServiceFactory resolves a session to a email service diff --git a/flows/engine/session.go b/flows/engine/session.go index e358cf09d..7d07a392c 100644 --- a/flows/engine/session.go +++ b/flows/engine/session.go @@ -2,6 +2,7 @@ package engine import ( "encoding/json" + "errors" "fmt" "strings" @@ -16,8 +17,6 @@ import ( "github.com/nyaruka/goflow/flows/runs" "github.com/nyaruka/goflow/flows/triggers" "github.com/nyaruka/goflow/utils" - - "github.com/pkg/errors" ) // used to spawn a new run or sub-flow in the event loop @@ -88,7 +87,7 @@ func (s *session) GetRun(uuid flows.RunUUID) (flows.Run, error) { if exists { return run, nil } - return nil, errors.Errorf("unable to find run with UUID '%s'", uuid) + return nil, fmt.Errorf("unable to find run with UUID '%s'", uuid) } func (s *session) FindStep(uuid flows.StepUUID) (flows.Run, flows.Step) { @@ -222,7 +221,7 @@ func (s *session) prepareForSprint() error { if hasRun { r, err := runs.ReadRunSummary(s.Assets(), triggerWithRun.RunSummary(), assets.IgnoreMissing) if err != nil { - return errors.Wrap(err, "error reading parent run from trigger") + return fmt.Errorf("error reading parent run from trigger: %w", err) } s.parentRun = r } @@ -234,7 +233,7 @@ func (s *session) prepareForSprint() error { func (s *session) tryToResume(sprint *sprint, waitingRun flows.Run, resume flows.Resume) error { failSession := func(msg string, args ...any) { // put failure event in waiting run - failRun(sprint, waitingRun, nil, errors.Errorf(msg, args...)) + failRun(sprint, waitingRun, nil, fmt.Errorf(msg, args...)) // but also fail any other non-exited runs for _, r := range s.runs { @@ -389,13 +388,13 @@ func (s *session) continueUntilWait(sprint *sprint, currentRun flows.Run, node f failRun(sprint, currentRun, nil, errors.New("can't resume run with missing flow asset")) } else { if exit, operand, err = s.findResumeExit(sprint, currentRun, false); err != nil { - failRun(sprint, currentRun, nil, errors.Wrapf(err, "can't resume run as node no longer exists")) + failRun(sprint, currentRun, nil, fmt.Errorf("can't resume run as node no longer exists: %w", err)) } } } else { // if we did fail then that needs to bubble back up through the run hierarchy step, _, _ := currentRun.PathLocation() - failRun(sprint, currentRun, step, errors.Errorf("child run for flow '%s' ended in error, ending execution", childRun.FlowReference().UUID)) + failRun(sprint, currentRun, step, fmt.Errorf("child run for flow '%s' ended in error, ending execution", childRun.FlowReference().UUID)) } } else { @@ -417,11 +416,11 @@ func (s *session) continueUntilWait(sprint *sprint, currentRun flows.Run, node f if numNewSteps > s.engine.Options().MaxStepsPerSprint { // we've hit the step limit - usually a sign of a loop - failRun(sprint, currentRun, step, errors.Errorf("reached maximum number of steps per sprint (%d)", s.engine.Options().MaxStepsPerSprint)) + failRun(sprint, currentRun, step, fmt.Errorf("reached maximum number of steps per sprint (%d)", s.engine.Options().MaxStepsPerSprint)) } else { node = currentRun.Flow().GetNode(destination) if node == nil { - return errors.Errorf("unable to find destination node %s in flow %s", destination, currentRun.Flow().UUID()) + return fmt.Errorf("unable to find destination node %s in flow %s", destination, currentRun.Flow().UUID()) } step, exit, operand, err = s.visitNode(sprint, currentRun, node, trigger) @@ -460,7 +459,7 @@ func (s *session) visitNode(sprint *sprint, run flows.Run, node flows.Node, trig if node.Actions() != nil { for _, action := range node.Actions() { if err := action.Execute(run, step, sprint.logModifier, logEvent); err != nil { - return step, nil, "", errors.Wrapf(err, "error executing action[type=%s,uuid=%s]", action.Type(), action.UUID()) + return step, nil, "", fmt.Errorf("error executing action[type=%s,uuid=%s]: %w", action.Type(), action.UUID(), err) } // check if this action has errored the run @@ -512,11 +511,11 @@ func (s *session) pickNodeExit(sprint *sprint, run flows.Run, node flows.Node, s } if err != nil { - return nil, "", errors.Wrapf(err, "error routing from node[uuid=%s]", node.UUID()) + return nil, "", fmt.Errorf("error routing from node[uuid=%s]: %w", node.UUID(), err) } // router didn't error.. but it failed to pick a category if exitUUID == "" { - failRun(sprint, run, step, errors.Errorf("router on node[uuid=%s] failed to pick a category", node.UUID())) + failRun(sprint, run, step, fmt.Errorf("router on node[uuid=%s] failed to pick a category", node.UUID())) return nil, "", nil } } else if len(node.Exits()) > 0 { @@ -592,7 +591,7 @@ func readSession(eng flows.Engine, sessionAssets flows.SessionAssets, data json. var err error if err = utils.UnmarshalAndValidate(data, e); err != nil { - return nil, errors.Wrap(err, "unable to read session") + return nil, fmt.Errorf("unable to read session: %w", err) } s := &session{ @@ -607,20 +606,20 @@ func readSession(eng flows.Engine, sessionAssets flows.SessionAssets, data json. // read our environment s.env, err = envs.ReadEnvironment(e.Environment) if err != nil { - return nil, errors.Wrap(err, "unable to read environment") + return nil, fmt.Errorf("unable to read environment: %w", err) } // read our trigger if e.Trigger != nil { if s.trigger, err = triggers.ReadTrigger(s.Assets(), e.Trigger, missing); err != nil { - return nil, errors.Wrap(err, "unable to read trigger") + return nil, fmt.Errorf("unable to read trigger: %w", err) } } // read our contact if e.Contact != nil { if s.contact, err = flows.ReadContact(s.Assets(), *e.Contact, missing); err != nil { - return nil, errors.Wrap(err, "unable to read contact") + return nil, fmt.Errorf("unable to read contact: %w", err) } } @@ -628,7 +627,7 @@ func readSession(eng flows.Engine, sessionAssets flows.SessionAssets, data json. for i := range e.Runs { run, err := runs.ReadRun(s, e.Runs[i], missing) if err != nil { - return nil, errors.Wrapf(err, "unable to read run %d", i) + return nil, fmt.Errorf("unable to read run %d: %w", i, err) } s.addRun(run) } @@ -636,7 +635,7 @@ func readSession(eng flows.Engine, sessionAssets flows.SessionAssets, data json. // and our input if e.Input != nil { if s.input, err = inputs.ReadInput(s.Assets(), e.Input, missing); err != nil { - return nil, errors.Wrap(err, "unable to read input") + return nil, fmt.Errorf("unable to read input: %w", err) } } diff --git a/flows/engine/sprint_test.go b/flows/engine/sprint_test.go index 13bd0e3bf..89d2908a1 100644 --- a/flows/engine/sprint_test.go +++ b/flows/engine/sprint_test.go @@ -1,6 +1,7 @@ package engine import ( + "errors" "testing" "time" @@ -11,8 +12,6 @@ import ( "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/flows/events" "github.com/nyaruka/goflow/flows/modifiers" - - "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/flows/events/base.go b/flows/events/base.go index c8b4fbea6..08963ff31 100644 --- a/flows/events/base.go +++ b/flows/events/base.go @@ -2,13 +2,12 @@ package events import ( "encoding/json" + "fmt" "time" "github.com/nyaruka/gocommon/dates" "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/utils" - - "github.com/pkg/errors" ) var registeredTypes = map[string](func() flows.Event){} @@ -55,7 +54,7 @@ func ReadEvent(data json.RawMessage) (flows.Event, error) { f := registeredTypes[typeName] if f == nil { - return nil, errors.Errorf("unknown type: '%s'", typeName) + return nil, fmt.Errorf("unknown type: '%s'", typeName) } event := f() diff --git a/flows/events/msg_wait.go b/flows/events/msg_wait.go index a3f64953c..c163b47d2 100644 --- a/flows/events/msg_wait.go +++ b/flows/events/msg_wait.go @@ -2,13 +2,12 @@ package events import ( "encoding/json" + "fmt" "time" "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/flows/routers/waits/hints" "github.com/nyaruka/goflow/utils" - - "github.com/pkg/errors" ) func init() { @@ -82,7 +81,7 @@ func (e *MsgWaitEvent) UnmarshalJSON(data []byte) error { var err error if v.Hint != nil { if e.Hint, err = hints.ReadHint(v.Hint); err != nil { - return errors.Wrap(err, "unable to read hint") + return fmt.Errorf("unable to read hint: %w", err) } } diff --git a/flows/inputs/base.go b/flows/inputs/base.go index f1f39f0c4..112fcbb52 100644 --- a/flows/inputs/base.go +++ b/flows/inputs/base.go @@ -2,13 +2,12 @@ package inputs import ( "encoding/json" + "fmt" "time" "github.com/nyaruka/goflow/assets" "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/utils" - - "github.com/pkg/errors" ) type readFunc func(flows.SessionAssets, json.RawMessage, assets.MissingCallback) (flows.Input, error) @@ -65,7 +64,7 @@ func ReadInput(sessionAssets flows.SessionAssets, data json.RawMessage, missing f := registeredTypes[typeName] if f == nil { - return nil, errors.Errorf("unknown type: '%s'", typeName) + return nil, fmt.Errorf("unknown type: '%s'", typeName) } return f(sessionAssets, data, missing) diff --git a/flows/modifiers/base.go b/flows/modifiers/base.go index db573751e..680999435 100644 --- a/flows/modifiers/base.go +++ b/flows/modifiers/base.go @@ -2,14 +2,14 @@ package modifiers import ( "encoding/json" + "errors" + "fmt" "github.com/nyaruka/goflow/assets" "github.com/nyaruka/goflow/envs" "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/flows/events" "github.com/nyaruka/goflow/utils" - - "github.com/pkg/errors" ) // ErrNoModifier is the error instance returned when a modifier is read but due to missing assets can't be returned @@ -80,7 +80,7 @@ func ReadModifier(assets flows.SessionAssets, data json.RawMessage, missing asse f := RegisteredTypes[typeName] if f == nil { - return nil, errors.Errorf("unknown type: '%s'", typeName) + return nil, fmt.Errorf("unknown type: '%s'", typeName) } return f(assets, data, missing) } diff --git a/flows/resumes/base.go b/flows/resumes/base.go index a46711f5d..2d002861a 100644 --- a/flows/resumes/base.go +++ b/flows/resumes/base.go @@ -2,6 +2,7 @@ package resumes import ( "encoding/json" + "fmt" "time" "github.com/nyaruka/gocommon/dates" @@ -12,8 +13,6 @@ import ( "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/flows/events" "github.com/nyaruka/goflow/utils" - - "github.com/pkg/errors" ) // ReadFunc is a function that can read a resume from JSON @@ -126,7 +125,7 @@ func ReadResume(sessionAssets flows.SessionAssets, data json.RawMessage, missing f := registeredTypes[typeName] if f == nil { - return nil, errors.Errorf("unknown type: '%s'", typeName) + return nil, fmt.Errorf("unknown type: '%s'", typeName) } return f(sessionAssets, data, missing) } @@ -139,12 +138,12 @@ func (r *baseResume) unmarshal(sessionAssets flows.SessionAssets, e *baseResumeE if e.Environment != nil { if r.environment, err = envs.ReadEnvironment(e.Environment); err != nil { - return errors.Wrap(err, "unable to read environment") + return fmt.Errorf("unable to read environment: %w", err) } } if e.Contact != nil { if r.contact, err = flows.ReadContact(sessionAssets, e.Contact, missing); err != nil { - return errors.Wrap(err, "unable to read contact") + return fmt.Errorf("unable to read contact: %w", err) } } return nil diff --git a/flows/resumes/base_test.go b/flows/resumes/base_test.go index 8423ac52a..5180d8d29 100644 --- a/flows/resumes/base_test.go +++ b/flows/resumes/base_test.go @@ -22,8 +22,6 @@ import ( "github.com/nyaruka/goflow/flows/resumes" "github.com/nyaruka/goflow/flows/triggers" "github.com/nyaruka/goflow/test" - - "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -85,7 +83,7 @@ func testResumeType(t *testing.T, assetsJSON json.RawMessage, typeName string) { resume, err := resumes.ReadResume(sa, tc.Resume, assets.PanicOnMissing) if tc.ReadError != "" { - rootErr := errors.Cause(err) + rootErr := test.RootError(err) assert.EqualError(t, rootErr, tc.ReadError, "read error mismatch in %s", testName) continue } else { diff --git a/flows/routers/base.go b/flows/routers/base.go index b25ed87aa..2553ab72f 100644 --- a/flows/routers/base.go +++ b/flows/routers/base.go @@ -2,6 +2,8 @@ package routers import ( "encoding/json" + "errors" + "fmt" "time" "github.com/nyaruka/gocommon/dates" @@ -14,7 +16,6 @@ import ( "github.com/nyaruka/goflow/flows/events" "github.com/nyaruka/goflow/flows/routers/waits" "github.com/nyaruka/goflow/utils" - "github.com/pkg/errors" ) var registeredTypes = map[string](func() flows.Router){} @@ -92,18 +93,18 @@ func (r *baseRouter) EnumerateLocalizables(include func(uuids.UUID, string, []st func (r *baseRouter) validate(flow flows.Flow, exits []flows.Exit) error { // check wait timeout category is valid if r.AllowTimeout() && !r.isValidCategory(r.wait.Timeout().CategoryUUID()) { - return errors.Errorf("timeout category %s is not a valid category", r.wait.Timeout().CategoryUUID()) + return fmt.Errorf("timeout category %s is not a valid category", r.wait.Timeout().CategoryUUID()) } // check each category points to a valid exit for _, c := range r.categories { if c.ExitUUID() != "" && !r.isValidExit(c.ExitUUID(), exits) { - return errors.Errorf("category exit %s is not a valid exit", c.ExitUUID()) + return fmt.Errorf("category exit %s is not a valid exit", c.ExitUUID()) } } if r.wait != nil && !flow.Type().Allows(r.wait) { - return errors.Errorf("wait type '%s' is not allowed in a flow of type '%s'", r.wait.Type(), flow.Type()) + return fmt.Errorf("wait type '%s' is not allowed in a flow of type '%s'", r.wait.Type(), flow.Type()) } return nil @@ -164,7 +165,7 @@ func (r *baseRouter) routeToCategory(run flows.Run, step flows.Step, categoryUUI } if category == nil { - return "", errors.Errorf("category %s is not a valid category", categoryUUID) + return "", fmt.Errorf("category %s is not a valid category", categoryUUID) } // save result if we have a result name @@ -204,7 +205,7 @@ func ReadRouter(data json.RawMessage) (flows.Router, error) { f := registeredTypes[typeName] if f == nil { - return nil, errors.Errorf("unknown type: '%s'", typeName) + return nil, fmt.Errorf("unknown type: '%s'", typeName) } router := f() @@ -228,7 +229,7 @@ func (r *baseRouter) unmarshal(e *baseRouterEnvelope) error { if e.Wait != nil { r.wait, err = waits.ReadWait(e.Wait) if err != nil { - return errors.Wrap(err, "unable to read wait") + return fmt.Errorf("unable to read wait: %w", err) } } diff --git a/flows/routers/base_test.go b/flows/routers/base_test.go index a615324ba..90992733f 100644 --- a/flows/routers/base_test.go +++ b/flows/routers/base_test.go @@ -17,8 +17,6 @@ import ( "github.com/nyaruka/goflow/flows/routers" "github.com/nyaruka/goflow/flows/triggers" "github.com/nyaruka/goflow/test" - - "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -89,7 +87,7 @@ func testRouterType(t *testing.T, assetsJSON json.RawMessage, typeName string) { // now try to read the flow, and if we expect a read error, check that flow, err := sa.Flows().Get("16f6eee7-9843-4333-bad2-1d7fd636452c") if tc.ReadError != "" { - rootErr := errors.Cause(err) + rootErr := test.RootError(err) assert.EqualError(t, rootErr, tc.ReadError, "read error mismatch in %s", testName) continue } else { diff --git a/flows/routers/category.go b/flows/routers/category.go index c44109ba6..04f8d502e 100644 --- a/flows/routers/category.go +++ b/flows/routers/category.go @@ -1,12 +1,12 @@ package routers import ( + "fmt" + "github.com/nyaruka/gocommon/jsonx" "github.com/nyaruka/gocommon/uuids" "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/utils" - - "github.com/pkg/errors" ) type Category struct { @@ -44,7 +44,7 @@ func ReadCategory(data []byte) (flows.Category, error) { e := &categoryEnvelope{} if err := utils.UnmarshalAndValidate(data, e); err != nil { - return nil, errors.Wrap(err, "unable to read category") + return nil, fmt.Errorf("unable to read category: %w", err) } return NewCategory(e.UUID, e.Name, e.ExitUUID), nil diff --git a/flows/routers/switch.go b/flows/routers/switch.go index c990616f4..0a0f3cada 100644 --- a/flows/routers/switch.go +++ b/flows/routers/switch.go @@ -14,8 +14,6 @@ import ( "github.com/nyaruka/goflow/flows/inspect" "github.com/nyaruka/goflow/flows/routers/cases" "github.com/nyaruka/goflow/utils" - - "github.com/pkg/errors" ) func init() { @@ -98,18 +96,18 @@ func (r *SwitchRouter) Cases() []*Case { return r.cases } func (r *SwitchRouter) Validate(flow flows.Flow, exits []flows.Exit) error { // check the default category is valid if r.defaultCategoryUUID != "" && !r.isValidCategory(r.defaultCategoryUUID) { - return errors.Errorf("default category %s is not a valid category", r.defaultCategoryUUID) + return fmt.Errorf("default category %s is not a valid category", r.defaultCategoryUUID) } for _, c := range r.cases { // check each case points to a valid category if !r.isValidCategory(c.CategoryUUID) { - return errors.Errorf("case category %s is not a valid category", c.CategoryUUID) + return fmt.Errorf("case category %s is not a valid category", c.CategoryUUID) } // and each case test is valid if _, exists := cases.XTESTS[c.Type]; !exists { - return errors.Errorf("case test %s is not a registered test function", c.Type) + return fmt.Errorf("case test %s is not a registered test function", c.Type) } } @@ -159,7 +157,7 @@ func (r *SwitchRouter) matchCase(run flows.Run, step flows.Step, operand types.X // try to look up our function xtest := cases.XTESTS[test] if xtest == nil { - return "", "", nil, errors.Errorf("unknown case test '%s'", c.Type) + return "", "", nil, fmt.Errorf("unknown case test '%s'", c.Type) } // build our argument list which starts with the operand diff --git a/flows/routers/waits/base.go b/flows/routers/waits/base.go index b30984aa5..c4ef26f92 100644 --- a/flows/routers/waits/base.go +++ b/flows/routers/waits/base.go @@ -2,13 +2,12 @@ package waits import ( "encoding/json" + "fmt" "time" "github.com/nyaruka/gocommon/dates" "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/utils" - - "github.com/pkg/errors" ) type readFunc func(data json.RawMessage) (flows.Wait, error) @@ -82,7 +81,7 @@ func ReadWait(data []byte) (flows.Wait, error) { f := registeredTypes[typeName] if f == nil { - return nil, errors.Errorf("unknown type: '%s'", typeName) + return nil, fmt.Errorf("unknown type: '%s'", typeName) } return f(data) } diff --git a/flows/routers/waits/hints/base.go b/flows/routers/waits/hints/base.go index 86e923007..e95e72f5a 100644 --- a/flows/routers/waits/hints/base.go +++ b/flows/routers/waits/hints/base.go @@ -2,11 +2,10 @@ package hints import ( "encoding/json" + "fmt" "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/utils" - - "github.com/pkg/errors" ) var registeredTypes = map[string](func() flows.Hint){} @@ -41,7 +40,7 @@ func ReadHint(data json.RawMessage) (flows.Hint, error) { f := registeredTypes[typeName] if f == nil { - return nil, errors.Errorf("unknown type: '%s'", typeName) + return nil, fmt.Errorf("unknown type: '%s'", typeName) } hint := f() diff --git a/flows/routers/waits/msg.go b/flows/routers/waits/msg.go index 8984f8474..d0ef8176b 100644 --- a/flows/routers/waits/msg.go +++ b/flows/routers/waits/msg.go @@ -2,6 +2,7 @@ package waits import ( "encoding/json" + "fmt" "github.com/nyaruka/gocommon/jsonx" "github.com/nyaruka/goflow/flows" @@ -10,8 +11,6 @@ import ( "github.com/nyaruka/goflow/flows/routers/waits/hints" "github.com/nyaruka/goflow/flows/triggers" "github.com/nyaruka/goflow/utils" - - "github.com/pkg/errors" ) func init() { @@ -102,7 +101,7 @@ func readMsgWait(data json.RawMessage) (flows.Wait, error) { var err error if e.Hint != nil { if w.hint, err = hints.ReadHint(e.Hint); err != nil { - return nil, errors.Wrap(err, "unable to read hint") + return nil, fmt.Errorf("unable to read hint: %w", err) } } diff --git a/flows/runs/run.go b/flows/runs/run.go index 19ede67f3..fb0abd172 100644 --- a/flows/runs/run.go +++ b/flows/runs/run.go @@ -2,6 +2,7 @@ package runs import ( "encoding/json" + "fmt" "time" "github.com/nyaruka/gocommon/dates" @@ -16,7 +17,6 @@ import ( "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/flows/events" "github.com/nyaruka/goflow/utils" - "github.com/pkg/errors" ) type run struct { @@ -164,7 +164,7 @@ func (r *run) CreateStep(node flows.Node) flows.Step { func (r *run) PathLocation() (flows.Step, flows.Node, error) { if r.Path() == nil { - return nil, nil, errors.Errorf("run has no location as path is empty") + return nil, nil, fmt.Errorf("run has no location as path is empty") } step := r.Path()[len(r.Path())-1] @@ -175,7 +175,7 @@ func (r *run) PathLocation() (flows.Step, flows.Node, error) { node = r.Flow().GetNode(step.NodeUUID()) } if node == nil { - return nil, nil, errors.Errorf("run is located at a flow node that no longer exists") + return nil, nil, fmt.Errorf("run is located at a flow node that no longer exists") } return step, node, nil @@ -426,7 +426,7 @@ func ReadRun(session flows.Session, data json.RawMessage, missing assets.Missing var err error if err = utils.UnmarshalAndValidate(data, e); err != nil { - return nil, errors.Wrap(err, "unable to read run") + return nil, fmt.Errorf("unable to read run: %w", err) } r := &run{ @@ -467,7 +467,7 @@ func ReadRun(session flows.Session, data json.RawMessage, missing assets.Missing r.events = make([]flows.Event, len(e.Events)) for i := range r.events { if r.events[i], err = events.ReadEvent(e.Events[i]); err != nil { - return nil, errors.Wrap(err, "unable to read event") + return nil, fmt.Errorf("unable to read event: %w", err) } } @@ -504,7 +504,7 @@ func (r *run) MarshalJSON() ([]byte, error) { e.Events = make([]json.RawMessage, len(r.events)) for i := range r.events { if e.Events[i], err = jsonx.Marshal(r.events[i]); err != nil { - return nil, errors.Wrapf(err, "unable to marshal event[type=%s]", r.events[i].Type()) + return nil, fmt.Errorf("unable to marshal event[type=%s]: %w", r.events[i].Type(), err) } } diff --git a/flows/triggers/base.go b/flows/triggers/base.go index 709962a24..2a85db6e1 100644 --- a/flows/triggers/base.go +++ b/flows/triggers/base.go @@ -2,6 +2,8 @@ package triggers import ( "encoding/json" + "errors" + "fmt" "time" "github.com/nyaruka/gocommon/dates" @@ -11,8 +13,6 @@ import ( "github.com/nyaruka/goflow/excellent/types" "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/utils" - - "github.com/pkg/errors" ) // ReadFunc is a function that can read a trigger from JSON @@ -74,7 +74,7 @@ func (t *baseTrigger) Initialize(session flows.Session, logEvent flows.EventCall // try to load the flow flow, err := session.Assets().Flows().Get(t.Flow().UUID) if err != nil { - return errors.Wrapf(err, "unable to load %s", t.Flow()) + return fmt.Errorf("unable to load %s: %w", t.Flow(), err) } if flow.Type() == flows.FlowTypeVoice && t.call == nil { @@ -196,7 +196,7 @@ func ReadTrigger(sessionAssets flows.SessionAssets, data json.RawMessage, missin f := registeredTypes[typeName] if f == nil { - return nil, errors.Errorf("unknown type: '%s'", typeName) + return nil, fmt.Errorf("unknown type: '%s'", typeName) } return f(sessionAssets, data, missing) } @@ -212,18 +212,18 @@ func (t *baseTrigger) unmarshal(sessionAssets flows.SessionAssets, e *baseTrigge t.triggeredOn = e.TriggeredOn if t.contact, err = flows.ReadContact(sessionAssets, e.Contact, missing); err != nil { - return errors.Wrap(err, "unable to read contact") + return fmt.Errorf("unable to read contact: %w", err) } if e.Environment != nil { if t.environment, err = envs.ReadEnvironment(e.Environment); err != nil { - return errors.Wrap(err, "unable to read environment") + return fmt.Errorf("unable to read environment: %w", err) } } if e.Params != nil { if t.params, err = types.ReadXObject(e.Params); err != nil { - return errors.Wrap(err, "unable to read params") + return fmt.Errorf("unable to read params: %w", err) } } diff --git a/flows/triggers/base_test.go b/flows/triggers/base_test.go index 008887e2d..e567d25e9 100644 --- a/flows/triggers/base_test.go +++ b/flows/triggers/base_test.go @@ -21,7 +21,6 @@ import ( "github.com/nyaruka/goflow/flows/engine" "github.com/nyaruka/goflow/flows/triggers" "github.com/nyaruka/goflow/test" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -74,7 +73,7 @@ func testTriggerType(t *testing.T, assetsJSON json.RawMessage, typeName string) trigger, err := triggers.ReadTrigger(sa, tc.Trigger, assets.PanicOnMissing) if tc.ReadError != "" { - rootErr := errors.Cause(err) + rootErr := test.RootError(err) assert.EqualError(t, rootErr, tc.ReadError, "read error mismatch in %s", testName) continue } else { diff --git a/flows/triggers/ticket.go b/flows/triggers/ticket.go index fa582295b..8f9bffedc 100644 --- a/flows/triggers/ticket.go +++ b/flows/triggers/ticket.go @@ -2,6 +2,7 @@ package triggers import ( "encoding/json" + "fmt" "github.com/nyaruka/gocommon/jsonx" "github.com/nyaruka/goflow/assets" @@ -9,7 +10,6 @@ import ( "github.com/nyaruka/goflow/excellent/types" "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/utils" - "github.com/pkg/errors" ) func init() { @@ -122,7 +122,7 @@ func readTicketTrigger(sa flows.SessionAssets, data json.RawMessage, missing ass var err error t.event.ticket, err = flows.ReadTicket(sa, e.Event.Ticket, missing) if err != nil { - return nil, errors.Wrap(err, "unable to read ticket") + return nil, fmt.Errorf("unable to read ticket: %w", err) } if err := t.unmarshal(sa, &e.baseTriggerEnvelope, missing); err != nil { diff --git a/go.mod b/go.mod index 7b4102a3d..b6ae37af3 100644 --- a/go.mod +++ b/go.mod @@ -9,8 +9,7 @@ require ( github.com/blevesearch/segment v0.9.1 github.com/buger/jsonparser v1.1.1 github.com/go-playground/validator/v10 v10.20.0 - github.com/nyaruka/gocommon v1.55.0 - github.com/pkg/errors v0.9.1 + github.com/nyaruka/gocommon v1.55.1 github.com/sergi/go-diff v1.3.1 github.com/shopspring/decimal v1.4.0 github.com/stretchr/testify v1.9.0 diff --git a/go.sum b/go.sum index 14aee9fda..3917ca46c 100644 --- a/go.sum +++ b/go.sum @@ -40,14 +40,12 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/nyaruka/gocommon v1.55.0 h1:wkV6fZIVOFMtnRw/3+fFIxSpz8IiQydAmPlMlkPUxvc= -github.com/nyaruka/gocommon v1.55.0/go.mod h1:rWkEIpYIK98zL9Qm6PeMXJ+84WcWlArf01RfuWWCYvQ= +github.com/nyaruka/gocommon v1.55.1 h1:f6c8Ud06ZQKrnTp5c753Q1ZA78WW/TOqbJRYeTpXOtc= +github.com/nyaruka/gocommon v1.55.1/go.mod h1:l+MMiuz0XsXT4wJD0lc8KH1+wPqfZ/a8aK6RAANOaZo= github.com/nyaruka/null/v2 v2.0.3 h1:rdmMRQyVzrOF3Jff/gpU/7BDR9mQX0lcLl4yImsA3kw= github.com/nyaruka/null/v2 v2.0.3/go.mod h1:OCVeCkCXwrg5/qE6RU0c1oUVZBy+ZDrT+xYg1XSaIWA= github.com/nyaruka/phonenumbers v1.3.5 h1:WZLbQn61j2E1OFnvpUTYbK/6hViUgl6tppJ55/E2iQM= github.com/nyaruka/phonenumbers v1.3.5/go.mod h1:Ut+eFwikULbmCenH6InMKL9csUNLyxHuBLyfkpum11s= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= diff --git a/services/airtime/dtone/service.go b/services/airtime/dtone/service.go index a26ddd892..805363f8e 100644 --- a/services/airtime/dtone/service.go +++ b/services/airtime/dtone/service.go @@ -1,6 +1,7 @@ package dtone import ( + "fmt" "net/http" "github.com/nyaruka/gocommon/httpx" @@ -9,7 +10,6 @@ import ( "github.com/nyaruka/gocommon/uuids" "github.com/nyaruka/goflow/flows" - "github.com/pkg/errors" "github.com/shopspring/decimal" ) @@ -40,7 +40,7 @@ func (s *service) Transfer(sender urns.URN, recipient urns.URN, amounts map[stri logHTTP(flows.NewHTTPLog(trace, flows.HTTPStatusFromCode, s.redactor)) } if err != nil { - return transfer, errors.Wrap(err, "number lookup failed") + return transfer, fmt.Errorf("number lookup failed: %w", err) } // look for an exact match @@ -52,7 +52,7 @@ func (s *service) Transfer(sender urns.URN, recipient urns.URN, amounts map[stri } } if operator == nil { - return transfer, errors.Errorf("unable to find operator for number %s", recipient.Path()) + return transfer, fmt.Errorf("unable to find operator for number %s", recipient.Path()) } // fetch available products for this operator @@ -61,7 +61,7 @@ func (s *service) Transfer(sender urns.URN, recipient urns.URN, amounts map[stri logHTTP(flows.NewHTTPLog(trace, flows.HTTPStatusFromCode, s.redactor)) } if err != nil { - return transfer, errors.Wrap(err, "product fetch failed") + return transfer, fmt.Errorf("product fetch failed: %w", err) } // closest product for each currency we have a desired amount for @@ -80,7 +80,7 @@ func (s *service) Transfer(sender urns.URN, recipient urns.URN, amounts map[stri } } if len(closestProducts) == 0 { - return transfer, errors.Errorf("unable to find a suitable product for operator '%s'", operator.Name) + return transfer, fmt.Errorf("unable to find a suitable product for operator '%s'", operator.Name) } // it's possible we have more than one supported currency/product.. use any @@ -99,11 +99,11 @@ func (s *service) Transfer(sender urns.URN, recipient urns.URN, amounts map[stri logHTTP(flows.NewHTTPLog(trace, flows.HTTPStatusFromCode, s.redactor)) } if err != nil { - return transfer, errors.Wrap(err, "transaction creation failed") + return transfer, fmt.Errorf("transaction creation failed: %w", err) } if tx.Status.Class.ID != StatusCIDConfirmed && tx.Status.Class.ID != StatusCIDSubmitted && tx.Status.Class.ID != StatusCIDCompleted { - return transfer, errors.Errorf("transaction to send product %d on operator %d ended with status %s", product.ID, operator.ID, tx.Status.Message) + return transfer, fmt.Errorf("transaction to send product %d on operator %d ended with status %s", product.ID, operator.ID, tx.Status.Message) } transfer.ActualAmount = product.Destination.Amount diff --git a/test/engine.go b/test/engine.go index ca7680993..02d4b01e5 100644 --- a/test/engine.go +++ b/test/engine.go @@ -1,6 +1,7 @@ package test import ( + "fmt" "net/http" "time" @@ -10,7 +11,6 @@ import ( "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/flows/engine" "github.com/nyaruka/goflow/services/webhooks" - "github.com/pkg/errors" "github.com/shopspring/decimal" ) @@ -116,7 +116,7 @@ func (s *airtimeService) Transfer(sender urns.URN, recipient urns.URN, amounts m amount, hasAmount := amounts[s.fixedCurrency] if !hasAmount { - return nil, errors.Errorf("no amount configured for transfers in %s", s.fixedCurrency) + return nil, fmt.Errorf("no amount configured for transfers in %s", s.fixedCurrency) } transfer := &flows.AirtimeTransfer{ diff --git a/test/runner_test.go b/test/runner_test.go index 8685ae488..a11d50fcb 100644 --- a/test/runner_test.go +++ b/test/runner_test.go @@ -25,8 +25,6 @@ import ( "github.com/nyaruka/goflow/services/email/smtp" "github.com/nyaruka/goflow/services/webhooks" "github.com/nyaruka/goflow/utils/smtpx" - - "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -53,7 +51,7 @@ func loadTestCases() ([]runnerTest, error) { directory := "testdata/runner/" files, err := os.ReadDir(directory) if err != nil { - return nil, errors.Wrap(err, "error reading test directory") + return nil, fmt.Errorf("error reading test directory: %w", err) } tests := make([]runnerTest, 0) @@ -111,7 +109,7 @@ func runFlow(assetsPath string, rawTrigger json.RawMessage, rawResumes []json.Ra trigger, err := triggers.ReadTrigger(sa, rawTrigger, assets.PanicOnMissing) if err != nil { - return runResult{}, errors.Wrapf(err, "error unmarshalling trigger") + return runResult{}, fmt.Errorf("error unmarshalling trigger: %w", err) } eng := engine.NewBuilder(). @@ -138,7 +136,7 @@ func runFlow(assetsPath string, rawTrigger json.RawMessage, rawResumes []json.Ra for i, rawResume := range rawResumes { sessionJSON, err := jsonx.MarshalPretty(session) if err != nil { - return runResult{}, errors.Wrap(err, "error marshalling output") + return runResult{}, fmt.Errorf("error marshalling output: %w", err) } outputs = append(outputs, &Output{ @@ -149,12 +147,12 @@ func runFlow(assetsPath string, rawTrigger json.RawMessage, rawResumes []json.Ra session, err = eng.ReadSession(sa, sessionJSON, assets.PanicOnMissing) if err != nil { - return runResult{}, errors.Wrap(err, "error marshalling output") + return runResult{}, fmt.Errorf("error marshalling output: %w", err) } // if session isn't waiting for another resume, that's an error if session.Status() != flows.SessionStatusWaiting { - return runResult{}, errors.Errorf("did not stop at expected wait, have unused resumes: %d", len(rawResumes[i:])) + return runResult{}, fmt.Errorf("did not stop at expected wait, have unused resumes: %d", len(rawResumes[i:])) } resume, err := resumes.ReadResume(sa, rawResume, assets.PanicOnMissing) @@ -170,7 +168,7 @@ func runFlow(assetsPath string, rawTrigger json.RawMessage, rawResumes []json.Ra sessionJSON, err := jsonx.MarshalPretty(session) if err != nil { - return runResult{}, errors.Wrap(err, "error marshalling output") + return runResult{}, fmt.Errorf("error marshalling output: %w", err) } outputs = append(outputs, &Output{ diff --git a/test/session.go b/test/session.go index e0a566a81..40b5c6097 100644 --- a/test/session.go +++ b/test/session.go @@ -2,6 +2,7 @@ package test import ( "encoding/json" + "errors" "fmt" "os" "strings" @@ -19,8 +20,6 @@ import ( "github.com/nyaruka/goflow/flows/events" "github.com/nyaruka/goflow/flows/resumes" "github.com/nyaruka/goflow/flows/triggers" - - "github.com/pkg/errors" ) var sessionAssets = `{ @@ -502,7 +501,7 @@ func CreateTestSession(testServerURL string, redact envs.RedactionPolicy) (flows sa, err := CreateSessionAssets(assetsJSON, testServerURL) if err != nil { - return nil, nil, errors.Wrap(err, "error creating test session") + return nil, nil, fmt.Errorf("error creating test session: %w", err) } // read our trigger @@ -511,20 +510,20 @@ func CreateTestSession(testServerURL string, redact envs.RedactionPolicy) (flows trigger, err := triggers.ReadTrigger(sa, triggerJSON, assets.PanicOnMissing) if err != nil { - return nil, nil, errors.Wrap(err, "error reading trigger") + return nil, nil, fmt.Errorf("error reading trigger: %w", err) } eng := NewEngine() session, _, err := eng.NewSession(sa, trigger) if err != nil { - return nil, nil, errors.Wrap(err, "error starting test session") + return nil, nil, fmt.Errorf("error starting test session: %w", err) } // read our resume resume, err := resumes.ReadResume(sa, json.RawMessage(sessionResume), assets.PanicOnMissing) if err != nil { - return nil, nil, errors.Wrap(err, "error reading resume") + return nil, nil, fmt.Errorf("error reading resume: %w", err) } sprint, err := session.Resume(resume) @@ -537,19 +536,19 @@ func CreateTestVoiceSession(testServerURL string) (flows.Session, []flows.Event, sa, err := CreateSessionAssets(assetsJSON, testServerURL) if err != nil { - return nil, nil, errors.Wrap(err, "error creating test voice session assets") + return nil, nil, fmt.Errorf("error creating test voice session assets: %w", err) } // read our trigger trigger, err := triggers.ReadTrigger(sa, json.RawMessage(voiceSessionTrigger), assets.PanicOnMissing) if err != nil { - return nil, nil, errors.Wrap(err, "error reading trigger") + return nil, nil, fmt.Errorf("error reading trigger: %w", err) } eng := NewEngine() session, sprint, err := eng.NewSession(sa, trigger) if err != nil { - return nil, nil, errors.Wrap(err, "error starting test voice session") + return nil, nil, fmt.Errorf("error starting test voice session: %w", err) } return session, sprint.Events(), err @@ -569,13 +568,13 @@ func CreateSessionAssets(assetsJSON json.RawMessage, testServerURL string) (flow // read our assets into a source source, err := static.NewSource(assetsJSON) if err != nil { - return nil, errors.Wrap(err, "error loading test assets") + return nil, fmt.Errorf("error loading test assets: %w", err) } // create our engine session sa, err := engine.NewSessionAssets(env, source, nil) if err != nil { - return nil, errors.Wrap(err, "error creating test session assets") + return nil, fmt.Errorf("error creating test session assets: %w", err) } return sa, nil @@ -666,20 +665,20 @@ func (b *SessionBuilder) Build() (flows.SessionAssets, flows.Session, flows.Spri if b.assetsPath != "" { b.assetsJSON, err = os.ReadFile(b.assetsPath) if err != nil { - errors.Wrapf(err, "error reading assets from %s", b.assetsPath) + return nil, nil, nil, fmt.Errorf("error reading assets from %s: %w", b.assetsPath, err) } } if b.assetsJSON != nil { sa, err = CreateSessionAssets(b.assetsJSON, "") if err != nil { - return nil, nil, nil, errors.Wrap(err, "error creating session assets") + return nil, nil, nil, fmt.Errorf("error creating session assets: %w", err) } } } flow, err := sa.Flows().Get(b.flowUUID) if err != nil { - return nil, nil, nil, errors.Wrapf(err, "error getting flow %s from assets", b.flowUUID) + return nil, nil, nil, fmt.Errorf("error getting flow %s from assets: %w", b.flowUUID, err) } var urnz []urns.URN @@ -703,7 +702,7 @@ func (b *SessionBuilder) Build() (flows.SessionAssets, flows.Session, flows.Spri assets.PanicOnMissing, ) if err != nil { - return nil, nil, nil, errors.Wrap(err, "error creating contact") + return nil, nil, nil, fmt.Errorf("error creating contact: %w", err) } var trigger flows.Trigger diff --git a/test/utils.go b/test/utils.go index b17f41837..4e18508eb 100644 --- a/test/utils.go +++ b/test/utils.go @@ -2,6 +2,7 @@ package test import ( "encoding/json" + "errors" "fmt" "testing" @@ -87,3 +88,14 @@ func fmtMsgAndArgs(msgAndArgs []any) string { } return "" } + +// RootError returns the root cause of an error by following the unwrap chain +func RootError(err error) error { + for { + cause := errors.Unwrap(err) + if cause == nil { + return err + } + err = cause + } +} diff --git a/utils/smtpx/client.go b/utils/smtpx/client.go index 5c3855dd8..685197592 100644 --- a/utils/smtpx/client.go +++ b/utils/smtpx/client.go @@ -1,12 +1,12 @@ package smtpx import ( + "errors" "fmt" "net/url" "strconv" "github.com/Shopify/gomail" - "github.com/pkg/errors" ) // Client is an SMTP client @@ -46,7 +46,7 @@ func NewClientFromURL(connectionURL string) (*Client, error) { if url.Port() != "" { port, err = strconv.Atoi(url.Port()) if err != nil || port < 0 || port > 65535 { - return nil, errors.Errorf("%s is not a valid port number", url.Port()) + return nil, fmt.Errorf("%s is not a valid port number", url.Port()) } } diff --git a/utils/smtpx/mock.go b/utils/smtpx/mock.go index d201eab8b..a607cd3b1 100644 --- a/utils/smtpx/mock.go +++ b/utils/smtpx/mock.go @@ -3,8 +3,6 @@ package smtpx import ( "fmt" "strings" - - "github.com/pkg/errors" ) // MockSender is a mocked sender for testing that just logs would-be commands @@ -25,7 +23,7 @@ func (s *MockSender) Logs() []string { func (s *MockSender) Send(c *Client, m *Message) error { if len(s.errs) == 0 { - panic(errors.Errorf("missing mock for send number %d", len(s.logs))) + panic(fmt.Errorf("missing mock for send number %d", len(s.logs))) } err := s.errs[0] diff --git a/utils/smtpx/retries_test.go b/utils/smtpx/retries_test.go index bbceed3eb..03452bf36 100644 --- a/utils/smtpx/retries_test.go +++ b/utils/smtpx/retries_test.go @@ -1,12 +1,11 @@ package smtpx_test import ( + "errors" "testing" "time" "github.com/nyaruka/goflow/utils/smtpx" - - "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) diff --git a/utils/validator.go b/utils/validator.go index 9ec6189b7..4cc8fa63b 100644 --- a/utils/validator.go +++ b/utils/validator.go @@ -8,7 +8,6 @@ import ( "github.com/go-playground/validator/v10" "github.com/nyaruka/gocommon/jsonx" - "github.com/pkg/errors" ) // our system validator, it can be shared across threads @@ -142,7 +141,7 @@ func Validate(obj any) error { problem = fmt.Sprintf("failed tag '%s'", fieldErr.Tag()) } - newErrors[i] = errors.Errorf("field '%s' %s", location, problem) + newErrors[i] = fmt.Errorf("field '%s' %s", location, problem) } return ValidationErrors(newErrors) }