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
32 changes: 32 additions & 0 deletions pkg/cmd/libraryvariableset/libraryvariableset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package libraryvariableset

import (
"github.com/MakeNowJust/heredoc/v2"
cmdList "github.com/OctopusDeploy/cli/pkg/cmd/libraryvariableset/list"
cmdView "github.com/OctopusDeploy/cli/pkg/cmd/libraryvariableset/view"
"github.com/OctopusDeploy/cli/pkg/constants"
"github.com/OctopusDeploy/cli/pkg/constants/annotations"
"github.com/OctopusDeploy/cli/pkg/factory"
"github.com/spf13/cobra"
)

func NewCmdLibraryVariableSet(f factory.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "library-variable-set <command>",
Aliases: []string{"library-variable-sets", "lvs"},
Short: "Manage library variable sets",
Long: "Manage library variable sets in Octopus Deploy",
Example: heredoc.Docf(`
%[1]s library-variable-set list
%[1]s library-variable-set view "Slack Variables"
`, constants.ExecutableName),
Annotations: map[string]string{
annotations.IsLibrary: "true",
},
}

cmd.AddCommand(cmdList.NewCmdList(f))
cmd.AddCommand(cmdView.NewCmdView(f))

return cmd
}
105 changes: 105 additions & 0 deletions pkg/cmd/libraryvariableset/list/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package list

import (
"sort"
"strings"

"github.com/MakeNowJust/heredoc/v2"
"github.com/OctopusDeploy/cli/pkg/apiclient"
"github.com/OctopusDeploy/cli/pkg/constants"
"github.com/OctopusDeploy/cli/pkg/factory"
"github.com/OctopusDeploy/cli/pkg/output"
sharedVariable "github.com/OctopusDeploy/cli/pkg/question/shared/variables"
"github.com/OctopusDeploy/cli/pkg/util/flag"
"github.com/spf13/cobra"
)

const (
FlagFilter = "filter"
)

type ListFlags struct {
Filter *flag.Flag[string]
}

func NewListFlags() *ListFlags {
return &ListFlags{
Filter: flag.New[string](FlagFilter, false),
}
}

type LibraryVariableSetViewModel struct {
ID string `json:"Id"`
Name string `json:"Name"`
Description string `json:"Description"`
VariableSetID string `json:"VariableSetId"`
}

func NewCmdList(f factory.Factory) *cobra.Command {
listFlags := NewListFlags()
cmd := &cobra.Command{
Use: "list",
Short: "List library variable sets",
Long: "List library variable sets in Octopus Deploy",
Example: heredoc.Docf(`
%[1]s library-variable-set list
%[1]s library-variable-set ls --filter Slack
%[1]s library-variable-set ls -q Slack -f json
`, constants.ExecutableName),
Aliases: []string{"ls"},
RunE: func(cmd *cobra.Command, args []string) error {
return listRun(cmd, f, listFlags)
},
}

flags := cmd.Flags()
flags.StringVarP(&listFlags.Filter.Value, listFlags.Filter.Name, "q", "", "Filter library variable sets to match only ones with a name containing the given string")
return cmd
}

func listRun(cmd *cobra.Command, f factory.Factory, flags *ListFlags) error {
octopus, err := f.GetSpacedClient(apiclient.NewRequester(cmd))
if err != nil {
return err
}

// script modules share the libraryvariablesets endpoint; this command only deals
// with variable sets, and GetAllLibraryVariableSets filters them out for us.
allSets, err := sharedVariable.GetAllLibraryVariableSets(octopus)
if err != nil {
return err
}

filter := strings.ToLower(flags.Filter.Value)
viewModels := make([]LibraryVariableSetViewModel, 0, len(allSets))
for _, s := range allSets {
if filter != "" && !strings.Contains(strings.ToLower(s.Name), filter) {
continue
}
viewModels = append(viewModels, LibraryVariableSetViewModel{
ID: s.GetID(),
Name: s.Name,
Description: s.Description,
VariableSetID: s.VariableSetID,
})
}

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

return output.PrintArray(viewModels, cmd, output.Mappers[LibraryVariableSetViewModel]{
Json: func(item LibraryVariableSetViewModel) any {
return item
},
Table: output.TableDefinition[LibraryVariableSetViewModel]{
Header: []string{"NAME", "DESCRIPTION", "ID"},
Row: func(item LibraryVariableSetViewModel) []string {
return []string{output.Bold(item.Name), item.Description, output.Dim(item.ID)}
},
},
Basic: func(item LibraryVariableSetViewModel) string {
return item.Name
},
})
}
151 changes: 151 additions & 0 deletions pkg/cmd/libraryvariableset/list/list_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package list_test

import (
"bytes"
"testing"

"github.com/MakeNowJust/heredoc/v2"
cmdRoot "github.com/OctopusDeploy/cli/pkg/cmd/root"
"github.com/OctopusDeploy/cli/pkg/question"
"github.com/OctopusDeploy/cli/test/fixtures"
"github.com/OctopusDeploy/cli/test/testutil"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/variables"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
)

var rootResource = testutil.NewRootResource()

