Repository: hetznercloud/hcloud-go
Pull request #582 (2025-01-30 10:23:16)

feat: add deprecated_api_endpoint error code
This error code is returned when a user tries to access functionality that was removed from the API. It contains a link to the changelog announcement describing the removal.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/hetznercloud/hcloud-go/pull/582?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hetznercloud) Report
Attention: Patch coverage is `85.00000%` with `3 lines` in your changes missing coverage. Please review.
> Project coverage is 67.72%. Comparing base [(`a07df4e`)](https://app.codecov.io/gh/hetznercloud/hcloud-go/commit/a07df4e4da097ab0c9774a359553bbc4903a0d58?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hetznercloud) to head [(`9e59917`)](https://app.codecov.io/gh/hetznercloud/hcloud-go/commit/9e5991737983db51fb05208bca5a0673300c4deb?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hetznercloud).

| [Files with missing lines](https://app.codecov.io/gh/hetznercloud/hcloud-go/pull/582?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hetznercloud) | Patch % | Lines |
|---|---|---|
| [hcloud/schema/error.go](https://app.codecov.io/gh/hetznercloud/hcloud-go/pull/582?src=pr&el=tree&filepath=hcloud%2Fschema%2Ferror.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hetznercloud#diff-aGNsb3VkL3NjaGVtYS9lcnJvci5nbw==) | 50.00% | [2 Missing and 1 partial :warning: ](https://app.codecov.io/gh/hetznercloud/hcloud-go/pull/582?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hetznercloud) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #582      +/-   ##
==========================================
+ Coverage   67.69%   67.72%   +0.03%     
==========================================
  Files          49       49              
  Lines        5003     5017      +14     
==========================================
+ Hits         3387     3398      +11     
- Misses       1196     1198       +2     
- Partials      420      421       +1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/hetznercloud/hcloud-go/pull/582?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hetznercloud).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hetznercloud).


Patch:
diff --git a/hcloud/error.go b/hcloud/error.go
index ca1a44f..a3aa446 100644
--- a/hcloud/error.go
+++ b/hcloud/error.go
@@ -30,6 +30,7 @@ const (
 	ErrorCodeRobotUnavailable      ErrorCode = "robot_unavailable"       // Robot was not available. The caller may retry the operation after a short delay
 	ErrorCodeResourceLocked        ErrorCode = "resource_locked"         // The resource is locked. The caller should contact support
 	ErrorUnsupportedError          ErrorCode = "unsupported_error"       // The given resource does not support this
+	ErrorDeprecatedAPIEndpoint     ErrorCode = "deprecated_api_endpoint" // The request can not be answered because the API functionality was removed
 
 	// Server related error codes.
 
@@ -127,6 +128,11 @@ type ErrorDetailsInvalidInputField struct {
 	Messages []string
 }
 
+// ErrorDetailsDeprecatedAPIEndpoint contains the details of a 'deprecated_api_endpoint' error.
+type ErrorDetailsDeprecatedAPIEndpoint struct {
+	Announcement string
+}
+
 // IsError returns whether err is an API error with one of the given error codes.
 func IsError(err error, code ...ErrorCode) bool {
 	var apiErr Error
diff --git a/hcloud/schema/error.go b/hcloud/schema/error.go
index 86a5455..b69c792 100644
--- a/hcloud/schema/error.go
+++ b/hcloud/schema/error.go
@@ -24,6 +24,13 @@ func (e *Error) UnmarshalJSON(data []byte) (err error) {
 		}
 		alias.Details = details
 	}
+	if e.Code == "deprecated_api_endpoint" && len(e.DetailsRaw) > 0 {
+		details := ErrorDetailsDeprecatedAPIEndpoint{}
+		if err = json.Unmarshal(e.DetailsRaw, &details); err != nil {
+			return
+		}
+		alias.Details = details
+	}
 	return
 }
 
@@ -40,3 +47,9 @@ type ErrorDetailsInvalidInput struct {
 		Messages []string `json:"messages"`
 	} `json:"fields"`
 }
