From 5d8a0c6d1d3db99f8d94aaf3b06c07c6fe945546 Mon Sep 17 00:00:00 2001 From: Jeremy West Date: Wed, 27 Jan 2021 09:14:25 -0600 Subject: [PATCH] feat: support version creation during actions create (#68) * feat: support version creation during actions create * lint fixes * bump auth0 dep * update action version view * fix action and version rendering * Fix regex * Using backtick to skip double escape * add hack to fetch latest version/draft state after create for display * lint Co-authored-by: Danny Turcotte --- go.mod | 2 +- go.sum | 12 +- internal/auth0/actions.go | 3 +- internal/cli/actions.go | 109 ++++++++++++++++-- internal/display/actions.go | 42 ++++--- internal/validators/action.go | 35 ++++++ .../gopkg.in/auth0.v5/management/actions.go | 8 +- vendor/modules.txt | 4 +- 8 files changed, 174 insertions(+), 41 deletions(-) diff --git a/go.mod b/go.mod index 86638b69d..d88f0ddc6 100644 --- a/go.mod +++ b/go.mod @@ -27,4 +27,4 @@ require ( // replace gopkg.in/auth0.v5 => ../auth0 -replace gopkg.in/auth0.v5 => github.com/go-auth0/auth0 v1.3.1-0.20210127040011-acb7c665d062 +replace gopkg.in/auth0.v5 => github.com/go-auth0/auth0 v1.3.1-0.20210127122814-819354e637e9 diff --git a/go.sum b/go.sum index ecbe6d9f4..72b11bf22 100644 --- a/go.sum +++ b/go.sum @@ -50,8 +50,6 @@ github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLj github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/briandowns/spinner v1.11.1 h1:OixPqDEcX3juo5AjQZAnFPbeUA0jvkp2qzB5gOZJ/L0= -github.com/briandowns/spinner v1.11.1/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ= github.com/briandowns/spinner v1.12.0 h1:72O0PzqGJb6G3KgrcIOtL/JAGGZ5ptOMCn9cUHmqsmw= github.com/briandowns/spinner v1.12.0/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -84,14 +82,8 @@ github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-auth0/auth0 v1.3.1-0.20210126164439-7451a183abbd h1:O1ZCQlPODGWJNDT0qH6SpGdFEcEBWz7dd/mHFw6Kh10= -github.com/go-auth0/auth0 v1.3.1-0.20210126164439-7451a183abbd/go.mod h1:QQ9fgGj2Wpza15+Ho3mM6amMeKfhzHo2cixcOqdkoKk= -github.com/go-auth0/auth0 v1.3.1-0.20210126214458-2807092480f8 h1:x5rbuD/ay7ieXnEynRnTA92qHbe6uMTxumOWfsCPiws= -github.com/go-auth0/auth0 v1.3.1-0.20210126214458-2807092480f8/go.mod h1:QQ9fgGj2Wpza15+Ho3mM6amMeKfhzHo2cixcOqdkoKk= -github.com/go-auth0/auth0 v1.3.1-0.20210127020221-38fd79682c4a h1:TAnbIrM2gdxfqT4pdPso61aO74mS+mNuIbRDYhfSNKI= -github.com/go-auth0/auth0 v1.3.1-0.20210127020221-38fd79682c4a/go.mod h1:QQ9fgGj2Wpza15+Ho3mM6amMeKfhzHo2cixcOqdkoKk= -github.com/go-auth0/auth0 v1.3.1-0.20210127040011-acb7c665d062 h1:WOCTdmjxtCBWcSL34/v0lzvyqGvlPlH76ns86EUuF98= -github.com/go-auth0/auth0 v1.3.1-0.20210127040011-acb7c665d062/go.mod h1:QQ9fgGj2Wpza15+Ho3mM6amMeKfhzHo2cixcOqdkoKk= +github.com/go-auth0/auth0 v1.3.1-0.20210127122814-819354e637e9 h1:/Lolghe1pufQWJ+JcmX0UToYIr7bpNpDzo6PoVbFLX8= +github.com/go-auth0/auth0 v1.3.1-0.20210127122814-819354e637e9/go.mod h1:QQ9fgGj2Wpza15+Ho3mM6amMeKfhzHo2cixcOqdkoKk= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= diff --git a/internal/auth0/actions.go b/internal/auth0/actions.go index 53cf3b3f1..750cc601e 100644 --- a/internal/auth0/actions.go +++ b/internal/auth0/actions.go @@ -14,7 +14,8 @@ type ActionAPI interface { type ActionVersionAPI interface { Create(actionID string, v *management.ActionVersion) error Read(actionID string, id string) (*management.ActionVersion, error) - UpsertDraft(id string, a *management.ActionVersion) error + UpsertDraft(actionID string, v *management.ActionVersion) error + ReadDraft(actionID string) (*management.ActionVersion, error) Delete(actionID string, id string, opts ...management.RequestOption) error List(actionID string, opts ...management.RequestOption) (c *management.ActionVersionList, err error) Test(actionID string, id string, payload management.Object) (management.Object, error) diff --git a/internal/cli/actions.go b/internal/cli/actions.go index f87b33e97..5b1330d26 100644 --- a/internal/cli/actions.go +++ b/internal/cli/actions.go @@ -2,6 +2,7 @@ package cli import ( "encoding/json" + "errors" "fmt" "io/ioutil" "os" @@ -256,27 +257,35 @@ func listActionVersionsCmd(cli *cli) *cobra.Command { } func createActionCmd(cli *cli) *cobra.Command { + var ( + name string + trigger string + file string + script string + dependency []string + createVersion bool + ) + cmd := &cobra.Command{ Use: "create", Short: "Creates a new action", Long: `$ auth0 actions create Creates a new action: - $ auth0 actions create my-action --trigger post-login + $ auth0 actions create --name my-action --trigger post-login --file action.js --dependency lodash@4.17.19 `, - Args: func(cmd *cobra.Command, args []string) error { - if err := validators.ExactArgs("name")(cmd, args); err != nil { + RunE: func(cmd *cobra.Command, args []string) error { + if err := validators.TriggerID(trigger); err != nil { return err } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - trigger, err := cmd.LocalFlags().GetString("trigger") + + source, err := sourceFromFileOrScript(file, script) if err != nil { return err } - if err := validators.TriggerID(trigger); err != nil { + dependencies, err := validators.Dependencies(dependency) + if err != nil { return err } @@ -289,24 +298,73 @@ Creates a new action: } action := &management.Action{ - Name: auth0.String(args[0]), + Name: auth0.String(name), SupportedTriggers: &triggers, } + version := &management.ActionVersion{ + Code: source, + Dependencies: dependencies, + Runtime: "node12", + } + err = ansi.Spinner("Creating action", func() error { - return cli.api.Action.Create(action) + if err := cli.api.Action.Create(action); err != nil { + return err + } + + if createVersion { + if err := cli.api.ActionVersion.Create(auth0.StringValue(action.ID), version); err != nil { + return err + } + + // TODO(iamjem): this is a hack since the SDK won't decode 202 responses + list, err := cli.api.ActionVersion.List(auth0.StringValue(action.ID)) + if err != nil { + return err + } + + if len(list.Versions) > 0 { + version = list.Versions[0] + } + } else { + if err := cli.api.ActionVersion.UpsertDraft(auth0.StringValue(action.ID), version); err != nil { + return err + } + + // TODO(iamjem): this is a hack since the SDK won't decode 202 responses + draft, err := cli.api.ActionVersion.ReadDraft(auth0.StringValue(action.ID)) + if err != nil { + return err + } + version = draft + } + + return nil }) if err != nil { return err } - cli.renderer.Action(action) + cli.renderer.ActionVersion(version) + return nil }, } - cmd.Flags().StringP("trigger", "t", string(management.PostLogin), "Trigger type for action.") + cmd.Flags().StringVarP(&name, "name", "n", "", "Unique name for the action.") + cmd.Flags().StringVarP(&trigger, "trigger", "t", string(management.PostLogin), "Trigger type for action.") + cmd.Flags().StringVarP(&file, "file", "f", "", "File containing the action source code.") + cmd.Flags().StringVarP(&script, "script", "s", "", "Raw source code for the action.") + cmd.Flags().StringSliceVarP(&dependency, "dependency", "d", nil, "Dependency for the source code (@).") + // TODO: This name is kind of overloaded since it could also refer to the version of the trigger (though there's only v1's at this time) + cmd.Flags().BoolVarP(&createVersion, "version", "v", false, "Create an explicit action version from the source code instead of a draft.") + + mustRequireFlags(cmd, "name") + if err := cmd.MarkFlagFilename("file"); err != nil { + panic(err) + } return cmd } @@ -448,3 +506,30 @@ func createTriggerCmd(cli *cli) *cobra.Command { return cmd } + +var errNoSource = errors.New("please provide source code via --file or --script") + +func sourceFromFileOrScript(file, script string) (string, error) { + if script != "" { + return script, nil + } + + if file != "" { + f, err := os.Open(file) + if err != nil { + return "", err + } + defer f.Close() + + contents, err := ioutil.ReadAll(f) + if err != nil { + return "", err + } + + if len(contents) > 0 { + return string(contents), nil + } + } + + return "", errNoSource +} diff --git a/internal/display/actions.go b/internal/display/actions.go index 2b1cb1a21..7333ecb19 100644 --- a/internal/display/actions.go +++ b/internal/display/actions.go @@ -38,18 +38,28 @@ func (v *triggerView) AsTableRow() []string { } type actionVersionView struct { - ID string - Status string - Runtime string - CreatedAt string + ID string + ActionID string + ActionName string + Runtime string + Status string + CreatedAt string } func (v *actionVersionView) AsTableHeader() []string { - return []string{"ID", "Status", "Runtime", "Created At"} + return []string{"ID", "Action ID", "Action Name", "Runtime", "Status", "Created At"} } func (v *actionVersionView) AsTableRow() []string { - return []string{v.ID, v.Status, v.Runtime, v.CreatedAt} + return []string{v.getID(), v.ActionID, v.ActionName, v.Runtime, v.Status, v.CreatedAt} +} + +func (v *actionVersionView) getID() string { + // draft versions don't have a unique id + if v.ID == "" { + return "draft" + } + return v.ID } func (r *Renderer) ActionList(actions []*management.Action) { @@ -118,10 +128,12 @@ func (r *Renderer) ActionVersion(version *management.ActionVersion) { r.Heading(ansi.Bold(r.Tenant), "action version\n") v := &actionVersionView{ - ID: auth0.StringValue(&version.ID), - Status: string(version.Status), - Runtime: auth0.StringValue(&version.Runtime), - CreatedAt: timeAgo(auth0.TimeValue(version.CreatedAt)), + ID: version.ID, + ActionID: auth0.StringValue(version.Action.ID), + ActionName: auth0.StringValue(version.Action.ID), + Runtime: auth0.StringValue(&version.Runtime), + Status: string(version.Status), + CreatedAt: timeAgo(auth0.TimeValue(version.CreatedAt)), } r.Results([]View{v}) @@ -133,10 +145,12 @@ func (r *Renderer) ActionVersionList(list []*management.ActionVersion) { var res []View for _, version := range list { res = append(res, &actionVersionView{ - ID: auth0.StringValue(&version.ID), - Status: string(version.Status), - Runtime: auth0.StringValue(&version.Runtime), - CreatedAt: timeAgo(auth0.TimeValue(version.CreatedAt)), + ID: auth0.StringValue(&version.ID), + ActionID: auth0.StringValue(version.Action.ID), + ActionName: auth0.StringValue(version.Action.Name), + Runtime: auth0.StringValue(&version.Runtime), + Status: string(version.Status), + CreatedAt: timeAgo(auth0.TimeValue(version.CreatedAt)), }) } diff --git a/internal/validators/action.go b/internal/validators/action.go index 2555661d1..528b43f69 100644 --- a/internal/validators/action.go +++ b/internal/validators/action.go @@ -2,6 +2,7 @@ package validators import ( "fmt" + "regexp" "strings" "gopkg.in/auth0.v5/management" @@ -24,3 +25,37 @@ func TriggerID(trigger string) error { return fmt.Errorf("%s is not a valid trigger type (%s)", trigger, strings.Join(allTriggerIDs, ", ")) } + +var semverRe = regexp.MustCompile(`^(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`) + +// Dependencies parses a slice of dependencies of the format @ and returns them in their +// management.Dependency form. +func Dependencies(deps []string) ([]management.Dependency, error) { + dependencies := make([]management.Dependency, 0) + for _, dep := range deps { + name, version, err := splitNameVersion(dep) + if err != nil { + return nil, err + } + dependencies = append(dependencies, management.Dependency{ + Name: name, + Version: version, + }) + } + return dependencies, nil +} + +func splitNameVersion(val string) (string, string, error) { + parts := strings.SplitN(val, "@", 2) + if len(parts) != 2 { + return "", "", fmt.Errorf("dependency %s missing version", val) + } + + name, version := parts[0], parts[1] + + if !semverRe.MatchString(version) { + return "", "", fmt.Errorf("invalid semver %s for dependency %s", name, version) + } + + return name, version, nil +} diff --git a/vendor/gopkg.in/auth0.v5/management/actions.go b/vendor/gopkg.in/auth0.v5/management/actions.go index f65d537fe..b591ff390 100644 --- a/vendor/gopkg.in/auth0.v5/management/actions.go +++ b/vendor/gopkg.in/auth0.v5/management/actions.go @@ -41,7 +41,7 @@ type ActionVersion struct { ID string `json:"id,omitempty"` Action *Action `json:"action,omitempty"` Code string `json:"code,omitempty"` - Dependencies []Dependency `json:"dependencies,omitempty"` + Dependencies []Dependency `json:"dependencies"` Runtime string `json:"runtime,omitempty"` Status VersionStatus `json:"status,omitempty"` Number int `json:"number,omitempty"` @@ -156,6 +156,12 @@ func (m *ActionVersionManager) UpsertDraft(actionID string, v *ActionVersion) er return m.Request("PATCH", m.URI("actions", "actions", actionID, "versions", "draft"), v) } +func (m *ActionVersionManager) ReadDraft(actionID string) (*ActionVersion, error) { + var v ActionVersion + err := m.Request("GET", m.URI("actions", "actions", actionID, "versions", "draft"), &v) + return &v, err +} + func (m *ActionVersionManager) Read(actionID, id string) (*ActionVersion, error) { var v ActionVersion err := m.Request("GET", m.URI("actions", "actions", actionID, "versions", id), &v) diff --git a/vendor/modules.txt b/vendor/modules.txt index deeac9a9b..2fc13924e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -139,7 +139,7 @@ google.golang.org/protobuf/reflect/protoreflect google.golang.org/protobuf/reflect/protoregistry google.golang.org/protobuf/runtime/protoiface google.golang.org/protobuf/runtime/protoimpl -# gopkg.in/auth0.v5 v5.8.0 => github.com/go-auth0/auth0 v1.3.1-0.20210127040011-acb7c665d062 +# gopkg.in/auth0.v5 v5.8.0 => github.com/go-auth0/auth0 v1.3.1-0.20210127122814-819354e637e9 ## explicit gopkg.in/auth0.v5 gopkg.in/auth0.v5/internal/client @@ -150,4 +150,4 @@ gopkg.in/auth0.v5/management # gopkg.in/yaml.v2 v2.2.8 ## explicit gopkg.in/yaml.v2 -# gopkg.in/auth0.v5 => github.com/go-auth0/auth0 v1.3.1-0.20210127040011-acb7c665d062 +# gopkg.in/auth0.v5 => github.com/go-auth0/auth0 v1.3.1-0.20210127122814-819354e637e9