func TestLibraryVariableSetList(t *testing.T) {
const spaceID = "Spaces-1"

space1 := fixtures.NewSpace(spaceID, "Default Space")

slackSet := fixtures.NewLibraryVariableSet(spaceID, "LibraryVariableSets-1", "Slack Variables")
slackSet.Description = "Slack webhooks"

sharedSet := fixtures.NewLibraryVariableSet(spaceID, "LibraryVariableSets-2", "Global Variables")

scriptModule := fixtures.NewLibraryVariableSet(spaceID, "LibraryVariableSets-3", "Helper Functions")
scriptModule.ContentType = "ScriptModule"

tests := []struct {
name string
run func(t *testing.T, api *testutil.MockHttpServer, qa *testutil.AskMocker, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer)
}{
{"library variable set list prints the sets, sorted, excluding script modules", func(t *testing.T, api *testutil.MockHttpServer, qa *testutil.AskMocker, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer) {
cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer api.Close()
rootCmd.SetArgs([]string{"library-variable-set", "list", "--no-prompt", "-f", "table"})
return rootCmd.ExecuteC()
})

api.ExpectRequest(t, "GET", "/api/").RespondWith(rootResource)
api.ExpectRequest(t, "GET", "/api/Spaces-1").RespondWith(rootResource)

api.ExpectRequest(t, "GET", "/api/Spaces-1/libraryvariablesets/all").
RespondWith([]*variables.LibraryVariableSet{slackSet, scriptModule, sharedSet})

_, err := testutil.ReceivePair(cmdReceiver)
assert.Nil(t, err)

assert.Equal(t, heredoc.Doc(`
NAME DESCRIPTION ID
Global Variables LibraryVariableSets-2
Slack Variables Slack webhooks LibraryVariableSets-1
`), stdOut.String())
assert.Equal(t, "", stdErr.String())
}},

{"library variable set list filters by name", func(t *testing.T, api *testutil.MockHttpServer, qa *testutil.AskMocker, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer) {
cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer api.Close()
rootCmd.SetArgs([]string{"library-variable-set", "ls", "-q", "slack", "--no-prompt", "-f", "table"})
return rootCmd.ExecuteC()
})

api.ExpectRequest(t, "GET", "/api/").RespondWith(rootResource)
api.ExpectRequest(t, "GET", "/api/Spaces-1").RespondWith(rootResource)

api.ExpectRequest(t, "GET", "/api/Spaces-1/libraryvariablesets/all").
RespondWith([]*variables.LibraryVariableSet{slackSet, sharedSet})

_, err := testutil.ReceivePair(cmdReceiver)
assert.Nil(t, err)

assert.Equal(t, heredoc.Doc(`
NAME DESCRIPTION ID
Slack Variables Slack webhooks LibraryVariableSets-1
`), stdOut.String())
assert.Equal(t, "", stdErr.String())
}},

{"outputFormat json", func(t *testing.T, api *testutil.MockHttpServer, qa *testutil.AskMocker, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer) {
cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer api.Close()
rootCmd.SetArgs([]string{"library-variable-set", "list", "--output-format", "json", "--no-prompt"})
return rootCmd.ExecuteC()
})

api.ExpectRequest(t, "GET", "/api/").RespondWith(rootResource)
api.ExpectRequest(t, "GET", "/api/Spaces-1").RespondWith(rootResource)

api.ExpectRequest(t, "GET", "/api/Spaces-1/libraryvariablesets/all").
RespondWith([]*variables.LibraryVariableSet{slackSet, sharedSet})

_, err := testutil.ReceivePair(cmdReceiver)
assert.Nil(t, err)

type x struct {
ID string
Name string
Description string
VariableSetID string
}
parsedStdout, err := testutil.ParseJsonStrict[[]x](stdOut)
assert.Nil(t, err)

assert.Equal(t, []x{
{ID: "LibraryVariableSets-2", Name: "Global Variables", Description: "", VariableSetID: "variableset-LibraryVariableSets-2"},
{ID: "LibraryVariableSets-1", Name: "Slack Variables", Description: "Slack webhooks", VariableSetID: "variableset-LibraryVariableSets-1"},
}, parsedStdout)
assert.Equal(t, "", stdErr.String())
}},

{"outputFormat basic", func(t *testing.T, api *testutil.MockHttpServer, qa *testutil.AskMocker, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer) {
cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer api.Close()
rootCmd.SetArgs([]string{"library-variable-set", "list", "--output-format", "basic", "--no-prompt"})
return rootCmd.ExecuteC()
})

api.ExpectRequest(t, "GET", "/api/").RespondWith(rootResource)
api.ExpectRequest(t, "GET", "/api/Spaces-1").RespondWith(rootResource)

api.ExpectRequest(t, "GET", "/api/Spaces-1/libraryvariablesets/all").
RespondWith([]*variables.LibraryVariableSet{slackSet, sharedSet})

_, err := testutil.ReceivePair(cmdReceiver)
assert.Nil(t, err)

assert.Equal(t, heredoc.Doc(`
Global Variables
Slack Variables
`), stdOut.String())
assert.Equal(t, "", stdErr.String())
}},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
api, qa := testutil.NewMockServerAndAsker()
askProvider := question.NewAskProvider(qa.AsAsker())
fac := testutil.NewMockFactoryWithSpaceAndPrompt(api, space1, askProvider)
rootCmd := cmdRoot.NewCmdRoot(fac, nil, askProvider)
rootCmd.SetOut(stdout)
rootCmd.SetErr(stderr)
test.run(t, api, qa, rootCmd, stdout, stderr)
})
}
}
Loading