github.com/fastly/cli@v1.7.2-0.20240304164155-9d0f1d77c3bf/pkg/commands/vcl/snippet/update.go (about)

     1  package snippet
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  
     7  	"github.com/fastly/go-fastly/v9/fastly"
     8  
     9  	"github.com/fastly/cli/pkg/argparser"
    10  	fsterr "github.com/fastly/cli/pkg/errors"
    11  	"github.com/fastly/cli/pkg/global"
    12  	"github.com/fastly/cli/pkg/text"
    13  )
    14  
    15  // NewUpdateCommand returns a usable command registered under the parent.
    16  func NewUpdateCommand(parent argparser.Registerer, g *global.Data) *UpdateCommand {
    17  	c := UpdateCommand{
    18  		Base: argparser.Base{
    19  			Globals: g,
    20  		},
    21  	}
    22  	c.CmdClause = parent.Command("update", "Update a VCL snippet for a particular service and version")
    23  
    24  	// Required.
    25  	c.RegisterFlag(argparser.StringFlagOpts{
    26  		Name:        argparser.FlagVersionName,
    27  		Description: argparser.FlagVersionDesc,
    28  		Dst:         &c.serviceVersion.Value,
    29  		Required:    true,
    30  	})
    31  
    32  	// Optional.
    33  	c.RegisterAutoCloneFlag(argparser.AutoCloneFlagOpts{
    34  		Action: c.autoClone.Set,
    35  		Dst:    &c.autoClone.Value,
    36  	})
    37  	c.CmdClause.Flag("content", "VCL snippet passed as file path or content, e.g. $(< snippet.vcl)").Action(c.content.Set).StringVar(&c.content.Value)
    38  	c.CmdClause.Flag("dynamic", "Whether the VCL snippet is dynamic or versioned").Action(c.dynamic.Set).BoolVar(&c.dynamic.Value)
    39  	c.CmdClause.Flag("name", "The name of the VCL snippet to update").StringVar(&c.name)
    40  	c.CmdClause.Flag("new-name", "New name for the VCL snippet").Action(c.newName.Set).StringVar(&c.newName.Value)
    41  	c.CmdClause.Flag("priority", "Priority determines execution order. Lower numbers execute first").Short('p').Action(c.priority.Set).IntVar(&c.priority.Value)
    42  	c.RegisterFlag(argparser.StringFlagOpts{
    43  		Name:        argparser.FlagServiceIDName,
    44  		Description: argparser.FlagServiceIDDesc,
    45  		Dst:         &g.Manifest.Flag.ServiceID,
    46  		Short:       's',
    47  	})
    48  	c.RegisterFlag(argparser.StringFlagOpts{
    49  		Action:      c.serviceName.Set,
    50  		Name:        argparser.FlagServiceName,
    51  		Description: argparser.FlagServiceDesc,
    52  		Dst:         &c.serviceName.Value,
    53  	})
    54  	c.CmdClause.Flag("snippet-id", "Alphanumeric string identifying a VCL Snippet").StringVar(&c.snippetID)
    55  
    56  	// NOTE: Locations is defined in the same snippet package inside create.go
    57  	c.CmdClause.Flag("type", "The location in generated VCL where the snippet should be placed").HintOptions(Locations...).Action(c.location.Set).EnumVar(&c.location.Value, Locations...)
    58  
    59  	return &c
    60  }
    61  
    62  // UpdateCommand calls the Fastly API to update an appropriate resource.
    63  type UpdateCommand struct {
    64  	argparser.Base
    65  
    66  	autoClone      argparser.OptionalAutoClone
    67  	content        argparser.OptionalString
    68  	dynamic        argparser.OptionalBool
    69  	location       argparser.OptionalString
    70  	name           string
    71  	newName        argparser.OptionalString
    72  	priority       argparser.OptionalInt
    73  	serviceName    argparser.OptionalServiceNameID
    74  	serviceVersion argparser.OptionalServiceVersion
    75  	snippetID      string
    76  }
    77  
    78  // Exec invokes the application logic for the command.
    79  func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error {
    80  	serviceID, serviceVersion, err := argparser.ServiceDetails(argparser.ServiceDetailsOpts{
    81  		AllowActiveLocked:  c.dynamic.WasSet && c.dynamic.Value,
    82  		AutoCloneFlag:      c.autoClone,
    83  		APIClient:          c.Globals.APIClient,
    84  		Manifest:           *c.Globals.Manifest,
    85  		Out:                out,
    86  		ServiceNameFlag:    c.serviceName,
    87  		ServiceVersionFlag: c.serviceVersion,
    88  		VerboseMode:        c.Globals.Flags.Verbose,
    89  	})
    90  	if err != nil {
    91  		c.Globals.ErrLog.AddWithContext(err, map[string]any{
    92  			"Service ID":      serviceID,
    93  			"Service Version": fsterr.ServiceVersion(serviceVersion),
    94  		})
    95  		return err
    96  	}
    97  
    98  	serviceVersionNumber := fastly.ToValue(serviceVersion.Number)
    99  
   100  	if c.dynamic.WasSet {
   101  		input, err := c.constructDynamicInput(serviceID, serviceVersionNumber)
   102  		if err != nil {
   103  			c.Globals.ErrLog.AddWithContext(err, map[string]any{
   104  				"Service ID":      serviceID,
   105  				"Service Version": serviceVersionNumber,
   106  			})
   107  			return err
   108  		}
   109  		v, err := c.Globals.APIClient.UpdateDynamicSnippet(input)
   110  		if err != nil {
   111  			c.Globals.ErrLog.AddWithContext(err, map[string]any{
   112  				"Service ID":      serviceID,
   113  				"Service Version": serviceVersionNumber,
   114  			})
   115  			return err
   116  		}
   117  		text.Success(out, "Updated dynamic VCL snippet '%s' (service: %s)", fastly.ToValue(v.SnippetID), fastly.ToValue(v.ServiceID))
   118  		return nil
   119  	}
   120  
   121  	input, err := c.constructInput(serviceID, serviceVersionNumber)
   122  	if err != nil {
   123  		c.Globals.ErrLog.AddWithContext(err, map[string]any{
   124  			"Service ID":      serviceID,
   125  			"Service Version": serviceVersionNumber,
   126  		})
   127  		return err
   128  	}
   129  	v, err := c.Globals.APIClient.UpdateSnippet(input)
   130  	if err != nil {
   131  		c.Globals.ErrLog.AddWithContext(err, map[string]any{
   132  			"Service ID":      serviceID,
   133  			"Service Version": serviceVersionNumber,
   134  		})
   135  		return err
   136  	}
   137  	text.Success(out,
   138  		"Updated VCL snippet '%s' (previously: '%s', service: %s, version: %d, type: %v, priority: %d)",
   139  		fastly.ToValue(v.Name),
   140  		input.Name,
   141  		fastly.ToValue(v.ServiceID),
   142  		fastly.ToValue(v.ServiceVersion),
   143  		fastly.ToValue(v.Type),
   144  		fastly.ToValue(v.Priority),
   145  	)
   146  	return nil
   147  }
   148  
   149  // constructDynamicInput transforms values parsed from CLI flags into an object to be used by the API client library.
   150  func (c *UpdateCommand) constructDynamicInput(serviceID string, _ int) (*fastly.UpdateDynamicSnippetInput, error) {
   151  	var input fastly.UpdateDynamicSnippetInput
   152  
   153  	input.SnippetID = c.snippetID
   154  	input.ServiceID = serviceID
   155  
   156  	if c.newName.WasSet {
   157  		return nil, fmt.Errorf("error parsing arguments: --new-name is not supported when updating a dynamic VCL snippet")
   158  	}
   159  	if c.snippetID == "" {
   160  		return nil, fmt.Errorf("error parsing arguments: must provide --snippet-id to update a dynamic VCL snippet")
   161  	}
   162  	if c.content.WasSet {
   163  		input.Content = fastly.ToPointer(argparser.Content(c.content.Value))
   164  	}
   165  
   166  	return &input, nil
   167  }
   168  
   169  // constructInput transforms values parsed from CLI flags into an object to be used by the API client library.
   170  func (c *UpdateCommand) constructInput(serviceID string, serviceVersion int) (*fastly.UpdateSnippetInput, error) {
   171  	var input fastly.UpdateSnippetInput
   172  
   173  	input.Name = c.name
   174  	input.ServiceID = serviceID
   175  	input.ServiceVersion = serviceVersion
   176  
   177  	if c.snippetID != "" {
   178  		return nil, fmt.Errorf("error parsing arguments: --snippet-id is not supported when updating a versioned VCL snippet")
   179  	}
   180  	if c.name == "" {
   181  		return nil, fmt.Errorf("error parsing arguments: must provide --name to update a versioned VCL snippet")
   182  	}
   183  	if c.newName.WasSet {
   184  		input.NewName = &c.newName.Value
   185  	}
   186  	if c.priority.WasSet {
   187  		input.Priority = &c.priority.Value
   188  	}
   189  	if c.content.WasSet {
   190  		input.Content = fastly.ToPointer(argparser.Content(c.content.Value))
   191  	}
   192  	if c.location.WasSet {
   193  		location := fastly.SnippetType(c.location.Value)
   194  		input.Type = &location
   195  	}
   196  
   197  	return &input, nil
   198  }