From 6cccc16031c1cc4322c403eda2a73fc23e24c1d7 Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Wed, 25 Feb 2026 02:44:35 +0000 Subject: [PATCH] Add structured error output response --- stadia/bulk.go | 13 +++++++++-- stadia/cmd/structured-geocode/main.go | 2 ++ stadia/response_type.go | 22 +++++++++++++----- stadia/structured_geocode.go | 32 +++++++++++++++++++++------ 4 files changed, 54 insertions(+), 15 deletions(-) diff --git a/stadia/bulk.go b/stadia/bulk.go index 672fb1b7..4c1c303a 100644 --- a/stadia/bulk.go +++ b/stadia/bulk.go @@ -2,6 +2,7 @@ package stadia import ( "fmt" + "io" ) type BulkGeocodeQuery interface { @@ -32,12 +33,13 @@ func (s *StadiaMaps) BulkGeocode(requests []BulkGeocodeQuery) ([]BulkGeocodeResp }) } var results []BulkGeocodeResponseItem - + var api_error Error resp, err := s.client.R(). SetBody(body). SetContentType("application/json"). SetPathParam("urlBase", s.urlBase). SetQueryParam("api_key", s.APIKey). + SetError(&api_error). SetResult(&results). Post("https://{urlBase}/geocoding/v1/search/bulk") @@ -46,7 +48,14 @@ func (s *StadiaMaps) BulkGeocode(requests []BulkGeocodeQuery) ([]BulkGeocodeResp } if !resp.IsSuccess() { - return nil, fmt.Errorf("bulk geocoding request failed with status code: %d", resp.StatusCode()) + if api_error.Error() != "" { + return nil, &api_error + } + content, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("read all failure: %w", err) + } + return nil, fmt.Errorf("bulk geocoding request failed with status code: %d: %s", resp.StatusCode(), content) } return results, nil diff --git a/stadia/cmd/structured-geocode/main.go b/stadia/cmd/structured-geocode/main.go index 43858fb3..bb0f262e 100644 --- a/stadia/cmd/structured-geocode/main.go +++ b/stadia/cmd/structured-geocode/main.go @@ -1,6 +1,8 @@ package main import ( + "context" + "flag" "log" "os" diff --git a/stadia/response_type.go b/stadia/response_type.go index f169b79d..14cc9d46 100644 --- a/stadia/response_type.go +++ b/stadia/response_type.go @@ -1,20 +1,30 @@ package stadia +type Error struct { + ErrorMessage string `json:"error"` + Errors []string `json:"errors"` +} + +func (e *Error) Error() string { + return e.ErrorMessage +} + // GeocodeResponse represents the top-level response from the geocoding API type GeocodeResponse struct { - Geocode GeocodeMeta `json:"geocoding"` - Type string `json:"type"` // Should be "FeatureCollection" - BBox []float64 `json:"bbox"` // [W, S, E, N] - Features []GeocodeFeature `json:"features"` + BBox []float64 `json:"bbox"` // [W, S, E, N] + ErrorMessage string `json:"error"` + Features []GeocodeFeature `json:"features"` + Geocode GeocodeMeta `json:"geocoding"` + Type string `json:"type"` // Should be "FeatureCollection" } // GeocodeMeta contains metadata about the geocoding request type GeocodeMeta struct { Attribution string `json:"attribution"` + Error string `json:"error,omitempty"` // v2 + Errors []string `json:"errors,omitempty"` // v1 Query map[string]interface{} `json:"query,omitempty"` Warnings []string `json:"warnings,omitempty"` - Errors []string `json:"errors,omitempty"` // v1 - Error string `json:"error,omitempty"` // v2 } // GeocodeFeature represents a GeoJSON feature in the response diff --git a/stadia/structured_geocode.go b/stadia/structured_geocode.go index a4965b65..1c831b65 100644 --- a/stadia/structured_geocode.go +++ b/stadia/structured_geocode.go @@ -1,8 +1,9 @@ package stadia import ( + "context" "fmt" - "net/url" + "strings" "github.com/google/go-querystring/query" ) @@ -35,17 +36,20 @@ type StructuredGeocodeRequest struct { Lang *string `url:"lang,omitempty" json:"lang,omitempty"` } -func (s *StadiaMaps) StructuredGeocode(req StructuredGeocodeRequest) (*GeocodeResponse, error) { +func (s *StadiaMaps) StructuredGeocode(ctx context.Context, req StructuredGeocodeRequest) (*GeocodeResponse, error) { // https://docs.stadiamaps.com/geocoding-search-autocomplete/structured-search/ // curl "https://api.stadiamaps.com/geocoding/v1/search/structured?address=P%C3%B5hja%20pst%2027a®ion=Harju&country=EE&api_key=YOUR-API-KEY" var result GeocodeResponse - query, err := req.toQueryParams() + query, err := query.Values(req) if err != nil { return nil, fmt.Errorf("structured geocode query: %w", err) } + //var api_error Error resp, err := s.client.R(). SetQueryParamsFromValues(query). + SetContext(ctx). + SetError(&result). SetResult(&result). SetPathParam("urlBase", s.urlBase). SetQueryParam("api_key", s.APIKey). @@ -55,7 +59,24 @@ func (s *StadiaMaps) StructuredGeocode(req StructuredGeocodeRequest) (*GeocodeRe } if !resp.IsSuccess() { - return nil, fmt.Errorf("structude geocoding status: %w", err) + /* + if api_error.Error() != "" { + return nil, &api_error + } + content, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("read all failure: %w", err) + } + */ + fmt.Printf("geocoding error: %s\n", result.Geocode.Error) + if len(result.Geocode.Errors) > 0 { + joined := strings.Join(result.Geocode.Errors, ", ") + return nil, fmt.Errorf("structured geocoding failure: %d '%s'", resp.StatusCode(), joined) + } else if result.Geocode.Error != "" { + return nil, fmt.Errorf("structured geocoding failure: %d '%s'", resp.StatusCode(), result.Geocode.Error) + } else { + return nil, fmt.Errorf("structured geocoding failure: %d", resp.StatusCode()) + } } return &result, nil } @@ -63,6 +84,3 @@ func (s *StadiaMaps) StructuredGeocode(req StructuredGeocodeRequest) (*GeocodeRe func (sgr StructuredGeocodeRequest) endpoint() string { return "/v1/search/structured" } -func (sgr StructuredGeocodeRequest) toQueryParams() (url.Values, error) { - return query.Values(sgr) -}