Skip to content
Merged
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
11 changes: 11 additions & 0 deletions cmd/cmd_rules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,27 @@ func TestNounsAndVerbsExistAsFilesInDirectories(t *testing.T) {
}

func TestVerbCommandCheckForJsonOuput(t *testing.T) {
var jsonCheckAllowList = map[string]struct{}{
"otelcomponentmapping/otelcomponentmapping_status.go": {},
"otelrelationmapping/otelrelationmapping_status.go": {},
}

root := setupCmd(t)
for _, nounCmd := range root.Commands() {
for _, verbCmd := range nounCmd.Commands() {
nounName := strings.ReplaceAll(nounCmd.Name(), "-", "")
verbName := strings.ReplaceAll(verbCmd.Name(), "-", "_")
verCmdGoFile := fmt.Sprintf("%s/%s_%s.go", nounName, nounName, verbName)

if _, ok := jsonCheckAllowList[verCmdGoFile]; ok {
continue
}

verbCmdGoCode, err := os.ReadFile(verCmdGoFile)
if err != nil {
t.Fatal(err)
}

if !strings.Contains(string(verbCmdGoCode), "if cli.IsJson() {") {
t.Errorf("%s does not check whether to print to json!", verCmdGoFile)
}
Expand Down
20 changes: 20 additions & 0 deletions cmd/otelcomponentmapping.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package cmd

import (
"github.com/spf13/cobra"
"github.com/stackvista/stackstate-cli/cmd/otelcomponentmapping"
"github.com/stackvista/stackstate-cli/internal/di"
)

func OtelComponentMappingCommand(deps *di.Deps) *cobra.Command {
cmd := &cobra.Command{
Use: "otel-component-mapping",
Short: "Manage the Otel Component Mapping",
Long: "Manage the Otel Component Mapping.",
}

cmd.AddCommand(otelcomponentmapping.OtelComponentMappingListCommand(deps))
cmd.AddCommand(otelcomponentmapping.OtelComponentMappingStatusCommand(deps))

return cmd
}
43 changes: 43 additions & 0 deletions cmd/otelcomponentmapping/otelcomponentmapping_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package otelcomponentmapping

import (
"sort"

"github.com/spf13/cobra"
"github.com/stackvista/stackstate-cli/cmd/otelmapping"
"github.com/stackvista/stackstate-cli/generated/stackstate_api"
"github.com/stackvista/stackstate-cli/internal/common"
"github.com/stackvista/stackstate-cli/internal/di"
)

func OtelComponentMappingListCommand(deps *di.Deps) *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "Lists active Otel Component Mappings",
Long: "Lists active Otel Component Mappings.",
RunE: deps.CmdRunEWithApi(RunListComponentCommand),
}

return cmd
}

func RunListComponentCommand(cmd *cobra.Command, cli *di.Deps, api *stackstate_api.APIClient, serverInfo *stackstate_api.ServerInfo) common.CLIError {
mappingsList, resp, err := api.OtelMappingApi.GetOtelComponentMappings(cli.Context).Execute()
if err != nil {
return common.NewResponseError(err, resp)
}

sort.SliceStable(mappingsList, func(i, j int) bool {
return mappingsList[i].Name < mappingsList[j].Name
})

if cli.IsJson() {
cli.Printer.PrintJson(map[string]interface{}{
"otel component mappings": mappingsList,
})
} else {
cli.Printer.Table(otelmapping.FormatOtelMappingTable(mappingsList))
}

return nil
}
54 changes: 54 additions & 0 deletions cmd/otelcomponentmapping/otelcomponentmapping_list_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package otelcomponentmapping_test

import (
"testing"

"github.com/stackvista/stackstate-cli/cmd/otelcomponentmapping"
"github.com/stackvista/stackstate-cli/cmd/otelmapping_test"
"github.com/stackvista/stackstate-cli/internal/di"
"github.com/stackvista/stackstate-cli/internal/printer"
"github.com/stretchr/testify/assert"
)

func TestListOtelComponentMappingsJson(t *testing.T) {
cli := di.NewMockDeps(t)
cmd := otelcomponentmapping.OtelComponentMappingListCommand(&cli.Deps)
cli.MockClient.ApiMocks.OtelMappingApi.GetOtelComponentMappingsResponse.Result = otelmapping_test.TestAllMappingItems

di.ExecuteCommandWithContextUnsafe(&cli.Deps, cmd, "-o", "json")

calls := *cli.MockClient.ApiMocks.OtelMappingApi.GetOtelComponentMappingsCalls
assert.Len(t, calls, 1)

expected := []map[string]interface{}{
{
"otel component mappings": otelmapping_test.TestAllMappingItems,
},
}

assert.Equal(t, expected, *cli.MockPrinter.PrintJsonCalls)
}

func TestOtelComponentMappingListTable(t *testing.T) {
cli := di.NewMockDeps(t)
cmd := otelcomponentmapping.OtelComponentMappingListCommand(&cli.Deps)
cli.MockClient.ApiMocks.OtelMappingApi.GetOtelComponentMappingsResponse.Result = otelmapping_test.TestAllMappingItems

di.ExecuteCommandWithContextUnsafe(&cli.Deps, cmd)

calls := *cli.MockClient.ApiMocks.OtelMappingApi.GetOtelComponentMappingsCalls
assert.Len(t, calls, 1)

expectedTableCall := []printer.TableData{
{
Header: []string{"Name", "Identifier"},
Data: [][]interface{}{
{otelmapping_test.TestSomeOtelMappingItem.Name, "identifier"},
{otelmapping_test.TestSomeOtelMappingItem2.Name, "identifier2"},
},
MissingTableDataMsg: printer.NotFoundMsg{Types: "otel mappings"},
},
}

assert.Equal(t, expectedTableCall, *cli.MockPrinter.TableCalls)
}
20 changes: 20 additions & 0 deletions cmd/otelcomponentmapping/otelcomponentmapping_status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package otelcomponentmapping

import (
"github.com/spf13/cobra"
"github.com/stackvista/stackstate-cli/cmd/otelmapping"
"github.com/stackvista/stackstate-cli/internal/common"
"github.com/stackvista/stackstate-cli/internal/di"
)

func OtelComponentMappingStatusCommand(deps *di.Deps) *cobra.Command {
args := &otelmapping.StatusArgs{}
cmd := &cobra.Command{
Use: "status",
Short: "Get the status of an Otel Component Mappings",
Long: "Get the status of an Otel Component Mappings.",
RunE: deps.CmdRunEWithApi(otelmapping.RunStatus(args, "component", otelmapping.FetchComponentStatus)),
}
common.AddRequiredIdentifierFlagVar(cmd, &args.Identifier, "Identifier of the Otel Component Mapping")
return cmd
}
68 changes: 68 additions & 0 deletions cmd/otelcomponentmapping/otelcomponentmapping_status_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package otelcomponentmapping_test

import (
"testing"

"github.com/stackvista/stackstate-cli/cmd/otelcomponentmapping"
"github.com/stackvista/stackstate-cli/cmd/otelmapping_test"
"github.com/stackvista/stackstate-cli/internal/di"
"github.com/stackvista/stackstate-cli/internal/printer"
"github.com/stretchr/testify/assert"
)

func TestOtelComponentMappingStatusJson(t *testing.T) {
cli := di.NewMockDeps(t)
cmd := otelcomponentmapping.OtelComponentMappingStatusCommand(&cli.Deps)
cli.MockClient.ApiMocks.OtelMappingApi.GetOtelComponentMappingStatusResponse.Result = *otelmapping_test.TestSomeOtelMappingStatus

di.ExecuteCommandWithContextUnsafe(&cli.Deps, cmd, "--identifier", "identifier", "-o", "json")

calls := *cli.MockClient.ApiMocks.OtelMappingApi.GetOtelComponentMappingStatusCalls
assert.Len(t, calls, 1)

expected := []map[string]interface{}{
{
"otel-component-mapping": otelmapping_test.TestSomeOtelMappingStatus,
},
}

assert.Equal(t, expected, *cli.MockPrinter.PrintJsonCalls)
}

func TestOtelComponentMappingStatusTable(t *testing.T) {
cli := di.NewMockDeps(t)
cmd := otelcomponentmapping.OtelComponentMappingStatusCommand(&cli.Deps)
cli.MockClient.ApiMocks.OtelMappingApi.GetOtelComponentMappingStatusResponse.Result = *otelmapping_test.TestSomeOtelMappingStatus

di.ExecuteCommandWithContextUnsafe(&cli.Deps, cmd, "--identifier", "identifier")

calls := *cli.MockClient.ApiMocks.OtelMappingApi.GetOtelComponentMappingStatusCalls
assert.Len(t, calls, 1)

expectedTableCall := []printer.TableData{
{
Header: []string{"Name", "Identifier", "Components", "Relations"},
Data: [][]interface{}{
{otelmapping_test.TestSomeOtelMappingStatusItem.Name, "identifier", otelmapping_test.TestComponentCount, otelmapping_test.TestRelationCount},
},
MissingTableDataMsg: printer.NotFoundMsg{Types: "otel mappings"},
},
{
Header: []string{"Metric", "10s ago", "10-20s ago", "20-30s ago"},
Data: [][]interface{}{
{"latency seconds", otelmapping_test.TestMetricValue, otelmapping_test.TestMetricValue, otelmapping_test.TestMetricValue},
},
MissingTableDataMsg: printer.NotFoundMsg{Types: "metrics"},
},
{
Header: []string{"Issue Id", "Level", "Message"},
Data: [][]interface{}{
{"-", otelmapping_test.TestError1.Level, otelmapping_test.TestError1.Message},
{"-", otelmapping_test.TestError2.Level, otelmapping_test.TestError2.Message},
},
MissingTableDataMsg: printer.NotFoundMsg{Types: "otel component mapping errors"},
},
}

assert.Equal(t, expectedTableCall, *cli.MockPrinter.TableCalls)
}
130 changes: 130 additions & 0 deletions cmd/otelmapping/otelmapping.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package otelmapping

import (
"fmt"
"net/http"

"github.com/spf13/cobra"
"github.com/stackvista/stackstate-cli/generated/stackstate_api"
"github.com/stackvista/stackstate-cli/internal/common"
"github.com/stackvista/stackstate-cli/internal/di"
"github.com/stackvista/stackstate-cli/internal/printer"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)

type StatusArgs struct {
Identifier string
}

func FormatOtelMappingStatusTable(otelmappings []stackstate_api.OtelMappingStatusItem) printer.TableData {
data := make([][]interface{}, len(otelmappings))

for i, otelmapping := range otelmappings {
identifier := "-"
if otelmapping.HasIdentifier() {
identifier = otelmapping.GetIdentifier()
}
data[i] = []interface{}{
otelmapping.Name,
identifier,
otelmapping.ComponentCount,
otelmapping.RelationCount,
}
}
return printer.TableData{
Header: []string{"Name", "Identifier", "Components", "Relations"},
Data: data,
MissingTableDataMsg: printer.NotFoundMsg{Types: "otel mappings"},
}
}

func FormatOtelMappingTable(otelmappings []stackstate_api.OtelMappingItem) printer.TableData {
data := make([][]interface{}, len(otelmappings))

for i, otelmapping := range otelmappings {
identifier := "-"
if otelmapping.HasIdentifier() {
identifier = otelmapping.GetIdentifier()
}
data[i] = []interface{}{
otelmapping.Name,
identifier,
}
}
return printer.TableData{
Header: []string{"Name", "Identifier"},
Data: data,
MissingTableDataMsg: printer.NotFoundMsg{Types: "otel mappings"},
}
}

type StatusFetcher func(cli *di.Deps, api *stackstate_api.APIClient, identifier string) (*stackstate_api.OtelMappingStatus, *http.Response, error)

func FetchComponentStatus(cli *di.Deps, api *stackstate_api.APIClient, identifier string) (*stackstate_api.OtelMappingStatus, *http.Response, error) {
return api.OtelMappingApi.GetOtelComponentMappingStatus(cli.Context, identifier).Execute()
}

func FetchRelationStatus(cli *di.Deps, api *stackstate_api.APIClient, identifier string) (*stackstate_api.OtelMappingStatus, *http.Response, error) {
return api.OtelMappingApi.GetOtelRelationMappingStatus(cli.Context, identifier).Execute()
}

func RunStatus(args *StatusArgs, mappingType string, fetch StatusFetcher) di.CmdWithApiFn {
return func(cmd *cobra.Command, cli *di.Deps, api *stackstate_api.APIClient, serverInfo *stackstate_api.ServerInfo) common.CLIError {
mappingStatus, resp, err := fetch(cli, api, args.Identifier)
if err != nil {
return common.NewResponseError(err, resp)
}

jsonKey := fmt.Sprintf("otel-%s-mapping", mappingType)
title := cases.Title(language.English).String(mappingType)
mappingTitle := fmt.Sprintf("Otel %s Mapping:", title)
metricsTitle := fmt.Sprintf("Otel %s Mapping Metrics:", title)
errorsTitle := fmt.Sprintf("Otel %s Mapping Errors:", title)
errorsNotFoundMsg := fmt.Sprintf("otel %s mapping errors", mappingType)

if cli.IsJson() {
cli.Printer.PrintJson(map[string]interface{}{
jsonKey: mappingStatus,
})
} else {
cli.Printer.PrintLn("\n")
cli.Printer.PrintLn(mappingTitle)
cli.Printer.Table(FormatOtelMappingStatusTable([]stackstate_api.OtelMappingStatusItem{
mappingStatus.Item,
}))

if mappingStatus.HasMetrics() {
cli.Printer.PrintLn("\n")
cli.Printer.PrintLn(metricsTitle)
size := mappingStatus.Metrics.BucketSizeSeconds
cli.Printer.Table(printer.TableData{
Header: []string{"Metric", fmt.Sprintf("%ds ago", size), fmt.Sprintf("%d-%ds ago", size, 2*size), fmt.Sprintf("%d-%ds ago", 2*size, 3*size)}, //nolint:mnd
Data: [][]interface{}{
printer.MetricBucketToRow("latency seconds", mappingStatus.Metrics.LatencySeconds),
},
MissingTableDataMsg: printer.NotFoundMsg{Types: "metrics"},
})
}

data := make([][]interface{}, len(mappingStatus.ErrorDetails))
for i, error := range mappingStatus.ErrorDetails {
id := "-"
if error.HasIssueId() {
id = *error.IssueId
}
data[i] = []interface{}{id, error.Level, error.Message}
}

cli.Printer.PrintLn("\n")
cli.Printer.PrintLn(errorsTitle)
cli.Printer.Table(printer.TableData{
Header: []string{"Issue Id", "Level", "Message"},
Data: data,
MissingTableDataMsg: printer.NotFoundMsg{Types: errorsNotFoundMsg},
})
}

return nil
}
}
Loading