Skip to content

Commit cfb46ba

Browse files
feat(apisecurity/tags): add crud operations for api security tags
1 parent cd5b9d0 commit cfb46ba

15 files changed

Lines changed: 1338 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55
### Breaking:
66

77
### Bug Fixes:
8-
- fix(stats): `stats historical` now returns write errors instead of silently swallowing them. [#1678](https://github.com/fastly/cli/pull/1678)
8+
- fix(stats): `stats historical` now returns write errors instead of silently swallowing them [#1678](https://github.com/fastly/cli/pull/1678)
99

1010
### Enhancements:
11-
- feat(stats): add `--field` flag to `stats historical` to filter to a single stats field. [#1678](https://github.com/fastly/cli/pull/1678)
12-
- feat(stats): add `stats aggregate` subcommand for cross-service aggregated stats. [#1678](https://github.com/fastly/cli/pull/1678)
13-
- feat(stats): add `stats usage` subcommand for bandwidth/request usage, with `--by-service` breakdown. [#1678](https://github.com/fastly/cli/pull/1678)
14-
- feat(stats): add `stats domain-inspector` subcommand for domain-level metrics. [#1678](https://github.com/fastly/cli/pull/1678)
15-
- feat(stats): add `stats origin-inspector` subcommand for origin-level metrics. [#1678](https://github.com/fastly/cli/pull/1678)
11+
- feat(stats): add `--field` flag to `stats historical` to filter to a single stats field [#1678](https://github.com/fastly/cli/pull/1678)
12+
- feat(stats): add `stats aggregate` subcommand for cross-service aggregated stats [#1678](https://github.com/fastly/cli/pull/1678)
13+
- feat(stats): add `stats usage` subcommand for bandwidth/request usage, with `--by-service` breakdown [#1678](https://github.com/fastly/cli/pull/1678)
14+
- feat(stats): add `stats domain-inspector` subcommand for domain-level metrics [#1678](https://github.com/fastly/cli/pull/1678)
15+
- feat(stats): add `stats origin-inspector` subcommand for origin-level metrics [#1678](https://github.com/fastly/cli/pull/1678)
16+
- feat(apisecurity/tags): add API Security Operations API support ([#1688](https://github.com/fastly/cli/pull/1688))
1617

1718
### Dependencies:
1819
- build(deps): `golang.org/x/net` from 0.50.0 to 0.51.0 ([#1674](https://github.com/fastly/cli/pull/1674))

pkg/app/run_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ kv-store
7272
kv-store-entry
7373
log-tail
7474
ngwaf
75+
apisecurity
7576
object-storage
7677
pops
7778
products

pkg/commands/apisecurity/doc.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package apisecurity contains commands to manage API operations discovered for services.
2+
package apisecurity

pkg/commands/apisecurity/root.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package apisecurity
2+
3+
import (
4+
"io"
5+
6+
"github.com/fastly/cli/pkg/argparser"
7+
"github.com/fastly/cli/pkg/global"
8+
)
9+
10+
// RootCommand is the parent command for all subcommands in this package.
11+
// It should be installed under the primary root command.
12+
type RootCommand struct {
13+
argparser.Base
14+
// no flags
15+
}
16+
17+
// CommandName is the string to be used to invoke this command.
18+
const CommandName = "apisecurity"
19+
20+
// NewRootCommand returns a new command registered in the parent.
21+
func NewRootCommand(parent argparser.Registerer, globals *global.Data) *RootCommand {
22+
var c RootCommand
23+
c.Globals = globals
24+
c.CmdClause = parent.Command(CommandName, "Manipulate Fastly API security operations")
25+
return &c
26+
}
27+
28+
// Exec implements the command interface.
29+
func (c *RootCommand) Exec(_ io.Reader, _ io.Writer) error {
30+
panic("unreachable")
31+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package tags
2+
3+
import (
4+
"context"
5+
"errors"
6+
"io"
7+
8+
"github.com/fastly/go-fastly/v13/fastly"
9+
"github.com/fastly/go-fastly/v13/fastly/apisecurity/operations"
10+
11+
"github.com/fastly/cli/pkg/argparser"
12+
fsterr "github.com/fastly/cli/pkg/errors"
13+
"github.com/fastly/cli/pkg/global"
14+
"github.com/fastly/cli/pkg/text"
15+
)
16+
17+
// AddBulkCommand calls the Fastly API to add tags to multiple operations.
18+
type AddBulkCommand struct {
19+
argparser.Base
20+
argparser.JSONOutput
21+
22+
// Required.
23+
operationIDs []string
24+
tagIDs []string
25+
26+
// Optional.
27+
serviceName argparser.OptionalServiceNameID
28+
}
29+
30+
// NewAddBulkCommand returns a usable command registered under the parent.
31+
func NewAddBulkCommand(parent argparser.Registerer, g *global.Data) *AddBulkCommand {
32+
c := AddBulkCommand{
33+
Base: argparser.Base{
34+
Globals: g,
35+
},
36+
}
37+
38+
c.CmdClause = parent.Command("add-bulk", "Add tags to multiple operations")
39+
40+
// Required.
41+
c.CmdClause.Flag("operation-id", "Operation ID. Set flag multiple times to include multiple operations").Required().StringsVar(&c.operationIDs)
42+
c.CmdClause.Flag("tag-id", "Tag ID to add. Set flag multiple times to include multiple tags").Required().StringsVar(&c.tagIDs)
43+
44+
// Optional.
45+
c.RegisterFlag(argparser.StringFlagOpts{
46+
Name: argparser.FlagServiceIDName,
47+
Description: argparser.FlagServiceIDDesc,
48+
Dst: &g.Manifest.Flag.ServiceID,
49+
})
50+
c.RegisterFlag(argparser.StringFlagOpts{
51+
Action: c.serviceName.Set,
52+
Name: argparser.FlagServiceName,
53+
Description: argparser.FlagServiceNameDesc,
54+
Dst: &c.serviceName.Value,
55+
})
56+
c.RegisterFlagBool(c.JSONFlag())
57+
58+
return &c
59+
}
60+
61+
// Exec invokes the application logic for the command.
62+
func (c *AddBulkCommand) Exec(_ io.Reader, out io.Writer) error {
63+
if c.Globals.Verbose() && c.JSONOutput.Enabled {
64+
return fsterr.ErrInvalidVerboseJSONCombo
65+
}
66+
67+
serviceID, source, flag, err := argparser.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog)
68+
if err != nil {
69+
return err
70+
}
71+
if c.Globals.Verbose() {
72+
argparser.DisplayServiceID(serviceID, flag, source, out)
73+
}
74+
75+
if serviceID == "" {
76+
return errors.New("service-id is required")
77+
}
78+
79+
if len(c.operationIDs) == 0 {
80+
return errors.New("at least one operation-id must be provided")
81+
}
82+
if len(c.tagIDs) == 0 {
83+
return errors.New("at least one tag-id must be provided")
84+
}
85+
86+
fc, ok := c.Globals.APIClient.(*fastly.Client)
87+
if !ok {
88+
return errors.New("failed to convert interface to a fastly client")
89+
}
90+
91+
result, err := operations.BulkAddTags(context.TODO(), fc, &operations.BulkAddTagsInput{
92+
ServiceID: &serviceID,
93+
OperationIDs: c.operationIDs,
94+
TagIDs: c.tagIDs,
95+
})
96+
if err != nil {
97+
c.Globals.ErrLog.Add(err)
98+
return err
99+
}
100+
101+
if ok, err := c.WriteJSON(out, result); ok {
102+
return err
103+
}
104+
105+
text.Success(out, "Bulk add tags completed. Processed %d operations", len(result.Data))
106+
return nil
107+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package tags
2+
3+
import (
4+
"context"
5+
"errors"
6+
"io"
7+
8+
"github.com/fastly/go-fastly/v13/fastly"
9+
"github.com/fastly/go-fastly/v13/fastly/apisecurity/operations"
10+
11+
"github.com/fastly/cli/pkg/argparser"
12+
fsterr "github.com/fastly/cli/pkg/errors"
13+
"github.com/fastly/cli/pkg/global"
14+
"github.com/fastly/cli/pkg/text"
15+
)
16+
17+
// CreateCommand calls the Fastly API to create an operation tag.
18+
type CreateCommand struct {
19+
argparser.Base
20+
argparser.JSONOutput
21+
22+
// Required.
23+
name string
24+
25+
// Optional.
26+
description argparser.OptionalString
27+
serviceName argparser.OptionalServiceNameID
28+
}
29+
30+
// NewCreateCommand returns a usable command registered under the parent.
31+
func NewCreateCommand(parent argparser.Registerer, g *global.Data) *CreateCommand {
32+
c := CreateCommand{
33+
Base: argparser.Base{
34+
Globals: g,
35+
},
36+
}
37+
38+
c.CmdClause = parent.Command("create", "Create an operation tag")
39+
40+
// Required.
41+
c.CmdClause.Flag("name", "Name of the operation tag").Required().StringVar(&c.name)
42+
43+
// Optional.
44+
c.RegisterFlag(argparser.StringFlagOpts{
45+
Name: argparser.FlagServiceIDName,
46+
Description: argparser.FlagServiceIDDesc,
47+
Dst: &g.Manifest.Flag.ServiceID,
48+
})
49+
c.RegisterFlag(argparser.StringFlagOpts{
50+
Action: c.serviceName.Set,
51+
Name: argparser.FlagServiceName,
52+
Description: argparser.FlagServiceNameDesc,
53+
Dst: &c.serviceName.Value,
54+
})
55+
c.CmdClause.Flag("description", "Description of the operation tag").Action(c.description.Set).StringVar(&c.description.Value)
56+
c.RegisterFlagBool(c.JSONFlag())
57+
58+
return &c
59+
}
60+
61+
// Exec invokes the application logic for the command.
62+
func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error {
63+
if c.Globals.Verbose() && c.JSONOutput.Enabled {
64+
return fsterr.ErrInvalidVerboseJSONCombo
65+
}
66+
67+
serviceID, source, flag, err := argparser.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog)
68+
if err != nil {
69+
return err
70+
}
71+
if c.Globals.Verbose() {
72+
argparser.DisplayServiceID(serviceID, flag, source, out)
73+
}
74+
75+
if serviceID == "" {
76+
return errors.New("service-id is required")
77+
}
78+
79+
fc, ok := c.Globals.APIClient.(*fastly.Client)
80+
if !ok {
81+
return errors.New("failed to convert interface to a fastly client")
82+
}
83+
84+
input := &operations.CreateTagInput{
85+
ServiceID: &serviceID,
86+
Name: &c.name,
87+
}
88+
89+
if c.description.WasSet {
90+
input.Description = &c.description.Value
91+
}
92+
93+
tag, err := operations.CreateTag(context.TODO(), fc, input)
94+
if err != nil {
95+
c.Globals.ErrLog.Add(err)
96+
return err
97+
}
98+
99+
if ok, err := c.WriteJSON(out, tag); ok {
100+
return err
101+
}
102+
103+
text.Success(out, "Created operation tag '%s' (id: %s)", tag.Name, tag.ID)
104+
return nil
105+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package tags
2+
3+
import (
4+
"context"
5+
"errors"
6+
"io"
7+
8+
"github.com/fastly/go-fastly/v13/fastly"
9+
"github.com/fastly/go-fastly/v13/fastly/apisecurity/operations"
10+
11+
"github.com/fastly/cli/pkg/argparser"
12+
fsterr "github.com/fastly/cli/pkg/errors"
13+
"github.com/fastly/cli/pkg/global"
14+
"github.com/fastly/cli/pkg/text"
15+
)
16+
17+
// DeleteCommand calls the Fastly API to delete an operation tag.
18+
type DeleteCommand struct {
19+
argparser.Base
20+
argparser.JSONOutput
21+
22+
// Required.
23+
tagID string
24+
25+
// Optional.
26+
serviceName argparser.OptionalServiceNameID
27+
}
28+
29+
// NewDeleteCommand returns a usable command registered under the parent.
30+
func NewDeleteCommand(parent argparser.Registerer, g *global.Data) *DeleteCommand {
31+
c := DeleteCommand{
32+
Base: argparser.Base{
33+
Globals: g,
34+
},
35+
}
36+
37+
c.CmdClause = parent.Command("delete", "Delete an operation tag")
38+
39+
// Required.
40+
c.CmdClause.Flag("tag-id", "Tag ID").Required().StringVar(&c.tagID)
41+
42+
// Optional.
43+
c.RegisterFlag(argparser.StringFlagOpts{
44+
Name: argparser.FlagServiceIDName,
45+
Description: argparser.FlagServiceIDDesc,
46+
Dst: &g.Manifest.Flag.ServiceID,
47+
})
48+
c.RegisterFlag(argparser.StringFlagOpts{
49+
Action: c.serviceName.Set,
50+
Name: argparser.FlagServiceName,
51+
Description: argparser.FlagServiceNameDesc,
52+
Dst: &c.serviceName.Value,
53+
})
54+
c.RegisterFlagBool(c.JSONFlag())
55+
56+
return &c
57+
}
58+
59+
// Exec invokes the application logic for the command.
60+
func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error {
61+
if c.Globals.Verbose() && c.JSONOutput.Enabled {
62+
return fsterr.ErrInvalidVerboseJSONCombo
63+
}
64+
65+
serviceID, source, flag, err := argparser.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog)
66+
if err != nil {
67+
return err
68+
}
69+
if c.Globals.Verbose() {
70+
argparser.DisplayServiceID(serviceID, flag, source, out)
71+
}
72+
73+
if serviceID == "" {
74+
return errors.New("service-id is required")
75+
}
76+
77+
fc, ok := c.Globals.APIClient.(*fastly.Client)
78+
if !ok {
79+
return errors.New("failed to convert interface to a fastly client")
80+
}
81+
82+
err = operations.DeleteTag(context.TODO(), fc, &operations.DeleteTagInput{
83+
ServiceID: &serviceID,
84+
TagID: &c.tagID,
85+
})
86+
if err != nil {
87+
c.Globals.ErrLog.Add(err)
88+
return err
89+
}
90+
91+
if c.JSONOutput.Enabled {
92+
o := struct {
93+
ServiceID string `json:"service_id"`
94+
TagID string `json:"tag_id"`
95+
Deleted bool `json:"deleted"`
96+
}{
97+
serviceID,
98+
c.tagID,
99+
true,
100+
}
101+
_, err := c.WriteJSON(out, o)
102+
return err
103+
}
104+
105+
text.Success(out, "Deleted operation tag (id: %s)", c.tagID)
106+
return nil
107+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package tags contains commands to manipulate Fastly API Security operation tags.
2+
package tags

0 commit comments

Comments
 (0)