Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions internal/productcore/base.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package productcore

import (
"fmt"

"github.com/fastly/cli/pkg/api"
"github.com/fastly/cli/pkg/argparser"
"github.com/fastly/cli/pkg/global"
"github.com/fastly/cli/pkg/manifest"
"github.com/fastly/go-fastly/v9/fastly/products"

Check failure on line 10 in internal/productcore/base.go

View workflow job for this annotation

GitHub Actions / test (0.31.2, 1.24.x, 18, macos-latest)

no required module provides package github.com/fastly/go-fastly/v9/fastly/products; to add it:

Check failure on line 10 in internal/productcore/base.go

View workflow job for this annotation

GitHub Actions / test (0.31.2, 1.24.x, 18, macos-latest)

no required module provides package github.com/fastly/go-fastly/v9/fastly/products; to add it:

Check failure on line 10 in internal/productcore/base.go

View workflow job for this annotation

GitHub Actions / test (0.31.2, 1.24.x, 18, macos-latest)

no required module provides package github.com/fastly/go-fastly/v9/fastly/products; to add it:

Check failure on line 10 in internal/productcore/base.go

View workflow job for this annotation

GitHub Actions / test (0.31.2, 1.24.x, 18, macos-latest)

no required module provides package github.com/fastly/go-fastly/v9/fastly/products; to add it:

Check failure on line 10 in internal/productcore/base.go

View workflow job for this annotation

GitHub Actions / test (0.31.2, 1.24.x, 18, ubuntu-latest)

no required module provides package github.com/fastly/go-fastly/v9/fastly/products; to add it:

Check failure on line 10 in internal/productcore/base.go

View workflow job for this annotation

GitHub Actions / test (0.31.2, 1.24.x, 18, ubuntu-latest)

no required module provides package github.com/fastly/go-fastly/v9/fastly/products; to add it:

Check failure on line 10 in internal/productcore/base.go

View workflow job for this annotation

GitHub Actions / test (0.31.2, 1.24.x, 18, ubuntu-latest)

no required module provides package github.com/fastly/go-fastly/v9/fastly/products; to add it:

Check failure on line 10 in internal/productcore/base.go

View workflow job for this annotation

GitHub Actions / test (0.31.2, 1.24.x, 18, ubuntu-latest)

no required module provides package github.com/fastly/go-fastly/v9/fastly/products; to add it:
)

// Base is a base type for all product commands.
type Base struct {
argparser.Base
argparser.JSONOutput
Manifest manifest.Data

ServiceName argparser.OptionalServiceNameID
}

// Init prepares the structure for use by the CLI core.
func (cmd *Base) Init(parent argparser.Registerer, g *global.Data) {
cmd.Globals = g

// Optional flags.
cmd.RegisterFlag(argparser.StringFlagOpts{
Name: argparser.FlagServiceIDName,
Description: argparser.FlagServiceIDDesc,
Dst: &g.Manifest.Flag.ServiceID,
Short: 's',
})
cmd.RegisterFlag(argparser.StringFlagOpts{
Action: cmd.ServiceName.Set,
Name: argparser.FlagServiceName,
Description: argparser.FlagServiceNameDesc,
Dst: &cmd.ServiceName.Value,
})
cmd.RegisterFlagBool(cmd.JSONFlag()) // --json
}

// EnablementStatus is a structure used to generate output from
// the enablement-related commands
type EnablementStatus[_ products.ProductOutput] struct {
ProductName string `json:"-"`
ProductID string `json:"product_id"`
ServiceID string `json:"service_id"`
Enabled bool `json:"enabled"`
}

type StatusManager[O products.ProductOutput] interface {
SetEnabled(bool)
GetEnabled() string
GetProductName() string
SetProductID(string)
SetServiceID(string)
TransformOutput(O)
GetTextResult() string
}

func (s *EnablementStatus[_]) SetEnabled(e bool) {
s.Enabled = e
}

func (s *EnablementStatus[_]) GetEnabled() string {
if s.Enabled {
return "enabled"
}
return "disabled"
}

func (s *EnablementStatus[_]) GetProductName() string {
return s.ProductName
}

func (s *EnablementStatus[_]) SetProductID(id string) {
s.ProductID = id
}

func (s *EnablementStatus[_]) SetServiceID(id string) {
s.ServiceID = id
}

func (s *EnablementStatus[O]) TransformOutput(o O) {
s.ProductID = o.ProductID()

Check failure on line 85 in internal/productcore/base.go

View workflow job for this annotation

GitHub Actions / lint

o.ProductID undefined (type O has no field or method ProductID) (typecheck)
s.ServiceID = o.ServiceID()

Check failure on line 86 in internal/productcore/base.go

View workflow job for this annotation

GitHub Actions / lint

o.ServiceID undefined (type O has no field or method ServiceID) (typecheck)
}

