Repository: hetznercloud/hcloud-go
Pull request #16 (2017-12-13 17:45:00)

Define and use error schema


Patch:
diff --git a/hcloud/client.go b/hcloud/client.go
index 4795dcd..f2d3bc1 100644
--- a/hcloud/client.go
+++ b/hcloud/client.go
@@ -191,22 +191,14 @@ func errorFromResponse(resp *http.Response, body []byte) error {
 		return nil
 	}
 
-	var apiError struct {
-		Error struct {
-			Code    string `json:"code"`
-			Message string `json:"message"`
-		} `json:"error"`
-	}
-	if err := json.Unmarshal(body, &apiError); err != nil {
+	var respBody schema.ErrorResponse
+	if err := json.Unmarshal(body, &respBody); err != nil {
 		return nil
 	}
-	if apiError.Error.Code == "" && apiError.Error.Message == "" {
+	if respBody.Error.Code == "" && respBody.Error.Message == "" {
 		return nil
 	}
-	return Error{
-		Code:    ErrorCode(apiError.Error.Code),
-		Message: apiError.Error.Message,
-	}
+	return ErrorFromSchema(respBody.Error)
 }
 
 // Response represents a response from the API. It embeds http.Response.
diff --git a/hcloud/schema.go b/hcloud/schema.go
index 188beeb..9f0206c 100644
--- a/hcloud/schema.go
+++ b/hcloud/schema.go
@@ -173,3 +173,11 @@ func PaginationFromSchema(s schema.MetaPagination) Pagination {
 		TotalEntries: s.TotalEntries,
 	}
 }
+
+// ErrorFromSchema converts a schema.Error to an Error.
+func ErrorFromSchema(s schema.Error) Error {
+	return Error{
+		Code:    ErrorCode(s.Code),
+		Message: s.Message,
+	}
+}
diff --git a/hcloud/schema/error.go b/hcloud/schema/error.go
new file mode 100644
index 0000000..6b02067
--- /dev/null
+++ b/hcloud/schema/error.go
@@ -0,0 +1,12 @@
+package schema
+
+// Error represents the schema of an error response.
+type Error struct {
+	Code    string `json:"code"`
+	Message string `json:"message"`
+}
+
+// ErrorResponse defines the schema of a response containing an error.
+type ErrorResponse struct {
+	Error Error `json:"error"`
+}


Test patch:
diff --git a/hcloud/client_test.go b/hcloud/client_test.go
index 3f65ce5..71395b5 100644
--- a/hcloud/client_test.go
+++ b/hcloud/client_test.go
@@ -55,12 +55,12 @@ func TestClientError(t *testing.T) {
 	env.Mux.HandleFunc("/error", func(w http.ResponseWriter, r *http.Request) {
 		w.Header().Set("Content-Type", "application/json")
 		w.WriteHeader(http.StatusUnprocessableEntity)
-		fmt.Fprint(w, `{
-			"error": {
-				"code": "service_error",
-				"message": "An error occured"
-			}
-		}`)
+		json.NewEncoder(w).Encode(schema.ErrorResponse{
+			Error: schema.Error{
+				Code:    "service_error",
+				Message: "An error occured",
+			},
+		})
 	})
 
 	ctx := context.Background()
diff --git a/hcloud/schema_test.go b/hcloud/schema_test.go
index db2f3cd..52697af 100644
--- a/hcloud/schema_test.go
+++ b/hcloud/schema_test.go
@@ -411,3 +411,23 @@ func TestSSHKeyFromSchema(t *testing.T) {
 		t.Errorf("unexpected public key: %v", sshKey.PublicKey)
 	}
 }
+
+func TestErrorFromSchema(t *testing.T) {
+	data := []byte(`{
+		"code": "service_error",
+		"message": "An error occured"
+	}`)
+
+	var s schema.Error
+	if err := json.Unmarshal(data, &s); err != nil {
+		t.Fatal(err)
+	}
+	err := ErrorFromSchema(s)
+
+	if err.Code != "service_error" {
+		t.Errorf("unexpected code: %v", err.Code)
+	}
+	if err.Message != "An error occured" {
+		t.Errorf("unexpected message: %v", err.Message)
+	}
+}
diff --git a/hcloud/server_test.go b/hcloud/server_test.go
index 903d277..ccf1705 100644
--- a/hcloud/server_test.go
+++ b/hcloud/server_test.go
@@ -78,12 +78,12 @@ func TestServersAll(t *testing.T) {
 		if firstRequest {
 			firstRequest = false
 			w.WriteHeader(http.StatusTooManyRequests)
-			fmt.Fprint(w, `{
-				"error": {
-					"code": "limit_reached",
-					"message": "ratelimited"
-				}
-			}`)
+			json.NewEncoder(w).Encode(schema.ErrorResponse{
+				Error: schema.Error{
+					Code:    "limit_reached",
+					Message: "ratelimited",
+				},
+			})
 			return
 		}
 
diff --git a/hcloud/server_type_test.go b/hcloud/server_type_test.go
index f45122e..4f6638d 100644
--- a/hcloud/server_type_test.go
+++ b/hcloud/server_type_test.go
@@ -79,12 +79,12 @@ func TestServerTypeClient(t *testing.T) {
 			if firstRequest {
 				firstRequest = false
 				w.WriteHeader(http.StatusTooManyRequests)
-				fmt.Fprint(w, `{
-					"error": {
-						"code": "limit_reached",
-						"message": "ratelimited"
-					}
-				}`)
+				json.NewEncoder(w).Encode(schema.ErrorResponse{
+					Error: schema.Error{
+						Code:    "limit_reached",
+						Message: "ratelimited",
+					},
+				})
 				return
 			}