|
1 | 1 | package api |
2 | 2 |
|
3 | 3 | import ( |
4 | | - "errors" |
5 | 4 | "fmt" |
6 | | - "net/http" |
7 | 5 |
|
8 | | - "github.com/major-technology/cli/ui" |
9 | | - "github.com/spf13/cobra" |
| 6 | + clierrors "github.com/major-technology/cli/errors" |
10 | 7 | ) |
11 | 8 |
|
12 | 9 | // Error code constants from @repo/errors |
@@ -47,145 +44,45 @@ type ErrorResponse struct { |
47 | 44 | Error *AppErrorDetail `json:"error,omitempty"` |
48 | 45 | } |
49 | 46 |
|
50 | | -// APIError represents an API error with status code and message |
51 | | -type APIError struct { |
52 | | - StatusCode int |
53 | | - InternalCode int // Internal error code from the API |
54 | | - Message string |
55 | | - ErrorType string |
56 | | -} |
57 | | - |
58 | | -func (e *APIError) Error() string { |
59 | | - if e.ErrorType != "" { |
60 | | - return fmt.Sprintf("API error (status %d): %s - %s", e.StatusCode, e.ErrorType, e.Message) |
61 | | - } |
62 | | - return fmt.Sprintf("API error (status %d): %s", e.StatusCode, e.Message) |
63 | | -} |
64 | | - |
65 | | -// NoTokenError represents an error when no token is available |
66 | | -type NoTokenError struct { |
67 | | - OriginalError error |
68 | | -} |
69 | | - |
70 | | -func (e *NoTokenError) Error() string { |
71 | | - return fmt.Sprintf("not logged in: %v", e.OriginalError) |
72 | | -} |
73 | | - |
74 | | -// ForceUpgradeError represents an error when the CLI version is too old and must be upgraded |
75 | | -type ForceUpgradeError struct { |
76 | | - LatestVersion string |
77 | | -} |
78 | | - |
79 | | -func (e *ForceUpgradeError) Error() string { |
80 | | - return "CLI version is out of date and must be upgraded" |
81 | | -} |
82 | | - |
83 | | -// IsUnauthorized checks if the error is an unauthorized error |
84 | | -func IsUnauthorized(err error) bool { |
85 | | - var apiErr *APIError |
86 | | - if errors.As(err, &apiErr) { |
87 | | - return apiErr.StatusCode == http.StatusUnauthorized |
88 | | - } |
89 | | - return false |
90 | | -} |
91 | | - |
92 | | -// IsNotFound checks if the error is a not found error |
93 | | -func IsNotFound(err error) bool { |
94 | | - var apiErr *APIError |
95 | | - if errors.As(err, &apiErr) { |
96 | | - return apiErr.StatusCode == http.StatusNotFound |
97 | | - } |
98 | | - return false |
99 | | -} |
100 | | - |
101 | | -// IsBadRequest checks if the error is a bad request error |
102 | | -func IsBadRequest(err error) bool { |
103 | | - var apiErr *APIError |
104 | | - if errors.As(err, &apiErr) { |
105 | | - return apiErr.StatusCode == http.StatusBadRequest |
106 | | - } |
107 | | - return false |
108 | | -} |
109 | | - |
110 | | -// IsNoToken checks if the error is a no token error |
111 | | -func IsNoToken(err error) bool { |
112 | | - var noTokenErr *NoTokenError |
113 | | - return errors.As(err, &noTokenErr) |
114 | | -} |
115 | | - |
116 | | -// HasErrorCode checks if the error has a specific internal error code |
117 | | -func HasErrorCode(err error, code int) bool { |
118 | | - var apiErr *APIError |
119 | | - if errors.As(err, &apiErr) { |
120 | | - return apiErr.InternalCode == code |
121 | | - } |
122 | | - return false |
123 | | -} |
124 | | - |
125 | | -// IsAuthorizationPending checks if the error is an authorization pending error |
126 | | -func IsAuthorizationPending(err error) bool { |
127 | | - return HasErrorCode(err, ErrorCodeAuthorizationPending) |
128 | | -} |
129 | | - |
130 | | -// IsInvalidDeviceCode checks if the error is an invalid device code error |
131 | | -func IsInvalidDeviceCode(err error) bool { |
132 | | - return HasErrorCode(err, ErrorCodeInvalidDeviceCode) |
133 | | -} |
134 | | - |
135 | | -// GetErrorCode returns the internal error code from an error, or 0 if not an APIError |
136 | | -func GetErrorCode(err error) int { |
137 | | - var apiErr *APIError |
138 | | - if errors.As(err, &apiErr) { |
139 | | - return apiErr.InternalCode |
140 | | - } |
141 | | - return 0 |
142 | | -} |
143 | | - |
144 | | -// IsForceUpgrade checks if the error is a force upgrade error |
145 | | -func IsForceUpgrade(err error) bool { |
146 | | - var forceUpgradeErr *ForceUpgradeError |
147 | | - return errors.As(err, &forceUpgradeErr) |
148 | | -} |
149 | | - |
150 | | -// IsTokenExpired checks if the error is a token expiration error |
151 | | -func IsTokenExpired(err error) bool { |
152 | | - return HasErrorCode(err, ErrorCodeInvalidToken) |
153 | | -} |
154 | | - |
155 | | -// CheckErr checks for errors and prints appropriate messages using the command's output |
156 | | -// Returns true if no error (ok to continue), false if there was an error |
157 | | -func CheckErr(cmd *cobra.Command, err error) bool { |
158 | | - if err == nil { |
159 | | - return true |
160 | | - } |
| 47 | +// errorCodeToCLIError maps API error codes to CLIError instances |
| 48 | +var errorCodeToCLIError = map[int]*clierrors.CLIError{ |
| 49 | + // Authentication & Authorization Errors (2000-2099) |
| 50 | + ErrorCodeUnauthorized: clierrors.ErrorUnauthorized, |
| 51 | + ErrorCodeInvalidToken: clierrors.ErrorInvalidToken, |
| 52 | + ErrorCodeInvalidUserCode: clierrors.ErrorInvalidUserCode, |
| 53 | + ErrorCodeTokenNotFound: clierrors.ErrorTokenNotFound, |
| 54 | + ErrorCodeInvalidDeviceCode: clierrors.ErrorInvalidDeviceCode, |
| 55 | + ErrorCodeAuthorizationPending: clierrors.ErrorAuthorizationPending, |
161 | 56 |
|
162 | | - // Check if it's a force upgrade error |
163 | | - if IsForceUpgrade(err) { |
164 | | - ui.PrintError(cmd, "Your CLI version is out of date and must be upgraded.", "brew update && brew upgrade major") |
165 | | - return false |
166 | | - } |
| 57 | + // Organization Errors (3000-3099) |
| 58 | + ErrorCodeOrganizationNotFound: clierrors.ErrorOrganizationNotFoundAPI, |
| 59 | + ErrorCodeNotOrgMember: clierrors.ErrorNotOrgMember, |
| 60 | + ErrorCodeNoCreatePermission: clierrors.ErrorNoCreatePermission, |
167 | 61 |
|
168 | | - // Check if it's a token expiration error |
169 | | - if IsTokenExpired(err) { |
170 | | - ui.PrintError(cmd, "Your session has expired!", "major user login") |
171 | | - return false |
172 | | - } |
| 62 | + // Application Errors (4000-4099) |
| 63 | + ErrorCodeApplicationNotFound: clierrors.ErrorApplicationNotFoundAPI, |
| 64 | + ErrorCodeNoApplicationAccess: clierrors.ErrorNoApplicationAccess, |
| 65 | + ErrorCodeDuplicateAppName: clierrors.ErrorDuplicateAppName, |
173 | 66 |
|
174 | | - // Check if it's a no token error |
175 | | - if IsNoToken(err) { |
176 | | - ui.PrintError(cmd, "Not logged in!", "major user login") |
177 | | - return false |
| 67 | + // GitHub Integration Errors (5000-5099) |
| 68 | + ErrorCodeGitHubRepoNotFound: clierrors.ErrorGitHubRepoNotFound, |
| 69 | + ErrorCodeGitHubRepoAccessDenied: clierrors.ErrorGitHubRepoAccessDenied, |
| 70 | + ErrorCodeGitHubCollaboratorAddFailed: clierrors.ErrorGitHubCollaboratorAddFailed, |
| 71 | +} |
| 72 | + |
| 73 | +// ToCLIError converts an APIError to a CLIError |
| 74 | +// If a specific error code mapping exists, it returns that CLIError |
| 75 | +// Otherwise, it creates a generic CLIError with the API error details |
| 76 | +func ToCLIError(errResp *ErrorResponse) error { |
| 77 | + // Check if we have a specific mapping for this error code |
| 78 | + if cliErr, exists := errorCodeToCLIError[errResp.Error.InternalCode]; exists { |
| 79 | + return cliErr |
178 | 80 | } |
179 | 81 |
|
180 | | - // Check if it's an API error |
181 | | - var apiErr *APIError |
182 | | - if errors.As(err, &apiErr) { |
183 | | - // Just print the error description/message, nothing else |
184 | | - cmd.Printf("Error: %s\n", apiErr.Message) |
185 | | - return false |
| 82 | + // No specific mapping - create a generic CLIError with API details |
| 83 | + return &clierrors.CLIError{ |
| 84 | + Title: fmt.Sprintf("API Error (Code: %d)", errResp.Error.InternalCode), |
| 85 | + Suggestion: "Please try again or contact support if the issue persists.", |
| 86 | + Err: fmt.Errorf("%s", errResp.Error.ErrorString), |
186 | 87 | } |
187 | | - |
188 | | - // Generic error |
189 | | - cmd.Printf("Error: %v\n", err) |
190 | | - return false |
191 | 88 | } |
0 commit comments