func (s *EnablementStatus[O]) GetTextResult() string {
return fmt.Sprintf("%s is %s on service %s", s.ProductName, s.GetEnabled(), s.ServiceID)
}

// EnablementHookFuncs is a structure of dependency-injection points
// used by unit tests to provide mock behaviors
type EnablementHookFuncs[O products.ProductOutput] struct {
DisableFunc func(api.Interface, string) error
EnableFunc func(api.Interface, string) (O, error)
GetFunc func(api.Interface, string) (O, error)
}

// ConfigurationHookFuncs is a structure of dependency-injection
// points used by unit tests to provide mock behaviors
type ConfigurationHookFuncs[O, I any] struct {
GetConfigurationFunc func(api.Interface, string) (O, error)
UpdateConfigurationFunc func(api.Interface, string, I) (O, error)
}
69 changes: 69 additions & 0 deletions internal/productcore/disable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package productcore

import (
"io"

fsterr "github.com/fastly/cli/pkg/errors"

"github.com/fastly/cli/pkg/argparser"
"github.com/fastly/cli/pkg/global"
"github.com/fastly/cli/pkg/text"
"github.com/fastly/go-fastly/v9/fastly/products"
)

// Disable is a base type for all 'disable' commands.
type Disable[O products.ProductOutput, _ StatusManager[O]] struct {
Base
ProductID string
// hooks is a pointer to an EnablementHookFuncs structure so
// that tests can modify the contents of the structure after
// this structure has been initialized
hooks *EnablementHookFuncs[O]
}

// Init prepares the structure for use by the CLI core.
func (cmd *Disable[O, _]) Init(parent argparser.Registerer, g *global.Data, productID, productName string, hooks *EnablementHookFuncs[O]) {
cmd.CmdClause = parent.Command("disable", "Disable the "+productName+" product")
cmd.hooks = hooks

cmd.Base.Init(parent, g)
cmd.ProductID = productID
}

// Exec executes the disablement operation.
func (cmd *Disable[O, S]) Exec(out io.Writer, status S) error {
if cmd.Globals.Verbose() && cmd.JSONOutput.Enabled {
return fsterr.ErrInvalidVerboseJSONCombo
}

serviceID, source, flag, err := argparser.ServiceID(cmd.ServiceName, *cmd.Globals.Manifest, cmd.Globals.APIClient, cmd.Globals.ErrLog)
if err != nil {
cmd.Globals.ErrLog.Add(err)
return err
}

if cmd.Globals.Verbose() {
argparser.DisplayServiceID(serviceID, flag, source, out)
}

err = cmd.hooks.DisableFunc(cmd.Globals.APIClient, serviceID)
if err != nil {
cmd.Globals.ErrLog.Add(err)
return err
}

status.SetEnabled(false)
// The API does not return details of the service and product
// which were disabled, so they have to be inserted into
// 'status' directly
status.SetProductID(cmd.ProductID)
status.SetServiceID(serviceID)

if ok, err := cmd.WriteJSON(out, status); ok {
return err
}

text.Success(out, status.GetTextResult())

return nil
}
4 changes: 4 additions & 0 deletions internal/productcore/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Package productcore contains a group of generic structures and
// functions which are used to implement common behaviors in product
// enablement/configuration commands
package productcore
62 changes: 62 additions & 0 deletions internal/productcore/enable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package productcore

import (
"io"

"github.com/fastly/cli/pkg/argparser"
fsterr "github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/global"
"github.com/fastly/cli/pkg/text"
"github.com/fastly/go-fastly/v9/fastly/products"
)

// Enable is a base type for all 'enable' commands.
type Enable[O products.ProductOutput, _ StatusManager[O]] struct {
Base
// hooks is a pointer to an EnablementHookFuncs structure so
// that tests can modify the contents of the structure after
// this structure has been initialized
hooks *EnablementHookFuncs[O]
}

// Init prepares the structure for use by the CLI core.
func (cmd *Enable[O, _]) Init(parent argparser.Registerer, g *global.Data, productName string, hooks *EnablementHookFuncs[O]) {
cmd.CmdClause = parent.Command("enable", "Enable the "+productName+" product")
cmd.hooks = hooks

cmd.Base.Init(parent, g)
}

// Exec executes the enablement operation.
func (cmd *Enable[O, S]) Exec(out io.Writer, status S) error {
if cmd.Globals.Verbose() && cmd.JSONOutput.Enabled {
return fsterr.ErrInvalidVerboseJSONCombo
}

serviceID, source, flag, err := argparser.ServiceID(cmd.ServiceName, *cmd.Globals.Manifest, cmd.Globals.APIClient, cmd.Globals.ErrLog)
if err != nil {
cmd.Globals.ErrLog.Add(err)
return err
}

if cmd.Globals.Verbose() {
argparser.DisplayServiceID(serviceID, flag, source, out)
}

o, err := cmd.hooks.EnableFunc(cmd.Globals.APIClient, serviceID)
if err != nil {
cmd.Globals.ErrLog.Add(err)
return err
}

status.SetEnabled(true)
status.TransformOutput(o)

if ok, err := cmd.WriteJSON(out, status); ok {
return err
}

text.Success(out, status.GetTextResult())

return nil
}
71 changes: 71 additions & 0 deletions internal/productcore/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package productcore

