Skip to content

Commit

Permalink
feat(dashboard): Add support for Observability custom dashboards. (#1357
Browse files Browse the repository at this point in the history
)

* Scaffold out custom dashboard base commands

* Implement dashboard print funcs

* Implement dashboard list command

* Implement dashboard describe command

* Implement dashboard delete command

* Implement dashboard create command

* Implement dashboard update command

* Scaffold dashboard/item command package

* Implement dashboard item create command

* Implement dashboard item describe command

* Implement dashboard item delete command

* Implement dashboard item update command

* Improve/fix JSON printing for dashboard commands

* Update dashboard print funcs/commands to behave consistently with other resources

* Add tests for dashboard commands

* Fix app.Run test

* Remove unnecessary mock fns

* Add tests for dashboard item commands

* Apply suggestions from code review

Co-authored-by: Kevin P. Fleming <[email protected]>

* Address feedback

* gofmt

* Fix linting errors

* Fix dashboard item output assertions

---------

Co-authored-by: Kevin P. Fleming <[email protected]>
  • Loading branch information
tlindsay and kpfleming authored Jan 14, 2025
1 parent aaaf525 commit c3deac6
Show file tree
Hide file tree
Showing 21 changed files with 1,663 additions and 0 deletions.
6 changes: 6 additions & 0 deletions pkg/api/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,12 @@ type Interface interface {
DeleteAlertDefinition(i *fastly.DeleteAlertDefinitionInput) error
TestAlertDefinition(i *fastly.TestAlertDefinitionInput) error
ListAlertHistory(i *fastly.ListAlertHistoryInput) (*fastly.AlertHistoryResponse, error)

ListObservabilityCustomDashboards(i *fastly.ListObservabilityCustomDashboardsInput) (*fastly.ListDashboardsResponse, error)
CreateObservabilityCustomDashboard(i *fastly.CreateObservabilityCustomDashboardInput) (*fastly.ObservabilityCustomDashboard, error)
GetObservabilityCustomDashboard(i *fastly.GetObservabilityCustomDashboardInput) (*fastly.ObservabilityCustomDashboard, error)
UpdateObservabilityCustomDashboard(i *fastly.UpdateObservabilityCustomDashboardInput) (*fastly.ObservabilityCustomDashboard, error)
DeleteObservabilityCustomDashboard(i *fastly.DeleteObservabilityCustomDashboardInput) error
}

