Skip to content

Commit 47e2954

Browse files
committed
add project validation function, errors and apply for list command
1 parent a2d59a5 commit 47e2954

File tree

3 files changed

+96
-12
lines changed

3 files changed

+96
-12
lines changed

internal/cmd/ske/cluster/list/list.go

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ import (
1313
"github.com/stackitcloud/stackit-cli/internal/pkg/flags"
1414
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
1515
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
16-
"github.com/stackitcloud/stackit-cli/internal/pkg/projectname"
1716
serviceEnablementClient "github.com/stackitcloud/stackit-cli/internal/pkg/services/service-enablement/client"
1817
serviceEnablementUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/service-enablement/utils"
1918
"github.com/stackitcloud/stackit-cli/internal/pkg/services/ske/client"
2019
"github.com/stackitcloud/stackit-cli/internal/pkg/tables"
2120
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
21+
"github.com/stackitcloud/stackit-cli/internal/pkg/validation"
2222

2323
"github.com/spf13/cobra"
2424
"github.com/stackitcloud/stackit-sdk-go/services/ske"
@@ -57,6 +57,12 @@ func NewCmd(params *params.CmdParams) *cobra.Command {
5757
return err
5858
}
5959

60+
// Validate project ID (exists and user has access)
61+
projectLabel, err := validation.ValidateProject(ctx, params.Printer, params.CliVersion, cmd, model.ProjectId)
62+
if err != nil {
63+
return err
64+
}
65+
6066
// Configure API client
6167
apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion)
6268
if err != nil {
@@ -91,14 +97,6 @@ func NewCmd(params *params.CmdParams) *cobra.Command {
9197
clusters = clusters[:*model.Limit]
9298
}
9399

94-
projectLabel := model.ProjectId
95-
if len(clusters) == 0 {
96-
projectLabel, err = projectname.GetProjectName(ctx, params.Printer, params.CliVersion, cmd)
97-
if err != nil {
98-
params.Printer.Debug(print.ErrorLevel, "get project name: %v", err)
99-
}
100-
}
101-
102100
return outputResult(params.Printer, model.OutputFormat, projectLabel, clusters)
103101
},
104102
}
@@ -113,9 +111,6 @@ func configureFlags(cmd *cobra.Command) {
113111

114112
func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) {
115113
globalFlags := globalflags.Parse(p, cmd)
116-
if globalFlags.ProjectId == "" {
117-
return nil, &errors.ProjectIdError{}
118-
}
119114

120115
limit := flags.FlagToInt64Pointer(p, cmd, limitFlag)
121116
if limit != nil && *limit < 1 {

internal/pkg/errors/errors.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,16 @@ To list all profiles, run:
178178
$ stackit config profile list`
179179

180180
FILE_ALREADY_EXISTS = `file %q already exists in the export path. Delete the existing file or define a different export path`
181+
182+
PROJECT_NOT_FOUND = `the project %[2]q (ID: %[1]s) does not exist.
183+
184+
To list all available projects, run:
185+
$ stackit project list`
186+
187+
PROJECT_ACCESS_DENIED = `you don't have access to the project %[2]q (ID: %[1]s).
188+
189+
To list all available projects, run:
190+
$ stackit project list`
181191
)
182192

183193
type ServerNicAttachMissingNicIdError struct {
@@ -499,3 +509,21 @@ type FileAlreadyExistsError struct {
499509
}
500510

501511
func (e *FileAlreadyExistsError) Error() string { return fmt.Sprintf(FILE_ALREADY_EXISTS, e.Filename) }
512+
513+
type ProjectNotFoundError struct {
514+
ProjectId string
515+
ProjectLabel string
516+
}
517+
518+
func (e *ProjectNotFoundError) Error() string {
519+
return fmt.Sprintf(PROJECT_NOT_FOUND, e.ProjectId, e.ProjectLabel)
520+
}
521+
522+
type ProjectAccessDeniedError struct {
523+
ProjectId string
524+
ProjectLabel string
525+
}
526+
527+
func (e *ProjectAccessDeniedError) Error() string {
528+
return fmt.Sprintf(PROJECT_ACCESS_DENIED, e.ProjectId, e.ProjectLabel)
529+
}

internal/pkg/validation/project.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package validation
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
8+
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
9+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
10+
"github.com/stackitcloud/stackit-cli/internal/pkg/projectname"
11+
"github.com/stackitcloud/stackit-cli/internal/pkg/services/resourcemanager/client"
12+
13+
"github.com/spf13/cobra"
14+
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
15+
)
16+
17+
// ValidateProject validates that the project ID is not empty, exists, and the user has access to it.
18+
// It returns the project name for display purposes.
19+
func ValidateProject(ctx context.Context, p *print.Printer, cliVersion string, cmd *cobra.Command, projectId string) (string, error) {
20+
// Check if project ID is empty
21+
if projectId == "" {
22+
return "", &errors.ProjectIdError{}
23+
}
24+
25+
// Configure Resource Manager API client
26+
apiClient, err := client.ConfigureClient(p, cliVersion)
27+
if err != nil {
28+
return "", fmt.Errorf("configure resource manager client: %w", err)
29+
}
30+
31+
// Try to get project details to validate existence and access
32+
req := apiClient.GetProject(ctx, projectId)
33+
resp, err := req.Execute()
34+
if err != nil {
35+
// Check for specific HTTP status codes
36+
if httpErr, ok := err.(*oapierror.GenericOpenAPIError); ok {
37+
switch httpErr.StatusCode {
38+
case http.StatusNotFound:
39+
// Try to get project name for better error message
40+
projectLabel := projectId
41+
if projectName, nameErr := projectname.GetProjectName(ctx, p, cliVersion, cmd); nameErr == nil {
42+
projectLabel = projectName
43+
}
44+
return "", &errors.ProjectNotFoundError{ProjectId: projectId, ProjectLabel: projectLabel}
45+
case http.StatusForbidden:
46+
// Try to get project name for better error message
47+
projectLabel := projectId
48+
if projectName, nameErr := projectname.GetProjectName(ctx, p, cliVersion, cmd); nameErr == nil {
49+
projectLabel = projectName
50+
}
51+
return "", &errors.ProjectAccessDeniedError{ProjectId: projectId, ProjectLabel: projectLabel}
52+
case http.StatusUnauthorized:
53+
return "", &errors.AuthError{}
54+
}
55+
}
56+
return "", fmt.Errorf("validate project: %w", err)
57+
}
58+
59+
// Project exists and user has access, returning the project name
60+
return *resp.Name, nil
61+
}

0 commit comments

Comments
 (0)