Skip to content

Commit a2b5190

Browse files
feat(commands/ngwaf/workspace/update): add missing update functionality
1 parent 7bfb94f commit a2b5190

4 files changed

Lines changed: 204 additions & 2 deletions

File tree

pkg/commands/commands.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ func Define( // nolint:revive // function-length
401401
ngwafWorkspacesDelete := workspaces.NewDeleteCommand(ngwafWorkspacesRoot.CmdClause, data)
402402
ngwafWorkspacesGet := workspaces.NewGetCommand(ngwafWorkspacesRoot.CmdClause, data)
403403
ngwafWorkspacesList := workspaces.NewListCommand(ngwafWorkspacesRoot.CmdClause, data)
404+
ngwafWorkspacesUpdate := workspaces.NewUpdateCommand(ngwafWorkspacesRoot.CmdClause, data)
404405
objectStorageRoot := objectstorage.NewRootCommand(app, data)
405406
objectStorageAccesskeysRoot := accesskeys.NewRootCommand(objectStorageRoot.CmdClause, data)
406407
objectStorageAccesskeysCreate := accesskeys.NewCreateCommand(objectStorageAccesskeysRoot.CmdClause, data)
@@ -826,6 +827,7 @@ func Define( // nolint:revive // function-length
826827
ngwafWorkspacesDelete,
827828
ngwafWorkspacesGet,
828829
ngwafWorkspacesList,
830+
ngwafWorkspacesUpdate,
829831
objectStorageRoot,
830832
objectStorageAccesskeysRoot,
831833
objectStorageAccesskeysCreate,

pkg/commands/ngwaf/workspaces/create.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/fastly/go-fastly/v12/fastly/ngwaf/v1/workspaces"
1212

1313
"github.com/fastly/cli/pkg/argparser"
14+
fsterr "github.com/fastly/cli/pkg/errors"
1415
"github.com/fastly/cli/pkg/global"
1516
"github.com/fastly/cli/pkg/text"
1617
)
@@ -60,14 +61,17 @@ func NewCreateCommand(parent argparser.Registerer, g *global.Data) *CreateComman
6061

6162
// Exec invokes the application logic for the command.
6263
func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error {
64+
if c.Globals.Verbose() && c.JSONOutput.Enabled {
65+
return fsterr.ErrInvalidVerboseJSONCombo
66+
}
6367
var err error
6468
input := &workspaces.CreateInput{
6569
Description: &c.description,
6670
Mode: &c.blockingMode,
6771
Name: &c.name,
6872
}
6973
if c.attackThresholds.WasSet {
70-
input.AttackSignalThresholds, err = parseAttackSignalThresholds(c.attackThresholds.Value)
74+
input.AttackSignalThresholds, err = parseCreateAttackSignalThresholds(c.attackThresholds.Value)
7175
if err != nil {
7276
return err
7377
}
@@ -103,7 +107,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error {
103107
return nil
104108
}
105109

106-
func parseAttackSignalThresholds(thresholds string) (*workspaces.AttackSignalThresholdsCreateInput, error) {
110+
func parseCreateAttackSignalThresholds(thresholds string) (*workspaces.AttackSignalThresholdsCreateInput, error) {
107111
thresholdsArray := strings.Split(thresholds, ":")
108112
if len(thresholdsArray) != 4 {
109113
return nil, errors.New("wrong number of inputs for Attack Signal Thresholds")
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package workspaces
2+
3+
import (
4+
"context"
5+
"errors"
6+
"io"
7+
"strconv"
8+
"strings"
9+
10+
"github.com/fastly/go-fastly/v12/fastly"
11+
"github.com/fastly/go-fastly/v12/fastly/ngwaf/v1/workspaces"
12+
13+
"github.com/fastly/cli/pkg/argparser"
14+
fsterr "github.com/fastly/cli/pkg/errors"
15+
"github.com/fastly/cli/pkg/global"
16+
"github.com/fastly/cli/pkg/text"
17+
)
18+
19+
// UpdateCommand calls the Fastly API to create domains.
20+
type UpdateCommand struct {
21+
argparser.Base
22+
argparser.JSONOutput
23+
24+
// Required.
25+
workspaceID string
26+
27+
// Optional.
28+
description argparser.OptionalString
29+
blockingMode argparser.OptionalString
30+
name argparser.OptionalString
31+
attackThresholds argparser.OptionalString
32+
defaultBlockingCode argparser.OptionalInt
33+
defaultRedirectURL argparser.OptionalString
34+
clientIPHeaders argparser.OptionalString
35+
ipAnonimization argparser.OptionalString
36+
}
37+
38+
// NewUpdateCommand returns a usable command registered under the parent.
39+
func NewUpdateCommand(parent argparser.Registerer, g *global.Data) *UpdateCommand {
40+
c := UpdateCommand{
41+
Base: argparser.Base{
42+
Globals: g,
43+
},
44+
}
45+
c.CmdClause = parent.Command("update", "Update a workspace")
46+
47+
// Required.
48+
c.CmdClause.Flag("workspace-id", "Workspace ID").Required().StringVar(&c.workspaceID)
49+
50+
// Optional.
51+
c.CmdClause.Flag("description", "User submitted description of a workspace.").Action(c.description.Set).StringVar(&c.description.Value)
52+
c.CmdClause.Flag("blockingMode", "User configured mode blocking mode.").Action(c.blockingMode.Set).StringVar(&c.blockingMode.Value)
53+
c.CmdClause.Flag("name", "User submitted display name of a workspace.").Action(c.name.Set).StringVar(&c.name.Value)
54+
c.CmdClause.Flag("attackThresholds", "Attack threshold parameters for system site alerts. Each threshold value is the number of attack signals per IP address that must be detected during the interval before the related IP address is flagged. Input accepted as colon separated string: Immediate:OneMinute:TenMinutes:OneHour").Action(c.attackThresholds.Set).StringVar(&c.attackThresholds.Value)
55+
c.CmdClause.Flag("clientIPHeaders", "Specify the request header containing the client IP address. Input accepted as colon separated string.").Action(c.clientIPHeaders.Set).StringVar(&c.clientIPHeaders.Value)
56+
c.CmdClause.Flag("defaultBlockingCode", "Default status code that is returned when a request to your web application is blocked.").Action(c.defaultBlockingCode.Set).IntVar(&c.defaultBlockingCode.Value)
57+
c.CmdClause.Flag("defaultRedirectURL", "Redirect url to be used if code 301 or 302 is used.").Action(c.defaultRedirectURL.Set).StringVar(&c.defaultRedirectURL.Value)
58+
c.CmdClause.Flag("ipAnonimization", "Agents will anonymize IP addresses according to the option selected.").Action(c.ipAnonimization.Set).StringVar(&c.ipAnonimization.Value)
59+
c.RegisterFlagBool(c.JSONFlag())
60+
61+
return &c
62+
}
63+
64+
// Exec invokes the application logic for the command.
65+
func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error {
66+
if c.Globals.Verbose() && c.JSONOutput.Enabled {
67+
return fsterr.ErrInvalidVerboseJSONCombo
68+
}
69+
var err error
70+
input := &workspaces.UpdateInput{
71+
WorkspaceID: &c.workspaceID,
72+
}
73+
74+
if c.blockingMode.WasSet {
75+
input.Mode = &c.blockingMode.Value
76+
}
77+
if c.description.WasSet {
78+
input.Description = &c.description.Value
79+
}
80+
if c.name.WasSet {
81+
input.Name = &c.name.Value
82+
}
83+
84+
if c.attackThresholds.WasSet {
85+
input.AttackSignalThresholds, err = parseUpdateAttackSignalThresholds(c.attackThresholds.Value)
86+
if err != nil {
87+
return err
88+
}
89+
}
90+
if c.clientIPHeaders.WasSet {
91+
input.ClientIPHeaders = strings.Split(c.clientIPHeaders.Value, ":")
92+
}
93+
if c.defaultBlockingCode.WasSet {
94+
input.DefaultBlockingResponseCode = &c.defaultBlockingCode.Value
95+
}
96+
if c.defaultRedirectURL.WasSet {
97+
input.DefaultRedirectURL = &c.defaultRedirectURL.Value
98+
}
99+
if c.ipAnonimization.WasSet {
100+
input.IPAnonymization = &c.ipAnonimization.Value
101+
}
102+
103+
fc, ok := c.Globals.APIClient.(*fastly.Client)
104+
if !ok {
105+
return errors.New("failed to convert interface to a fastly client")
106+
}
107+
108+
data, err := workspaces.Update(context.TODO(), fc, input)
109+
if err != nil {
110+
return err
111+
}
112+
113+
if ok, err := c.WriteJSON(out, data); ok {
114+
return err
115+
}
116+
117+
text.Success(out, "Updated workspace '%s' (workspace-id: %s)", data.Name, data.WorkspaceID)
118+
return nil
119+
}
120+
121+
func parseUpdateAttackSignalThresholds(thresholds string) (*workspaces.AttackSignalThresholdsUpdateInput, error) {
122+
thresholdsArray := strings.Split(thresholds, ":")
123+
if len(thresholdsArray) != 4 {
124+
return nil, errors.New("wrong number of inputs for Attack Signal Thresholds")
125+
}
126+
immediate, err := strconv.ParseBool(thresholdsArray[0])
127+
if err != nil {
128+
return nil, err
129+
}
130+
oneMinute, err := strconv.Atoi(thresholdsArray[1])
131+
if err != nil {
132+
return nil, err
133+
}
134+
tenMinutes, err := strconv.Atoi(thresholdsArray[2])
135+
if err != nil {
136+
return nil, err
137+
}
138+
oneHour, err := strconv.Atoi(thresholdsArray[3])
139+
if err != nil {
140+
return nil, err
141+
}
142+
143+
return &workspaces.AttackSignalThresholdsUpdateInput{
144+
OneMinute: &oneMinute,
145+
TenMinutes: &tenMinutes,
146+
OneHour: &oneHour,
147+
Immediate: &immediate,
148+
}, nil
149+
}

pkg/commands/ngwaf/workspaces/workspaces_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,53 @@ func TestWorkspaceList(t *testing.T) {
304304
testutil.RunCLIScenarios(t, []string{root.CommandName, sub.CommandName, "list"}, scenarios)
305305
}
306306

307+
func TestWorkspaceUpdate(t *testing.T) {
308+
workspacesObject := workspaces.Workspace{
309+
CreatedAt: testutil.Date,
310+
Description: workspaceDescription,
311+
Mode: workspaceMode,
312+
Name: workspaceName,
313+
WorkspaceID: workspaceID,
314+
}
315+
316+
scenarios := []testutil.CLIScenario{
317+
{
318+
Name: "validate missing --workspace-id flag",
319+
Args: "",
320+
WantError: "error parsing arguments: required flag --workspace-id not provided",
321+
},
322+
{
323+
Name: "validate API success",
324+
Args: fmt.Sprintf("--workspace-id %s --description %s --name %s --blockingMode %s --clientIPHeaders %s", workspaceID, workspaceDescription, workspaceName, workspaceMode, workspaceClientIPHeaders),
325+
Client: &http.Client{
326+
Transport: &testutil.MockRoundTripper{
327+
Response: &http.Response{
328+
StatusCode: http.StatusOK,
329+
Status: http.StatusText(http.StatusOK),
330+
Body: io.NopCloser(bytes.NewReader(testutil.GenJSON(workspacesObject))),
331+
},
332+
},
333+
},
334+
WantOutput: fstfmt.Success("Updated workspace '%s' (workspace-id: %s)", workspaceName, workspaceID),
335+
},
336+
{
337+
Name: "validate optional --json flag",
338+
Args: fmt.Sprintf("--workspace-id %s --description %s --name %s --blockingMode %s --clientIPHeaders %s --json", workspaceID, workspaceDescription, workspaceName, workspaceMode, workspaceClientIPHeaders),
339+
Client: &http.Client{
340+
Transport: &testutil.MockRoundTripper{
341+
Response: &http.Response{
342+
StatusCode: http.StatusOK,
343+
Status: http.StatusText(http.StatusOK),
344+
Body: io.NopCloser(bytes.NewReader(testutil.GenJSON(workspace))),
345+
},
346+
},
347+
},
348+
WantOutput: fstfmt.EncodeJSON(workspace),
349+
},
350+
}
351+
testutil.RunCLIScenarios(t, []string{root.CommandName, sub.CommandName, "update"}, scenarios)
352+
}
353+
307354
var listWorkspaceString = strings.TrimSpace(`
308355
ID Name Description Mode Created At
309356
someID CLIWorkspace NGWAFCLIWorkspace log 2021-06-15 23:00:00 +0000 UTC

0 commit comments

Comments
 (0)