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
13 changes: 7 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
### Breaking:

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

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

### Dependencies:
- build(deps): `golang.org/x/net` from 0.50.0 to 0.51.0 ([#1674](https://github.com/fastly/cli/pull/1674))
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 @@ -72,6 +72,7 @@ kv-store
kv-store-entry
log-tail
ngwaf
apisecurity
object-storage
pops
products
Expand Down
2 changes: 2 additions & 0 deletions pkg/commands/apisecurity/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package apisecurity contains commands to manage API operations discovered for services.
package apisecurity
31 changes: 31 additions & 0 deletions pkg/commands/apisecurity/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package apisecurity

import (
"io"

"github.com/fastly/cli/pkg/argparser"
"github.com/fastly/cli/pkg/global"
)

// RootCommand is the parent command for all subcommands in this package.
// It should be installed under the primary root command.
type RootCommand struct {
argparser.Base
// no flags
}

// CommandName is the string to be used to invoke this command.
const CommandName = "apisecurity"

// NewRootCommand returns a new command registered in the parent.
func NewRootCommand(parent argparser.Registerer, globals *global.Data) *RootCommand {
var c RootCommand
c.Globals = globals
c.CmdClause = parent.Command(CommandName, "Manipulate Fastly API security operations")
return &c
}

// Exec implements the command interface.
func (c *RootCommand) Exec(_ io.Reader, _ io.Writer) error {
panic("unreachable")
}
105 changes: 105 additions & 0 deletions pkg/commands/apisecurity/tags/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package tags

import (
"context"
"errors"
"io"

"github.com/fastly/go-fastly/v13/fastly"
"github.com/fastly/go-fastly/v13/fastly/apisecurity/operations"

"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"
)

// CreateCommand calls the Fastly API to create an operation tag.
type CreateCommand struct {
argparser.Base
argparser.JSONOutput

// Required.
name string

// Optional.
description argparser.OptionalString
serviceName argparser.OptionalServiceNameID
}

// NewCreateCommand returns a usable command registered under the parent.
func NewCreateCommand(parent argparser.Registerer, g *global.Data) *CreateCommand {
c := CreateCommand{
Base: argparser.Base{
Globals: g,
},
}

c.CmdClause = parent.Command("create", "Create an operation tag")

// Required.
c.CmdClause.Flag("name", "Name of the operation tag").Required().StringVar(&c.name)

// Optional.
c.RegisterFlag(argparser.StringFlagOpts{
Name: argparser.FlagServiceIDName,
Description: argparser.FlagServiceIDDesc,
Dst: &g.Manifest.Flag.ServiceID,
})
c.RegisterFlag(argparser.StringFlagOpts{
Action: c.serviceName.Set,
Name: argparser.FlagServiceName,
Description: argparser.FlagServiceNameDesc,
Dst: &c.serviceName.Value,
})
c.CmdClause.Flag("description", "Description of the operation tag").Action(c.description.Set).StringVar(&c.description.Value)
c.RegisterFlagBool(c.JSONFlag())

return &c
}

// Exec invokes the application logic for the command.
func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error {
if c.Globals.Verbose() && c.JSONOutput.Enabled {
return fsterr.ErrInvalidVerboseJSONCombo
}

serviceID, source, flag, err := argparser.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog)
if err != nil {
return err
}
if c.Globals.Verbose() {
argparser.DisplayServiceID(serviceID, flag, source, out)
}

if serviceID == "" {
return errors.New("service-id is required")
}

fc, ok := c.Globals.APIClient.(*fastly.Client)
if !ok {
return errors.New("failed to convert interface to a fastly client")
}

input := &operations.CreateTagInput{
ServiceID: &serviceID,
Name: &c.name,
}

if c.description.WasSet {
input.Description = &c.description.Value
}

tag, err := operations.CreateTag(context.TODO(), fc, input)
if err != nil {
c.Globals.ErrLog.Add(err)
return err
}

if ok, err := c.WriteJSON(out, tag); ok {
return err
}

text.Success(out, "Created operation tag '%s' (id: %s)", tag.Name, tag.ID)
return nil
}
107 changes: 107 additions & 0 deletions pkg/commands/apisecurity/tags/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package tags

import (
"context"
"errors"
"io"

"github.com/fastly/go-fastly/v13/fastly"
"github.com/fastly/go-fastly/v13/fastly/apisecurity/operations"

"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"
)

// DeleteCommand calls the Fastly API to delete an operation tag.
type DeleteCommand struct {
argparser.Base
argparser.JSONOutput

// Required.
tagID string

// Optional.
serviceName argparser.OptionalServiceNameID
}

// NewDeleteCommand returns a usable command registered under the parent.
func NewDeleteCommand(parent argparser.Registerer, g *global.Data) *DeleteCommand {
c := DeleteCommand{
Base: argparser.Base{
Globals: g,
},
}

c.CmdClause = parent.Command("delete", "Delete an operation tag")

// Required.
c.CmdClause.Flag("tag-id", "Tag ID").Required().StringVar(&c.tagID)

// Optional.
c.RegisterFlag(argparser.StringFlagOpts{
Name: argparser.FlagServiceIDName,
Description: argparser.FlagServiceIDDesc,
Dst: &g.Manifest.Flag.ServiceID,
})
c.RegisterFlag(argparser.StringFlagOpts{
Action: c.serviceName.Set,
Name: argparser.FlagServiceName,
Description: argparser.FlagServiceNameDesc,
Dst: &c.serviceName.Value,
})
c.RegisterFlagBool(c.JSONFlag())

return &c
}

// Exec invokes the application logic for the command.
func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error {
if c.Globals.Verbose() && c.JSONOutput.Enabled {
return fsterr.ErrInvalidVerboseJSONCombo
}

serviceID, source, flag, err := argparser.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog)
if err != nil {
return err
}
if c.Globals.Verbose() {
argparser.DisplayServiceID(serviceID, flag, source, out)
}

if serviceID == "" {
return errors.New("service-id is required")
}

fc, ok := c.Globals.APIClient.(*fastly.Client)
if !ok {
return errors.New("failed to convert interface to a fastly client")
}

err = operations.DeleteTag(context.TODO(), fc, &operations.DeleteTagInput{
ServiceID: &serviceID,
TagID: &c.tagID,
})
if err != nil {
c.Globals.ErrLog.Add(err)
return err
}

if c.JSONOutput.Enabled {
o := struct {
ServiceID string `json:"service_id"`
TagID string `json:"tag_id"`
Deleted bool `json:"deleted"`
}{
serviceID,
c.tagID,
true,
}
_, err := c.WriteJSON(out, o)
return err
}

text.Success(out, "Deleted operation tag (id: %s)", c.tagID)
return nil
}
2 changes: 2 additions & 0 deletions pkg/commands/apisecurity/tags/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package tags contains commands to manipulate Fastly API Security operation tags.
package tags
97 changes: 97 additions & 0 deletions pkg/commands/apisecurity/tags/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package tags