// RealtimeStatsInterface is the subset of go-fastly's realtime stats API used here.
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 @@ -68,6 +68,7 @@ compute
config
config-store
config-store-entry
dashboard
dictionary
dictionary-entry
domain
Expand Down
24 changes: 24 additions & 0 deletions pkg/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"github.com/fastly/cli/pkg/commands/config"
"github.com/fastly/cli/pkg/commands/configstore"
"github.com/fastly/cli/pkg/commands/configstoreentry"
"github.com/fastly/cli/pkg/commands/dashboard"
dashboardItem "github.com/fastly/cli/pkg/commands/dashboard/item"
"github.com/fastly/cli/pkg/commands/dictionary"
"github.com/fastly/cli/pkg/commands/dictionaryentry"
"github.com/fastly/cli/pkg/commands/domain"
Expand Down Expand Up @@ -154,6 +156,17 @@ func Define(
configstoreentryDescribe := configstoreentry.NewDescribeCommand(configstoreentryCmdRoot.CmdClause, data)
configstoreentryList := configstoreentry.NewListCommand(configstoreentryCmdRoot.CmdClause, data)
configstoreentryUpdate := configstoreentry.NewUpdateCommand(configstoreentryCmdRoot.CmdClause, data)
dashboardCmdRoot := dashboard.NewRootCommand(app, data)
dashboardList := dashboard.NewListCommand(dashboardCmdRoot.CmdClause, data)
dashboardCreate := dashboard.NewCreateCommand(dashboardCmdRoot.CmdClause, data)
dashboardDescribe := dashboard.NewDescribeCommand(dashboardCmdRoot.CmdClause, data)
dashboardUpdate := dashboard.NewUpdateCommand(dashboardCmdRoot.CmdClause, data)
dashboardDelete := dashboard.NewDeleteCommand(dashboardCmdRoot.CmdClause, data)
dashboardItemCmdRoot := dashboardItem.NewRootCommand(dashboardCmdRoot.CmdClause, data)
dashboardItemCreate := dashboardItem.NewCreateCommand(dashboardItemCmdRoot.CmdClause, data)
dashboardItemDescribe := dashboardItem.NewDescribeCommand(dashboardItemCmdRoot.CmdClause, data)
dashboardItemUpdate := dashboardItem.NewUpdateCommand(dashboardItemCmdRoot.CmdClause, data)
dashboardItemDelete := dashboardItem.NewDeleteCommand(dashboardItemCmdRoot.CmdClause, data)
dictionaryCmdRoot := dictionary.NewRootCommand(app, data)
dictionaryCreate := dictionary.NewCreateCommand(dictionaryCmdRoot.CmdClause, data)
dictionaryDelete := dictionary.NewDeleteCommand(dictionaryCmdRoot.CmdClause, data)
Expand Down Expand Up @@ -535,6 +548,17 @@ func Define(
configstoreentryDescribe,
configstoreentryList,
configstoreentryUpdate,
dashboardCmdRoot,
dashboardList,
dashboardCreate,
dashboardDescribe,
dashboardUpdate,
dashboardDelete,
dashboardItemCmdRoot,
dashboardItemCreate,
dashboardItemDescribe,
dashboardItemUpdate,
dashboardItemDelete,
dictionaryCmdRoot,
dictionaryCreate,
dictionaryDelete,
Expand Down
92 changes: 92 additions & 0 deletions pkg/commands/dashboard/common/print.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Package common contains functions used by both dashboard and dashboard/item packages
package common

import (
"fmt"
"io"
"strings"

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

"github.com/fastly/cli/pkg/text"
)

// PrintSummary displays the information returned from the API in a summarised
// format.
func PrintSummary(out io.Writer, ds []fastly.ObservabilityCustomDashboard) {
t := text.NewTable(out)
t.AddHeader("DASHBOARD ID", "NAME", "DESCRIPTION", "# ITEMS")
for _, d := range ds {
t.AddLine(
d.ID,
d.Name,
d.Description,
len(d.Items),
)
}
t.Print()
}

// PrintVerbose displays the information returned from the API in a verbose
// format.
func PrintVerbose(out io.Writer, ds []fastly.ObservabilityCustomDashboard) {
for _, d := range ds {
PrintDashboard(out, 0, &d)
fmt.Fprintf(out, "\n")
}
}

// PrintDashboard displays the Dashboard returned from the API in a human-
// readable format.
func PrintDashboard(out io.Writer, indent uint, dashboard *fastly.ObservabilityCustomDashboard) {
indentStep := uint(4)
level := indent
text.Indent(out, level, "Name: %s", dashboard.Name)
text.Indent(out, level, "Description: %s", dashboard.Description)
text.Indent(out, level, "Items:")

level += indentStep
for i, di := range dashboard.Items {
text.Indent(out, level, "[%d]:", i)
level += indentStep
PrintItem(out, level, &di)
level -= indentStep
}
level -= indentStep

text.Indent(out, level, "Meta:")
level += indentStep
text.Indent(out, level, "Created at: %s", dashboard.CreatedAt)
text.Indent(out, level, "Updated at: %s", dashboard.UpdatedAt)
text.Indent(out, level, "Created by: %s", dashboard.CreatedBy)
text.Indent(out, level, "Updated by: %s", dashboard.UpdatedBy)
}

// PrintItem displays a single DashboardItem in a human-readable format.
func PrintItem(out io.Writer, indent uint, item *fastly.DashboardItem) {
indentStep := uint(4)
level := indent
if item != nil {
text.Indent(out, level, "ID: %s", item.ID)
text.Indent(out, level, "Title: %s", item.Title)
text.Indent(out, level, "Subtitle: %s", item.Subtitle)
text.Indent(out, level, "Span: %d", item.Span)

text.Indent(out, level, "Data Source:")
level += indentStep
text.Indent(out, level, "Type: %s", item.DataSource.Type)
text.Indent(out, level, "Metrics: %s", strings.Join(item.DataSource.Config.Metrics, ", "))
level -= indentStep

text.Indent(out, level, "Visualization:")
level += indentStep
text.Indent(out, level, "Type: %s", item.Visualization.Type)
text.Indent(out, level, "Plot Type: %s", item.Visualization.Config.PlotType)
if item.Visualization.Config.CalculationMethod != nil {
text.Indent(out, level, "Calculation Method: %s", *item.Visualization.Config.CalculationMethod)
}
if item.Visualization.Config.Format != nil {
text.Indent(out, level, "Format: %s", *item.Visualization.Config.Format)
}
}
}
71 changes: 71 additions & 0 deletions pkg/commands/dashboard/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package dashboard

import (
"io"

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

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

// NewCreateCommand returns a usable command registered under the parent.
func NewCreateCommand(parent argparser.Registerer, globals *global.Data) *CreateCommand {
var c CreateCommand
c.CmdClause = parent.Command("create", "Create a custom dashboard").Alias("add")
c.Globals = globals

// Required flags
c.CmdClause.Flag("name", "A human-readable name for the dashboard").Short('n').Required().StringVar(&c.name) // --name

// Optional flags
c.RegisterFlagBool(c.JSONFlag()) // --json
c.CmdClause.Flag("description", "A short description of the dashboard").Action(c.description.Set).StringVar(&c.description.Value) // --description

return &c
}

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

name string
description argparser.OptionalString
}

// 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
}

input := c.constructInput()
dashboard, err := c.Globals.APIClient.CreateObservabilityCustomDashboard(input)
if err != nil {
return err
}

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

text.Success(out, `Created Custom Dashboard "%s" (id: %s)`, dashboard.Name, dashboard.ID)
return nil
}

// constructInput transforms values parsed from CLI flags into an object to be used by the API client library.
func (c *CreateCommand) constructInput() *fastly.CreateObservabilityCustomDashboardInput {
input := fastly.CreateObservabilityCustomDashboardInput{
Name: c.name,
Items: []fastly.DashboardItem{},
}

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

return &input
}
Loading

0 comments on commit c3deac6

Please sign in to comment.