import (
"errors"
"io"

"github.com/fastly/cli/pkg/argparser"
fsterr "github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/global"
"github.com/fastly/cli/pkg/text"
"github.com/fastly/go-fastly/v9/fastly"

Check failure on line 11 in internal/productcore/status.go

View workflow job for this annotation

GitHub Actions / test (0.31.2, 1.24.x, 18, macos-latest)

no required module provides package github.com/fastly/go-fastly/v9/fastly; to add it:

Check failure on line 11 in internal/productcore/status.go

View workflow job for this annotation

GitHub Actions / test (0.31.2, 1.24.x, 18, macos-latest)

no required module provides package github.com/fastly/go-fastly/v9/fastly; to add it:

Check failure on line 11 in internal/productcore/status.go

View workflow job for this annotation

GitHub Actions / test (0.31.2, 1.24.x, 18, macos-latest)

no required module provides package github.com/fastly/go-fastly/v9/fastly; to add it:

Check failure on line 11 in internal/productcore/status.go

View workflow job for this annotation

GitHub Actions / test (0.31.2, 1.24.x, 18, macos-latest)

no required module provides package github.com/fastly/go-fastly/v9/fastly; to add it:

Check failure on line 11 in internal/productcore/status.go

View workflow job for this annotation

GitHub Actions / test (0.31.2, 1.24.x, 18, ubuntu-latest)

no required module provides package github.com/fastly/go-fastly/v9/fastly; to add it:

Check failure on line 11 in internal/productcore/status.go

View workflow job for this annotation

GitHub Actions / test (0.31.2, 1.24.x, 18, ubuntu-latest)

no required module provides package github.com/fastly/go-fastly/v9/fastly; to add it:

Check failure on line 11 in internal/productcore/status.go

View workflow job for this annotation

GitHub Actions / test (0.31.2, 1.24.x, 18, ubuntu-latest)

no required module provides package github.com/fastly/go-fastly/v9/fastly; to add it:

Check failure on line 11 in internal/productcore/status.go

View workflow job for this annotation

GitHub Actions / test (0.31.2, 1.24.x, 18, ubuntu-latest)

no required module provides package github.com/fastly/go-fastly/v9/fastly; to add it:
"github.com/fastly/go-fastly/v9/fastly/products"
)

// Status is a base type for all 'status' commands.
type Status[O products.ProductOutput, _ StatusManager[O]] struct {
Base
// hooks is a pointer to an EnablementHookFuncs structure so
// that tests can modify the contents of the structure after
// this structure has been initialized
hooks *EnablementHookFuncs[O]
}

// Init prepares the structure for use by the CLI core.
func (cmd *Status[O, _]) Init(parent argparser.Registerer, g *global.Data, productName string, hooks *EnablementHookFuncs[O]) {
cmd.CmdClause = parent.Command("status", "Get the enablement status of the "+productName+" product")
cmd.hooks = hooks

cmd.Base.Init(parent, g)
}

// Exec executes the status operation.
func (cmd *Status[O, S]) Exec(out io.Writer, status S) error {
if cmd.Globals.Verbose() && cmd.JSONOutput.Enabled {
return fsterr.ErrInvalidVerboseJSONCombo
}

serviceID, source, flag, err := argparser.ServiceID(cmd.ServiceName, *cmd.Globals.Manifest, cmd.Globals.APIClient, cmd.Globals.ErrLog)
if err != nil {
cmd.Globals.ErrLog.Add(err)
return err
}

if cmd.Globals.Verbose() {
argparser.DisplayServiceID(serviceID, flag, source, out)
}

o, err := cmd.hooks.GetFunc(cmd.Globals.APIClient, serviceID)
if err != nil {
var herr *fastly.HTTPError

// The API returns a 'Bad Request' error when the
// product has not been enabled on the service; any
// other error should be reported
if !errors.As(err, &herr) || !herr.IsBadRequest() {
cmd.Globals.ErrLog.Add(err)
return err
}
} else {
status.SetEnabled(true)
status.TransformOutput(o)
}

if ok, err := cmd.WriteJSON(out, status); ok {
return err
}

text.Info(out, status.GetTextResult())

return nil
}
4 changes: 4 additions & 0 deletions internal/productcore_test/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Package productcore_test contains a group of generic structures and
// functions which are used to implement common behaviors in product
// enablement/configuration tests
package productcore_test
Loading
Loading