Skip to content

Commit 267fa11

Browse files
vicentepinto98GokceGKjoaopalet
authored
Onboard object storage credentials-group (#98)
* Object storage credentials group create command * Delete credentials-group * List credentials-group, modify create * add unit test - group name empty check * Update long description Co-authored-by: João Palet <joao.palet@outlook.com> * Change text formatting Co-authored-by: João Palet <joao.palet@outlook.com> * Change text formatting Co-authored-by: João Palet <joao.palet@outlook.com> * Update command in example Co-authored-by: João Palet <joao.palet@outlook.com> * Update command in example Co-authored-by: João Palet <joao.palet@outlook.com> * add name to prompt and success messages * Change condition handling Co-authored-by: João Palet <joao.palet@outlook.com> * fix return value issue --------- Co-authored-by: Gökçe Gök Klingel <goekce.goek_klingel@mail.schwarz> Co-authored-by: GokceGK <161626272+GokceGK@users.noreply.github.com> Co-authored-by: João Palet <joao.palet@outlook.com>
1 parent e74c653 commit 267fa11

File tree

10 files changed

+1124
-0
lines changed

10 files changed

+1124
-0
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package create
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
8+
"github.com/stackitcloud/stackit-cli/internal/pkg/confirm"
9+
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
10+
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
11+
"github.com/stackitcloud/stackit-cli/internal/pkg/flags"
12+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
13+
"github.com/stackitcloud/stackit-cli/internal/pkg/services/object-storage/client"
14+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
15+
16+
"github.com/spf13/cobra"
17+
"github.com/stackitcloud/stackit-sdk-go/services/objectstorage"
18+
)
19+
20+
const (
21+
nameFlag = "name"
22+
)
23+
24+
type inputModel struct {
25+
*globalflags.GlobalFlagModel
26+
DisplayName string
27+
}
28+
29+
func NewCmd() *cobra.Command {
30+
cmd := &cobra.Command{
31+
Use: "create",
32+
Short: "Creates a credentials group to hold Object Storage access credentials",
33+
Long: "Creates a credentials group to hold Object Storage access credentials.",
34+
Args: args.NoArgs,
35+
Example: examples.Build(
36+
examples.NewExample(
37+
`Create credentials group to hold Object Storage access credentials`,
38+
"$ stackit object-storage credentials-group create --name example"),
39+
),
40+
RunE: func(cmd *cobra.Command, args []string) error {
41+
ctx := context.Background()
42+
model, err := parseInput(cmd)
43+
if err != nil {
44+
return err
45+
}
46+
47+
// Configure API client
48+
apiClient, err := client.ConfigureClient(cmd)
49+
if err != nil {
50+
return err
51+
}
52+
53+
if !model.AssumeYes {
54+
prompt := fmt.Sprintf("Are you sure you want to create a credentials group with name %q?", model.DisplayName)
55+
err = confirm.PromptForConfirmation(cmd, prompt)
56+
if err != nil {
57+
return err
58+
}
59+
}
60+
61+
// Call API
62+
req := buildRequest(ctx, model, apiClient)
63+
resp, err := req.Execute()
64+
if err != nil {
65+
return fmt.Errorf("create Object Storage credentials group: %w", err)
66+
}
67+
68+
cmd.Printf("Created credentials group %q. Credentials group ID: %s\n\n", *resp.CredentialsGroup.DisplayName, *resp.CredentialsGroup.CredentialsGroupId)
69+
cmd.Printf("URN: %s\n", *resp.CredentialsGroup.Urn)
70+
return nil
71+
},
72+
}
73+
configureFlags(cmd)
74+
return cmd
75+
}
76+
77+
func configureFlags(cmd *cobra.Command) {
78+
cmd.Flags().String(nameFlag, "", "Name of the group holding credentials")
79+
80+
err := flags.MarkFlagsRequired(cmd, nameFlag)
81+
cobra.CheckErr(err)
82+
}
83+
84+
func parseInput(cmd *cobra.Command) (*inputModel, error) {
85+
globalFlags := globalflags.Parse(cmd)
86+
if globalFlags.ProjectId == "" {
87+
return nil, &errors.ProjectIdError{}
88+
}
89+
90+
return &inputModel{
91+
GlobalFlagModel: globalFlags,
92+
DisplayName: flags.FlagToStringValue(cmd, nameFlag),
93+
}, nil
94+
}
95+
96+
func buildRequest(ctx context.Context, model *inputModel, apiClient *objectstorage.APIClient) objectstorage.ApiCreateCredentialsGroupRequest {
97+
req := apiClient.CreateCredentialsGroup(ctx, model.ProjectId)
98+
req = req.CreateCredentialsGroupPayload(objectstorage.CreateCredentialsGroupPayload{
99+
DisplayName: utils.Ptr(model.DisplayName),
100+
})
101+
return req
102+
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
package create
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
8+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
9+
10+
"github.com/google/go-cmp/cmp"
11+
"github.com/google/go-cmp/cmp/cmpopts"
12+
"github.com/google/uuid"
13+
"github.com/stackitcloud/stackit-sdk-go/services/objectstorage"
14+
)
15+
16+
var projectIdFlag = globalflags.ProjectIdFlag
17+
18+
type testCtxKey struct{}
19+
20+
var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo")
21+
var testClient = &objectstorage.APIClient{}
22+
var testProjectId = uuid.NewString()
23+
var testDisplayName = "test-name"
24+
25+
func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string {
26+
flagValues := map[string]string{
27+
projectIdFlag: testProjectId,
28+
nameFlag: testDisplayName,
29+
}
30+
for _, mod := range mods {
31+
mod(flagValues)
32+
}
33+
return flagValues
34+
}
35+
36+
func fixtureInputModel(mods ...func(model *inputModel)) *inputModel {
37+
model := &inputModel{
38+
GlobalFlagModel: &globalflags.GlobalFlagModel{
39+
ProjectId: testProjectId,
40+
},
41+
DisplayName: testDisplayName,
42+
}
43+
for _, mod := range mods {
44+
mod(model)
45+
}
46+
return model
47+
}
48+
49+
func fixturePayload(mods ...func(payload *objectstorage.CreateCredentialsGroupPayload)) objectstorage.CreateCredentialsGroupPayload {
50+
payload := objectstorage.CreateCredentialsGroupPayload{
51+
DisplayName: utils.Ptr(testDisplayName),
52+
}
53+
for _, mod := range mods {
54+
mod(&payload)
55+
}
56+
return payload
57+
}
58+
59+
func fixtureRequest(mods ...func(request *objectstorage.ApiCreateCredentialsGroupRequest)) objectstorage.ApiCreateCredentialsGroupRequest {
60+
request := testClient.CreateCredentialsGroup(testCtx, testProjectId)
61+
request = request.CreateCredentialsGroupPayload(fixturePayload())
62+
for _, mod := range mods {
63+
mod(&request)
64+
}
65+
return request
66+
}
67+
68+
func TestParseInput(t *testing.T) {
69+
tests := []struct {
70+
description string
71+
flagValues map[string]string
72+
isValid bool
73+
expectedModel *inputModel
74+
}{
75+
{
76+
description: "base",
77+
flagValues: fixtureFlagValues(),
78+
isValid: true,
79+
expectedModel: fixtureInputModel(),
80+
},
81+
{
82+
description: "no values",
83+
flagValues: map[string]string{},
84+
isValid: false,
85+
},
86+
{
87+
description: "project id missing",
88+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
89+
delete(flagValues, projectIdFlag)
90+
}),
91+
isValid: false,
92+
},
93+
{
94+
description: "project id invalid 1",
95+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
96+
flagValues[projectIdFlag] = ""
97+
}),
98+
isValid: false,
99+
},
100+
{
101+
description: "project id invalid 2",
102+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
103+
flagValues[projectIdFlag] = "invalid-uuid"
104+
}),
105+
isValid: false,
106+
},
107+
{
108+
description: "display name missing",
109+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
110+
delete(flagValues, nameFlag)
111+
}),
112+
isValid: false,
113+
},
114+
}
115+
116+
for _, tt := range tests {
117+
t.Run(tt.description, func(t *testing.T) {
118+
cmd := NewCmd()
119+
err := globalflags.Configure(cmd.Flags())
120+
if err != nil {
121+
t.Fatalf("configure global flags: %v", err)
122+
}
123+
124+
for flag, value := range tt.flagValues {
125+
err := cmd.Flags().Set(flag, value)
126+
if err != nil {
127+
if !tt.isValid {
128+
return
129+
}
130+
t.Fatalf("setting flag --%s=%s: %v", flag, value, err)
131+
}
132+
}
133+
134+
err = cmd.ValidateRequiredFlags()
135+
if err != nil {
136+
if !tt.isValid {
137+
return
138+
}
139+
t.Fatalf("error validating flags: %v", err)
140+
}
141+
142+
model, err := parseInput(cmd)
143+
if err != nil {
144+
if !tt.isValid {
145+
return
146+
}
147+
t.Fatalf("error parsing flags: %v", err)
148+
}
149+
150+
if !tt.isValid {
151+
t.Fatalf("did not fail on invalid input")
152+
}
153+
diff := cmp.Diff(model, tt.expectedModel)
154+
if diff != "" {
155+
t.Fatalf("Data does not match: %s", diff)
156+
}
157+
})
158+
}
159+
}
160+
161+
func TestBuildRequest(t *testing.T) {
162+
tests := []struct {
163+
description string
164+
model *inputModel
165+
expectedRequest objectstorage.ApiCreateCredentialsGroupRequest
166+
}{
167+
{
168+
description: "base",
169+
model: fixtureInputModel(),
170+
expectedRequest: fixtureRequest(),
171+
},
172+
}
173+
174+
for _, tt := range tests {
175+
t.Run(tt.description, func(t *testing.T) {
176+
request := buildRequest(testCtx, tt.model, testClient)
177+
178+
diff := cmp.Diff(request, tt.expectedRequest,
179+
cmp.AllowUnexported(tt.expectedRequest),
180+
cmpopts.EquateComparable(testCtx),
181+
)
182+
if diff != "" {
183+
t.Fatalf("Data does not match: %s", diff)
184+
}
185+
})
186+
}
187+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package credentialsgroup
2+
3+
import (
4+
"github.com/stackitcloud/stackit-cli/internal/cmd/object-storage/credentials-group/create"
5+
"github.com/stackitcloud/stackit-cli/internal/cmd/object-storage/credentials-group/delete"
6+
"github.com/stackitcloud/stackit-cli/internal/cmd/object-storage/credentials-group/list"
7+
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
8+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
9+
10+
"github.com/spf13/cobra"
11+
)
12+
13+
func NewCmd() *cobra.Command {
14+
cmd := &cobra.Command{
15+
Use: "credentials-group",
16+
Short: "Provides functionality for Object Storage credentials group",
17+
Long: "Provides functionality for Object Storage credentials group.",
18+
Args: args.NoArgs,
19+
Run: utils.CmdHelp,
20+
}
21+
addSubcommands(cmd)
22+
return cmd
23+
}
24+
25+
func addSubcommands(cmd *cobra.Command) {
26+
cmd.AddCommand(create.NewCmd())
27+
cmd.AddCommand(delete.NewCmd())
28+
cmd.AddCommand(list.NewCmd())
29+
}

0 commit comments

Comments
 (0)