Skip to content

Commit 9abe765

Browse files
committed
feat: consistent API errors
Adds an `APIError` type to our API spec and modifies our error handling code to return the new error type. In this commit, requests to nonexistent routes will still return the generic Goa error type. This will change in a subsequent commit. PLAT-86
1 parent 1e7a582 commit 9abe765

File tree

20 files changed

+6216
-5801
lines changed

20 files changed

+6216
-5801
lines changed

api/design/api.go

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,29 @@ var _ = g.API("control-plane", func() {
2222
g.Meta("openapi:operationId", "{method}")
2323

2424
// Common errors
25-
g.Error("cluster_already_initialized")
26-
g.Error("cluster_not_initialized")
27-
g.Error("invalid_join_token")
28-
g.Error("invalid_input")
29-
g.Error("not_found")
30-
g.Error("database_not_modifiable")
25+
g.Error("cluster_already_initialized", APIError)
26+
g.Error("cluster_not_initialized", APIError)
27+
g.Error("database_not_modifiable", APIError)
28+
g.Error("invalid_input", APIError)
29+
g.Error("invalid_join_token", APIError)
30+
g.Error("not_found", APIError)
31+
g.Error("operation_already_in_progress", APIError)
32+
g.Error("server_error", APIError)
3133
g.HTTP(func() {
3234
g.Response("cluster_already_initialized", http.StatusConflict)
3335
g.Response("cluster_not_initialized", http.StatusConflict)
34-
g.Response("invalid_join_token", http.StatusUnauthorized)
36+
g.Response("database_not_modifiable", http.StatusConflict)
3537
g.Response("invalid_input", http.StatusBadRequest)
38+
g.Response("invalid_join_token", http.StatusUnauthorized)
3639
g.Response("not_found", http.StatusNotFound)
37-
g.Response("database_not_modifiable", http.StatusConflict)
40+
g.Response("operation_already_in_progress", http.StatusConflict)
41+
g.Response("server_error", http.StatusInternalServerError)
3842
})
3943
})
4044

