diff --git a/templates/go/api.mustache b/templates/go/api.mustache index c5b644b7dc..0804ec8718 100644 --- a/templates/go/api.mustache +++ b/templates/go/api.mustache @@ -459,19 +459,7 @@ func (c *APIClient) {{nickname}}({{#hasParams}}r {{#structPrefix}}{{&classname}} } if res.StatusCode >= 300 { - newErr := &APIError{ - Message: string(resBody), - Status: res.StatusCode, - } - - var v ErrorBase - err = c.decode(&v, resBody) - if err != nil { - newErr.Message = err.Error() - return {{#returnType}}returnValue, {{/returnType}}newErr - } - - return {{#returnType}}returnValue, {{/returnType}}newErr + return {{#returnType}}returnValue, {{/returnType}}c.decodeError(res, resBody) } {{#returnType}} diff --git a/templates/go/client.mustache b/templates/go/client.mustache index 577fc7491d..6a202a78af 100644 --- a/templates/go/client.mustache +++ b/templates/go/client.mustache @@ -234,7 +234,7 @@ func (c *APIClient) decode(v any, b []byte) error { return fmt.Errorf("failed to unmarshal one of in response body: %w", err) } } else { - return errors.New("Unknown type with GetActualInstance but no unmarshalObj.UnmarshalJSON defined") + return errors.New("unknown type with GetActualInstance but no unmarshalObj.UnmarshalJSON defined") } } else if err := json.Unmarshal(b, v); err != nil { // simple model return fmt.Errorf("failed to unmarshal response body: %w", err) @@ -243,6 +243,33 @@ func (c *APIClient) decode(v any, b []byte) error { return nil } +func (c *APIClient) decodeError(res *http.Response, body []byte) error { + apiErr := &APIError{ + Message: string(body), // default to the full body if we cannot guess the type of the error. + Status: res.StatusCode, + } + + if strings.Contains(res.Header.Get("Content-Type"), "application/json") { + var errBase ErrorBase + + err := c.decode(&errBase, body) + if err != nil { + apiErr.Message = err.Error() + + return apiErr + } + if errBase.Message != nil { + apiErr.Message = *errBase.Message + } + + apiErr.AdditionalProperties = errBase.AdditionalProperties + } else if strings.Contains(res.Header.Get("Content-Type"), "text/html") { + apiErr.Message = http.StatusText(res.StatusCode) + } + + return apiErr +} + // Prevent trying to import "fmt" func reportError(format string, a ...any) error { return fmt.Errorf(format, a...) @@ -299,7 +326,7 @@ func setBody(body any, c compression.Compression) (*bytes.Buffer, error) { } if bodyBuf.Len() == 0 { - return nil, errors.New("Invalid body type, or empty body") + return nil, errors.New("invalid body type, or empty body") } return bodyBuf, nil } @@ -307,8 +334,50 @@ func setBody(body any, c compression.Compression) (*bytes.Buffer, error) { type APIError struct { Message string Status int + AdditionalProperties map[string]any } func (e APIError) Error() string { return fmt.Sprintf("API error [%d] %s", e.Status, e.Message) } + +func (o APIError) MarshalJSON() ([]byte, error) { + toSerialize := map[string]any{ + "message": o.Message, + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + serialized, err := json.Marshal(toSerialize) + if err != nil { + return nil, fmt.Errorf("failed to marshal APIError: %w", err) + } + + return serialized, nil +} + +func (o *APIError) UnmarshalJSON(bytes []byte) error { + type _APIError APIError + apiErr := _APIError{} + + err := json.Unmarshal(bytes, &apiErr) + if err != nil { + return fmt.Errorf("failed to unmarshal APIError: %w", err) + } + + *o = APIError(apiErr) + + additionalProperties := make(map[string]any) + + err = json.Unmarshal(bytes, &additionalProperties) + if err != nil { + return fmt.Errorf("failed to unmarshal additionalProperties in APIError: %w", err) + } + + delete(additionalProperties, "message") + o.AdditionalProperties = additionalProperties + + return nil +} diff --git a/tests/CTS/client/search/indexExists.json b/tests/CTS/client/search/indexExists.json index ab609f9d6f..fab0be4263 100644 --- a/tests/CTS/client/search/indexExists.json +++ b/tests/CTS/client/search/indexExists.json @@ -82,7 +82,7 @@ "expected": { "error": { "csharp": "{\\\"message\\\":\\\"Invalid API key\\\"}", - "go": "API error [403] {\\\"message\\\":\\\"Invalid API key\\\"}", + "go": "API error [403] Invalid API key", "java": "Status Code: 403 - {\\\"message\\\":\\\"Invalid API key\\\"}", "javascript": "Invalid API key", "kotlin": "Client request(GET http://%localhost%:6681/1/indexes/indexExistsERROR/settings) invalid: 403 Forbidden. Text: \\\"{\\\"message\\\":\\\"Invalid API key\\\"}\\\"", diff --git a/tests/CTS/client/search/saveObjects.json b/tests/CTS/client/search/saveObjects.json index b461f75cd5..2c594f6424 100644 --- a/tests/CTS/client/search/saveObjects.json +++ b/tests/CTS/client/search/saveObjects.json @@ -81,7 +81,7 @@ "expected": { "error": { "csharp": "{\\\"message\\\":\\\"Invalid Application-ID or API key\\\",\\\"status\\\":403}", - "go": "API error [403] {\\\"message\\\":\\\"Invalid Application-ID or API key\\\",\\\"status\\\":403}", + "go": "API error [403] Invalid Application-ID or API key", "java": "Status Code: 403 - {\\\"message\\\":\\\"Invalid Application-ID or API key\\\",\\\"status\\\":403}", "javascript": "Invalid Application-ID or API key", "kotlin": "Client request(POST http://%localhost%:6680/1/indexes/cts_e2e_saveObjects_kotlin/batch) invalid: 403 Forbidden. Text: \\\"{\\\"message\\\":\\\"Invalid Application-ID or API key\\\",\\\"status\\\":403}\\\"",