Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(product_enablement): add products command #1036

Merged
merged 9 commits into from
Oct 10, 2023
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/fastly/cli

go 1.20
go 1.21

require (
github.com/Masterminds/semver/v3 v3.2.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/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=
Expand Down Expand Up @@ -83,6 +84,7 @@ github.com/nwaples/rardecode v1.1.2/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWk
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
Expand Down
4 changes: 4 additions & 0 deletions pkg/api/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,10 @@ type Interface interface {
GetCondition(i *fastly.GetConditionInput) (*fastly.Condition, error)
ListConditions(i *fastly.ListConditionsInput) ([]*fastly.Condition, error)
UpdateCondition(i *fastly.UpdateConditionInput) (*fastly.Condition, error)

GetProduct(i *fastly.ProductEnablementInput) (*fastly.ProductEnablement, error)
EnableProduct(i *fastly.ProductEnablementInput) (*fastly.ProductEnablement, error)
DisableProduct(i *fastly.ProductEnablementInput) error
}

// RealtimeStatsInterface is the subset of go-fastly's realtime stats API used here.
Expand Down
3 changes: 3 additions & 0 deletions pkg/app/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import (
"github.com/fastly/cli/pkg/commands/logging/syslog"
"github.com/fastly/cli/pkg/commands/logtail"
"github.com/fastly/cli/pkg/commands/pop"
"github.com/fastly/cli/pkg/commands/products"
"github.com/fastly/cli/pkg/commands/profile"
"github.com/fastly/cli/pkg/commands/purge"
"github.com/fastly/cli/pkg/commands/ratelimit"
Expand Down Expand Up @@ -329,6 +330,7 @@ func defineCommands(
loggingSyslogList := syslog.NewListCommand(loggingSyslogCmdRoot.CmdClause, g, m)
loggingSyslogUpdate := syslog.NewUpdateCommand(loggingSyslogCmdRoot.CmdClause, g, m)
popCmdRoot := pop.NewRootCommand(app, g)
productsCmdRoot := products.NewRootCommand(app, g, m)
profileCmdRoot := profile.NewRootCommand(app, g)
profileCreate := profile.NewCreateCommand(profileCmdRoot.CmdClause, profile.APIClientFactory(opts.APIClient), g)
profileDelete := profile.NewDeleteCommand(profileCmdRoot.CmdClause, g)
Expand Down Expand Up @@ -691,6 +693,7 @@ func defineCommands(
loggingSyslogList,
loggingSyslogUpdate,
popCmdRoot,
productsCmdRoot,
profileCmdRoot,
profileCreate,
profileDelete,
Expand Down
1 change: 1 addition & 0 deletions pkg/app/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ kv-store-entry
log-tail
logging
pops
products
profile
purge
rate-limit
Expand Down
2 changes: 2 additions & 0 deletions pkg/commands/products/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package products contains commands to inspect and manipulate Fastly products.
package products
143 changes: 143 additions & 0 deletions pkg/commands/products/products_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package products_test

import (
"bytes"
"testing"

"github.com/fastly/go-fastly/v8/fastly"

"github.com/fastly/cli/pkg/app"
"github.com/fastly/cli/pkg/mock"
"github.com/fastly/cli/pkg/testutil"
)

func TestProductEnablement(t *testing.T) {
args := testutil.Args
scenarios := []testutil.TestScenario{
{
Name: "validate missing Service ID",
Args: args("products"),
WantError: "failed to identify Service ID: error reading service: no service ID found",
},
{
Name: "validate invalid enable/disable flag combo",
Args: args("products --enable fanout --disable fanout"),
WantError: "invalid flag combination: --enable and --disable",
},
{
Name: "validate API error for product status",
API: mock.API{
GetProductFn: func(i *fastly.ProductEnablementInput) (*fastly.ProductEnablement, error) {
return nil, testutil.Err
},
},
Args: args("products --service-id 123"),
WantOutput: `PRODUCT ENABLED
Brotli Compression false
Domain Inspector false
Fanout false
Image Optimizer false
Origin Inspector false
Web Sockets false
`,
},
{
Name: "validate API success for product status",
API: mock.API{
GetProductFn: func(i *fastly.ProductEnablementInput) (*fastly.ProductEnablement, error) {
return nil, nil
},
},
Args: args("products --service-id 123"),
WantOutput: `PRODUCT ENABLED
Brotli Compression true
Domain Inspector true
Fanout true
Image Optimizer true
Origin Inspector true
Web Sockets true
`,
},
{
Name: "validate flag parsing error for enabling product",
Args: args("products --service-id 123 --enable foo"),
WantError: "error parsing arguments: enum value must be one of brotli_compression,domain_inspector,fanout,image_optimizer,origin_inspector,websockets, got 'foo'",
},
{
Name: "validate flag parsing error for disabling product",
Args: args("products --service-id 123 --disable foo"),
WantError: "error parsing arguments: enum value must be one of brotli_compression,domain_inspector,fanout,image_optimizer,origin_inspector,websockets, got 'foo'",
},
{
Name: "validate success for enabling product",
API: mock.API{
EnableProductFn: func(i *fastly.ProductEnablementInput) (*fastly.ProductEnablement, error) {
return nil, nil
},
},
Args: args("products --service-id 123 --enable brotli_compression"),
WantOutput: "SUCCESS: Successfully enabled product 'brotli_compression'",
},
{
Name: "validate success for disabling product",
API: mock.API{
DisableProductFn: func(i *fastly.ProductEnablementInput) error {
return nil
},
},
Args: args("products --service-id 123 --disable brotli_compression"),
WantOutput: "SUCCESS: Successfully disabled product 'brotli_compression'",
},
{
Name: "validate invalid json/verbose flag combo",
Args: args("products --service-id 123 --json --verbose"),
WantError: "invalid flag combination, --verbose and --json",
},
{
Name: "validate API error for product status with --json output",
API: mock.API{
GetProductFn: func(i *fastly.ProductEnablementInput) (*fastly.ProductEnablement, error) {
return nil, testutil.Err
},
},
Args: args("products --service-id 123 --json"),
WantOutput: `{
"brotli_compression": false,
"domain_inspector": false,
"fanout": false,
"image_optimizer": false,
"origin_inspector": false,
"websockets": false
}`,
},
{
Name: "validate API success for product status with --json output",
API: mock.API{
GetProductFn: func(i *fastly.ProductEnablementInput) (*fastly.ProductEnablement, error) {
return nil, nil
},
},
Args: args("products --service-id 123 --json"),
WantOutput: `{
"brotli_compression": true,
"domain_inspector": true,
"fanout": true,
"image_optimizer": true,
"origin_inspector": true,
"websockets": true
}`,
},
}

for testcaseIdx := range scenarios {
testcase := &scenarios[testcaseIdx]
t.Run(testcase.Name, func(t *testing.T) {
var stdout bytes.Buffer
opts := testutil.NewRunOpts(testcase.Args, &stdout)
opts.APIClient = mock.APIClient(testcase.API)
err := app.Run(opts)
testutil.AssertErrorContains(t, err, testcase.WantError)
testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput)
})
}
}
Loading
Loading