4145
var _ = g.Service("control-plane", func() {
46+
g.Error("server_error")
47+
4248
g.Method("init-cluster", func() {
4349
g.Description("Initializes a new cluster.")
4450
g.Result(ClusterJoinToken)
@@ -53,6 +59,7 @@ var _ = g.Service("control-plane", func() {
5359
g.Description("Join this host to an existing cluster.")
5460
g.Payload(ClusterJoinToken)
5561
g.Error("cluster_already_initialized")
62+
g.Error("invalid_join_token")
5663

5764
g.HTTP(func() {
5865
g.POST("/cluster/join")
@@ -86,7 +93,6 @@ var _ = g.Service("control-plane", func() {
8693
g.Description("Returns information about the cluster.")
8794
g.Result(Cluster)
8895
g.Error("cluster_not_initialized")
89-
g.Error("not_found")
9096

9197
g.HTTP(func() {
9298
g.GET("/cluster")
@@ -113,6 +119,7 @@ var _ = g.Service("control-plane", func() {
113119
})
114120
g.Result(Host)
115121
g.Error("cluster_not_initialized")
122+
g.Error("invalid_input")
116123
g.Error("not_found")
117124

118125
g.HTTP(func() {
@@ -129,6 +136,7 @@ var _ = g.Service("control-plane", func() {
129136
})
130137
})
131138
g.Error("cluster_not_initialized")
139+
g.Error("invalid_input")
132140
g.Error("not_found")
133141

134142
g.HTTP(func() {
@@ -153,8 +161,9 @@ var _ = g.Service("control-plane", func() {
153161
g.Payload(CreateDatabaseRequest)
154162
g.Result(CreateDatabaseResponse)
155163
g.Error("cluster_not_initialized")
164+
g.Error("database_already_exists", APIError)
156165
g.Error("invalid_input")
157-
g.Error("database_already_exists")
166+
g.Error("operation_already_in_progress")
158167

159168
g.HTTP(func() {
160169
g.POST("/databases")
@@ -174,6 +183,7 @@ var _ = g.Service("control-plane", func() {
174183
g.View("default")
175184
})
176185
g.Error("cluster_not_initialized")
186+
g.Error("invalid_input")
177187
g.Error("not_found")
178188

179189
g.HTTP(func() {
@@ -196,8 +206,10 @@ var _ = g.Service("control-plane", func() {
196206
})
197207
g.Result(UpdateDatabaseResponse)
198208
g.Error("cluster_not_initialized")
199-
g.Error("not_found")
200209
g.Error("database_not_modifiable")
210+
g.Error("invalid_input")
211+
g.Error("not_found")
212+
g.Error("operation_already_in_progress")
201213

202214
g.HTTP(func() {
203215
g.POST("/databases/{database_id}")
@@ -219,8 +231,10 @@ var _ = g.Service("control-plane", func() {
219231
})
220232
g.Result(DeleteDatabaseResponse)
221233
g.Error("cluster_not_initialized")
222-
g.Error("not_found")
223234
g.Error("database_not_modifiable")
235+
g.Error("invalid_input")
236+
g.Error("not_found")
237+
g.Error("operation_already_in_progress")
224238

225239
g.HTTP(func() {
226240
g.DELETE("/databases/{database_id}")
@@ -245,14 +259,14 @@ var _ = g.Service("control-plane", func() {
245259
})
246260
g.Result(BackupDatabaseNodeResponse)
247261
g.Error("cluster_not_initialized")
248-
g.Error("not_found")
249262
g.Error("database_not_modifiable")
250-
g.Error("backup_already_in_progress")
263+
g.Error("invalid_input")
264+
g.Error("not_found")
265+
g.Error("operation_already_in_progress")
251266

252267
g.HTTP(func() {
253268
g.POST("/databases/{database_id}/nodes/{node_name}/backups")
254269
g.Body("options")
255-
g.Response("backup_already_in_progress", http.StatusConflict)
256270
})
257271
})
258272

@@ -283,6 +297,7 @@ var _ = g.Service("control-plane", func() {
283297
})
284298
g.Result(g.ArrayOf(Task))
285299
g.Error("cluster_not_initialized")
300+
g.Error("invalid_input")
286301
g.Error("not_found")
287302

288303
g.HTTP(func() {
@@ -311,6 +326,7 @@ var _ = g.Service("control-plane", func() {
311326
})
312327
g.Result(Task)
313328
g.Error("cluster_not_initialized")
329+
g.Error("invalid_input")
314330
g.Error("not_found")
315331

316332
g.HTTP(func() {
@@ -345,6 +361,7 @@ var _ = g.Service("control-plane", func() {
345361
})
346362
g.Result(TaskLog)
347363
g.Error("cluster_not_initialized")
364+
g.Error("invalid_input")
348365
g.Error("not_found")
349366

350367
g.HTTP(func() {
@@ -368,9 +385,10 @@ var _ = g.Service("control-plane", func() {
368385
})
369386
g.Result(RestoreDatabaseResponse)
370387
g.Error("cluster_not_initialized")
371-
g.Error("not_found")
372388
g.Error("database_not_modifiable")
373389
g.Error("invalid_input")
390+
g.Error("not_found")
391+
g.Error("operation_already_in_progress")
374392

375393
g.HTTP(func() {
376394
g.POST("/databases/{database_id}/restore")
@@ -390,3 +408,10 @@ var _ = g.Service("control-plane", func() {
390408
// Serves the OpenAPI spec as a static file
391409
g.Files("/openapi.json", "./gen/http/openapi3.json")
392410
})
411+
412+
var APIError = g.Type("APIError", func() {
413+
g.Description("A Control Plane API error.")
414+
g.ErrorName("name", g.String, "The name of the error.")
415+
g.Attribute("message", g.String, "The error message.")
416+
g.Required("name", "message")
417+
})

api/gen/control_plane/client.go

Lines changed: 41 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)