Skip to content

Commit 8e20341

Browse files
feat: standard error structures (#177)
* feat: add standard errors
1 parent 01104ee commit 8e20341

File tree

1 file changed

+130
-1
lines changed

1 file changed

+130
-1
lines changed

scw/errors.go

+130-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package scw
33
import (
44
"encoding/json"
55
"fmt"
6+
"io/ioutil"
67
"net/http"
8+
"strings"
79

810
"github.com./scaleway/scaleway-sdk-go/internal/errors"
911
)
@@ -71,10 +73,137 @@ func hasResponseError(res *http.Response) SdkError {
7173
return newErr
7274
}
7375

74-
err := json.NewDecoder(res.Body).Decode(newErr)
76+
body, err := ioutil.ReadAll(res.Body)
77+
if err != nil {
78+
return errors.Wrap(err, "cannot read error response body")
79+
}
80+
81+
err = json.Unmarshal(body, newErr)
7582
if err != nil {
7683
return errors.Wrap(err, "could not parse error response body")
7784
}
85+
stdErr := unmarshalStandardError(newErr.Type, body)
86+
if stdErr != nil {
87+
return stdErr
88+
}
7889

7990
return newErr
8091
}
92+
93+
func unmarshalStandardError(errorType string, body []byte) SdkError {
94+
var stdErr SdkError
95+
96+
switch errorType {
97+
case "invalid_arguments":
98+
stdErr = &InvalidArgumentsError{}
99+
case "quotas_exceeded":
100+
stdErr = &QuotasExceededError{}
101+
case "transient_state":
102+
stdErr = &TransientStateError{}
103+
case "not_found":
104+
stdErr = &ResourceNotFound{}
105+
case "permissions_denied":
106+
stdErr = &PermissionsDeniedError{}
107+
default:
108+
return nil
109+
}
110+
111+
err := json.Unmarshal(body, stdErr)
112+
if err != nil {
113+
return errors.Wrap(err, "could not parse error %s response body", errorType)
114+
}
115+
116+
return stdErr
117+
}
118+
119+
type InvalidArgumentsError struct {
120+
Details []struct {
121+
ArgumentName string `json:"argument_name"`
122+
Reason string `json:"reason"`
123+
HelpMessage string `json:"help_message"`
124+
} `json:"details"`
125+
}
126+
127+
// IsScwSdkError implements the SdkError interface
128+
func (e *InvalidArgumentsError) IsScwSdkError() {}
129+
func (e *InvalidArgumentsError) Error() string {
130+
invalidArgs := make([]string, len(e.Details))
131+
for i, d := range e.Details {
132+
invalidArgs[i] = d.ArgumentName
133+
switch d.Reason {
134+
case "unknown":
135+
invalidArgs[i] += " is invalid for unexpected reason"
136+
case "required":
137+
invalidArgs[i] += " is required"
138+
case "format":
139+
invalidArgs[i] += " is wrongly formatted"
140+
case "constraint":
141+
invalidArgs[i] += " does not respect constraint"
142+
}
143+
if d.HelpMessage != "" {
144+
invalidArgs[i] += ", " + d.HelpMessage
145+
}
146+
}
147+
148+
return "scaleway-sdk-go: invalid argument(s): " + strings.Join(invalidArgs, "; ")
149+
}
150+
151+
type QuotasExceededError struct {
152+
Details []struct {
153+
Resource string `json:"resource"`
154+
Quota uint32 `json:"quota"`
155+
Current uint32 `json:"current"`
156+
} `json:"details"`
157+
}
158+
159+
// IsScwSdkError implements the SdkError interface
160+
func (e *QuotasExceededError) IsScwSdkError() {}
161+
func (e *QuotasExceededError) Error() string {
162+
invalidArgs := make([]string, len(e.Details))
163+
for i, d := range e.Details {
164+
invalidArgs[i] = fmt.Sprintf("%s has reached its quota (%d/%d)", d.Resource, d.Current, d.Current)
165+
}
166+
167+
return "scaleway-sdk-go: quota exceeded(s): " + strings.Join(invalidArgs, "; ")
168+
}
169+
170+
type PermissionsDeniedError struct {
171+
Details []struct {
172+
Resource string `json:"resource"`
173+
Action string `json:"action"`
174+
} `json:"details"`
175+
}
176+
177+
// IsScwSdkError implements the SdkError interface
178+
func (e *PermissionsDeniedError) IsScwSdkError() {}
179+
func (e *PermissionsDeniedError) Error() string {
180+
invalidArgs := make([]string, len(e.Details))
181+
for i, d := range e.Details {
182+
invalidArgs[i] = fmt.Sprintf("%s %s", d.Action, d.Resource)
183+
}
184+
185+
return "scaleway-sdk-go: insufficient permissions: " + strings.Join(invalidArgs, "; ")
186+
}
187+
188+
type TransientStateError struct {
189+
Resource string `json:"resource"`
190+
ResourceID string `json:"resource_id"`
191+
CurrentState string `json:"current_state"`
192+
}
193+
194+
// IsScwSdkError implements the SdkError interface
195+
func (e *TransientStateError) IsScwSdkError() {}
196+
func (e *TransientStateError) Error() string {
197+
return fmt.Sprintf("scaleway-sdk-go: resource %s with ID %s is in a transient state: %s", e.Resource, e.ResourceID, e.CurrentState)
198+
}
199+
200+
type ResourceNotFound struct {
201+
Resource string `json:"resource"`
202+
ResourceID string `json:"resource_id"`
203+
}
204+
205+
// IsScwSdkError implements the SdkError interface
206+
func (e *ResourceNotFound) IsScwSdkError() {}
207+
func (e *ResourceNotFound) Error() string {
208+
return fmt.Sprintf("scaleway-sdk-go: resource %s with ID %s is not found", e.Resource, e.ResourceID)
209+
}

0 commit comments

Comments
 (0)