From 58f932bfa29b0e30b88c1d19fd79ed3f47288e01 Mon Sep 17 00:00:00 2001 From: Adam Williams Date: Tue, 7 Feb 2023 13:37:47 -0700 Subject: [PATCH 01/10] feat(service-resource): Add Service Resource commands https://developer.fastly.com/reference/api/services/resource/ This adds a `service-resource` sub-command which has associated create, delete, list, and update commands. Some of the names in the API may be confusing. I've done my best to add clarification whenever possible. Specifically: - A resource, as named by the API, is truly a _link_ between a service and a resource (e.g. an Object Store), not the resource itself. I've tried to use "resource **link**" wherever appropriate, as also used by the API docs: > A resource represents a link between a resource type and a service version. - In some places, the API mixes `id` and `resource_id` which both refer to the ID of the resource _link_, and not the resource itself. I've changed this so that `id` always refers the resource link, and `resource_id` to the linked to object. See this code comment: https://github.com/fastly/go-fastly/blob/ef4694a59779f202d8ea630b41333eb2b76969e8/fastly/resource.go#L125-L129 - It's possible to provide a resource link name. If set, this is an alias for the resource. Otherwise, whatever name was used when creating the resource itself will be the name to reference from the service for the resource. I've added to the description that this is an _alias_. See this code comment: https://github.com/fastly/go-fastly/blob/ef4694a59779f202d8ea630b41333eb2b76969e8/fastly/resource.go#L86-L88 I'd like to look into modifying `go-fastly` to remove the need for the `jsonResource` type, and also into updating the API to be more consistent with `id` vs `resource_id`. --- pkg/api/interface.go | 4 + pkg/app/commands.go | 13 + pkg/app/run_test.go | 1 + pkg/commands/serviceresource/create.go | 120 ++++ pkg/commands/serviceresource/delete.go | 120 ++++ pkg/commands/serviceresource/describe.go | 104 +++ pkg/commands/serviceresource/doc.go | 4 + pkg/commands/serviceresource/json.go | 67 ++ pkg/commands/serviceresource/list.go | 103 +++ pkg/commands/serviceresource/root.go | 30 + .../serviceresource/serviceresource_test.go | 607 ++++++++++++++++++ pkg/commands/serviceresource/update.go | 119 ++++ pkg/mock/api.go | 24 + pkg/text/resource.go | 37 ++ 14 files changed, 1353 insertions(+) create mode 100644 pkg/commands/serviceresource/create.go create mode 100644 pkg/commands/serviceresource/delete.go create mode 100644 pkg/commands/serviceresource/describe.go create mode 100644 pkg/commands/serviceresource/doc.go create mode 100644 pkg/commands/serviceresource/json.go create mode 100644 pkg/commands/serviceresource/list.go create mode 100644 pkg/commands/serviceresource/root.go create mode 100644 pkg/commands/serviceresource/serviceresource_test.go create mode 100644 pkg/commands/serviceresource/update.go create mode 100644 pkg/text/resource.go diff --git a/pkg/api/interface.go b/pkg/api/interface.go index d20c674d3..1a664efd7 100644 --- a/pkg/api/interface.go +++ b/pkg/api/interface.go @@ -347,6 +347,10 @@ type Interface interface { ListSecrets(i *fastly.ListSecretsInput) (*fastly.Secrets, error) CreateResource(i *fastly.CreateResourceInput) (*fastly.Resource, error) + DeleteResource(i *fastly.DeleteResourceInput) error + GetResource(i *fastly.GetResourceInput) (*fastly.Resource, error) + ListResources(i *fastly.ListResourcesInput) ([]*fastly.Resource, error) + UpdateResource(i *fastly.UpdateResourceInput) (*fastly.Resource, error) } // RealtimeStatsInterface is the subset of go-fastly's realtime stats API used here. diff --git a/pkg/app/commands.go b/pkg/app/commands.go index d16561bdd..a784a0ddf 100644 --- a/pkg/app/commands.go +++ b/pkg/app/commands.go @@ -50,6 +50,7 @@ import ( "github.com/fastly/cli/pkg/commands/secretstoreentry" "github.com/fastly/cli/pkg/commands/service" "github.com/fastly/cli/pkg/commands/serviceauth" + "github.com/fastly/cli/pkg/commands/serviceresource" "github.com/fastly/cli/pkg/commands/serviceversion" "github.com/fastly/cli/pkg/commands/shellcomplete" "github.com/fastly/cli/pkg/commands/stats" @@ -341,6 +342,12 @@ func defineCommands( serviceauthDescribe := serviceauth.NewDescribeCommand(serviceauthCmdRoot.CmdClause, g, m) serviceauthList := serviceauth.NewListCommand(serviceauthCmdRoot.CmdClause, g) serviceauthUpdate := serviceauth.NewUpdateCommand(serviceauthCmdRoot.CmdClause, g, m) + serviceresourceCmdRoot := serviceresource.NewRootCommand(app, g) + serviceresourceCreate := serviceresource.NewCreateCommand(serviceresourceCmdRoot.CmdClause, g, m) + serviceresourceDelete := serviceresource.NewDeleteCommand(serviceresourceCmdRoot.CmdClause, g, m) + serviceresourceDescribe := serviceresource.NewDescribeCommand(serviceresourceCmdRoot.CmdClause, g, m) + serviceresourceList := serviceresource.NewListCommand(serviceresourceCmdRoot.CmdClause, g, m) + serviceresourceUpdate := serviceresource.NewUpdateCommand(serviceresourceCmdRoot.CmdClause, g, m) serviceVersionCmdRoot := serviceversion.NewRootCommand(app, g) serviceVersionActivate := serviceversion.NewActivateCommand(serviceVersionCmdRoot.CmdClause, g, m) serviceVersionClone := serviceversion.NewCloneCommand(serviceVersionCmdRoot.CmdClause, g, m) @@ -669,6 +676,12 @@ func defineCommands( serviceauthDescribe, serviceauthList, serviceauthUpdate, + serviceresourceCmdRoot, + serviceresourceCreate, + serviceresourceDelete, + serviceresourceDescribe, + serviceresourceList, + serviceresourceUpdate, serviceVersionActivate, serviceVersionClone, serviceVersionCmdRoot, diff --git a/pkg/app/run_test.go b/pkg/app/run_test.go index 1f81ca3bc..f377f961d 100644 --- a/pkg/app/run_test.go +++ b/pkg/app/run_test.go @@ -79,6 +79,7 @@ secret-store secret-store-entry service service-auth +service-resource service-version stats tls-config diff --git a/pkg/commands/serviceresource/create.go b/pkg/commands/serviceresource/create.go new file mode 100644 index 000000000..f574ccc4b --- /dev/null +++ b/pkg/commands/serviceresource/create.go @@ -0,0 +1,120 @@ +package serviceresource + +import ( + "io" + + "github.com/fastly/cli/pkg/cmd" + fsterr "github.com/fastly/cli/pkg/errors" + "github.com/fastly/cli/pkg/global" + "github.com/fastly/cli/pkg/manifest" + "github.com/fastly/cli/pkg/text" + "github.com/fastly/go-fastly/v7/fastly" +) + +// CreateCommand calls the Fastly API to create a resource link. +type CreateCommand struct { + cmd.Base + jsonOutput + + autoClone cmd.OptionalAutoClone + input fastly.CreateResourceInput + manifest manifest.Data + serviceVersion cmd.OptionalServiceVersion +} + +// NewCreateCommand returns a usable command registered under the parent. +func NewCreateCommand(parent cmd.Registerer, globals *global.Data, data manifest.Data) *CreateCommand { + c := CreateCommand{ + Base: cmd.Base{ + Globals: globals, + }, + manifest: data, + input: fastly.CreateResourceInput{ + // Kingpin requires the following to be initialized. + ResourceID: new(string), + Name: new(string), + }, + } + c.CmdClause = parent.Command("create", "Create a Fastly service resource link").Alias("link") + + // Required. + c.RegisterFlag(cmd.StringFlagOpts{ + Name: "resource-id", + Short: 'r', + Description: "Resource ID", + Dst: c.input.ResourceID, + Required: true, + }) + c.RegisterFlag(cmd.StringFlagOpts{ + Name: cmd.FlagServiceIDName, + Short: 's', + Description: cmd.FlagServiceIDDesc, + Dst: &c.manifest.Flag.ServiceID, + Required: true, + }) + c.RegisterFlag(cmd.StringFlagOpts{ + Name: cmd.FlagVersionName, + Description: cmd.FlagVersionDesc, + Dst: &c.serviceVersion.Value, + Required: true, + }) + + // Optional. + c.RegisterAutoCloneFlag(cmd.AutoCloneFlagOpts{ + Action: c.autoClone.Set, + Dst: &c.autoClone.Value, + }) + c.RegisterFlagBool(c.jsonFlag()) // --json + c.RegisterFlag(cmd.StringFlagOpts{ + Name: "name", + Short: 'n', + Description: "Resource alias. Defaults to name of resource", + Dst: c.input.Name, + }) + + return &c +} + +// Exec invokes the application logic for the command. +func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { + if c.Globals.Verbose() && c.jsonOutput.enabled { + return fsterr.ErrInvalidVerboseJSONCombo + } + + serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ + AutoCloneFlag: c.autoClone, + APIClient: c.Globals.APIClient, + Manifest: c.manifest, + Out: out, + ServiceNameFlag: cmd.OptionalServiceNameID{}, // ServiceID flag is required, no need to lookup service by name. + ServiceVersionFlag: c.serviceVersion, + VerboseMode: c.Globals.Flags.Verbose, + }) + if err != nil { + c.Globals.ErrLog.AddWithContext(err, map[string]any{ + "Service ID": c.manifest.Flag.ServiceID, + "Service Version": fsterr.ServiceVersion(serviceVersion), + }) + return err + } + + c.input.ServiceID = serviceID + c.input.ServiceVersion = serviceVersion.Number + + resource, err := c.Globals.APIClient.CreateResource(&c.input) + if err != nil { + c.Globals.ErrLog.AddWithContext(err, map[string]any{ + "ID": c.input.ResourceID, + "Service ID": c.input.ServiceID, + "Service Version": c.input.ServiceVersion, + }) + return err + } + + if ok, err := c.WriteJSON(out, resource); ok { + return err + } + + text.Success(out, "Created service resource link %s on service %s version %s", resource.ID, resource.ServiceID, resource.ServiceVersion) + return nil +} diff --git a/pkg/commands/serviceresource/delete.go b/pkg/commands/serviceresource/delete.go new file mode 100644 index 000000000..b9a41cf33 --- /dev/null +++ b/pkg/commands/serviceresource/delete.go @@ -0,0 +1,120 @@ +package serviceresource + +import ( + "io" + + "github.com/fastly/cli/pkg/cmd" + fsterr "github.com/fastly/cli/pkg/errors" + "github.com/fastly/cli/pkg/global" + "github.com/fastly/cli/pkg/manifest" + "github.com/fastly/cli/pkg/text" + "github.com/fastly/go-fastly/v7/fastly" +) + +// DeleteCommand calls the Fastly API to delete service resource links. +type DeleteCommand struct { + cmd.Base + jsonOutput + + autoClone cmd.OptionalAutoClone + input fastly.DeleteResourceInput + manifest manifest.Data + serviceVersion cmd.OptionalServiceVersion +} + +// NewDeleteCommand returns a usable command registered under the parent. +func NewDeleteCommand(parent cmd.Registerer, globals *global.Data, data manifest.Data) *DeleteCommand { + c := DeleteCommand{ + Base: cmd.Base{ + Globals: globals, + }, + manifest: data, + } + c.CmdClause = parent.Command("delete", "Delete a resource link for a Fastly service version").Alias("remove") + + // Required. + c.RegisterFlag(cmd.StringFlagOpts{ + Name: "id", + Description: "ID of resource link", + Dst: &c.input.ResourceID, + Required: true, + }) + c.RegisterFlag(cmd.StringFlagOpts{ + Name: cmd.FlagServiceIDName, + Short: 's', + Description: cmd.FlagServiceIDDesc, + Dst: &c.manifest.Flag.ServiceID, + Required: true, + }) + c.RegisterFlag(cmd.StringFlagOpts{ + Name: cmd.FlagVersionName, + Description: cmd.FlagVersionDesc, + Dst: &c.serviceVersion.Value, + Required: true, + }) + + // Optional. + c.RegisterAutoCloneFlag(cmd.AutoCloneFlagOpts{ + Action: c.autoClone.Set, + Dst: &c.autoClone.Value, + }) + c.RegisterFlagBool(c.jsonFlag()) // --json + + return &c +} + +// Exec invokes the application logic for the command. +func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { + if c.Globals.Verbose() && c.jsonOutput.enabled { + return fsterr.ErrInvalidVerboseJSONCombo + } + + serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ + AutoCloneFlag: c.autoClone, + APIClient: c.Globals.APIClient, + Manifest: c.manifest, + Out: out, + ServiceNameFlag: cmd.OptionalServiceNameID{}, // ServiceID flag is required, no need to lookup service by name. + ServiceVersionFlag: c.serviceVersion, + VerboseMode: c.Globals.Flags.Verbose, + }) + if err != nil { + c.Globals.ErrLog.AddWithContext(err, map[string]any{ + "Service ID": c.manifest.Flag.ServiceID, + "Service Version": fsterr.ServiceVersion(serviceVersion), + }) + return err + } + + c.input.ServiceID = serviceID + c.input.ServiceVersion = serviceVersion.Number + + err = c.Globals.APIClient.DeleteResource(&c.input) + if err != nil { + c.Globals.ErrLog.AddWithContext(err, map[string]any{ + "ID": c.input.ResourceID, + "Service ID": c.input.ServiceID, + "Service Version": c.input.ServiceVersion, + }) + return err + } + + if c.jsonOutput.enabled { + o := struct { + ResourceID string `json:"id"` + ServiceID string `json:"service_id"` + ServiceVersion int `json:"service_version"` + Deleted bool `json:"deleted"` + }{ + c.input.ResourceID, + c.input.ServiceID, + c.input.ServiceVersion, + true, + } + _, err := c.WriteJSON(out, o) + return err + } + + text.Success(out, "Deleted service resource link %s from service %s version %d", c.input.ResourceID, c.input.ServiceID, c.input.ServiceVersion) + return nil +} diff --git a/pkg/commands/serviceresource/describe.go b/pkg/commands/serviceresource/describe.go new file mode 100644 index 000000000..12b89e1f8 --- /dev/null +++ b/pkg/commands/serviceresource/describe.go @@ -0,0 +1,104 @@ +package serviceresource + +import ( + "io" + + "github.com/fastly/cli/pkg/cmd" + fsterr "github.com/fastly/cli/pkg/errors" + "github.com/fastly/cli/pkg/global" + "github.com/fastly/cli/pkg/manifest" + "github.com/fastly/cli/pkg/text" + "github.com/fastly/go-fastly/v7/fastly" +) + +// DescribeCommand calls the Fastly API to describe a service resource link. +type DescribeCommand struct { + cmd.Base + jsonOutput + + input fastly.GetResourceInput + manifest manifest.Data + serviceVersion cmd.OptionalServiceVersion +} + +// NewDescribeCommand returns a usable command registered under the parent. +func NewDescribeCommand(parent cmd.Registerer, globals *global.Data, data manifest.Data) *DescribeCommand { + c := DescribeCommand{ + Base: cmd.Base{ + Globals: globals, + }, + manifest: data, + } + c.CmdClause = parent.Command("describe", "Show detailed information about a Fastly service resource link").Alias("get") + + // Required. + c.RegisterFlag(cmd.StringFlagOpts{ + Name: "id", + Description: "ID of resource link", + Dst: &c.input.ResourceID, + Required: true, + }) + c.RegisterFlag(cmd.StringFlagOpts{ + Name: cmd.FlagServiceIDName, + Short: 's', + Description: cmd.FlagServiceIDDesc, + Dst: &c.manifest.Flag.ServiceID, + Required: true, + }) + c.RegisterFlag(cmd.StringFlagOpts{ + Name: cmd.FlagVersionName, + Description: cmd.FlagVersionDesc, + Dst: &c.serviceVersion.Value, + Required: true, + }) + + // Optional. + c.RegisterFlagBool(c.jsonFlag()) // --json + + return &c +} + +// Exec invokes the application logic for the command. +func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { + if c.Globals.Verbose() && c.jsonOutput.enabled { + return fsterr.ErrInvalidVerboseJSONCombo + } + + serviceID, source, flag, err := cmd.ServiceID(cmd.OptionalServiceNameID{}, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + if err != nil { + return err + } + if c.Globals.Verbose() { + cmd.DisplayServiceID(serviceID, flag, source, out) + } + + serviceVersion, err := c.serviceVersion.Parse(serviceID, c.Globals.APIClient) + if err != nil { + return err + } + + c.input.ServiceID = serviceID + c.input.ServiceVersion = serviceVersion.Number + + resource, err := c.Globals.APIClient.GetResource(&c.input) + if err != nil { + c.Globals.ErrLog.AddWithContext(err, map[string]any{ + "ID": c.input.ResourceID, + "Service ID": c.input.ServiceID, + "Service Version": c.input.ServiceVersion, + }) + return err + } + + if ok, err := c.WriteJSON(out, resource); ok { + return err + } + + if !c.Globals.Verbose() { + text.Output(out, "Service ID: %s", resource.ServiceID) + } + text.Output(out, "Service Version: %s", resource.ServiceVersion) + text.PrintResource(out, "", resource) + + return nil +} diff --git a/pkg/commands/serviceresource/doc.go b/pkg/commands/serviceresource/doc.go new file mode 100644 index 000000000..5b1e24d9e --- /dev/null +++ b/pkg/commands/serviceresource/doc.go @@ -0,0 +1,4 @@ +// Package serviceresource contains commands to inspect and +// manipulate service resources. +// https://developer.fastly.com/reference/api/services/resource/ +package serviceresource diff --git a/pkg/commands/serviceresource/json.go b/pkg/commands/serviceresource/json.go new file mode 100644 index 000000000..aed1a400f --- /dev/null +++ b/pkg/commands/serviceresource/json.go @@ -0,0 +1,67 @@ +package serviceresource + +import ( + "encoding/json" + "io" + "time" + + "github.com/fastly/cli/pkg/cmd" + "github.com/fastly/go-fastly/v7/fastly" +) + +// jsonOutput is a helper for adding a `--json` flag and encoding +// values to JSON. It can be embedded into command structs. +type jsonOutput struct { + enabled bool // Set via flag. +} + +// jsonFlag creates a flag for enabling JSON output. +func (j *jsonOutput) jsonFlag() cmd.BoolFlagOpts { + return cmd.BoolFlagOpts{ + Name: cmd.FlagJSONName, + Description: cmd.FlagJSONDesc, + Dst: &j.enabled, + Short: 'j', + } +} + +// WriteJSON checks whether the enabled flag is set or not. If set, +// then the given value is written as JSON to out. Otherwise, false is returned. +func (j *jsonOutput) WriteJSON(out io.Writer, value any) (bool, error) { + if !j.enabled { + return false, nil + } + + // If value is a fastly.Resource (or variations of), then convert into + // a jsonResource for improved JSON encoding. + switch v := value.(type) { + case *fastly.Resource: + value = jsonResource(*v) + case fastly.Resource: + value = jsonResource(v) + case []*fastly.Resource: + cp := make([]jsonResource, len(v)) + for i := range v { + cp[i] = jsonResource(*v[i]) + } + value = cp + } + + enc := json.NewEncoder(out) + enc.SetIndent("", " ") + return true, enc.Encode(value) +} + +// jsonResource is a fastly.Resource with `json` field tags defined. +type jsonResource struct { + CreatedAt *time.Time `json:"created_at"` + DeletedAt *time.Time `json:"deleted_at"` + HREF string `json:"-"` // Omit this field from JSON output. + ID string `json:"id"` + Name string `json:"name"` + ResourceID string `json:"resource_id"` + ResourceType string `json:"resource_type"` + ServiceID string `json:"service_id"` + ServiceVersion string `json:"service_version"` + UpdatedAt *time.Time `json:"updated_at"` +} diff --git a/pkg/commands/serviceresource/list.go b/pkg/commands/serviceresource/list.go new file mode 100644 index 000000000..25c388832 --- /dev/null +++ b/pkg/commands/serviceresource/list.go @@ -0,0 +1,103 @@ +package serviceresource + +import ( + "fmt" + "io" + + "github.com/fastly/cli/pkg/cmd" + fsterr "github.com/fastly/cli/pkg/errors" + "github.com/fastly/cli/pkg/global" + "github.com/fastly/cli/pkg/manifest" + "github.com/fastly/cli/pkg/text" + "github.com/fastly/go-fastly/v7/fastly" +) + +// ListCommand calls the Fastly API to list service resource links +type ListCommand struct { + cmd.Base + jsonOutput + + input fastly.ListResourcesInput + manifest manifest.Data + serviceVersion cmd.OptionalServiceVersion +} + +// NewListCommand returns a usable command registered under the parent +func NewListCommand(parent cmd.Registerer, globals *global.Data, data manifest.Data) *ListCommand { + c := ListCommand{ + Base: cmd.Base{ + Globals: globals, + }, + manifest: data, + } + c.CmdClause = parent.Command("list", "List all resource links for a Fastly service version") + + // Required. + c.RegisterFlag(cmd.StringFlagOpts{ + Name: cmd.FlagServiceIDName, + Short: 's', + Description: cmd.FlagServiceIDDesc, + Dst: &c.manifest.Flag.ServiceID, + Required: true, + }) + c.RegisterFlag(cmd.StringFlagOpts{ + Name: cmd.FlagVersionName, + Description: cmd.FlagVersionDesc, + Dst: &c.serviceVersion.Value, + Required: true, + }) + + // Optional. + c.RegisterFlagBool(c.jsonFlag()) // --json + + return &c +} + +// Exec invokes the application logic for the command. +func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { + if c.Globals.Verbose() && c.jsonOutput.enabled { + return fsterr.ErrInvalidVerboseJSONCombo + } + + serviceID, source, flag, err := cmd.ServiceID(cmd.OptionalServiceNameID{}, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + if err != nil { + return err + } + if c.Globals.Verbose() { + cmd.DisplayServiceID(serviceID, flag, source, out) + } + + serviceVersion, err := c.serviceVersion.Parse(serviceID, c.Globals.APIClient) + if err != nil { + return err + } + + c.input.ServiceID = serviceID + c.input.ServiceVersion = serviceVersion.Number + + resources, err := c.Globals.APIClient.ListResources(&c.input) + if err != nil { + c.Globals.ErrLog.AddWithContext(err, map[string]any{ + "Service ID": c.input.ServiceID, + "Service Version": c.input.ServiceVersion, + }) + return err + } + + if ok, err := c.WriteJSON(out, resources); ok { + return err + } + + if !c.Globals.Verbose() { + fmt.Fprintf(out, "Service ID: %s\n", c.input.ServiceID) + } + text.Output(out, "Service Version: %d\n", c.input.ServiceVersion) + + for i, resource := range resources { + fmt.Fprintf(out, "Resource Link %d/%d\n", i+1, len(resources)) + text.PrintResource(out, "\t", resource) + fmt.Fprintln(out) + } + + return nil +} diff --git a/pkg/commands/serviceresource/root.go b/pkg/commands/serviceresource/root.go new file mode 100644 index 000000000..7e29f0cb3 --- /dev/null +++ b/pkg/commands/serviceresource/root.go @@ -0,0 +1,30 @@ +package serviceresource + +import ( + "io" + + "github.com/fastly/cli/pkg/cmd" + "github.com/fastly/cli/pkg/global" +) + +const RootName = "service-resource" + +// RootCommand is the parent command for all subcommands in this package. +// It should be installed under the primary root command. +type RootCommand struct { + cmd.Base + // no flags +} + +// NewRootCommand returns a new command registered in the parent. +func NewRootCommand(parent cmd.Registerer, globals *global.Data) *RootCommand { + var c RootCommand + c.Globals = globals + c.CmdClause = parent.Command(RootName, "Manipulate Fastly service resources") + return &c +} + +// Exec implements the command interface. +func (c *RootCommand) Exec(_ io.Reader, _ io.Writer) error { + panic("unreachable") +} diff --git a/pkg/commands/serviceresource/serviceresource_test.go b/pkg/commands/serviceresource/serviceresource_test.go new file mode 100644 index 000000000..3f69d81a1 --- /dev/null +++ b/pkg/commands/serviceresource/serviceresource_test.go @@ -0,0 +1,607 @@ +package serviceresource_test + +import ( + "bytes" + "fmt" + "strings" + "testing" + "time" + + "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/commands/serviceresource" + "github.com/fastly/cli/pkg/mock" + "github.com/fastly/cli/pkg/testutil" + "github.com/fastly/go-fastly/v7/fastly" +) + +func TestCreateServiceResourceCommand(t *testing.T) { + scenarios := []struct { + args string + api mock.API + wantAPIInvoked bool + wantError string + wantOutput string + }{ + // Missing required arguments. + { + args: "create --service-id abc --resource-id 123", + wantError: "error parsing arguments: required flag --version not provided", + wantAPIInvoked: false, + }, + { + args: "create --service-id abc --version latest", + wantError: "error parsing arguments: required flag --resource-id not provided", + wantAPIInvoked: false, + }, + { + args: "create --resource-id abc --version latest", + wantError: "error parsing arguments: required flag --service-id not provided", + wantAPIInvoked: false, + }, + // Success. + { + args: "create --resource-id abc --service-id 123 --version 42", + api: mock.API{ + ListVersionsFn: func(i *fastly.ListVersionsInput) ([]*fastly.Version, error) { + return []*fastly.Version{{Number: 42}}, nil + }, + CreateResourceFn: func(i *fastly.CreateResourceInput) (*fastly.Resource, error) { + if got, want := *i.ResourceID, "abc"; got != want { + return nil, fmt.Errorf("ResourceID: got %q, want %q", got, want) + } + if got, want := i.ServiceID, "123"; got != want { + return nil, fmt.Errorf("ServiceID: got %q, want %q", got, want) + } + if got, want := *i.Name, ""; got != want { + return nil, fmt.Errorf("Name: got %q, want %q", got, want) + } + now := time.Now() + return &fastly.Resource{ + ID: "rand-id", + ResourceID: "abc", + ServiceID: "123", + ServiceVersion: "42", + CreatedAt: &now, + UpdatedAt: &now, + }, nil + }, + }, + wantAPIInvoked: true, + wantOutput: "SUCCESS: Created service resource link rand-id on service 123 version 42", + }, + // Success with --name. + { + args: "create --resource-id abc --service-id 123 --version 42 --name testing", + api: mock.API{ + ListVersionsFn: func(i *fastly.ListVersionsInput) ([]*fastly.Version, error) { + return []*fastly.Version{{Number: 42}}, nil + }, + CreateResourceFn: func(i *fastly.CreateResourceInput) (*fastly.Resource, error) { + if got, want := *i.ResourceID, "abc"; got != want { + return nil, fmt.Errorf("ResourceID: got %q, want %q", got, want) + } + if got, want := i.ServiceID, "123"; got != want { + return nil, fmt.Errorf("ServiceID: got %q, want %q", got, want) + } + if got, want := *i.Name, "testing"; got != want { + return nil, fmt.Errorf("Name: got %q, want %q", got, want) + } + now := time.Now() + return &fastly.Resource{ + ID: "rand-id", + ResourceID: "abc", + ServiceID: "123", + ServiceVersion: "42", + CreatedAt: &now, + UpdatedAt: &now, + }, nil + }, + }, + wantAPIInvoked: true, + wantOutput: "SUCCESS: Created service resource link rand-id on service 123 version 42", + }, + // Success with --autoclone. + { + args: "create --resource-id abc --service-id 123 --version=latest --autoclone", + api: mock.API{ + ListVersionsFn: func(i *fastly.ListVersionsInput) ([]*fastly.Version, error) { + // Specified version is active, meaning a service clone will be attempted. + return []*fastly.Version{{Active: true, Number: 42}}, nil + }, + CloneVersionFn: func(i *fastly.CloneVersionInput) (*fastly.Version, error) { + return &fastly.Version{Number: 43}, nil + }, + CreateResourceFn: func(i *fastly.CreateResourceInput) (*fastly.Resource, error) { + if got, want := *i.ResourceID, "abc"; got != want { + return nil, fmt.Errorf("ResourceID: got %q, want %q", got, want) + } + if got, want := i.ServiceID, "123"; got != want { + return nil, fmt.Errorf("ServiceID: got %q, want %q", got, want) + } + if got, want := *i.Name, ""; got != want { + return nil, fmt.Errorf("Name: got %q, want %q", got, want) + } + now := time.Now() + return &fastly.Resource{ + ID: "rand-id", + ResourceID: "abc", + ServiceID: "123", + ServiceVersion: "43", // Cloned version. + CreatedAt: &now, + UpdatedAt: &now, + }, nil + }, + }, + wantAPIInvoked: true, + wantOutput: "SUCCESS: Created service resource link rand-id on service 123 version 43", + }, + } + + for _, testcase := range scenarios { + testcase := testcase + t.Run(testcase.args, func(t *testing.T) { + var stdout bytes.Buffer + opts := testutil.NewRunOpts(testutil.Args(serviceresource.RootName+" "+testcase.args), &stdout) + + f := testcase.api.CreateResourceFn + var apiInvoked bool + testcase.api.CreateResourceFn = func(i *fastly.CreateResourceInput) (*fastly.Resource, error) { + apiInvoked = true + return f(i) + } + + opts.APIClient = mock.APIClient(testcase.api) + + err := app.Run(opts) + + testutil.AssertErrorContains(t, err, testcase.wantError) + testutil.AssertString(t, testcase.wantOutput, strings.TrimSpace(stdout.String())) + if apiInvoked != testcase.wantAPIInvoked { + t.Fatalf("API CreateResource invoked = %v, want %v", apiInvoked, testcase.wantAPIInvoked) + } + }) + } +} + +func TestDeleteServiceResourceCommand(t *testing.T) { + scenarios := []struct { + args string + api mock.API + wantAPIInvoked bool + wantError string + wantOutput string + }{ + // Missing required arguments. + { + args: "delete --id LINK-ID --service-id abc", + wantError: "error parsing arguments: required flag --version not provided", + wantAPIInvoked: false, + }, + { + args: "delete --id LINK-ID --version 123", + wantError: "error parsing arguments: required flag --service-id not provided", + wantAPIInvoked: false, + }, + { + args: "delete --service-id abc --version 123", + wantError: "error parsing arguments: required flag --id not provided", + wantAPIInvoked: false, + }, + // Success. + { + args: "delete --service-id 123 --version 42 --id LINKID", + api: mock.API{ + ListVersionsFn: func(i *fastly.ListVersionsInput) ([]*fastly.Version, error) { + return []*fastly.Version{{Number: 42}}, nil + }, + DeleteResourceFn: func(i *fastly.DeleteResourceInput) error { + if got, want := i.ResourceID, "LINKID"; got != want { + return fmt.Errorf("ResourceID: got %q, want %q", got, want) + } + if got, want := i.ServiceID, "123"; got != want { + return fmt.Errorf("ServiceID: got %q, want %q", got, want) + } + if got, want := i.ServiceVersion, 42; got != want { + return fmt.Errorf("ServiceVersion: got %d, want %d", got, want) + } + return nil + }, + }, + wantAPIInvoked: true, + wantOutput: "SUCCESS: Deleted service resource link LINKID from service 123 version 42", + }, + // Success with --autoclone. + { + args: "delete --service-id 123 --version 42 --id LINKID --autoclone", + api: mock.API{ + ListVersionsFn: func(i *fastly.ListVersionsInput) ([]*fastly.Version, error) { + // Specified version is active, meaning a service clone will be attempted. + return []*fastly.Version{{Active: true, Number: 42}}, nil + }, + CloneVersionFn: func(i *fastly.CloneVersionInput) (*fastly.Version, error) { + return &fastly.Version{Number: 43}, nil + }, + DeleteResourceFn: func(i *fastly.DeleteResourceInput) error { + if got, want := i.ResourceID, "LINKID"; got != want { + return fmt.Errorf("ResourceID: got %q, want %q", got, want) + } + if got, want := i.ServiceID, "123"; got != want { + return fmt.Errorf("ServiceID: got %q, want %q", got, want) + } + if got, want := i.ServiceVersion, 43; got != want { + return fmt.Errorf("ServiceVersion: got %d, want %d", got, want) + } + return nil + }, + }, + wantAPIInvoked: true, + wantOutput: "SUCCESS: Deleted service resource link LINKID from service 123 version 43", + }, + } + + for _, testcase := range scenarios { + testcase := testcase + t.Run(testcase.args, func(t *testing.T) { + var stdout bytes.Buffer + opts := testutil.NewRunOpts(testutil.Args(serviceresource.RootName+" "+testcase.args), &stdout) + + f := testcase.api.DeleteResourceFn + var apiInvoked bool + testcase.api.DeleteResourceFn = func(i *fastly.DeleteResourceInput) error { + apiInvoked = true + return f(i) + } + + opts.APIClient = mock.APIClient(testcase.api) + + err := app.Run(opts) + + testutil.AssertErrorContains(t, err, testcase.wantError) + testutil.AssertString(t, testcase.wantOutput, strings.TrimSpace(stdout.String())) + if apiInvoked != testcase.wantAPIInvoked { + t.Fatalf("API DeleteResource invoked = %v, want %v", apiInvoked, testcase.wantAPIInvoked) + } + }) + } +} + +func TestDescribeServiceResourceCommand(t *testing.T) { + scenarios := []struct { + args string + api mock.API + wantAPIInvoked bool + wantError string + wantOutput string + }{ + // Missing required arguments. + { + args: "describe --id LINK-ID --service-id abc", + wantError: "error parsing arguments: required flag --version not provided", + wantAPIInvoked: false, + }, + { + args: "describe --id LINK-ID --version 123", + wantError: "error parsing arguments: required flag --service-id not provided", + wantAPIInvoked: false, + }, + { + args: "describe --service-id abc --version 123", + wantError: "error parsing arguments: required flag --id not provided", + wantAPIInvoked: false, + }, + // Success. + { + args: "describe --service-id 123 --version 42 --id LINKID", + api: mock.API{ + ListVersionsFn: func(i *fastly.ListVersionsInput) ([]*fastly.Version, error) { + return []*fastly.Version{{Number: 42}}, nil + }, + GetResourceFn: func(i *fastly.GetResourceInput) (*fastly.Resource, error) { + if got, want := i.ResourceID, "LINKID"; got != want { + return nil, fmt.Errorf("ResourceID: got %q, want %q", got, want) + } + if got, want := i.ServiceID, "123"; got != want { + return nil, fmt.Errorf("ServiceID: got %q, want %q", got, want) + } + if got, want := i.ServiceVersion, 42; got != want { + return nil, fmt.Errorf("ServiceVersion: got %d, want %d", got, want) + } + now := time.Unix(1697372322, 0) + return &fastly.Resource{ + ID: "LINKID", + ResourceID: "abc", + ResourceType: "secret-store", + Name: "test-name", + ServiceID: "123", + ServiceVersion: "42", + CreatedAt: &now, + UpdatedAt: &now, + }, nil + }, + }, + wantAPIInvoked: true, + wantOutput: `Service ID: 123 +Service Version: 42 +ID: LINKID +Name: test-name +Service ID: 123 +Service Version: 42 +Resource ID: abc +Resource Type: secret-store +Created (UTC): 2023-10-15 12:18 +Last edited (UTC): 2023-10-15 12:18`, + }, + } + + for _, testcase := range scenarios { + testcase := testcase + t.Run(testcase.args, func(t *testing.T) { + var stdout bytes.Buffer + opts := testutil.NewRunOpts(testutil.Args(serviceresource.RootName+" "+testcase.args), &stdout) + + f := testcase.api.GetResourceFn + var apiInvoked bool + testcase.api.GetResourceFn = func(i *fastly.GetResourceInput) (*fastly.Resource, error) { + apiInvoked = true + return f(i) + } + + opts.APIClient = mock.APIClient(testcase.api) + + err := app.Run(opts) + + testutil.AssertErrorContains(t, err, testcase.wantError) + testutil.AssertString(t, testcase.wantOutput, strings.TrimSpace(stdout.String())) + if apiInvoked != testcase.wantAPIInvoked { + t.Fatalf("API DescribeResource invoked = %v, want %v", apiInvoked, testcase.wantAPIInvoked) + } + }) + } +} + +func TestListServiceResourceCommand(t *testing.T) { + scenarios := []struct { + args string + api mock.API + wantAPIInvoked bool + wantError string + wantOutput string + }{ + // Missing required arguments. + { + args: "list --service-id abc", + wantError: "error parsing arguments: required flag --version not provided", + wantAPIInvoked: false, + }, + { + args: "list --version 123", + wantError: "error parsing arguments: required flag --service-id not provided", + wantAPIInvoked: false, + }, + // Success. + { + args: "list --service-id 123 --version 42", + api: mock.API{ + ListVersionsFn: func(i *fastly.ListVersionsInput) ([]*fastly.Version, error) { + return []*fastly.Version{{Number: 42}}, nil + }, + ListResourcesFn: func(i *fastly.ListResourcesInput) ([]*fastly.Resource, error) { + if got, want := i.ServiceID, "123"; got != want { + return nil, fmt.Errorf("ServiceID: got %q, want %q", got, want) + } + if got, want := i.ServiceVersion, 42; got != want { + return nil, fmt.Errorf("ServiceVersion: got %d, want %d", got, want) + } + + now := time.Unix(1697372322, 0) + resources := make([]*fastly.Resource, 3) + for i := range resources { + resources[i] = &fastly.Resource{ + ID: fmt.Sprintf("LINKID-%02d", i), + ResourceID: "abc", + ResourceType: "secret-store", + Name: "test-name", + ServiceID: "123", + ServiceVersion: "42", + CreatedAt: &now, + UpdatedAt: &now, + } + } + return resources, nil + }, + }, + wantAPIInvoked: true, + wantOutput: `Service ID: 123 +Service Version: 42 +Resource Link 1/3 + ID: LINKID-00 + Name: test-name + Service ID: 123 + Service Version: 42 + Resource ID: abc + Resource Type: secret-store + Created (UTC): 2023-10-15 12:18 + Last edited (UTC): 2023-10-15 12:18 + +Resource Link 2/3 + ID: LINKID-01 + Name: test-name + Service ID: 123 + Service Version: 42 + Resource ID: abc + Resource Type: secret-store + Created (UTC): 2023-10-15 12:18 + Last edited (UTC): 2023-10-15 12:18 + +Resource Link 3/3 + ID: LINKID-02 + Name: test-name + Service ID: 123 + Service Version: 42 + Resource ID: abc + Resource Type: secret-store + Created (UTC): 2023-10-15 12:18 + Last edited (UTC): 2023-10-15 12:18`, + }, + } + + for _, testcase := range scenarios { + testcase := testcase + t.Run(testcase.args, func(t *testing.T) { + var stdout bytes.Buffer + opts := testutil.NewRunOpts(testutil.Args(serviceresource.RootName+" "+testcase.args), &stdout) + + f := testcase.api.ListResourcesFn + var apiInvoked bool + testcase.api.ListResourcesFn = func(i *fastly.ListResourcesInput) ([]*fastly.Resource, error) { + apiInvoked = true + return f(i) + } + + opts.APIClient = mock.APIClient(testcase.api) + + err := app.Run(opts) + + testutil.AssertErrorContains(t, err, testcase.wantError) + testutil.AssertString(t, testcase.wantOutput, strings.ReplaceAll(strings.TrimSpace(stdout.String()), "\t", " ")) + if apiInvoked != testcase.wantAPIInvoked { + t.Fatalf("API ListResources invoked = %v, want %v", apiInvoked, testcase.wantAPIInvoked) + } + }) + } +} + +func TestUpdateServiceResourceCommand(t *testing.T) { + scenarios := []struct { + args string + api mock.API + wantAPIInvoked bool + wantError string + wantOutput string + }{ + // Missing required arguments. + { + args: "update --id LINK-ID --name new-name --service-id abc", + wantError: "error parsing arguments: required flag --version not provided", + wantAPIInvoked: false, + }, + { + args: "update --id LINK-ID --name new-name --version 123", + wantError: "error parsing arguments: required flag --service-id not provided", + wantAPIInvoked: false, + }, + { + args: "update --id LINK-ID --service-id abc --version 123", + wantError: "error parsing arguments: required flag --name not provided", + wantAPIInvoked: false, + }, + { + args: "update --name new-name --service-id abc --version 123", + wantError: "error parsing arguments: required flag --id not provided", + wantAPIInvoked: false, + }, + // Success. + { + args: "update --id LINK-ID --name new-name --service-id 123 --version 42", + api: mock.API{ + ListVersionsFn: func(i *fastly.ListVersionsInput) ([]*fastly.Version, error) { + return []*fastly.Version{{Number: 42}}, nil + }, + UpdateResourceFn: func(i *fastly.UpdateResourceInput) (*fastly.Resource, error) { + if got, want := i.ResourceID, "LINK-ID"; got != want { + return nil, fmt.Errorf("ResourceID: got %q, want %q", got, want) + } + if got, want := *i.Name, "new-name"; got != want { + return nil, fmt.Errorf("Name: got %q, want %q", got, want) + } + if got, want := i.ServiceID, "123"; got != want { + return nil, fmt.Errorf("ServiceID: got %q, want %q", got, want) + } + if got, want := i.ServiceVersion, 42; got != want { + return nil, fmt.Errorf("ServiceVersion: got %d, want %d", got, want) + } + + now := time.Now() + return &fastly.Resource{ + ID: "LINK-ID", + ResourceID: "abc", + ResourceType: "secret-store", + Name: "new-name", + ServiceID: "123", + ServiceVersion: "42", + CreatedAt: &now, + UpdatedAt: &now, + }, nil + }, + }, + wantAPIInvoked: true, + wantOutput: "SUCCESS: Updated service resource link LINK-ID on service 123 version 42", + }, + // Success with --autoclone. + { + args: "update --id LINK-ID --name new-name --service-id 123 --version 42 --autoclone", + api: mock.API{ + ListVersionsFn: func(i *fastly.ListVersionsInput) ([]*fastly.Version, error) { + // Specified version is active, meaning a service clone will be attempted. + return []*fastly.Version{{Active: true, Number: 42}}, nil + }, + CloneVersionFn: func(i *fastly.CloneVersionInput) (*fastly.Version, error) { + return &fastly.Version{Number: 43}, nil + }, + UpdateResourceFn: func(i *fastly.UpdateResourceInput) (*fastly.Resource, error) { + if got, want := i.ResourceID, "LINK-ID"; got != want { + return nil, fmt.Errorf("ResourceID: got %q, want %q", got, want) + } + if got, want := *i.Name, "new-name"; got != want { + return nil, fmt.Errorf("Name: got %q, want %q", got, want) + } + if got, want := i.ServiceID, "123"; got != want { + return nil, fmt.Errorf("ServiceID: got %q, want %q", got, want) + } + if got, want := i.ServiceVersion, 43; got != want { + return nil, fmt.Errorf("ServiceVersion: got %d, want %d", got, want) + } + + now := time.Now() + return &fastly.Resource{ + ID: "LINK-ID", + ResourceID: "abc", + ResourceType: "secret-store", + Name: "new-name", + ServiceID: "123", + ServiceVersion: "43", + CreatedAt: &now, + UpdatedAt: &now, + }, nil + }, + }, + wantAPIInvoked: true, + wantOutput: "SUCCESS: Updated service resource link LINK-ID on service 123 version 43", + }, + } + + for _, testcase := range scenarios { + testcase := testcase + t.Run(testcase.args, func(t *testing.T) { + var stdout bytes.Buffer + opts := testutil.NewRunOpts(testutil.Args(serviceresource.RootName+" "+testcase.args), &stdout) + + f := testcase.api.UpdateResourceFn + var apiInvoked bool + testcase.api.UpdateResourceFn = func(i *fastly.UpdateResourceInput) (*fastly.Resource, error) { + apiInvoked = true + return f(i) + } + + opts.APIClient = mock.APIClient(testcase.api) + + err := app.Run(opts) + + testutil.AssertErrorContains(t, err, testcase.wantError) + testutil.AssertString(t, testcase.wantOutput, strings.TrimSpace(stdout.String())) + if apiInvoked != testcase.wantAPIInvoked { + t.Fatalf("API UpdateResource invoked = %v, want %v", apiInvoked, testcase.wantAPIInvoked) + } + }) + } +} diff --git a/pkg/commands/serviceresource/update.go b/pkg/commands/serviceresource/update.go new file mode 100644 index 000000000..e25d6cc53 --- /dev/null +++ b/pkg/commands/serviceresource/update.go @@ -0,0 +1,119 @@ +package serviceresource + +import ( + "io" + + "github.com/fastly/cli/pkg/cmd" + fsterr "github.com/fastly/cli/pkg/errors" + "github.com/fastly/cli/pkg/global" + "github.com/fastly/cli/pkg/manifest" + "github.com/fastly/cli/pkg/text" + "github.com/fastly/go-fastly/v7/fastly" +) + +// UpdateCommand calls the Fastly API to update a dictionary. +type UpdateCommand struct { + cmd.Base + jsonOutput + + autoClone cmd.OptionalAutoClone + input fastly.UpdateResourceInput + manifest manifest.Data + serviceVersion cmd.OptionalServiceVersion +} + +// NewUpdateCommand returns a usable command registered under the parent. +func NewUpdateCommand(parent cmd.Registerer, globals *global.Data, data manifest.Data) *UpdateCommand { + c := UpdateCommand{ + Base: cmd.Base{ + Globals: globals, + }, + manifest: data, + input: fastly.UpdateResourceInput{ + // Kingpin requires the following to be initialized. + Name: new(string), + }, + } + c.CmdClause = parent.Command("update", "Update a resource link for a Fastly service version") + + // Required. + c.RegisterFlag(cmd.StringFlagOpts{ + Name: "id", + Description: "ID of resource link", + Dst: &c.input.ResourceID, + Required: true, + }) + c.RegisterFlag(cmd.StringFlagOpts{ + Name: "name", + Short: 'n', + Description: "Resource name (alias)", + Dst: c.input.Name, + Required: true, + }) + c.RegisterFlag(cmd.StringFlagOpts{ + Name: cmd.FlagServiceIDName, + Short: 's', + Description: cmd.FlagServiceIDDesc, + Dst: &c.manifest.Flag.ServiceID, + Required: true, + }) + c.RegisterFlag(cmd.StringFlagOpts{ + Name: cmd.FlagVersionName, + Description: cmd.FlagVersionDesc, + Dst: &c.serviceVersion.Value, + Required: true, + }) + + // Optional. + c.RegisterAutoCloneFlag(cmd.AutoCloneFlagOpts{ + Action: c.autoClone.Set, + Dst: &c.autoClone.Value, + }) + c.RegisterFlagBool(c.jsonFlag()) // --json + + return &c +} + +// Exec invokes the application logic for the command. +func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { + if c.Globals.Verbose() && c.jsonOutput.enabled { + return fsterr.ErrInvalidVerboseJSONCombo + } + + serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ + AutoCloneFlag: c.autoClone, + APIClient: c.Globals.APIClient, + Manifest: c.manifest, + Out: out, + ServiceNameFlag: cmd.OptionalServiceNameID{}, // ServiceID flag is required, no need to lookup service by name. + ServiceVersionFlag: c.serviceVersion, + VerboseMode: c.Globals.Flags.Verbose, + }) + if err != nil { + c.Globals.ErrLog.AddWithContext(err, map[string]any{ + "Service ID": c.manifest.Flag.ServiceID, + "Service Version": fsterr.ServiceVersion(serviceVersion), + }) + return err + } + + c.input.ServiceID = serviceID + c.input.ServiceVersion = serviceVersion.Number + + resource, err := c.Globals.APIClient.UpdateResource(&c.input) + if err != nil { + c.Globals.ErrLog.AddWithContext(err, map[string]any{ + "ID": c.input.ResourceID, + "Service ID": c.input.ServiceID, + "Service Version": c.input.ServiceVersion, + }) + return err + } + + if ok, err := c.WriteJSON(out, resource); ok { + return err + } + + text.Success(out, "Updated service resource link %s on service %s version %s", resource.ID, resource.ServiceID, resource.ServiceVersion) + return nil +} diff --git a/pkg/mock/api.go b/pkg/mock/api.go index 5b11405fa..08e3fb90f 100644 --- a/pkg/mock/api.go +++ b/pkg/mock/api.go @@ -338,6 +338,10 @@ type API struct { ListSecretsFn func(i *fastly.ListSecretsInput) (*fastly.Secrets, error) CreateResourceFn func(i *fastly.CreateResourceInput) (*fastly.Resource, error) + DeleteResourceFn func(i *fastly.DeleteResourceInput) error + GetResourceFn func(i *fastly.GetResourceInput) (*fastly.Resource, error) + ListResourcesFn func(i *fastly.ListResourcesInput) ([]*fastly.Resource, error) + UpdateResourceFn func(i *fastly.UpdateResourceInput) (*fastly.Resource, error) } // AllDatacenters implements Interface. @@ -1714,3 +1718,23 @@ func (m API) ListSecrets(i *fastly.ListSecretsInput) (*fastly.Secrets, error) { func (m API) CreateResource(i *fastly.CreateResourceInput) (*fastly.Resource, error) { return m.CreateResourceFn(i) } + +// DeleteResource implements Interface. +func (m API) DeleteResource(i *fastly.DeleteResourceInput) error { + return m.DeleteResourceFn(i) +} + +// GetResource implements Interface. +func (m API) GetResource(i *fastly.GetResourceInput) (*fastly.Resource, error) { + return m.GetResourceFn(i) +} + +// ListResources implements Interface. +func (m API) ListResources(i *fastly.ListResourcesInput) ([]*fastly.Resource, error) { + return m.ListResourcesFn(i) +} + +// UpdateResource implements Interface. +func (m API) UpdateResource(i *fastly.UpdateResourceInput) (*fastly.Resource, error) { + return m.UpdateResourceFn(i) +} diff --git a/pkg/text/resource.go b/pkg/text/resource.go new file mode 100644 index 000000000..86f45df5f --- /dev/null +++ b/pkg/text/resource.go @@ -0,0 +1,37 @@ +package text + +import ( + "fmt" + "io" + + "github.com/fastly/cli/pkg/time" + "github.com/fastly/go-fastly/v7/fastly" + "github.com/segmentio/textio" +) + +// PrintResource pretty prints a fastly.Resource structure in verbose +// format to a given io.Writer. Consumers can provide a prefix string which +// will be used as a prefix to each line, useful for indentation. +func PrintResource(out io.Writer, prefix string, r *fastly.Resource) { + if r == nil { + return + } + out = textio.NewPrefixWriter(out, prefix) + + fmt.Fprintf(out, "ID: %s\n", r.ID) + fmt.Fprintf(out, "Name: %s\n", r.Name) + fmt.Fprintf(out, "Service ID: %s\n", r.ServiceID) + fmt.Fprintf(out, "Service Version: %s\n", r.ServiceVersion) + fmt.Fprintf(out, "Resource ID: %s\n", r.ResourceID) + fmt.Fprintf(out, "Resource Type: %s\n", r.ResourceType) + + if r.CreatedAt != nil { + fmt.Fprintf(out, "Created (UTC): %s\n", r.CreatedAt.UTC().Format(time.Format)) + } + if r.UpdatedAt != nil { + fmt.Fprintf(out, "Last edited (UTC): %s\n", r.UpdatedAt.UTC().Format(time.Format)) + } + if r.DeletedAt != nil { + fmt.Fprintf(out, "Deleted (UTC): %s\n", r.DeletedAt.UTC().Format(time.Format)) + } +} From a45f3a4cadd43d540eda3c8621f1bcf1f8b6f4c1 Mon Sep 17 00:00:00 2001 From: Adam Williams Date: Wed, 8 Feb 2023 13:59:42 -0700 Subject: [PATCH 02/10] go-fastly: Upgrade to v7.2.0 This comes with changes specific to the Resource API: - Renamed `resource_id` to `id` when referencing resource (link) ID - Added `json` tags to the Resource type --- go.mod | 3 +- go.sum | 45 ++----------------- pkg/commands/serviceresource/delete.go | 10 ++--- pkg/commands/serviceresource/describe.go | 4 +- pkg/commands/serviceresource/json.go | 31 ------------- pkg/commands/serviceresource/root.go | 2 +- .../serviceresource/serviceresource_test.go | 20 ++++----- pkg/commands/serviceresource/update.go | 4 +- 8 files changed, 26 insertions(+), 93 deletions(-) diff --git a/go.mod b/go.mod index 474e09969..66a4b3a84 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( ) require ( - github.com/fastly/go-fastly/v7 v7.1.0 + github.com/fastly/go-fastly/v7 v7.2.0 github.com/kennygrant/sanitize v1.2.4 github.com/mholt/archiver v3.1.1+incompatible github.com/otiai10/copy v1.9.0 @@ -54,6 +54,7 @@ require ( github.com/rogpeppe/go-internal v1.8.0 // indirect github.com/ulikunitz/xz v0.5.10 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect + golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be // indirect golang.org/x/net v0.2.0 // indirect golang.org/x/text v0.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index c73c0c87f..4c2b8b628 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,3 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 h1:AUNCr9CiJuwrRYS3XieqF+Z9B9gNxo/eANAJCF2eiN4= @@ -14,14 +13,13 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= -github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/dustinkirkland/golang-petname v0.0.0-20191129215211-8e5a1ed0cff0 h1:90Ly+6UfUypEF6vvvW5rQIv9opIL8CbmW9FT20LDQoY= github.com/dustinkirkland/golang-petname v0.0.0-20191129215211-8e5a1ed0cff0/go.mod h1:V+Qd57rJe8gd4eiGzZyg4h54VLHmYVVw54iMnlAMrF8= -github.com/fastly/go-fastly/v7 v7.1.0 h1:Xrx4WvFEslI7Ry8KXgTk7OPKFe4VU2aDULSPlSmIzTU= -github.com/fastly/go-fastly/v7 v7.1.0/go.mod h1:WdssHSSIe41/a5juIJagw8MCTA9m7xQ1TVLRcBQQuS8= +github.com/fastly/go-fastly/v7 v7.2.0 h1:d2FlF2vwtXkCmp+hC5fmn6+wvrsZsC8ZhTfoaehQXzQ= +github.com/fastly/go-fastly/v7 v7.2.0/go.mod h1:K32VeBzD+RJikDMUSKPpYfno8YBMXmNWjgGAn6I/OU8= github.com/fastly/kingpin v2.1.12-0.20191105091915-95d230a53780+incompatible h1:FhrXlfhgGCS+uc6YwyiFUt04alnjpoX7vgDKJxS6Qbk= github.com/fastly/kingpin v2.1.12-0.20191105091915-95d230a53780+incompatible/go.mod h1:U8UynVoU1SQaqD2I4ZqgYd5lx3A1ipQYn4aSt2Y5h6c= github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= @@ -75,10 +73,8 @@ github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Cl github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= -github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/nicksnyder/go-i18n v1.10.1 h1:isfg77E/aCD7+0lD/D00ebR2MV5vgeQ276WYyDaCRQc= github.com/nicksnyder/go-i18n v1.10.1/go.mod h1:e4Di5xjP9oTVrC6y3C7C0HoSYXjSbhh/dU0eUV32nB4= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= @@ -129,51 +125,20 @@ github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A= +golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -181,9 +146,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= diff --git a/pkg/commands/serviceresource/delete.go b/pkg/commands/serviceresource/delete.go index b9a41cf33..d134863ef 100644 --- a/pkg/commands/serviceresource/delete.go +++ b/pkg/commands/serviceresource/delete.go @@ -36,7 +36,7 @@ func NewDeleteCommand(parent cmd.Registerer, globals *global.Data, data manifest c.RegisterFlag(cmd.StringFlagOpts{ Name: "id", Description: "ID of resource link", - Dst: &c.input.ResourceID, + Dst: &c.input.ID, Required: true, }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -92,7 +92,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { err = c.Globals.APIClient.DeleteResource(&c.input) if err != nil { c.Globals.ErrLog.AddWithContext(err, map[string]any{ - "ID": c.input.ResourceID, + "ID": c.input.ID, "Service ID": c.input.ServiceID, "Service Version": c.input.ServiceVersion, }) @@ -101,12 +101,12 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { if c.jsonOutput.enabled { o := struct { - ResourceID string `json:"id"` + ID string `json:"id"` ServiceID string `json:"service_id"` ServiceVersion int `json:"service_version"` Deleted bool `json:"deleted"` }{ - c.input.ResourceID, + c.input.ID, c.input.ServiceID, c.input.ServiceVersion, true, @@ -115,6 +115,6 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { return err } - text.Success(out, "Deleted service resource link %s from service %s version %d", c.input.ResourceID, c.input.ServiceID, c.input.ServiceVersion) + text.Success(out, "Deleted service resource link %s from service %s version %d", c.input.ID, c.input.ServiceID, c.input.ServiceVersion) return nil } diff --git a/pkg/commands/serviceresource/describe.go b/pkg/commands/serviceresource/describe.go index 12b89e1f8..38bdd72f9 100644 --- a/pkg/commands/serviceresource/describe.go +++ b/pkg/commands/serviceresource/describe.go @@ -35,7 +35,7 @@ func NewDescribeCommand(parent cmd.Registerer, globals *global.Data, data manife c.RegisterFlag(cmd.StringFlagOpts{ Name: "id", Description: "ID of resource link", - Dst: &c.input.ResourceID, + Dst: &c.input.ID, Required: true, }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -83,7 +83,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { resource, err := c.Globals.APIClient.GetResource(&c.input) if err != nil { c.Globals.ErrLog.AddWithContext(err, map[string]any{ - "ID": c.input.ResourceID, + "ID": c.input.ID, "Service ID": c.input.ServiceID, "Service Version": c.input.ServiceVersion, }) diff --git a/pkg/commands/serviceresource/json.go b/pkg/commands/serviceresource/json.go index aed1a400f..35c9b3dca 100644 --- a/pkg/commands/serviceresource/json.go +++ b/pkg/commands/serviceresource/json.go @@ -3,10 +3,8 @@ package serviceresource import ( "encoding/json" "io" - "time" "github.com/fastly/cli/pkg/cmd" - "github.com/fastly/go-fastly/v7/fastly" ) // jsonOutput is a helper for adding a `--json` flag and encoding @@ -32,36 +30,7 @@ func (j *jsonOutput) WriteJSON(out io.Writer, value any) (bool, error) { return false, nil } - // If value is a fastly.Resource (or variations of), then convert into - // a jsonResource for improved JSON encoding. - switch v := value.(type) { - case *fastly.Resource: - value = jsonResource(*v) - case fastly.Resource: - value = jsonResource(v) - case []*fastly.Resource: - cp := make([]jsonResource, len(v)) - for i := range v { - cp[i] = jsonResource(*v[i]) - } - value = cp - } - enc := json.NewEncoder(out) enc.SetIndent("", " ") return true, enc.Encode(value) } - -// jsonResource is a fastly.Resource with `json` field tags defined. -type jsonResource struct { - CreatedAt *time.Time `json:"created_at"` - DeletedAt *time.Time `json:"deleted_at"` - HREF string `json:"-"` // Omit this field from JSON output. - ID string `json:"id"` - Name string `json:"name"` - ResourceID string `json:"resource_id"` - ResourceType string `json:"resource_type"` - ServiceID string `json:"service_id"` - ServiceVersion string `json:"service_version"` - UpdatedAt *time.Time `json:"updated_at"` -} diff --git a/pkg/commands/serviceresource/root.go b/pkg/commands/serviceresource/root.go index 7e29f0cb3..0ad2a7ac9 100644 --- a/pkg/commands/serviceresource/root.go +++ b/pkg/commands/serviceresource/root.go @@ -20,7 +20,7 @@ type RootCommand struct { func NewRootCommand(parent cmd.Registerer, globals *global.Data) *RootCommand { var c RootCommand c.Globals = globals - c.CmdClause = parent.Command(RootName, "Manipulate Fastly service resources") + c.CmdClause = parent.Command(RootName, "Manipulate Fastly service resource links") return &c } diff --git a/pkg/commands/serviceresource/serviceresource_test.go b/pkg/commands/serviceresource/serviceresource_test.go index 3f69d81a1..f019e6c42 100644 --- a/pkg/commands/serviceresource/serviceresource_test.go +++ b/pkg/commands/serviceresource/serviceresource_test.go @@ -195,8 +195,8 @@ func TestDeleteServiceResourceCommand(t *testing.T) { return []*fastly.Version{{Number: 42}}, nil }, DeleteResourceFn: func(i *fastly.DeleteResourceInput) error { - if got, want := i.ResourceID, "LINKID"; got != want { - return fmt.Errorf("ResourceID: got %q, want %q", got, want) + if got, want := i.ID, "LINKID"; got != want { + return fmt.Errorf("ID: got %q, want %q", got, want) } if got, want := i.ServiceID, "123"; got != want { return fmt.Errorf("ServiceID: got %q, want %q", got, want) @@ -222,8 +222,8 @@ func TestDeleteServiceResourceCommand(t *testing.T) { return &fastly.Version{Number: 43}, nil }, DeleteResourceFn: func(i *fastly.DeleteResourceInput) error { - if got, want := i.ResourceID, "LINKID"; got != want { - return fmt.Errorf("ResourceID: got %q, want %q", got, want) + if got, want := i.ID, "LINKID"; got != want { + return fmt.Errorf("ID: got %q, want %q", got, want) } if got, want := i.ServiceID, "123"; got != want { return fmt.Errorf("ServiceID: got %q, want %q", got, want) @@ -297,8 +297,8 @@ func TestDescribeServiceResourceCommand(t *testing.T) { return []*fastly.Version{{Number: 42}}, nil }, GetResourceFn: func(i *fastly.GetResourceInput) (*fastly.Resource, error) { - if got, want := i.ResourceID, "LINKID"; got != want { - return nil, fmt.Errorf("ResourceID: got %q, want %q", got, want) + if got, want := i.ID, "LINKID"; got != want { + return nil, fmt.Errorf("ID: got %q, want %q", got, want) } if got, want := i.ServiceID, "123"; got != want { return nil, fmt.Errorf("ServiceID: got %q, want %q", got, want) @@ -508,8 +508,8 @@ func TestUpdateServiceResourceCommand(t *testing.T) { return []*fastly.Version{{Number: 42}}, nil }, UpdateResourceFn: func(i *fastly.UpdateResourceInput) (*fastly.Resource, error) { - if got, want := i.ResourceID, "LINK-ID"; got != want { - return nil, fmt.Errorf("ResourceID: got %q, want %q", got, want) + if got, want := i.ID, "LINK-ID"; got != want { + return nil, fmt.Errorf("ID: got %q, want %q", got, want) } if got, want := *i.Name, "new-name"; got != want { return nil, fmt.Errorf("Name: got %q, want %q", got, want) @@ -549,8 +549,8 @@ func TestUpdateServiceResourceCommand(t *testing.T) { return &fastly.Version{Number: 43}, nil }, UpdateResourceFn: func(i *fastly.UpdateResourceInput) (*fastly.Resource, error) { - if got, want := i.ResourceID, "LINK-ID"; got != want { - return nil, fmt.Errorf("ResourceID: got %q, want %q", got, want) + if got, want := i.ID, "LINK-ID"; got != want { + return nil, fmt.Errorf("ID: got %q, want %q", got, want) } if got, want := *i.Name, "new-name"; got != want { return nil, fmt.Errorf("Name: got %q, want %q", got, want) diff --git a/pkg/commands/serviceresource/update.go b/pkg/commands/serviceresource/update.go index e25d6cc53..d5523a77a 100644 --- a/pkg/commands/serviceresource/update.go +++ b/pkg/commands/serviceresource/update.go @@ -40,7 +40,7 @@ func NewUpdateCommand(parent cmd.Registerer, globals *global.Data, data manifest c.RegisterFlag(cmd.StringFlagOpts{ Name: "id", Description: "ID of resource link", - Dst: &c.input.ResourceID, + Dst: &c.input.ID, Required: true, }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -103,7 +103,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { resource, err := c.Globals.APIClient.UpdateResource(&c.input) if err != nil { c.Globals.ErrLog.AddWithContext(err, map[string]any{ - "ID": c.input.ResourceID, + "ID": c.input.ID, "Service ID": c.input.ServiceID, "Service Version": c.input.ServiceVersion, }) From 0882eb33e128433a2ca943473cbab73b270e9383 Mon Sep 17 00:00:00 2001 From: Adam Williams Date: Wed, 8 Feb 2023 14:01:54 -0700 Subject: [PATCH 03/10] Add resource link name to create command --- pkg/commands/serviceresource/create.go | 2 +- pkg/commands/serviceresource/serviceresource_test.go | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pkg/commands/serviceresource/create.go b/pkg/commands/serviceresource/create.go index f574ccc4b..a88455155 100644 --- a/pkg/commands/serviceresource/create.go +++ b/pkg/commands/serviceresource/create.go @@ -115,6 +115,6 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { return err } - text.Success(out, "Created service resource link %s on service %s version %s", resource.ID, resource.ServiceID, resource.ServiceVersion) + text.Success(out, "Created service resource link %q (%s) on service %s version %s", resource.Name, resource.ID, resource.ServiceID, resource.ServiceVersion) return nil } diff --git a/pkg/commands/serviceresource/serviceresource_test.go b/pkg/commands/serviceresource/serviceresource_test.go index f019e6c42..d76911784 100644 --- a/pkg/commands/serviceresource/serviceresource_test.go +++ b/pkg/commands/serviceresource/serviceresource_test.go @@ -58,6 +58,7 @@ func TestCreateServiceResourceCommand(t *testing.T) { now := time.Now() return &fastly.Resource{ ID: "rand-id", + Name: "the-name", ResourceID: "abc", ServiceID: "123", ServiceVersion: "42", @@ -67,7 +68,7 @@ func TestCreateServiceResourceCommand(t *testing.T) { }, }, wantAPIInvoked: true, - wantOutput: "SUCCESS: Created service resource link rand-id on service 123 version 42", + wantOutput: `SUCCESS: Created service resource link "the-name" (rand-id) on service 123 version 42`, }, // Success with --name. { @@ -89,6 +90,7 @@ func TestCreateServiceResourceCommand(t *testing.T) { now := time.Now() return &fastly.Resource{ ID: "rand-id", + Name: "a-name", ResourceID: "abc", ServiceID: "123", ServiceVersion: "42", @@ -98,7 +100,7 @@ func TestCreateServiceResourceCommand(t *testing.T) { }, }, wantAPIInvoked: true, - wantOutput: "SUCCESS: Created service resource link rand-id on service 123 version 42", + wantOutput: `SUCCESS: Created service resource link "a-name" (rand-id) on service 123 version 42`, }, // Success with --autoclone. { @@ -124,6 +126,7 @@ func TestCreateServiceResourceCommand(t *testing.T) { now := time.Now() return &fastly.Resource{ ID: "rand-id", + Name: "cloned", ResourceID: "abc", ServiceID: "123", ServiceVersion: "43", // Cloned version. @@ -133,7 +136,7 @@ func TestCreateServiceResourceCommand(t *testing.T) { }, }, wantAPIInvoked: true, - wantOutput: "SUCCESS: Created service resource link rand-id on service 123 version 43", + wantOutput: `SUCCESS: Created service resource link "cloned" (rand-id) on service 123 version 43`, }, } From 7b67275bf290b5eb4cfe0a7d7d8ea6a15165b441 Mon Sep 17 00:00:00 2001 From: Adam Williams Date: Wed, 8 Feb 2023 14:16:03 -0700 Subject: [PATCH 04/10] Consistent naming of global.Data and manifest.Data globals -> g data -> m --- pkg/commands/serviceresource/create.go | 6 +++--- pkg/commands/serviceresource/delete.go | 6 +++--- pkg/commands/serviceresource/describe.go | 6 +++--- pkg/commands/serviceresource/doc.go | 2 +- pkg/commands/serviceresource/list.go | 6 +++--- pkg/commands/serviceresource/root.go | 4 ++-- pkg/commands/serviceresource/update.go | 6 +++--- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/pkg/commands/serviceresource/create.go b/pkg/commands/serviceresource/create.go index a88455155..0d42206bf 100644 --- a/pkg/commands/serviceresource/create.go +++ b/pkg/commands/serviceresource/create.go @@ -23,12 +23,12 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, globals *global.Data, data manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ - Globals: globals, + Globals: g, }, - manifest: data, + manifest: m, input: fastly.CreateResourceInput{ // Kingpin requires the following to be initialized. ResourceID: new(string), diff --git a/pkg/commands/serviceresource/delete.go b/pkg/commands/serviceresource/delete.go index d134863ef..1f7ac5bd7 100644 --- a/pkg/commands/serviceresource/delete.go +++ b/pkg/commands/serviceresource/delete.go @@ -23,12 +23,12 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, globals *global.Data, data manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ - Globals: globals, + Globals: g, }, - manifest: data, + manifest: m, } c.CmdClause = parent.Command("delete", "Delete a resource link for a Fastly service version").Alias("remove") diff --git a/pkg/commands/serviceresource/describe.go b/pkg/commands/serviceresource/describe.go index 38bdd72f9..e57bcbfc3 100644 --- a/pkg/commands/serviceresource/describe.go +++ b/pkg/commands/serviceresource/describe.go @@ -22,12 +22,12 @@ type DescribeCommand struct { } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, globals *global.Data, data manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ - Globals: globals, + Globals: g, }, - manifest: data, + manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a Fastly service resource link").Alias("get") diff --git a/pkg/commands/serviceresource/doc.go b/pkg/commands/serviceresource/doc.go index 5b1e24d9e..ac9691f6d 100644 --- a/pkg/commands/serviceresource/doc.go +++ b/pkg/commands/serviceresource/doc.go @@ -1,4 +1,4 @@ // Package serviceresource contains commands to inspect and -// manipulate service resources. +// manipulate service resource links. // https://developer.fastly.com/reference/api/services/resource/ package serviceresource diff --git a/pkg/commands/serviceresource/list.go b/pkg/commands/serviceresource/list.go index 25c388832..47388c07b 100644 --- a/pkg/commands/serviceresource/list.go +++ b/pkg/commands/serviceresource/list.go @@ -23,12 +23,12 @@ type ListCommand struct { } // NewListCommand returns a usable command registered under the parent -func NewListCommand(parent cmd.Registerer, globals *global.Data, data manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ - Globals: globals, + Globals: g, }, - manifest: data, + manifest: m, } c.CmdClause = parent.Command("list", "List all resource links for a Fastly service version") diff --git a/pkg/commands/serviceresource/root.go b/pkg/commands/serviceresource/root.go index 0ad2a7ac9..3c31232e9 100644 --- a/pkg/commands/serviceresource/root.go +++ b/pkg/commands/serviceresource/root.go @@ -17,9 +17,9 @@ type RootCommand struct { } // NewRootCommand returns a new command registered in the parent. -func NewRootCommand(parent cmd.Registerer, globals *global.Data) *RootCommand { +func NewRootCommand(parent cmd.Registerer, g *global.Data) *RootCommand { var c RootCommand - c.Globals = globals + c.Globals = g c.CmdClause = parent.Command(RootName, "Manipulate Fastly service resource links") return &c } diff --git a/pkg/commands/serviceresource/update.go b/pkg/commands/serviceresource/update.go index d5523a77a..1cc49dae2 100644 --- a/pkg/commands/serviceresource/update.go +++ b/pkg/commands/serviceresource/update.go @@ -23,12 +23,12 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, globals *global.Data, data manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ - Globals: globals, + Globals: g, }, - manifest: data, + manifest: m, input: fastly.UpdateResourceInput{ // Kingpin requires the following to be initialized. Name: new(string), From 1d2eb20e521edf0e4afcc91404b8d5b16d58301d Mon Sep 17 00:00:00 2001 From: Adam Williams Date: Mon, 13 Feb 2023 10:01:15 -0700 Subject: [PATCH 05/10] Rename service-resource to resource-link This better matches the language used in Fastly's web UI. --- pkg/app/commands.go | 26 +++++++++---------- pkg/app/run_test.go | 2 +- .../create.go | 2 +- .../delete.go | 2 +- .../describe.go | 2 +- .../{serviceresource => resourcelink}/doc.go | 4 +-- .../{serviceresource => resourcelink}/json.go | 2 +- .../{serviceresource => resourcelink}/list.go | 2 +- .../{serviceresource => resourcelink}/root.go | 4 +-- .../serviceresource_test.go | 14 +++++----- .../update.go | 2 +- 11 files changed, 31 insertions(+), 31 deletions(-) rename pkg/commands/{serviceresource => resourcelink}/create.go (99%) rename pkg/commands/{serviceresource => resourcelink}/delete.go (99%) rename pkg/commands/{serviceresource => resourcelink}/describe.go (99%) rename pkg/commands/{serviceresource => resourcelink}/doc.go (55%) rename pkg/commands/{serviceresource => resourcelink}/json.go (97%) rename pkg/commands/{serviceresource => resourcelink}/list.go (99%) rename pkg/commands/{serviceresource => resourcelink}/root.go (91%) rename pkg/commands/{serviceresource => resourcelink}/serviceresource_test.go (97%) rename pkg/commands/{serviceresource => resourcelink}/update.go (99%) diff --git a/pkg/app/commands.go b/pkg/app/commands.go index a784a0ddf..0b45c25d9 100644 --- a/pkg/app/commands.go +++ b/pkg/app/commands.go @@ -46,11 +46,11 @@ import ( "github.com/fastly/cli/pkg/commands/pop" "github.com/fastly/cli/pkg/commands/profile" "github.com/fastly/cli/pkg/commands/purge" + "github.com/fastly/cli/pkg/commands/resourcelink" "github.com/fastly/cli/pkg/commands/secretstore" "github.com/fastly/cli/pkg/commands/secretstoreentry" "github.com/fastly/cli/pkg/commands/service" "github.com/fastly/cli/pkg/commands/serviceauth" - "github.com/fastly/cli/pkg/commands/serviceresource" "github.com/fastly/cli/pkg/commands/serviceversion" "github.com/fastly/cli/pkg/commands/shellcomplete" "github.com/fastly/cli/pkg/commands/stats" @@ -319,6 +319,12 @@ func defineCommands( profileToken := profile.NewTokenCommand(profileCmdRoot.CmdClause, g) profileUpdate := profile.NewUpdateCommand(profileCmdRoot.CmdClause, profile.APIClientFactory(opts.APIClient), g) purgeCmdRoot := purge.NewRootCommand(app, g, m) + resourcelinkCmdRoot := resourcelink.NewRootCommand(app, g) + resourcelinkCreate := resourcelink.NewCreateCommand(resourcelinkCmdRoot.CmdClause, g, m) + resourcelinkDelete := resourcelink.NewDeleteCommand(resourcelinkCmdRoot.CmdClause, g, m) + resourcelinkDescribe := resourcelink.NewDescribeCommand(resourcelinkCmdRoot.CmdClause, g, m) + resourcelinkList := resourcelink.NewListCommand(resourcelinkCmdRoot.CmdClause, g, m) + resourcelinkUpdate := resourcelink.NewUpdateCommand(resourcelinkCmdRoot.CmdClause, g, m) secretstoreCmdRoot := secretstore.NewRootCommand(app, g) secretstoreCreate := secretstore.NewCreateCommand(secretstoreCmdRoot.CmdClause, g, m) secretstoreDescribe := secretstore.NewDescribeCommand(secretstoreCmdRoot.CmdClause, g, m) @@ -342,12 +348,6 @@ func defineCommands( serviceauthDescribe := serviceauth.NewDescribeCommand(serviceauthCmdRoot.CmdClause, g, m) serviceauthList := serviceauth.NewListCommand(serviceauthCmdRoot.CmdClause, g) serviceauthUpdate := serviceauth.NewUpdateCommand(serviceauthCmdRoot.CmdClause, g, m) - serviceresourceCmdRoot := serviceresource.NewRootCommand(app, g) - serviceresourceCreate := serviceresource.NewCreateCommand(serviceresourceCmdRoot.CmdClause, g, m) - serviceresourceDelete := serviceresource.NewDeleteCommand(serviceresourceCmdRoot.CmdClause, g, m) - serviceresourceDescribe := serviceresource.NewDescribeCommand(serviceresourceCmdRoot.CmdClause, g, m) - serviceresourceList := serviceresource.NewListCommand(serviceresourceCmdRoot.CmdClause, g, m) - serviceresourceUpdate := serviceresource.NewUpdateCommand(serviceresourceCmdRoot.CmdClause, g, m) serviceVersionCmdRoot := serviceversion.NewRootCommand(app, g) serviceVersionActivate := serviceversion.NewActivateCommand(serviceVersionCmdRoot.CmdClause, g, m) serviceVersionClone := serviceversion.NewCloneCommand(serviceVersionCmdRoot.CmdClause, g, m) @@ -655,6 +655,12 @@ func defineCommands( profileToken, profileUpdate, purgeCmdRoot, + resourcelinkCmdRoot, + resourcelinkCreate, + resourcelinkDelete, + resourcelinkDescribe, + resourcelinkList, + resourcelinkUpdate, secretstoreCreate, secretstoreDescribe, secretstoreDelete, @@ -676,12 +682,6 @@ func defineCommands( serviceauthDescribe, serviceauthList, serviceauthUpdate, - serviceresourceCmdRoot, - serviceresourceCreate, - serviceresourceDelete, - serviceresourceDescribe, - serviceresourceList, - serviceresourceUpdate, serviceVersionActivate, serviceVersionClone, serviceVersionCmdRoot, diff --git a/pkg/app/run_test.go b/pkg/app/run_test.go index f377f961d..4c3dd25d2 100644 --- a/pkg/app/run_test.go +++ b/pkg/app/run_test.go @@ -75,11 +75,11 @@ object-store-entry pops profile purge +resource-link secret-store secret-store-entry service service-auth -service-resource service-version stats tls-config diff --git a/pkg/commands/serviceresource/create.go b/pkg/commands/resourcelink/create.go similarity index 99% rename from pkg/commands/serviceresource/create.go rename to pkg/commands/resourcelink/create.go index 0d42206bf..15cf17e9b 100644 --- a/pkg/commands/serviceresource/create.go +++ b/pkg/commands/resourcelink/create.go @@ -1,4 +1,4 @@ -package serviceresource +package resourcelink import ( "io" diff --git a/pkg/commands/serviceresource/delete.go b/pkg/commands/resourcelink/delete.go similarity index 99% rename from pkg/commands/serviceresource/delete.go rename to pkg/commands/resourcelink/delete.go index 1f7ac5bd7..7c827c15f 100644 --- a/pkg/commands/serviceresource/delete.go +++ b/pkg/commands/resourcelink/delete.go @@ -1,4 +1,4 @@ -package serviceresource +package resourcelink import ( "io" diff --git a/pkg/commands/serviceresource/describe.go b/pkg/commands/resourcelink/describe.go similarity index 99% rename from pkg/commands/serviceresource/describe.go rename to pkg/commands/resourcelink/describe.go index e57bcbfc3..0003466e1 100644 --- a/pkg/commands/serviceresource/describe.go +++ b/pkg/commands/resourcelink/describe.go @@ -1,4 +1,4 @@ -package serviceresource +package resourcelink import ( "io" diff --git a/pkg/commands/serviceresource/doc.go b/pkg/commands/resourcelink/doc.go similarity index 55% rename from pkg/commands/serviceresource/doc.go rename to pkg/commands/resourcelink/doc.go index ac9691f6d..23e69d696 100644 --- a/pkg/commands/serviceresource/doc.go +++ b/pkg/commands/resourcelink/doc.go @@ -1,4 +1,4 @@ -// Package serviceresource contains commands to inspect and +// Package resourcelink contains commands to inspect and // manipulate service resource links. // https://developer.fastly.com/reference/api/services/resource/ -package serviceresource +package resourcelink diff --git a/pkg/commands/serviceresource/json.go b/pkg/commands/resourcelink/json.go similarity index 97% rename from pkg/commands/serviceresource/json.go rename to pkg/commands/resourcelink/json.go index 35c9b3dca..f3a8b8e3a 100644 --- a/pkg/commands/serviceresource/json.go +++ b/pkg/commands/resourcelink/json.go @@ -1,4 +1,4 @@ -package serviceresource +package resourcelink import ( "encoding/json" diff --git a/pkg/commands/serviceresource/list.go b/pkg/commands/resourcelink/list.go similarity index 99% rename from pkg/commands/serviceresource/list.go rename to pkg/commands/resourcelink/list.go index 47388c07b..2672f0b91 100644 --- a/pkg/commands/serviceresource/list.go +++ b/pkg/commands/resourcelink/list.go @@ -1,4 +1,4 @@ -package serviceresource +package resourcelink import ( "fmt" diff --git a/pkg/commands/serviceresource/root.go b/pkg/commands/resourcelink/root.go similarity index 91% rename from pkg/commands/serviceresource/root.go rename to pkg/commands/resourcelink/root.go index 3c31232e9..c5443c184 100644 --- a/pkg/commands/serviceresource/root.go +++ b/pkg/commands/resourcelink/root.go @@ -1,4 +1,4 @@ -package serviceresource +package resourcelink import ( "io" @@ -7,7 +7,7 @@ import ( "github.com/fastly/cli/pkg/global" ) -const RootName = "service-resource" +const RootName = "resource-link" // RootCommand is the parent command for all subcommands in this package. // It should be installed under the primary root command. diff --git a/pkg/commands/serviceresource/serviceresource_test.go b/pkg/commands/resourcelink/serviceresource_test.go similarity index 97% rename from pkg/commands/serviceresource/serviceresource_test.go rename to pkg/commands/resourcelink/serviceresource_test.go index d76911784..d7aad206b 100644 --- a/pkg/commands/serviceresource/serviceresource_test.go +++ b/pkg/commands/resourcelink/serviceresource_test.go @@ -1,4 +1,4 @@ -package serviceresource_test +package resourcelink_test import ( "bytes" @@ -8,7 +8,7 @@ import ( "time" "github.com/fastly/cli/pkg/app" - "github.com/fastly/cli/pkg/commands/serviceresource" + "github.com/fastly/cli/pkg/commands/resourcelink" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" "github.com/fastly/go-fastly/v7/fastly" @@ -144,7 +144,7 @@ func TestCreateServiceResourceCommand(t *testing.T) { testcase := testcase t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testutil.Args(serviceresource.RootName+" "+testcase.args), &stdout) + opts := testutil.NewRunOpts(testutil.Args(resourcelink.RootName+" "+testcase.args), &stdout) f := testcase.api.CreateResourceFn var apiInvoked bool @@ -246,7 +246,7 @@ func TestDeleteServiceResourceCommand(t *testing.T) { testcase := testcase t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testutil.Args(serviceresource.RootName+" "+testcase.args), &stdout) + opts := testutil.NewRunOpts(testutil.Args(resourcelink.RootName+" "+testcase.args), &stdout) f := testcase.api.DeleteResourceFn var apiInvoked bool @@ -340,7 +340,7 @@ Last edited (UTC): 2023-10-15 12:18`, testcase := testcase t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testutil.Args(serviceresource.RootName+" "+testcase.args), &stdout) + opts := testutil.NewRunOpts(testutil.Args(resourcelink.RootName+" "+testcase.args), &stdout) f := testcase.api.GetResourceFn var apiInvoked bool @@ -452,7 +452,7 @@ Resource Link 3/3 testcase := testcase t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testutil.Args(serviceresource.RootName+" "+testcase.args), &stdout) + opts := testutil.NewRunOpts(testutil.Args(resourcelink.RootName+" "+testcase.args), &stdout) f := testcase.api.ListResourcesFn var apiInvoked bool @@ -587,7 +587,7 @@ func TestUpdateServiceResourceCommand(t *testing.T) { testcase := testcase t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testutil.Args(serviceresource.RootName+" "+testcase.args), &stdout) + opts := testutil.NewRunOpts(testutil.Args(resourcelink.RootName+" "+testcase.args), &stdout) f := testcase.api.UpdateResourceFn var apiInvoked bool diff --git a/pkg/commands/serviceresource/update.go b/pkg/commands/resourcelink/update.go similarity index 99% rename from pkg/commands/serviceresource/update.go rename to pkg/commands/resourcelink/update.go index 1cc49dae2..e8c789f56 100644 --- a/pkg/commands/serviceresource/update.go +++ b/pkg/commands/resourcelink/update.go @@ -1,4 +1,4 @@ -package serviceresource +package resourcelink import ( "io" From 44f4ad51be6d689ef17f47fa24bd6cad4aa90697 Mon Sep 17 00:00:00 2001 From: Adam Williams Date: Mon, 13 Feb 2023 10:08:54 -0700 Subject: [PATCH 06/10] Add comment to constant --- pkg/commands/resourcelink/root.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/commands/resourcelink/root.go b/pkg/commands/resourcelink/root.go index c5443c184..c1309bd03 100644 --- a/pkg/commands/resourcelink/root.go +++ b/pkg/commands/resourcelink/root.go @@ -7,6 +7,8 @@ import ( "github.com/fastly/cli/pkg/global" ) +// RootName is the name of this package's sub-command in the CLI, +// e.g. "fastly resource-link". const RootName = "resource-link" // RootCommand is the parent command for all subcommands in this package. From a3cbc6fe942f02a849d555860aca133eb679ac32 Mon Sep 17 00:00:00 2001 From: Adam Williams Date: Wed, 15 Feb 2023 11:25:36 -0700 Subject: [PATCH 07/10] Rename test file to match package name --- .../{serviceresource_test.go => resourcelink_test.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pkg/commands/resourcelink/{serviceresource_test.go => resourcelink_test.go} (100%) diff --git a/pkg/commands/resourcelink/serviceresource_test.go b/pkg/commands/resourcelink/resourcelink_test.go similarity index 100% rename from pkg/commands/resourcelink/serviceresource_test.go rename to pkg/commands/resourcelink/resourcelink_test.go From fc57b4ff17d49505d1fb0b65274fc51c82dca4e1 Mon Sep 17 00:00:00 2001 From: Adam Williams Date: Wed, 15 Feb 2023 11:30:19 -0700 Subject: [PATCH 08/10] Use cmd.JSONOutput instead of local version --- pkg/commands/resourcelink/create.go | 6 ++--- pkg/commands/resourcelink/delete.go | 8 +++--- pkg/commands/resourcelink/describe.go | 6 ++--- pkg/commands/resourcelink/json.go | 36 --------------------------- pkg/commands/resourcelink/list.go | 6 ++--- pkg/commands/resourcelink/update.go | 6 ++--- 6 files changed, 16 insertions(+), 52 deletions(-) delete mode 100644 pkg/commands/resourcelink/json.go diff --git a/pkg/commands/resourcelink/create.go b/pkg/commands/resourcelink/create.go index 15cf17e9b..d204f502e 100644 --- a/pkg/commands/resourcelink/create.go +++ b/pkg/commands/resourcelink/create.go @@ -14,7 +14,7 @@ import ( // CreateCommand calls the Fastly API to create a resource link. type CreateCommand struct { cmd.Base - jsonOutput + cmd.JSONOutput autoClone cmd.OptionalAutoClone input fastly.CreateResourceInput @@ -64,7 +64,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C Action: c.autoClone.Set, Dst: &c.autoClone.Value, }) - c.RegisterFlagBool(c.jsonFlag()) // --json + c.RegisterFlagBool(c.JSONFlag()) // --json c.RegisterFlag(cmd.StringFlagOpts{ Name: "name", Short: 'n', @@ -77,7 +77,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C // Exec invokes the application logic for the command. func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { - if c.Globals.Verbose() && c.jsonOutput.enabled { + if c.Globals.Verbose() && c.JSONOutput.Enabled { return fsterr.ErrInvalidVerboseJSONCombo } diff --git a/pkg/commands/resourcelink/delete.go b/pkg/commands/resourcelink/delete.go index 7c827c15f..46cc041bb 100644 --- a/pkg/commands/resourcelink/delete.go +++ b/pkg/commands/resourcelink/delete.go @@ -14,7 +14,7 @@ import ( // DeleteCommand calls the Fastly API to delete service resource links. type DeleteCommand struct { cmd.Base - jsonOutput + cmd.JSONOutput autoClone cmd.OptionalAutoClone input fastly.DeleteResourceInput @@ -58,14 +58,14 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D Action: c.autoClone.Set, Dst: &c.autoClone.Value, }) - c.RegisterFlagBool(c.jsonFlag()) // --json + c.RegisterFlagBool(c.JSONFlag()) // --json return &c } // Exec invokes the application logic for the command. func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { - if c.Globals.Verbose() && c.jsonOutput.enabled { + if c.Globals.Verbose() && c.JSONOutput.Enabled { return fsterr.ErrInvalidVerboseJSONCombo } @@ -99,7 +99,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { return err } - if c.jsonOutput.enabled { + if c.JSONOutput.Enabled { o := struct { ID string `json:"id"` ServiceID string `json:"service_id"` diff --git a/pkg/commands/resourcelink/describe.go b/pkg/commands/resourcelink/describe.go index 0003466e1..75df3624b 100644 --- a/pkg/commands/resourcelink/describe.go +++ b/pkg/commands/resourcelink/describe.go @@ -14,7 +14,7 @@ import ( // DescribeCommand calls the Fastly API to describe a service resource link. type DescribeCommand struct { cmd.Base - jsonOutput + cmd.JSONOutput input fastly.GetResourceInput manifest manifest.Data @@ -53,14 +53,14 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) }) // Optional. - c.RegisterFlagBool(c.jsonFlag()) // --json + c.RegisterFlagBool(c.JSONFlag()) // --json return &c } // Exec invokes the application logic for the command. func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { - if c.Globals.Verbose() && c.jsonOutput.enabled { + if c.Globals.Verbose() && c.JSONOutput.Enabled { return fsterr.ErrInvalidVerboseJSONCombo } diff --git a/pkg/commands/resourcelink/json.go b/pkg/commands/resourcelink/json.go deleted file mode 100644 index f3a8b8e3a..000000000 --- a/pkg/commands/resourcelink/json.go +++ /dev/null @@ -1,36 +0,0 @@ -package resourcelink - -import ( - "encoding/json" - "io" - - "github.com/fastly/cli/pkg/cmd" -) - -// jsonOutput is a helper for adding a `--json` flag and encoding -// values to JSON. It can be embedded into command structs. -type jsonOutput struct { - enabled bool // Set via flag. -} - -// jsonFlag creates a flag for enabling JSON output. -func (j *jsonOutput) jsonFlag() cmd.BoolFlagOpts { - return cmd.BoolFlagOpts{ - Name: cmd.FlagJSONName, - Description: cmd.FlagJSONDesc, - Dst: &j.enabled, - Short: 'j', - } -} - -// WriteJSON checks whether the enabled flag is set or not. If set, -// then the given value is written as JSON to out. Otherwise, false is returned. -func (j *jsonOutput) WriteJSON(out io.Writer, value any) (bool, error) { - if !j.enabled { - return false, nil - } - - enc := json.NewEncoder(out) - enc.SetIndent("", " ") - return true, enc.Encode(value) -} diff --git a/pkg/commands/resourcelink/list.go b/pkg/commands/resourcelink/list.go index 2672f0b91..0e66f31dc 100644 --- a/pkg/commands/resourcelink/list.go +++ b/pkg/commands/resourcelink/list.go @@ -15,7 +15,7 @@ import ( // ListCommand calls the Fastly API to list service resource links type ListCommand struct { cmd.Base - jsonOutput + cmd.JSONOutput input fastly.ListResourcesInput manifest manifest.Data @@ -48,14 +48,14 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis }) // Optional. - c.RegisterFlagBool(c.jsonFlag()) // --json + c.RegisterFlagBool(c.JSONFlag()) // --json return &c } // Exec invokes the application logic for the command. func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { - if c.Globals.Verbose() && c.jsonOutput.enabled { + if c.Globals.Verbose() && c.JSONOutput.Enabled { return fsterr.ErrInvalidVerboseJSONCombo } diff --git a/pkg/commands/resourcelink/update.go b/pkg/commands/resourcelink/update.go index e8c789f56..f1cd5448f 100644 --- a/pkg/commands/resourcelink/update.go +++ b/pkg/commands/resourcelink/update.go @@ -14,7 +14,7 @@ import ( // UpdateCommand calls the Fastly API to update a dictionary. type UpdateCommand struct { cmd.Base - jsonOutput + cmd.JSONOutput autoClone cmd.OptionalAutoClone input fastly.UpdateResourceInput @@ -69,14 +69,14 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U Action: c.autoClone.Set, Dst: &c.autoClone.Value, }) - c.RegisterFlagBool(c.jsonFlag()) // --json + c.RegisterFlagBool(c.JSONFlag()) // --json return &c } // Exec invokes the application logic for the command. func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { - if c.Globals.Verbose() && c.jsonOutput.enabled { + if c.Globals.Verbose() && c.JSONOutput.Enabled { return fsterr.ErrInvalidVerboseJSONCombo } From 3f19765bd8a3f0c63d58c9e6996de0954252f27b Mon Sep 17 00:00:00 2001 From: Adam Williams Date: Wed, 15 Feb 2023 13:26:24 -0700 Subject: [PATCH 09/10] Add --service-name flag to appropriate commands --- pkg/commands/resourcelink/create.go | 20 +++++++++++++------ pkg/commands/resourcelink/delete.go | 20 +++++++++++++------ pkg/commands/resourcelink/describe.go | 20 +++++++++++++------ pkg/commands/resourcelink/list.go | 20 +++++++++++++------ .../resourcelink/resourcelink_test.go | 10 +++++----- pkg/commands/resourcelink/update.go | 20 +++++++++++++------ 6 files changed, 75 insertions(+), 35 deletions(-) diff --git a/pkg/commands/resourcelink/create.go b/pkg/commands/resourcelink/create.go index d204f502e..c7a6b9426 100644 --- a/pkg/commands/resourcelink/create.go +++ b/pkg/commands/resourcelink/create.go @@ -19,6 +19,7 @@ type CreateCommand struct { autoClone cmd.OptionalAutoClone input fastly.CreateResourceInput manifest manifest.Data + serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } @@ -45,18 +46,25 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C Dst: c.input.ResourceID, Required: true, }) + c.RegisterFlag(cmd.StringFlagOpts{ + Name: cmd.FlagVersionName, + Description: cmd.FlagVersionDesc, + Dst: &c.serviceVersion.Value, + Required: true, + }) + + // At least one of the following is required. c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Short: 's', Description: cmd.FlagServiceIDDesc, Dst: &c.manifest.Flag.ServiceID, - Required: true, }) c.RegisterFlag(cmd.StringFlagOpts{ - Name: cmd.FlagVersionName, - Description: cmd.FlagVersionDesc, - Dst: &c.serviceVersion.Value, - Required: true, + Name: cmd.FlagServiceName, + Action: c.serviceName.Set, + Description: cmd.FlagServiceDesc, + Dst: &c.serviceName.Value, }) // Optional. @@ -86,7 +94,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { APIClient: c.Globals.APIClient, Manifest: c.manifest, Out: out, - ServiceNameFlag: cmd.OptionalServiceNameID{}, // ServiceID flag is required, no need to lookup service by name. + ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, VerboseMode: c.Globals.Flags.Verbose, }) diff --git a/pkg/commands/resourcelink/delete.go b/pkg/commands/resourcelink/delete.go index 46cc041bb..0eb3be2fe 100644 --- a/pkg/commands/resourcelink/delete.go +++ b/pkg/commands/resourcelink/delete.go @@ -19,6 +19,7 @@ type DeleteCommand struct { autoClone cmd.OptionalAutoClone input fastly.DeleteResourceInput manifest manifest.Data + serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } @@ -39,18 +40,25 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D Dst: &c.input.ID, Required: true, }) + c.RegisterFlag(cmd.StringFlagOpts{ + Name: cmd.FlagVersionName, + Description: cmd.FlagVersionDesc, + Dst: &c.serviceVersion.Value, + Required: true, + }) + + // At least one of the following is required. c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Short: 's', Description: cmd.FlagServiceIDDesc, Dst: &c.manifest.Flag.ServiceID, - Required: true, }) c.RegisterFlag(cmd.StringFlagOpts{ - Name: cmd.FlagVersionName, - Description: cmd.FlagVersionDesc, - Dst: &c.serviceVersion.Value, - Required: true, + Name: cmd.FlagServiceName, + Action: c.serviceName.Set, + Description: cmd.FlagServiceDesc, + Dst: &c.serviceName.Value, }) // Optional. @@ -74,7 +82,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { APIClient: c.Globals.APIClient, Manifest: c.manifest, Out: out, - ServiceNameFlag: cmd.OptionalServiceNameID{}, // ServiceID flag is required, no need to lookup service by name. + ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, VerboseMode: c.Globals.Flags.Verbose, }) diff --git a/pkg/commands/resourcelink/describe.go b/pkg/commands/resourcelink/describe.go index 75df3624b..9d11761ac 100644 --- a/pkg/commands/resourcelink/describe.go +++ b/pkg/commands/resourcelink/describe.go @@ -18,6 +18,7 @@ type DescribeCommand struct { input fastly.GetResourceInput manifest manifest.Data + serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } @@ -38,18 +39,25 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) Dst: &c.input.ID, Required: true, }) + c.RegisterFlag(cmd.StringFlagOpts{ + Name: cmd.FlagVersionName, + Description: cmd.FlagVersionDesc, + Dst: &c.serviceVersion.Value, + Required: true, + }) + + // At least one of the following is required. c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Short: 's', Description: cmd.FlagServiceIDDesc, Dst: &c.manifest.Flag.ServiceID, - Required: true, }) c.RegisterFlag(cmd.StringFlagOpts{ - Name: cmd.FlagVersionName, - Description: cmd.FlagVersionDesc, - Dst: &c.serviceVersion.Value, - Required: true, + Name: cmd.FlagServiceName, + Action: c.serviceName.Set, + Description: cmd.FlagServiceDesc, + Dst: &c.serviceName.Value, }) // Optional. @@ -64,7 +72,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { return fsterr.ErrInvalidVerboseJSONCombo } - serviceID, source, flag, err := cmd.ServiceID(cmd.OptionalServiceNameID{}, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/resourcelink/list.go b/pkg/commands/resourcelink/list.go index 0e66f31dc..0b63e625e 100644 --- a/pkg/commands/resourcelink/list.go +++ b/pkg/commands/resourcelink/list.go @@ -19,6 +19,7 @@ type ListCommand struct { input fastly.ListResourcesInput manifest manifest.Data + serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } @@ -33,18 +34,25 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.CmdClause = parent.Command("list", "List all resource links for a Fastly service version") // Required. + c.RegisterFlag(cmd.StringFlagOpts{ + Name: cmd.FlagVersionName, + Description: cmd.FlagVersionDesc, + Dst: &c.serviceVersion.Value, + Required: true, + }) + + // At least one of the following is required. c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Short: 's', Description: cmd.FlagServiceIDDesc, Dst: &c.manifest.Flag.ServiceID, - Required: true, }) c.RegisterFlag(cmd.StringFlagOpts{ - Name: cmd.FlagVersionName, - Description: cmd.FlagVersionDesc, - Dst: &c.serviceVersion.Value, - Required: true, + Name: cmd.FlagServiceName, + Action: c.serviceName.Set, + Description: cmd.FlagServiceDesc, + Dst: &c.serviceName.Value, }) // Optional. @@ -59,7 +67,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { return fsterr.ErrInvalidVerboseJSONCombo } - serviceID, source, flag, err := cmd.ServiceID(cmd.OptionalServiceNameID{}, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/resourcelink/resourcelink_test.go b/pkg/commands/resourcelink/resourcelink_test.go index d7aad206b..a041ebd75 100644 --- a/pkg/commands/resourcelink/resourcelink_test.go +++ b/pkg/commands/resourcelink/resourcelink_test.go @@ -35,7 +35,7 @@ func TestCreateServiceResourceCommand(t *testing.T) { }, { args: "create --resource-id abc --version latest", - wantError: "error parsing arguments: required flag --service-id not provided", + wantError: "error reading service: no service ID found", wantAPIInvoked: false, }, // Success. @@ -182,7 +182,7 @@ func TestDeleteServiceResourceCommand(t *testing.T) { }, { args: "delete --id LINK-ID --version 123", - wantError: "error parsing arguments: required flag --service-id not provided", + wantError: "error reading service: no service ID found", wantAPIInvoked: false, }, { @@ -284,7 +284,7 @@ func TestDescribeServiceResourceCommand(t *testing.T) { }, { args: "describe --id LINK-ID --version 123", - wantError: "error parsing arguments: required flag --service-id not provided", + wantError: "error reading service: no service ID found", wantAPIInvoked: false, }, { @@ -378,7 +378,7 @@ func TestListServiceResourceCommand(t *testing.T) { }, { args: "list --version 123", - wantError: "error parsing arguments: required flag --service-id not provided", + wantError: "error reading service: no service ID found", wantAPIInvoked: false, }, // Success. @@ -490,7 +490,7 @@ func TestUpdateServiceResourceCommand(t *testing.T) { }, { args: "update --id LINK-ID --name new-name --version 123", - wantError: "error parsing arguments: required flag --service-id not provided", + wantError: "error reading service: no service ID found", wantAPIInvoked: false, }, { diff --git a/pkg/commands/resourcelink/update.go b/pkg/commands/resourcelink/update.go index f1cd5448f..41e318929 100644 --- a/pkg/commands/resourcelink/update.go +++ b/pkg/commands/resourcelink/update.go @@ -19,6 +19,7 @@ type UpdateCommand struct { autoClone cmd.OptionalAutoClone input fastly.UpdateResourceInput manifest manifest.Data + serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } @@ -50,18 +51,25 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U Dst: c.input.Name, Required: true, }) + c.RegisterFlag(cmd.StringFlagOpts{ + Name: cmd.FlagVersionName, + Description: cmd.FlagVersionDesc, + Dst: &c.serviceVersion.Value, + Required: true, + }) + + // At least one of the following is required. c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Short: 's', Description: cmd.FlagServiceIDDesc, Dst: &c.manifest.Flag.ServiceID, - Required: true, }) c.RegisterFlag(cmd.StringFlagOpts{ - Name: cmd.FlagVersionName, - Description: cmd.FlagVersionDesc, - Dst: &c.serviceVersion.Value, - Required: true, + Name: cmd.FlagServiceName, + Action: c.serviceName.Set, + Description: cmd.FlagServiceDesc, + Dst: &c.serviceName.Value, }) // Optional. @@ -85,7 +93,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { APIClient: c.Globals.APIClient, Manifest: c.manifest, Out: out, - ServiceNameFlag: cmd.OptionalServiceNameID{}, // ServiceID flag is required, no need to lookup service by name. + ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, VerboseMode: c.Globals.Flags.Verbose, }) From 51c3ac8254db3c07b1d44a7489c677e41dcd984d Mon Sep 17 00:00:00 2001 From: Adam Williams Date: Wed, 15 Feb 2023 13:36:00 -0700 Subject: [PATCH 10/10] Create variables for common flag descriptions --- pkg/commands/resourcelink/create.go | 4 ++-- pkg/commands/resourcelink/delete.go | 2 +- pkg/commands/resourcelink/describe.go | 2 +- pkg/commands/resourcelink/root.go | 7 +++++++ pkg/commands/resourcelink/update.go | 4 ++-- 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/pkg/commands/resourcelink/create.go b/pkg/commands/resourcelink/create.go index c7a6b9426..e8c3ff1bd 100644 --- a/pkg/commands/resourcelink/create.go +++ b/pkg/commands/resourcelink/create.go @@ -42,7 +42,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: "resource-id", Short: 'r', - Description: "Resource ID", + Description: flagResourceIDDescription, Dst: c.input.ResourceID, Required: true, }) @@ -76,7 +76,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: "name", Short: 'n', - Description: "Resource alias. Defaults to name of resource", + Description: flagNameDescription, Dst: c.input.Name, }) diff --git a/pkg/commands/resourcelink/delete.go b/pkg/commands/resourcelink/delete.go index 0eb3be2fe..ce7a8e2a7 100644 --- a/pkg/commands/resourcelink/delete.go +++ b/pkg/commands/resourcelink/delete.go @@ -36,7 +36,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D // Required. c.RegisterFlag(cmd.StringFlagOpts{ Name: "id", - Description: "ID of resource link", + Description: flagIDDescription, Dst: &c.input.ID, Required: true, }) diff --git a/pkg/commands/resourcelink/describe.go b/pkg/commands/resourcelink/describe.go index 9d11761ac..7f37f5b28 100644 --- a/pkg/commands/resourcelink/describe.go +++ b/pkg/commands/resourcelink/describe.go @@ -35,7 +35,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) // Required. c.RegisterFlag(cmd.StringFlagOpts{ Name: "id", - Description: "ID of resource link", + Description: flagIDDescription, Dst: &c.input.ID, Required: true, }) diff --git a/pkg/commands/resourcelink/root.go b/pkg/commands/resourcelink/root.go index c1309bd03..daba8af73 100644 --- a/pkg/commands/resourcelink/root.go +++ b/pkg/commands/resourcelink/root.go @@ -11,6 +11,13 @@ import ( // e.g. "fastly resource-link". const RootName = "resource-link" +// Common flag descriptions. +const ( + flagNameDescription = "Resource link name (alias). Defaults to resource's name" + flagIDDescription = "Resource link ID" + flagResourceIDDescription = "Resource ID" +) + // RootCommand is the parent command for all subcommands in this package. // It should be installed under the primary root command. type RootCommand struct { diff --git a/pkg/commands/resourcelink/update.go b/pkg/commands/resourcelink/update.go index 41e318929..37701ac8c 100644 --- a/pkg/commands/resourcelink/update.go +++ b/pkg/commands/resourcelink/update.go @@ -40,14 +40,14 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U // Required. c.RegisterFlag(cmd.StringFlagOpts{ Name: "id", - Description: "ID of resource link", + Description: flagIDDescription, Dst: &c.input.ID, Required: true, }) c.RegisterFlag(cmd.StringFlagOpts{ Name: "name", Short: 'n', - Description: "Resource name (alias)", + Description: flagNameDescription, Dst: c.input.Name, Required: true, })