Skip to content

Commit c10e7bd

Browse files
committed
Merge with upstream and modify stackit git commands to use new params *params.CmdParams attribute
2 parents 9399e8b + 4f78047 commit c10e7bd

File tree

831 files changed

+4834
-4008
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

831 files changed

+4834
-4008
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package client
2+
3+
import (
4+
"github.com/spf13/viper"
5+
"github.com/stackitcloud/stackit-cli/internal/pkg/auth"
6+
"github.com/stackitcloud/stackit-cli/internal/pkg/config"
7+
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
8+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
9+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
10+
sdkConfig "github.com/stackitcloud/stackit-sdk-go/core/config"
11+
"github.com/stackitcloud/stackit-sdk-go/services/foo"
12+
// (...)
13+
)
14+
15+
func ConfigureClient(p *print.Printer, cliVersion string) (*foo.APIClient, error) {
16+
authCfgOption, err := auth.AuthenticationConfig(p, auth.AuthorizeUser)
17+
if err != nil {
18+
return nil, &errors.AuthError{}
19+
}
20+
21+
region := viper.GetString(config.RegionKey)
22+
cfgOptions := []sdkConfig.ConfigurationOption{
23+
utils.UserAgentConfigOption(cliVersion),
24+
sdkConfig.WithRegion(region), // Configuring region is needed if "foo" is a regional API
25+
authCfgOption,
26+
}
27+
28+
customEndpoint := viper.GetString(config.fooCustomEndpointKey)
29+
30+
if customEndpoint != "" {
31+
cfgOptions = append(cfgOptions, sdkConfig.WithEndpoint(customEndpoint))
32+
}
33+
34+
if p.IsVerbosityDebug() {
35+
cfgOptions = append(cfgOptions,
36+
sdkConfig.WithMiddleware(print.RequestResponseCapturer(p, nil)),
37+
)
38+
}
39+
40+
apiClient, err := foo.NewAPIClient(cfgOptions...)
41+
if err != nil {
42+
p.Debug(print.ErrorLevel, "create new API client: %v", err)
43+
return nil, &errors.AuthError{}
44+
}
45+
46+
return apiClient, nil
47+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package bar
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
8+
"github.com/spf13/cobra"
9+
"github.com/stackitcloud/stackit-cli/internal/cmd/params"
10+
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
11+
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
12+
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
13+
"github.com/stackitcloud/stackit-cli/internal/pkg/flags"
14+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
15+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
16+
"github.com/stackitcloud/stackit-cli/internal/pkg/projectname"
17+
"github.com/stackitcloud/stackit-cli/internal/pkg/services/alb/client"
18+
"github.com/stackitcloud/stackit-cli/internal/pkg/tables"
19+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
20+
"gopkg.in/yaml.v2"
21+
// (...)
22+
)
23+
24+
// Define consts for command flags
25+
const (
26+
someArg = "MY_ARG"
27+
someFlag = "my-flag"
28+
)
29+
30+
// Struct to model user input (arguments and/or flags)
31+
type inputModel struct {
32+
*globalflags.GlobalFlagModel
33+
MyArg string
34+
MyFlag *string
35+
}
36+
37+
// "bar" command constructor
38+
func NewCmd(params *params.CmdParams) *cobra.Command {
39+
cmd := &cobra.Command{
40+
Use: "bar",
41+
Short: "Short description of the command (is shown in the help of parent command)",
42+
Long: "Long description of the command. Can contain some more information about the command usage. It is shown in the help of the current command.",
43+
Args: args.SingleArg(someArg, utils.ValidateUUID), // Validate argument, with an optional validation function
44+
Example: examples.Build(
45+
examples.NewExample(
46+
`Do something with command "bar"`,
47+
"$ stackit foo bar arg-value --my-flag flag-value"),
48+
//...
49+
),
50+
RunE: func(cmd *cobra.Command, args []string) error {
51+
ctx := context.Background()
52+
model, err := parseInput(params.Printer, cmd, args)
53+
if err != nil {
54+
return err
55+
}
56+
57+
// Configure API client
58+
apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion)
59+
if err != nil {
60+
return err
61+
}
62+
63+
// Call API
64+
req := buildRequest(ctx, model, apiClient)
65+
resp, err := req.Execute()
66+
if err != nil {
67+
return fmt.Errorf("(...): %w", err)
68+
}
69+
70+
projectLabel, err := projectname.GetProjectName(ctx, params.Printer, params.CliVersion, cmd)
71+
if err != nil {
72+
projectLabel = model.ProjectId
73+
}
74+
75+
// Check API response "resp" and output accordingly
76+
if resp.Item == nil {
77+
params.Printer.Info("(...)", projectLabel)
78+
return nil
79+
}
80+
return outputResult(params.Printer, cmd, model.OutputFormat, instances)
81+
},
82+
}
83+
84+
configureFlags(cmd)
85+
return cmd
86+
}
87+
88+
// Configure command flags (type, default value, and description)
89+
func configureFlags(cmd *cobra.Command) {
90+
cmd.Flags().StringP(someFlag, "shorthand", "defaultValue", "My flag description")
91+
}
92+
93+
// Parse user input (arguments and/or flags)
94+
func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) {
95+
myArg := inputArgs[0]
96+
97+
globalFlags := globalflags.Parse(p, cmd)
98+
if globalFlags.ProjectId == "" {
99+
return nil, &errors.ProjectIdError{}
100+
}
101+
102+
model := inputModel{
103+
GlobalFlagModel: globalFlags,
104+
MyArg: myArg,
105+
MyFlag: flags.FlagToStringPointer(p, cmd, someFlag),
106+
}
107+
108+
// Write the input model to the debug logs
109+
if p.IsVerbosityDebug() {
110+
modelStr, err := print.BuildDebugStrFromInputModel(model)
111+
if err != nil {
112+
p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
113+
} else {
114+
p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
115+
}
116+
}
117+
118+
return &model, nil
119+
}
120+
121+
// Build request to the API
122+
func buildRequest(ctx context.Context, model *inputModel, apiClient *foo.APIClient) foo.ApiListInstancesRequest {
123+
req := apiClient.GetBar(ctx, model.ProjectId, model.MyArg, someArg)
124+
return req
125+
}
126+
127+
// Output result based on the configured output format
128+
func outputResult(p *print.Printer, cmd *cobra.Command, outputFormat string, resources []foo.Resource) error {
129+
switch outputFormat {
130+
case print.JSONOutputFormat:
131+
details, err := json.MarshalIndent(resources, "", " ")
132+
if err != nil {
133+
return fmt.Errorf("marshal resource list: %w", err)
134+
}
135+
p.Outputln(string(details))
136+
return nil
137+
case print.YAMLOutputFormat:
138+
details, err := yaml.Marshal(resources)
139+
if err != nil {
140+
return fmt.Errorf("marshal resource list: %w", err)
141+
}
142+
p.Outputln(string(details))
143+
return nil
144+
default:
145+
table := tables.NewTable()
146+
table.SetHeader("ID", "NAME", "STATE")
147+
for i := range resources {
148+
resource := resources[i]
149+
table.AddRow(*resource.ResourceId, *resource.Name, *resource.State)
150+
}
151+
err := table.Display(p)
152+
if err != nil {
153+
return fmt.Errorf("render table: %w", err)
154+
}
155+
return nil
156+
}
157+
}

