From aa3b05a35f9be234c34b104954addcf911167a17 Mon Sep 17 00:00:00 2001 From: Jason Hall Date: Thu, 9 Oct 2014 16:08:02 -0400 Subject: [PATCH] return responses that don't include Status and ErrorMessage, instead return an APIError if the API response is not 'OK' --- directions.go | 57 ++++++++++++++++++++++++++++----------------------- distance.go | 17 ++++++++++----- elevation.go | 51 ++++++++++++++++++++++++++++----------------- geocode.go | 57 +++++++++++++++++++++++++++++---------------------- maps.go | 14 +++++++++++++ timezone.go | 19 +++++++++++------ 6 files changed, 134 insertions(+), 81 deletions(-) diff --git a/directions.go b/directions.go index 31a6506..71d9916 100644 --- a/directions.go +++ b/directions.go @@ -32,12 +32,15 @@ const ( // TODO: via_waypoint not documented -func (c Client) Directions(orig, dest Location, opts *DirectionsOpts) (*DirectionsResponse, error) { - var d DirectionsResponse +func (c Client) Directions(orig, dest Location, opts *DirectionsOpts) ([]Route, error) { + var d directionsResponse if err := c.doDecode(baseURL+directions(orig, dest, opts), &d); err != nil { return nil, err } - return &d, nil + if d.Status != StatusOK { + return nil, APIError{d.Status, d.ErrorMessage} + } + return d.Routes, nil } func directions(orig, dest Location, opts *DirectionsOpts) string { @@ -89,29 +92,31 @@ func (do *DirectionsOpts) update(p url.Values) { } } -type DirectionsResponse struct { - Status string `json:"status"` - ErrorMessage string `json:"error_message"` - Routes []struct { - Summary string `json:"summary"` - Legs []struct { - Duration *Duration `json:"duration"` - DurationInTraffic *Duration `json:"duration_in_traffic"` - Distance *Distance `json:"distance"` - ArrivalTime Time `json:"arrival_time"` - DepartureTime Time `json:"departure_time"` - StartLocation *LatLng `json:"start_location"` - EndLocation *LatLng `json:"end_location"` - StartAddress string `json:"start_address"` - EndAddress string `json:"end_address"` - Steps []Step `json:"steps"` - } `json:"legs"` - Bounds Bounds `json:"bounds"` - Copyrights string `json:"copyrights"` - OverviewPolyline Polyline `json:"overview_polyline"` - Warnings []string `json:"warnings"` - WaypointOrder []int `json:"waypoint_order"` - } `json:"routes"` +type directionsResponse struct { + Status string `json:"status"` + ErrorMessage string `json:"error_message"` + Routes []Route `json:"routes"` +} + +type Route struct { + Summary string `json:"summary"` + Legs []struct { + Duration *Duration `json:"duration"` + DurationInTraffic *Duration `json:"duration_in_traffic"` + Distance *Distance `json:"distance"` + ArrivalTime Time `json:"arrival_time"` + DepartureTime Time `json:"departure_time"` + StartLocation *LatLng `json:"start_location"` + EndLocation *LatLng `json:"end_location"` + StartAddress string `json:"start_address"` + EndAddress string `json:"end_address"` + Steps []Step `json:"steps"` + } `json:"legs"` + Bounds Bounds `json:"bounds"` + Copyrights string `json:"copyrights"` + OverviewPolyline Polyline `json:"overview_polyline"` + Warnings []string `json:"warnings"` + WaypointOrder []int `json:"waypoint_order"` } type Time struct { diff --git a/distance.go b/distance.go index c9253d0..a7dc626 100644 --- a/distance.go +++ b/distance.go @@ -6,12 +6,15 @@ import ( "time" ) -func (c Client) DistanceMatrix(orig, dest []Location, opts *DistanceMatrixOpts) (*DistanceMatrixResponse, error) { - var d DistanceMatrixResponse +func (c Client) DistanceMatrix(orig, dest []Location, opts *DistanceMatrixOpts) (*DistanceMatrixResult, error) { + var d distanceResponse if err := c.doDecode(baseURL+distancematrix(orig, dest, opts), &d); err != nil { return nil, err } - return &d, nil + if d.Status != StatusOK { + return nil, APIError{d.Status, ""} + } + return &d.DistanceMatrixResult, nil } func distancematrix(orig, dest []Location, opts *DistanceMatrixOpts) string { @@ -49,8 +52,12 @@ func (o *DistanceMatrixOpts) update(p url.Values) { } } -type DistanceMatrixResponse struct { - Status string `json:"status"` +type distanceResponse struct { + Status string `json:"status"` + DistanceMatrixResult +} + +type DistanceMatrixResult struct { OriginAddresses []string `json:"origin_addresses"` DestinationAddresses []string `json:"destination_addresses"` Rows []struct { diff --git a/elevation.go b/elevation.go index 79a2fb7..834c46c 100644 --- a/elevation.go +++ b/elevation.go @@ -5,36 +5,48 @@ import ( "net/url" ) -func (c Client) Elevation(ll []LatLng) (*ElevationResponse, error) { - var r ElevationResponse +func (c Client) Elevation(ll []LatLng) ([]ElevationResult, error) { + var r elevationResponse if err := c.doDecode(baseURL+elevation(ll), &r); err != nil { return nil, err } - return &r, nil + if r.Status != StatusOK { + return nil, APIError{r.Status, ""} + } + return r.Results, nil } -func (c Client) ElevationPolyline(p string) (*ElevationResponse, error) { - var r ElevationResponse +func (c Client) ElevationPolyline(p string) ([]ElevationResult, error) { + var r elevationResponse if err := c.doDecode(baseURL+elevationpoly(p), &r); err != nil { return nil, err } - return &r, nil + if r.Status != StatusOK { + return nil, APIError{r.Status, ""} + } + return r.Results, nil } -func (c Client) ElevationPath(ll []LatLng, samples int) (*ElevationResponse, error) { - var r ElevationResponse +func (c Client) ElevationPath(ll []LatLng, samples int) ([]ElevationResult, error) { + var r elevationResponse if err := c.doDecode(baseURL+elevationpath(ll, samples), &r); err != nil { return nil, err } - return &r, nil + if r.Status != StatusOK { + return nil, APIError{r.Status, ""} + } + return r.Results, nil } -func (c Client) ElevationPathPoly(p string, samples int) (*ElevationResponse, error) { - var r ElevationResponse +func (c Client) ElevationPathPoly(p string, samples int) ([]ElevationResult, error) { + var r elevationResponse if err := c.doDecode(baseURL+elevationpathpoly(p, samples), &r); err != nil { return nil, err } - return &r, nil + if r.Status != StatusOK { + return nil, APIError{r.Status, ""} + } + return r.Results, nil } func elevation(ll []LatLng) string { @@ -63,11 +75,12 @@ func elevationpathpoly(poly string, s int) string { return "elevation/json?" + p.Encode() } -type ElevationResponse struct { - Results []struct { - Elevation float64 `json:"elevation"` - Location LatLng `json:"location"` - Resolution float64 `json:"resolution"` - } `json:"results"` - Status string `json:"status"` +type elevationResponse struct { + Results []ElevationResult `json:"results"` + Status string `json:"status"` +} +type ElevationResult struct { + Elevation float64 `json:"elevation"` + Location LatLng `json:"location"` + Resolution float64 `json:"resolution"` } diff --git a/geocode.go b/geocode.go index ac2bf4d..4613d2b 100644 --- a/geocode.go +++ b/geocode.go @@ -21,12 +21,15 @@ const ( // TODO: enums for https://developers.google.com/maps/documentation/geocoding/#Types ) -func (c Client) Geocode(opts *GeocodeOpts) (*GeocodeResponse, error) { - var r GeocodeResponse +func (c Client) Geocode(opts *GeocodeOpts) ([]GeocodeResult, error) { + var r geocodeResponse if err := c.doDecode(baseURL+geocode(opts), &r); err != nil { return nil, err } - return &r, nil + if r.Status != StatusOK { + return nil, APIError{r.Status, ""} + } + return r.Results, nil } func geocode(opts *GeocodeOpts) string { @@ -79,33 +82,37 @@ func (g *GeocodeOpts) update(p url.Values) { } } -type GeocodeResponse struct { - Results []struct { - AddressComponents []struct { - LongName string `json:"long_name"` - ShortName string `json:"short_name"` - Types []string `json:"types"` - } `json:"address_components"` - PostcodeLocalities []string `json:"postcode_localities"` - FormattedAddress string `json:"formatted_address"` - Geometry struct { - Location LatLng `json:"location"` - LocationType string `json:"location_type"` - Viewport Bounds `json:"viewport"` - Bounds Bounds `json:"bounds"` - } `json:"geometry"` - Types []string `json:"types"` - PartialMatch string `json:"partial_match"` - } `json:"results"` - Status string `json:"status"` +type geocodeResponse struct { + Results []GeocodeResult `json:"results"` + Status string `json:"status"` +} +type GeocodeResult struct { + AddressComponents []struct { + LongName string `json:"long_name"` + ShortName string `json:"short_name"` + Types []string `json:"types"` + } `json:"address_components"` + PostcodeLocalities []string `json:"postcode_localities"` + FormattedAddress string `json:"formatted_address"` + Geometry struct { + Location LatLng `json:"location"` + LocationType string `json:"location_type"` + Viewport Bounds `json:"viewport"` + Bounds Bounds `json:"bounds"` + } `json:"geometry"` + Types []string `json:"types"` + PartialMatch string `json:"partial_match"` } -func (c Client) ReverseGeocode(ll LatLng, opts *ReverseGeocodeOpts) (*GeocodeResponse, error) { - var r GeocodeResponse +func (c Client) ReverseGeocode(ll LatLng, opts *ReverseGeocodeOpts) ([]GeocodeResult, error) { + var r geocodeResponse if err := c.doDecode(baseURL+reversegeocode(ll, opts), &r); err != nil { return nil, err } - return &r, nil + if r.Status != StatusOK { + return nil, APIError{r.Status, ""} + } + return r.Results, nil } type ReverseGeocodeOpts struct { diff --git a/maps.go b/maps.go index fe89ff9..8484847 100644 --- a/maps.go +++ b/maps.go @@ -57,6 +57,7 @@ func (c Client) doDecode(url string, r interface{}) error { return nil } +// HTTPError indicates an error communicating with the API, and includes the HTTP response returned from the server. type HTTPError struct { Response *http.Response } @@ -65,6 +66,19 @@ func (e HTTPError) Error() string { return fmt.Sprintf("http error %d", e.Response.StatusCode) } +// APIError indicates a failure response from the API server, even though a successful HTTP response was returned. +// Its Status and Message fields can be consulted for more information about the specific error conditions. +type APIError struct { + Status, Message string +} + +func (e APIError) Error() string { + if e.Message != "" { + return fmt.Sprintf("API error %q: %s", e.Status, e.Message) + } + return fmt.Sprintf("API Error %q", e.Status) +} + type Location interface { Location() string } diff --git a/timezone.go b/timezone.go index 6ccde42..22e8a89 100644 --- a/timezone.go +++ b/timezone.go @@ -6,12 +6,15 @@ import ( "time" ) -func (c Client) TimeZone(ll LatLng, t time.Time, opts *TimeZoneOpts) (*TimeZoneResponse, error) { - var r TimeZoneResponse +func (c Client) TimeZone(ll LatLng, t time.Time, opts *TimeZoneOpts) (*TimeZoneResult, error) { + var r timeZoneResponse if err := c.doDecode(baseURL+timezone(ll, t, opts), &r); err != nil { return nil, err } - return &r, nil + if r.Status != StatusOK { + return nil, APIError{r.Status, r.ErrorMessage} + } + return &r.TimeZoneResult, nil } func timezone(ll LatLng, t time.Time, opts *TimeZoneOpts) string { @@ -35,19 +38,23 @@ func (t *TimeZoneOpts) update(p url.Values) { } } -type TimeZoneResponse struct { +type timeZoneResponse struct { Status string `json:"status"` ErrorMessage string `json:"error_message"` + TimeZoneResult +} + +type TimeZoneResult struct { DSTOffset int64 `json:"dstOffset"` // secs offset for DST RawOffset int64 `json:"rawOffset"` TimeZoneID string `json:"timeZoneId"` TimeZoneName string `json:"timeZoneName"` } -func (r TimeZoneResponse) DSTOffsetDuration() time.Duration { +func (r TimeZoneResult) DSTOffsetDuration() time.Duration { return time.Duration(r.DSTOffset) * time.Second } -func (r TimeZoneResponse) RawOffsetDuration() time.Duration { +func (r TimeZoneResult) RawOffsetDuration() time.Duration { return time.Duration(r.RawOffset) * time.Second }