Add typed StatusError for non-429 API failures #1
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "typed-status-error"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
responseErrorreturned a formatted string for every non-429 failure, so callers could only classify a failure by matching English prose. The quad download path detects an unlicensed mosaic withstrings.Contains(err.Error(), "Insufficient Privileges"), which misses the body the vendor documents forGET /mosaics/{id}/quads/{quad}/full:FirstMessageonly understood the general/field envelope, so the flat{"message": "..."}document was embedded as raw JSON and never matched.Change
responseErrornow returns*StatusError{StatusCode, Method, Path, Body}for every non-429 status; the 429*RateLimitErrorbranch stays first.StatusCode(err) (int, bool), errors.As-based, so callers can switch on 401/403/404 instead of on prose. A*RateLimitErroris deliberately not a*StatusError.StatusError.Error()keeps the historicalMETHOD path: status N: msgtext (production logs depend on it) by re-parsing Body and falling back to the raw body when nothing parses.APIErroradditionally decodes a top-level{"message": "..."}document;FirstMessage()precedence is general, then field, then top-level message. The flat body therefore renders asstatus 401: Insufficient Privilegesinstead of embedding the raw JSON.Tests
Over the real
doRedirectpath against an httptest TLS server:Body, and the exactError()string.{"general":[{"message":"Internal Server Error"}]}.*RateLimitError, status 429, and RetryAfter (42s honoured; 1s default with noretry-in).*RateLimitErroryieldok=false; a wrapped*StatusErrorstill unwraps.{"message":"Insufficient Privileges"}at 401 rendersGET basemaps/v1/mosaics/m/quads/q/full: status 401: Insufficient Privileges.status 401: {"message":"Insufficient Privileges"}and no*StatusErrorwas returned.Reviewer note (pre-existing, not introduced here)
go test ./...reports setup failures forcmd/planetandexample/items-and-assetsbecause they import the module pathsource.gleipnir.technology/Gleipnir/planet, which this module does not provide. This reproduces on a pristine checkout ofmain(verified fromgit archive HEAD), so the module root package is the meaningful target:go test .is green.responseError returned a formatted string for every non-429 failure, so callers could only classify a failure by matching English prose. The quad download path detects an unlicensed mosaic by looking for "Insufficient Privileges" in the error text, which misses the body the vendor documents for GET /mosaics/{id}/quads/{quad}/full: FirstMessage only understood the general/field envelope, so the flat {"message": "..."} document was embedded in the error as raw JSON and never matched. responseError now returns *StatusError carrying the status, method, path and raw body, and StatusCode reports the status of a (possibly wrapped) *StatusError so callers can switch on 401/403/404 instead of on prose. Error() keeps the historical "METHOD path: status N: msg" text that production logs depend on by re-parsing the body, and APIError also decodes a top-level message (general, then field, then message), so the flat body now renders as "status 401: Insufficient Privileges". 429 still returns *RateLimitError with its parsed Retry-After, and is deliberately not a *StatusError.