CONTRIBUTION.md

Lines changed: 2 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -53,148 +53,7 @@ Please remember to run `make generate-docs` after your changes to keep the comma
5353

5454
Below is a typical structure of a CLI command:
5555

56-
```go
57-
package bar
58-
59-
import (
60-
(...)
61-
)
62-
63-
// Define consts for command flags
64-
const (
65-
someArg = "MY_ARG"
66-
someFlag = "my-flag"
67-
)
68-
69-
// Struct to model user input (arguments and/or flags)
70-
type inputModel struct {
71-
*globalflags.GlobalFlagModel
72-
MyArg string
73-
MyFlag *string
74-
}
75-
76-
// "bar" command constructor
77-
func NewCmd(p *print.Printer) *cobra.Command {
78-
cmd := &cobra.Command{
79-
Use: "bar",
80-
Short: "Short description of the command (is shown in the help of parent command)",
81-
Long: "Long description of the command. Can contain some more information about the command usage. It is shown in the help of the current command.",
82-
Args: args.SingleArg(someArg, utils.ValidateUUID), // Validate argument, with an optional validation function
83-
Example: examples.Build(
84-
examples.NewExample(
85-
`Do something with command "bar"`,
86-
"$ stackit foo bar arg-value --my-flag flag-value"),
87-
...
88-
),
89-
RunE: func(cmd *cobra.Command, args []string) error {
90-
ctx := context.Background()
91-
model, err := parseInput(p, cmd, args)
92-
if err != nil {
93-
return err
94-
}
95-
96-
// Configure API client
97-
apiClient, err := client.ConfigureClient(p, cmd)
98-
if err != nil {
99-
return err
100-
}
101-
102-
// Call API
103-
req := buildRequest(ctx, model, apiClient)
104-
resp, err := req.Execute()
105-
if err != nil {
106-
return fmt.Errorf("(...): %w", err)
107-
}
108-
109-
projectLabel, err := projectname.GetProjectName(ctx, p, cmd)
110-
if err != nil {
111-
projectLabel = model.ProjectId
112-
}
113-
114-
// Check API response "resp" and output accordingly
115-
if resp.Item == nil {
116-
p.Info("(...)", projectLabel)
117-
return nil
118-
}
119-
return outputResult(p, cmd, model.OutputFormat, instances)
120-
},
121-
}
122-
123-
configureFlags(cmd)
124-
return cmd
125-
}
126-
127-
// Configure command flags (type, default value, and description)
128-
func configureFlags(cmd *cobra.Command) {
129-
cmd.Flags().StringP(myFlag, "defaultValue", "My flag description")
130-
}
131-
132-
// Parse user input (arguments and/or flags)
133-
func parseInput(cmd *cobra.Command, inputArgs []string) (*inputModel, error) {
134-
myArg := inputArgs[0]
135-
136-
globalFlags := globalflags.Parse(cmd)
137-
if globalFlags.ProjectId == "" {
138-
return nil, &errors.ProjectIdError{}
139-
}
140-
141-
model := inputModel{
142-
GlobalFlagModel: globalFlags,
143-
MyArg myArg,
144-
MyFlag: flags.FlagToStringPointer(cmd, myFlag),
145-
}, nil
146-
147-
// Write the input model to the debug logs
148-
if p.IsVerbosityDebug() {
149-
modelStr, err := print.BuildDebugStrFromInputModel(model)
150-
if err != nil {
151-
p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
152-
} else {
153-
p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
154-
}
155-
}
156-
157-
return &model, nil
158-
}
159-
160-
// Build request to the API
161-
func buildRequest(ctx context.Context, model *inputModel, apiClient *foo.APIClient) foo.ApiListInstancesRequest {
162-
req := apiClient.GetBar(ctx, model.ProjectId, model.MyArg, someParam)
163-
return req
164-
}
165-
166-
// Output result based on the configured output format
167-
func outputResult(p *print.Printer, cmd *cobra.Command, outputFormat string, resources []foo.Resource) error {
168-
switch outputFormat {
169-
case print.JSONOutputFormat:
170-
details, err := json.MarshalIndent(resources, "", " ")
171-
if err != nil {
172-
return fmt.Errorf("marshal resource list: %w", err)
173-
}
174-
p.Outputln(string(details))
175-
return nil
176-
case print.YAMLOutputFormat:
177-
details, err := yaml.Marshal(resources)
178-
if err != nil {
179-
return fmt.Errorf("marshal resource list: %w", err)
180-
}
181-
p.Outputln(string(details))
182-
return nil
183-
default:
184-
table := tables.NewTable()
185-
table.SetHeader("ID", "NAME", "STATE")
186-
for i := range resources {
187-
resource := resources[i]
188-
table.AddRow(*resource.ResourceId, *resource.Name, *resource.State)
189-
}
190-
err := table.Display(cmd)
191-
if err != nil {
192-
return fmt.Errorf("render table: %w", err)
193-
}
194-
return nil
195-
}
196-
}
197-
```
56+
https://github.com/stackitcloud/stackit-cli/blob/85ce44cd18d11169f2548d8657031b5fc6f94740/.github/docs/contribution-guide/cmd.go#L23-L156
19857

