Skip to content

Commit 3e43e1f

Browse files
authored
UX improvements (#187)
* add default to project list * project list: improve implementation, add testing * Fix examples in project list * Add version column to plans in DSAs * Add dns record data to list and describe * minor improvements to code * fix linting * add docs * change keyring testing * address comments in PR
1 parent 81b7594 commit 3e43e1f

File tree

13 files changed

+77
-45
lines changed

13 files changed

+77
-45
lines changed

docs/stackit_postgresflex_instance_clone.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ stackit postgresflex instance clone INSTANCE_ID [flags]
2727

2828
```
2929
-h, --help Help for "stackit postgresflex instance clone"
30-
--recovery-timestamp string Recovery timestamp for the instance, specified in UTC time following the format, e.g. 2024-03-12T09:28:00+00:00
30+
--recovery-timestamp string Recovery timestamp for the instance, in a date-time with the RFC3339 layout format, e.g. 2024-01-01T00:00:00Z
3131
--storage-class string Storage class. If not specified, storage class from the existing instance will be used.
3232
--storage-size int Storage size (in GB). If not specified, storage size from the existing instance will be used.
3333
```

docs/stackit_project_list.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ stackit project list [flags]
1313
### Examples
1414

1515
```
16+
List all STACKIT projects that authenticated user is a member of
17+
$ stackit project list
18+
1619
List all STACKIT projects that are children of a specific parent
1720
$ stackit project list --parent-id xxx
1821

docs/stackit_project_member_list.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ stackit project member list [flags]
1414

1515
```
1616
List all members of a project
17-
$ stackit project role list --project-id xxx
17+
$ stackit project member list --project-id xxx
1818
1919
List all members of a project, sorted by role
20-
$ stackit project role list --project-id xxx --sort-by role
20+
$ stackit project member list --project-id xxx --sort-by role
2121
2222
List up to 10 members of a project
23-
$ stackit project role list --project-id xxx --limit 10
23+
$ stackit project member list --project-id xxx --limit 10
2424
```
2525

2626
### Options

internal/cmd/dns/record-set/describe/describe.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,9 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *dns.APIClie
103103
func outputResult(cmd *cobra.Command, outputFormat string, recordSet *dns.RecordSet) error {
104104
switch outputFormat {
105105
case globalflags.PrettyOutputFormat:
106-
records := *recordSet.Records
107-
recordsData := []string{}
108-
for i := range records {
109-
recordsData = append(recordsData, *records[i].Content)
106+
recordsData := make([]string, 0, len(*recordSet.Records))
107+
for _, r := range *recordSet.Records {
108+
recordsData = append(recordsData, *r.Content)
110109
}
111110
recordsDataJoin := strings.Join(recordsData, ",")
112111

internal/cmd/dns/record-set/list/list.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,10 +232,15 @@ func outputResult(cmd *cobra.Command, outputFormat string, recordSets []dns.Reco
232232
return nil
233233
default:
234234
table := tables.NewTable()
235-
table.SetHeader("ID", "NAME", "STATUS", "TTL", "TYPE")
235+
table.SetHeader("ID", "NAME", "STATUS", "TTL", "TYPE", "RECORD DATA")
236236
for i := range recordSets {
237237
rs := recordSets[i]
238-
table.AddRow(*rs.Id, *rs.Name, *rs.State, *rs.Ttl, *rs.Type)
238+
recordData := make([]string, 0, len(*rs.Records))
239+
for _, r := range *rs.Records {
240+
recordData = append(recordData, *r.Content)
241+
}
242+
recordDataJoin := strings.Join(recordData, ", ")
243+
table.AddRow(*rs.Id, *rs.Name, *rs.State, *rs.Ttl, *rs.Type, recordDataJoin)
239244
}
240245
err := table.Display(cmd)
241246
if err != nil {

internal/cmd/logme/plans/plans.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,16 @@ func outputResult(cmd *cobra.Command, outputFormat string, plans []logme.Offerin
127127
return nil
128128
default:
129129
table := tables.NewTable()
130-
table.SetHeader("OFFERING NAME", "ID", "NAME", "DESCRIPTION")
130+
table.SetHeader("OFFERING NAME", "VERSION", "ID", "NAME", "DESCRIPTION")
131131
for i := range plans {
132132
o := plans[i]
133133
for j := range *o.Plans {
134134
p := (*o.Plans)[j]
135-
table.AddRow(*o.Name, *p.Id, *p.Name, *p.Description)
135+
table.AddRow(*o.Name, *o.Version, *p.Id, *p.Name, *p.Description)
136136
}
137137
table.AddSeparator()
138138
}
139-
table.EnableAutoMergeOnColumns(1)
139+
table.EnableAutoMergeOnColumns(1, 2)
140140
err := table.Display(cmd)
141141
if err != nil {
142142
return fmt.Errorf("render table: %w", err)

internal/cmd/mariadb/plans/plans.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,16 @@ func outputResult(cmd *cobra.Command, outputFormat string, plans []mariadb.Offer
127127
return nil
128128
default:
129129
table := tables.NewTable()
130-
table.SetHeader("OFFERING NAME", "ID", "NAME", "DESCRIPTION")
130+
table.SetHeader("OFFERING NAME", "VERSION", "ID", "NAME", "DESCRIPTION")
131131
for i := range plans {
132132
o := plans[i]
133133
for j := range *o.Plans {
134134
p := (*o.Plans)[j]
135-
table.AddRow(*o.Name, *p.Id, *p.Name, *p.Description)
135+
table.AddRow(*o.Name, *o.Version, *p.Id, *p.Name, *p.Description)
136136
}
137137
table.AddSeparator()
138138
}
139-
table.EnableAutoMergeOnColumns(1)
139+
table.EnableAutoMergeOnColumns(1, 2)
140140
err := table.Display(cmd)
141141
if err != nil {
142142
return fmt.Errorf("render table: %w", err)

internal/cmd/opensearch/plans/plans.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,16 @@ func outputResult(cmd *cobra.Command, outputFormat string, plans []opensearch.Of
127127
return nil
128128
default:
129129
table := tables.NewTable()
130-
table.SetHeader("OFFERING NAME", "ID", "NAME", "DESCRIPTION")
130+
table.SetHeader("OFFERING NAME", "VERSION", "ID", "NAME", "DESCRIPTION")
131131
for i := range plans {
132132
o := plans[i]
133133
for j := range *o.Plans {
134134
p := (*o.Plans)[j]
135-
table.AddRow(*o.Name, *p.Id, *p.Name, *p.Description)
135+
table.AddRow(*o.Name, *o.Version, *p.Id, *p.Name, *p.Description)
136136
}
137137
table.AddSeparator()
138138
}
139-
table.EnableAutoMergeOnColumns(1)
139+
table.EnableAutoMergeOnColumns(1, 2)
140140
err := table.Display(cmd)
141141
if err != nil {
142142
return fmt.Errorf("render table: %w", err)

internal/cmd/project/list/list.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"time"
88

99
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
10+
"github.com/stackitcloud/stackit-cli/internal/pkg/auth"
1011
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
1112
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
1213
"github.com/stackitcloud/stackit-cli/internal/pkg/flags"
@@ -47,6 +48,9 @@ func NewCmd() *cobra.Command {
4748
Long: "Lists all STACKIT projects that match certain criteria.",
4849
Args: args.NoArgs,
4950
Example: examples.Build(
51+
examples.NewExample(
52+
`List all STACKIT projects that the authenticated user is a member of`,
53+
"$ stackit project list"),
5054
examples.NewExample(
5155
`List all STACKIT projects that are children of a specific parent`,
5256
"$ stackit project list --parent-id xxx"),
@@ -94,9 +98,6 @@ func configureFlags(cmd *cobra.Command) {
9498
cmd.Flags().String(creationTimeAfterFlag, "", "Filter by creation timestamp, in a date-time with the RFC3339 layout format, e.g. 2023-01-01T00:00:00Z. The list of projects that were created after the given timestamp will be shown")
9599
cmd.Flags().Int64(limitFlag, 0, "Maximum number of entries to list")
96100
cmd.Flags().Int64(pageSizeFlag, pageSizeDefault, "Number of items fetched in each API call. Does not affect the number of items in the command output")
97-
98-
// At least one of parent-id, project-id-like or member flag must be provided
99-
cmd.MarkFlagsOneRequired(parentIdFlag, projectIdLikeFlag, memberFlag)
100101
}
101102

102103
func parseInput(cmd *cobra.Command) (*inputModel, error) {
@@ -137,7 +138,7 @@ func parseInput(cmd *cobra.Command) (*inputModel, error) {
137138
}, nil
138139
}
139140

140-
func buildRequest(ctx context.Context, model *inputModel, apiClient resourceManagerClient, offset int) resourcemanager.ApiListProjectsRequest {
141+
func buildRequest(ctx context.Context, model *inputModel, apiClient resourceManagerClient, offset int) (resourcemanager.ApiListProjectsRequest, error) {
141142
req := apiClient.ListProjects(ctx)
142143
if model.ParentId != nil {
143144
req = req.ContainerParentId(*model.ParentId)
@@ -151,9 +152,17 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient resourceMana
151152
if model.CreationTimeAfter != nil {
152153
req = req.CreationTimeStart(*model.CreationTimeAfter)
153154
}
155+
156+
if model.ParentId == nil && model.ProjectIdLike == nil && model.Member == nil {
157+
email, err := auth.GetAuthField(auth.USER_EMAIL)
158+
if err != nil {
159+
return req, fmt.Errorf("get email of authenticated user: %w", err)
160+
}
161+
req = req.Member(email)
162+
}
154163
req = req.Limit(float32(model.PageSize))
155164
req = req.Offset(float32(offset))
156-
return req
165+
return req, nil
157166
}
158167

159168
type resourceManagerClient interface {
@@ -169,7 +178,10 @@ func fetchProjects(ctx context.Context, model *inputModel, apiClient resourceMan
169178
projects := []resourcemanager.ProjectResponse{}
170179
for {
171180
// Call API
172-
req := buildRequest(ctx, model, apiClient, offset)
181+
req, err := buildRequest(ctx, model, apiClient, offset)
182+
if err != nil {
183+
return nil, fmt.Errorf("build list projects request: %w", err)
184+
}
173185
resp, err := req.Execute()
174186
if err != nil {
175187
return nil, fmt.Errorf("get projects: %w", err)

internal/cmd/project/list/list_test.go

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import (
99
"testing"
1010
"time"
1111

12+
"github.com/stackitcloud/stackit-cli/internal/pkg/auth"
1213
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
1314
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
15+
"github.com/zalando/go-keyring"
1416

1517
"github.com/google/go-cmp/cmp"
1618
"github.com/google/go-cmp/cmp/cmpopts"
@@ -139,15 +141,12 @@ func TestParseInput(t *testing.T) {
139141
{
140142
description: "no values",
141143
flagValues: map[string]string{},
142-
isValid: false,
143-
},
144-
{
145-
description: "none of required fields provided",
146-
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
147-
delete(flagValues, parentIdFlag)
148-
delete(flagValues, memberFlag)
144+
isValid: true,
145+
expectedModel: fixtureInputModel(func(model *inputModel) {
146+
model.ParentId = nil
147+
model.Member = nil
148+
model.CreationTimeAfter = nil
149149
}),
150-
isValid: false,
151150
},
152151
{
153152
description: "projectIdLike invalid",
@@ -258,6 +257,17 @@ func TestParseInput(t *testing.T) {
258257
}
259258

260259
func TestBuildRequest(t *testing.T) {
260+
keyring.MockInit()
261+
err := auth.SetAuthField(auth.USER_EMAIL, "[email protected]")
262+
if err != nil {
263+
t.Fatalf("Failed to set auth user email: %v", err)
264+
}
265+
266+
authUserEmail, err := auth.GetAuthField(auth.USER_EMAIL)
267+
if err != nil {
268+
t.Fatalf("Failed to get auth user email: %v", err)
269+
}
270+
261271
tests := []struct {
262272
description string
263273
model *inputModel
@@ -278,12 +288,12 @@ func TestBuildRequest(t *testing.T) {
278288
expectedRequest: fixtureRequest().Offset(10),
279289
},
280290
{
281-
description: "required fields only",
291+
description: "fetch email from auth user",
282292
model: &inputModel{
283293
PageSize: pageSizeDefault,
284294
},
285295
offset: 1,
286-
expectedRequest: testClient.ListProjects(testCtx).Offset(1).Limit(pageSizeDefault),
296+
expectedRequest: testClient.ListProjects(testCtx).Offset(1).Limit(pageSizeDefault).Member(authUserEmail),
287297
},
288298
{
289299
description: "projectIdLike set",
@@ -299,7 +309,10 @@ func TestBuildRequest(t *testing.T) {
299309
if tt.projectIdLike != nil {
300310
tt.model.ProjectIdLike = tt.projectIdLike
301311
}
302-
request := buildRequest(testCtx, tt.model, testClient, tt.offset)
312+
request, err := buildRequest(testCtx, tt.model, testClient, tt.offset)
313+
if err != nil {
314+
t.Fatalf("Failed to build request: %v", err)
315+
}
303316

304317
diff := cmp.Diff(request, tt.expectedRequest,
305318
cmp.AllowUnexported(tt.expectedRequest),

0 commit comments

Comments
 (0)