import (
"context"
"errors"
"io"

"github.com/fastly/go-fastly/v13/fastly"
"github.com/fastly/go-fastly/v13/fastly/apisecurity/operations"

"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"
)

// GetCommand calls the Fastly API to get an operation tag.
type GetCommand struct {
argparser.Base
argparser.JSONOutput

// Required.
tagID string

// Optional.
serviceName argparser.OptionalServiceNameID
}

// NewGetCommand returns a usable command registered under the parent.
func NewGetCommand(parent argparser.Registerer, g *global.Data) *GetCommand {
c := GetCommand{
Base: argparser.Base{
Globals: g,
},
}

c.CmdClause = parent.Command("get", "Get an operation tag")

// Required.
c.CmdClause.Flag("tag-id", "Tag ID").Required().StringVar(&c.tagID)

// Optional.
c.RegisterFlag(argparser.StringFlagOpts{
Name: argparser.FlagServiceIDName,
Description: argparser.FlagServiceIDDesc,
Dst: &g.Manifest.Flag.ServiceID,
})
c.RegisterFlag(argparser.StringFlagOpts{
Action: c.serviceName.Set,
Name: argparser.FlagServiceName,
Description: argparser.FlagServiceNameDesc,
Dst: &c.serviceName.Value,
})
c.RegisterFlagBool(c.JSONFlag())

return &c
}

// Exec invokes the application logic for the command.
func (c *GetCommand) Exec(_ io.Reader, out io.Writer) error {
if c.Globals.Verbose() && c.JSONOutput.Enabled {
return fsterr.ErrInvalidVerboseJSONCombo
}

serviceID, source, flag, err := argparser.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog)
if err != nil {
return err
}
if c.Globals.Verbose() {
argparser.DisplayServiceID(serviceID, flag, source, out)
}

if serviceID == "" {
return errors.New("service-id is required")
}

fc, ok := c.Globals.APIClient.(*fastly.Client)
if !ok {
return errors.New("failed to convert interface to a fastly client")
}

tag, err := operations.DescribeTag(context.TODO(), fc, &operations.DescribeTagInput{
ServiceID: &serviceID,
TagID: &c.tagID,
})
if err != nil {
c.Globals.ErrLog.Add(err)
return err
}

if ok, err := c.WriteJSON(out, tag); ok {
return err
}

text.PrintOperationTag(out, tag)
return nil
}
Loading
Loading