Skip to content

Commit f0b2566

Browse files
feat(errors): support more controller errors (#43)
1 parent acb333a commit f0b2566

3 files changed

Lines changed: 62 additions & 20 deletions

File tree

auth/auth_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ func (f *fakeHTTPServer) ServeHTTP(res http.ResponseWriter, req *http.Request) {
133133
if string(body) == cancelAdminExpected && !f.cancelUsername {
134134
f.cancelUsername = true
135135
res.WriteHeader(http.StatusConflict)
136-
res.Write(nil)
136+
res.Write([]byte(`{"detail":"foo still has applications assigned. Delete or transfer ownership"}`))
137137
return
138138
} else if string(body) == cancelUserExpected && !f.cancelUsername {
139139
f.cancelUsername = true
@@ -253,7 +253,7 @@ func TestDeleteUserApp(t *testing.T) {
253253
err = Delete(d, "admin")
254254
// should be a 409 Conflict
255255

256-
if err != deis.ErrConflict {
256+
if err != deis.ErrCancellationFailed {
257257
t.Errorf("got '%s' but expected '%s'", err, deis.ErrConflict)
258258
}
259259
}

errors.go

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,26 @@ import (
1313
const (
1414
// formatErrUnknown is used to create an dynamic error if no error matches
1515
formatErrUnknown = "Unknown Error (%d): %s"
16+
jsonParsingError = "error decoding json response (%s): %s"
17+
1618
// fieldReqMsg is API error stating a field is required.
17-
fieldReqMsg = "This field is required."
18-
invalidUserMsg = "Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters."
19-
failedLoginMsg = "Unable to log in with provided credentials."
20-
invalidAppNameMsg = "App name can only contain a-z (lowercase), 0-9 and hyphens"
21-
invalidNameMsg = "Can only contain a-z (lowercase), 0-9 and hyphens"
22-
invalidCertMsg = "Could not load certificate"
23-
invalidPodMsg = "does not exist in application"
24-
invalidDomainMsg = "Hostname does not look valid."
25-
invalidVersionMsg = "version cannot be below 0"
26-
invalidKeyMsg = "Key contains invalid base64 chars"
27-
duplicateUserMsg = "A user with that username already exists."
28-
invalidEmailMsg = "Enter a valid email address."
29-
invalidTagMsg = "No nodes matched the provided labels"
30-
duplicateIDMsg = "App with this id already exists."
19+
fieldReqMsg = "This field is required."
20+
invalidUserMsg = "Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters."
21+
failedLoginMsg = "Unable to log in with provided credentials."
22+
invalidAppNameMsg = "App name can only contain a-z (lowercase), 0-9 and hyphens"
23+
invalidNameMsg = "Can only contain a-z (lowercase), 0-9 and hyphens"
24+
invalidCertMsg = "Could not load certificate"
25+
invalidPodMsg = "does not exist in application"
26+
invalidDomainMsg = "Hostname does not look valid."
27+
invalidVersionMsg = "version cannot be below 0"
28+
invalidKeyMsg = "Key contains invalid base64 chars"
29+
duplicateUserMsg = "A user with that username already exists."
30+
invalidEmailMsg = "Enter a valid email address."
31+
invalidTagMsg = "No nodes matched the provided labels"
32+
duplicateIDMsg = "App with this id already exists."
33+
cancellationFailedMsg = "still has applications assigned. Delete or transfer ownership"
34+
duplicateDomainMsg = "Domain is already in use by another application"
35+
duplicateKeyMsg = "Public Key is already in use"
3136
)
3237

3338
var (
@@ -56,6 +61,8 @@ var (
5661
ErrForbidden = errors.New("You do not have permission to perform this action.")
5762
// ErrMissingKey is returned when a key is not sent with the request.
5863
ErrMissingKey = errors.New("A key is required")
64+
// ErrDuplicateKey is returned when adding a key that already exists.
65+
ErrDuplicateKey = errors.New(duplicateKeyMsg)
5966
// ErrInvalidName is returned when a name is invalid or missing.
6067
ErrInvalidName = errors.New(invalidNameMsg)
6168
// ErrInvalidCertificate is returned when a certififate is missing or invalid
@@ -64,6 +71,8 @@ var (
6471
ErrPodNotFound = errors.New("Pod not found in application")
6572
// ErrInvalidDomain is returned when a domain is missing or invalid
6673
ErrInvalidDomain = errors.New(invalidDomainMsg)
74+
// ErrDuplicateDomain is returned adding domain that is already in use
75+
ErrDuplicateDomain = errors.New(duplicateDomainMsg)
6776
// ErrInvalidImage is returned when a image is missing or invalid
6877
ErrInvalidImage = errors.New("The given image is invalid")
6978
// ErrInvalidVersion is returned when a version is invalid
@@ -78,6 +87,8 @@ var (
7887
ErrDuplicateApp = errors.New(duplicateIDMsg)
7988
// ErrUnprocessable is returned when the controller throws a 422.
8089
ErrUnprocessable = errors.New("Unable to process your request.")
90+
// ErrCancellationFailed is returned when cancelling a user fails.
91+
ErrCancellationFailed = errors.New("Failed to delete user because the user still has applications assigned. Delete or transfer ownership.")
8192
)
8293

8394
// checkForErrors tries to match up an API error with an predefined error in the SDK.
@@ -101,7 +112,7 @@ func checkForErrors(res *http.Response) error {
101112
case 400:
102113
bodyMap := make(map[string]interface{})
103114
if err := json.Unmarshal(out, &bodyMap); err != nil {
104-
return unknownServerError(res.StatusCode, fmt.Sprintf("error decoding json response (%s): %s", err, string(out)))
115+
return unknownServerError(res.StatusCode, fmt.Sprintf(jsonParsingError, err, string(out)))
105116
}
106117

107118
if scanResponse(bodyMap, "username", []string{fieldReqMsg, invalidUserMsg}, true) {
@@ -132,6 +143,10 @@ func checkForErrors(res *http.Response) error {
132143
return ErrMissingKey
133144
}
134145

146+
if scanResponse(bodyMap, "key", []string{duplicateKeyMsg}, true) {
147+
return ErrDuplicateKey
148+
}
149+
135150
if scanResponse(bodyMap, "public", []string{fieldReqMsg, invalidKeyMsg}, true) {
136151
return ErrMissingKey
137152
}
@@ -148,6 +163,10 @@ func checkForErrors(res *http.Response) error {
148163
return ErrInvalidDomain
149164
}
150165

166+
if scanResponse(bodyMap, "domain", []string{duplicateDomainMsg}, true) {
167+
return ErrDuplicateDomain
168+
}
169+
151170
if scanResponse(bodyMap, "image", []string{fieldReqMsg}, true) {
152171
return ErrInvalidImage
153172
}
@@ -181,7 +200,16 @@ func checkForErrors(res *http.Response) error {
181200
case 405:
182201
return ErrMethodNotAllowed
183202
case 409:
184-
return ErrConflict
203+
bodyMap := make(map[string]interface{})
204+
if err := json.Unmarshal(out, &bodyMap); err != nil {
205+
return unknownServerError(res.StatusCode, fmt.Sprintf(jsonParsingError, err, string(out)))
206+
}
207+
if v, ok := bodyMap["detail"].(string); ok {
208+
if strings.Contains(v, cancellationFailedMsg) {
209+
return ErrCancellationFailed
210+
}
211+
}
212+
return unknownServerError(res.StatusCode, string(out))
185213
case 422:
186214
return ErrUnprocessable
187215
case 500:

errors_test.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,13 @@ func TestErrors(t *testing.T) {
132132
},
133133
expected: ErrMissingKey,
134134
},
135+
{
136+
res: &http.Response{
137+
StatusCode: 400,
138+
Body: readCloser(`{"key": ["Public Key is already in use"]}`),
139+
},
140+
expected: ErrDuplicateKey,
141+
},
135142
{
136143
res: &http.Response{
137144
StatusCode: 400,
@@ -174,6 +181,13 @@ func TestErrors(t *testing.T) {
174181
},
175182
expected: ErrInvalidDomain,
176183
},
184+
{
185+
res: &http.Response{
186+
StatusCode: 400,
187+
Body: readCloser(`{"domain":["Domain is already in use by another application"]}`),
188+
},
189+
expected: ErrDuplicateDomain,
190+
},
177191
{
178192
res: &http.Response{
179193
StatusCode: 400,
@@ -240,9 +254,9 @@ func TestErrors(t *testing.T) {
240254
{
241255
res: &http.Response{
242256
StatusCode: 409,
243-
Body: readCloser(""),
257+
Body: readCloser(`{"detail":"foo still has applications assigned. Delete or transfer ownership"}`),
244258
},
245-
expected: ErrConflict,
259+
expected: ErrCancellationFailed,
246260
},
247261
{
248262
res: &http.Response{

0 commit comments

Comments
 (0)