|
4 | 4 | "encoding/json" |
5 | 5 | "fmt" |
6 | 6 | "net/http" |
| 7 | + "sort" |
| 8 | + "strings" |
7 | 9 | ) |
8 | 10 |
|
9 | 11 | // APIError is a structured error returned by the CreateOS API. |
@@ -31,17 +33,40 @@ func (e *APIError) Hint() string { |
31 | 33 | } |
32 | 34 |
|
33 | 35 | // ParseAPIError extracts a human-readable message from an API error response body. |
| 36 | +// |
| 37 | +// JSend "fail" bodies can shape `data` three different ways: |
| 38 | +// |
| 39 | +// "data": "shape is required" // plain string |
| 40 | +// "data": {"shape": "shape \"x\" not allowed..."} // field-error object |
| 41 | +// "data": {"auth": "invalid api key"} // single-field gate |
| 42 | +// |
| 43 | +// We try each in order and join field-error values into one human line so |
| 44 | +// the user actually sees what's wrong instead of "request failed with status 403". |
34 | 45 | func ParseAPIError(statusCode int, body []byte) *APIError { |
35 | 46 | var envelope struct { |
36 | 47 | Data json.RawMessage `json:"data"` |
37 | 48 | } |
38 | 49 | msg := "" |
39 | | - if err := json.Unmarshal(body, &envelope); err == nil { |
40 | | - // data may be a plain string or an object |
| 50 | + if err := json.Unmarshal(body, &envelope); err == nil && len(envelope.Data) > 0 { |
| 51 | + // 1. plain string |
41 | 52 | var s string |
42 | | - if err := json.Unmarshal(envelope.Data, &s); err == nil { |
| 53 | + if err := json.Unmarshal(envelope.Data, &s); err == nil && s != "" { |
43 | 54 | msg = s |
44 | 55 | } |
| 56 | + // 2. field-error map { "field": "msg", ... } |
| 57 | + if msg == "" { |
| 58 | + var fields map[string]string |
| 59 | + if err := json.Unmarshal(envelope.Data, &fields); err == nil && len(fields) > 0 { |
| 60 | + parts := make([]string, 0, len(fields)) |
| 61 | + for _, v := range fields { |
| 62 | + if v != "" { |
| 63 | + parts = append(parts, v) |
| 64 | + } |
| 65 | + } |
| 66 | + sort.Strings(parts) |
| 67 | + msg = strings.Join(parts, "; ") |
| 68 | + } |
| 69 | + } |
45 | 70 | } |
46 | 71 | if msg == "" { |
47 | 72 | msg = fmt.Sprintf("request failed with status %d", statusCode) |
|
0 commit comments