19958
Please remember to always add unit tests for `parseInput`, `buildRequest` (in `bar_test.go`), and any other util functions used.
20059

@@ -224,39 +83,7 @@ If you want to add a command that uses a STACKIT service `foo` that was not yet
22483
1. This is done in `internal/pkg/services/foo/client/client.go`
22584
2. Below is an example of a typical `client.go` file structure:
22685

227-
```go
228-
package client
229-
230-
import (
231-
(...)
232-
"github.com/stackitcloud/stackit-sdk-go/services/foo"
233-
)
234-
235-
func ConfigureClient(cmd *cobra.Command) (*foo.APIClient, error) {
236-
var err error
237-
var apiClient foo.APIClient
238-
var cfgOptions []sdkConfig.ConfigurationOption
239-
240-
authCfgOption, err := auth.AuthenticationConfig(cmd, auth.AuthorizeUser)
241-
if err != nil {
242-
return nil, &errors.AuthError{}
243-
}
244-
cfgOptions = append(cfgOptions, authCfgOption, sdkConfig.WithRegion("eu01")) // Configuring region is needed if "foo" is a regional API
245-
246-
customEndpoint := viper.GetString(config.fooCustomEndpointKey)
247-
248-
if customEndpoint != "" {
249-
cfgOptions = append(cfgOptions, sdkConfig.WithEndpoint(customEndpoint))
250-
}
251-
252-
apiClient, err = foo.NewAPIClient(cfgOptions...)
253-
if err != nil {
254-
return nil, &errors.AuthError{}
255-
}
256-
257-
return apiClient, nil
258-
}
259-
```
86+
https://github.com/stackitcloud/stackit-cli/blob/85ce44cd18d11169f2548d8657031b5fc6f94740/.github/docs/contribution-guide/client.go#L12-L35
26087

26188
### Local development
26289

0 commit comments

Comments
 (0)