+
+// ErrorDetailsDeprecatedAPIEndpoint defines the schema of the Details field
+// of an error with code 'deprecated_api_endpoint'.
+type ErrorDetailsDeprecatedAPIEndpoint struct {
+	Announcement string `json:"announcement"`
+}
diff --git a/hcloud/schema_gen.go b/hcloud/schema_gen.go
index 4566acf..dabdb64 100644
--- a/hcloud/schema_gen.go
+++ b/hcloud/schema_gen.go
@@ -609,37 +609,48 @@ func intSecondsFromDuration(d time.Duration) int {
 }
 
 func errorDetailsFromSchema(d interface{}) interface{} {
-	if d, ok := d.(schema.ErrorDetailsInvalidInput); ok {
+	switch typed := d.(type) {
+	case schema.ErrorDetailsInvalidInput:
 		details := ErrorDetailsInvalidInput{
-			Fields: make([]ErrorDetailsInvalidInputField, len(d.Fields)),
+			Fields: make([]ErrorDetailsInvalidInputField, len(typed.Fields)),
 		}
-		for i, field := range d.Fields {
+		for i, field := range typed.Fields {
 			details.Fields[i] = ErrorDetailsInvalidInputField{
 				Name:     field.Name,
 				Messages: field.Messages,
 			}
 		}
 		return details
+
+	case schema.ErrorDetailsDeprecatedAPIEndpoint:
+		return ErrorDetailsDeprecatedAPIEndpoint{
+			Announcement: typed.Announcement,
+		}
 	}
 	return nil
 }
 
 func schemaFromErrorDetails(d interface{}) interface{} {
-	if d, ok := d.(ErrorDetailsInvalidInput); ok {
+	switch typed := d.(type) {
+	case ErrorDetailsInvalidInput:
 		details := schema.ErrorDetailsInvalidInput{
 			Fields: make([]struct {
 				Name     string   `json:"name"`
 				Messages []string `json:"messages"`
-			}, len(d.Fields)),
+			}, len(typed.Fields)),
 		}
-		for i, field := range d.Fields {
+		for i, field := range typed.Fields {
 			details.Fields[i] = struct {
 				Name     string   `json:"name"`
 				Messages []string `json:"messages"`
 			}{Name: field.Name, Messages: field.Messages}
 		}
 		return details
+
+	case ErrorDetailsDeprecatedAPIEndpoint:
+		return schema.ErrorDetailsDeprecatedAPIEndpoint{Announcement: typed.Announcement}
 	}
+
 	return nil
 }
 


Test patch:
diff --git a/hcloud/schema/error_test.go b/hcloud/schema/error_test.go
index 3871df8..8a40209 100644
--- a/hcloud/schema/error_test.go
+++ b/hcloud/schema/error_test.go
@@ -5,7 +5,7 @@ import (
 	"testing"
 )
 
-func TestError(t *testing.T) {
+func TestInvalidInputError(t *testing.T) {
 	t.Run("UnmarshalJSON", func(t *testing.T) {
 		data := []byte(`{
 			"code": "invalid_input",
@@ -52,3 +52,32 @@ func TestError(t *testing.T) {
 		}
 	})
 }
+
+func TestDeprecatedAPIEndpointError(t *testing.T) {
+	t.Run("UnmarshalJSON", func(t *testing.T) {
+		data := []byte(`{
+			"code": "deprecated_api_endpoint",
+			"message": "API functionality was removed",
+			"details": {
+				"announcement": "https://docs.hetzner.cloud/changelog#2023-07-20-foo-endpoint-is-deprecated"
+			}
+		}`)
+
+		e := &Error{}
+		err := json.Unmarshal(data, e)
+		if err != nil {
+			t.Fatalf("unexpected error: %v", err)
+		}
+
+		if e.Details == nil {
+			t.Fatalf("unexpected Details: %v", e.Details)
+		}
+		d, ok := e.Details.(ErrorDetailsDeprecatedAPIEndpoint)
+		if !ok {
+			t.Fatalf("unexpected Details type (should be ErrorDetailsDeprecatedAPIEndpoint): %v", e.Details)
+		}
+		if d.Announcement != "https://docs.hetzner.cloud/changelog#2023-07-20-foo-endpoint-is-deprecated" {
+			t.Fatalf("unexpected Details.Announcement: %v", d.Announcement)
+		}
+	})
+}
diff --git a/hcloud/schema_test.go b/hcloud/schema_test.go
index 3bec207..f601a8c 100644
--- a/hcloud/schema_test.go
+++ b/hcloud/schema_test.go
@@ -703,6 +703,11 @@ func TestErrorSchema(t *testing.T) {
 			"message": "invalid input",
 			"details": {"fields":[{"name":"broken_field","messages":["is required"]}]}
 		}`,
+		"deprecated_api_endpoint": `{
+			"code": "deprecated_api_endpoint",
+			"message": "API functionality was removed",
+			"details": {"announcement":"https://docs.hetzner.cloud/changelog#2023-07-20-foo-endpoint-is-deprecated"}
+		}`,
 	}
 
 	for name, data := range testCases {