Skip to content

Commit

Permalink
Support intl autocomplete v2
Browse files Browse the repository at this point in the history
  • Loading branch information
XanSmarty committed Nov 2, 2023
1 parent 7c5a522 commit c240dc9
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 135 deletions.
6 changes: 5 additions & 1 deletion examples/international-autocomplete-api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ func main() {
log.Fatal("Error sending batch:", err)
}

fmt.Printf("Results for input: [%s]\n", lookup.Search)
if len(lookup.Search) > 0 {
fmt.Printf("Results for input: [%s]\n", lookup.Search)
} else {
fmt.Printf("Results for input: [%s]\n", lookup.AddressID)
}
for s, candidate := range lookup.Result.Candidates {
fmt.Printf("#%d: %#v\n", s, candidate)
}
Expand Down
16 changes: 9 additions & 7 deletions international-autocomplete-api/candidate.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package international_autocomplete_api

type Candidate struct {
Street string `json:"street"`
Locality string `json:"locality"`
AdministrativeArea string `json:"administrative_area"`
SuperAdministrativeArea string `json:"super_administrative_area"`
SubAdministrativeArea string `json:"sub_administrative_area"`
PostalCode string `json:"postal_code"`
CountryIso3 string `json:"country_iso3"`
Street string `json:"street"`
Locality string `json:"locality"`
AdministrativeArea string `json:"administrative_area"`
PostalCode string `json:"postal_code"`
CountryIso3 string `json:"country_iso3"`

Entries int `json:"entries"`
AddressText string `json:"address_text"`
AddressID string `json:"address_id"`
}
9 changes: 7 additions & 2 deletions international-autocomplete-api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func (c *Client) SendLookup(lookup *Lookup) error {
}

func (c *Client) SendLookupWithContext(ctx context.Context, lookup *Lookup) error {
if lookup == nil || len(lookup.Search) == 0 {
if lookup == nil || len(lookup.Country) == 0 || (len(lookup.Search) == 0 && len(lookup.AddressID) == 0) {
return nil
}

Expand All @@ -45,11 +45,16 @@ func deserializeResponse(response []byte, lookup *Lookup) error {
}

func buildRequest(lookup *Lookup) *http.Request {
request, _ := http.NewRequest("GET", suggestURL, nil) // We control the method and the URL. This is safe.
var addressID = ""
if len(lookup.AddressID) > 0 {
addressID = "/" + lookup.AddressID
}
request, _ := http.NewRequest("GET", suggestURL+addressID, nil) // We control the method and the URL. This is safe.
query := request.URL.Query()
lookup.populate(query)
request.URL.RawQuery = query.Encode()
return request
}

// TODO support /lookup and /v2/lookup
const suggestURL = "/lookup" // Remaining parts will be completed later by the sdk.BaseURLClient.
61 changes: 35 additions & 26 deletions international-autocomplete-api/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,20 @@ func (f *ClientFixture) TestAddressLookupSerializedAndSentWithContext__ResponseS
"street": "1",
"locality": "2",
"administrative_area": "3",
"super_administrative_area": "4",
"sub_administrative_area": "5",
"postal_code": "6",
"country_iso3": "7"
"postal_code": "4",
"country_iso3": "5"
},
{
"street": "8",
"locality": "9",
"administrative_area": "10",
"super_administrative_area": "11",
"sub_administrative_area": "12",
"postal_code": "13",
"country_iso3": "14"
"street": "6",
"locality": "7",
"administrative_area": "8",
"postal_code": "9",
"country_iso3": "10"
}
]
}`
f.input.Search = "42"
f.input.Country = "FRA"

ctx := context.WithValue(context.Background(), "key", "value")
err := f.client.SendLookupWithContext(ctx, f.input)
Expand All @@ -62,27 +59,23 @@ func (f *ClientFixture) TestAddressLookupSerializedAndSentWithContext__ResponseS
f.So(f.sender.request.Method, should.Equal, "GET")
f.So(f.sender.request.URL.Path, should.Equal, suggestURL)
f.So(f.sender.request.URL.Query().Get("search"), should.Equal, "42")
f.So(f.sender.request.URL.String(), should.Equal, suggestURL+"?distance=5&max_results=5&search=42")
f.So(f.sender.request.URL.String(), should.Equal, suggestURL+"?country=FRA&max_results=5&search=42")
f.So(f.sender.request.Context(), should.Resemble, ctx)

f.So(f.input.Result, should.Resemble, &Result{Candidates: []*Candidate{
{
Street: "1",
Locality: "2",
AdministrativeArea: "3",
SuperAdministrativeArea: "4",
SubAdministrativeArea: "5",
PostalCode: "6",
CountryIso3: "7",
Street: "1",
Locality: "2",
AdministrativeArea: "3",
PostalCode: "4",
CountryIso3: "5",
},
{
Street: "8",
Locality: "9",
AdministrativeArea: "10",
SuperAdministrativeArea: "11",
SubAdministrativeArea: "12",
PostalCode: "13",
CountryIso3: "14",
Street: "6",
Locality: "7",
AdministrativeArea: "8",
PostalCode: "9",
CountryIso3: "10",
},
}})
}
Expand All @@ -106,6 +99,7 @@ func (f *ClientFixture) TestSenderErrorPreventsDeserialization() {
{"text": "3"}
]}` // would be deserialized if not for the err (above)
f.input.Search = "HI"
f.input.Country = "FRA"

err := f.client.SendLookup(f.input)

Expand All @@ -116,13 +110,28 @@ func (f *ClientFixture) TestSenderErrorPreventsDeserialization() {
func (f *ClientFixture) TestDeserializationErrorPreventsDeserialization() {
f.sender.response = `I can't haz JSON`
f.input.Search = "HI"
f.input.Country = "FRA"

err := f.client.SendLookup(f.input)

f.So(err, should.NotBeNil)
f.So(f.input.Result, should.BeNil)
}

func (f *ClientFixture) TestAddressIDAppendsToURL() {
f.input.Country = "FRA"
f.input.AddressID = "thisisid"

err := f.client.SendLookup(f.input)

f.So(err, should.NotBeNil)

f.So(f.sender.request, should.NotBeNil)
f.So(f.sender.request.Method, should.Equal, "GET")
f.So(f.sender.request.URL.Path, should.Equal, suggestURL+"/thisisid")
f.So(f.sender.request.URL.String(), should.Equal, suggestURL+"/thisisid?country=FRA&max_results=5")
}

//////////////////////////////////////////////////////////////////

type FakeSender struct {
Expand Down
63 changes: 7 additions & 56 deletions international-autocomplete-api/lookup.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package international_autocomplete_api

import (
"math"
"net/url"
"strconv"
)
Expand All @@ -12,30 +11,21 @@ const (
)

type Lookup struct {
Country string
Search string
MaxResults int
Distance int
Geolocation InternationalGeolocateType
AdministrativeArea string
Locality string
PostalCode string
Latitude float64
Longitude float64
Result *Result
Country string
Search string
AddressID string
MaxResults int
Locality string
PostalCode string
Result *Result
}

func (l Lookup) populate(query url.Values) {
l.populateCountry(query)
l.populateSearch(query)
l.populateMaxResults(query)
l.populateDistance(query)
l.populateGeolocation(query)
l.populateAdministrativeArea(query)
l.populateLocality(query)
l.populatePostalCode(query)
l.populateLatitude(query)
l.populateLongitude(query)
}
func (l Lookup) populateCountry(query url.Values) {
if len(l.Country) > 0 {
Expand All @@ -54,25 +44,6 @@ func (l Lookup) populateMaxResults(query url.Values) {
}
query.Set("max_results", strconv.Itoa(maxResults))
}
func (l Lookup) populateDistance(query url.Values) {
distance := l.Distance
if distance < 1 {
distance = distanceDefault
}
query.Set("distance", strconv.Itoa(distance))
}
func (l Lookup) populateGeolocation(query url.Values) {
if l.Geolocation != None {
query.Set("geolocation", string(l.Geolocation))
} else {
query.Del("geolocation")
}
}
func (l Lookup) populateAdministrativeArea(query url.Values) {
if len(l.AdministrativeArea) > 0 {
query.Set("include_only_administrative_area", l.AdministrativeArea)
}
}
func (l Lookup) populateLocality(query url.Values) {
if len(l.Locality) > 0 {
query.Set("include_only_locality", l.Locality)
Expand All @@ -83,23 +54,3 @@ func (l Lookup) populatePostalCode(query url.Values) {
query.Set("include_only_postal_code", l.PostalCode)
}
}
func (l Lookup) populateLatitude(query url.Values) {
if math.Floor(l.Latitude) != 0 {
query.Set("latitude", strconv.FormatFloat(l.Latitude, 'f', 8, 64))
}
}
func (l Lookup) populateLongitude(query url.Values) {
if math.Floor(l.Longitude) != 0 {
query.Set("longitude", strconv.FormatFloat(l.Longitude, 'f', 8, 64))
}
}

type InternationalGeolocateType string

const (
AdminArea = InternationalGeolocateType("adminarea")
Locality = InternationalGeolocateType("locality")
PostalCode = InternationalGeolocateType("postalcode")
Geocodes = InternationalGeolocateType("geocodes")
None = InternationalGeolocateType("")
)
47 changes: 5 additions & 42 deletions international-autocomplete-api/lookup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,23 @@ func (f *LookupFixture) populate() {
func (f *LookupFixture) TestDefaults() {
f.populate()

f.So(f.query, should.HaveLength, 2)
f.So(f.query, should.HaveLength, 1)
f.So(f.query.Get("max_results"), should.Equal, "5")
f.So(f.query.Get("distance"), should.Equal, "5")
}
func (f *LookupFixture) TestCountry() {
f.lookup.Country = "Hello, World!"

f.populate()

f.So(f.query, should.HaveLength, 3)
f.So(f.query, should.HaveLength, 2)
f.So(f.query.Get("country"), should.Equal, "Hello, World!")
}
func (f *LookupFixture) TestSearch() {
f.lookup.Search = "Hello, World!"

f.populate()

f.So(f.query, should.HaveLength, 3)
f.So(f.query, should.HaveLength, 2)
f.So(f.query.Get("search"), should.Equal, "Hello, World!")
}
func (f *LookupFixture) TestMaxResults() {
Expand All @@ -56,55 +55,19 @@ func (f *LookupFixture) TestMaxResults() {

f.So(f.query.Get("max_results"), should.Equal, "7")
}
func (f *LookupFixture) TestDistance() {
f.lookup.Distance = 3
f.populate()

f.So(f.query.Get("distance"), should.Equal, "3")
}
func (f *LookupFixture) TestGeolocation() {
typeList := []InternationalGeolocateType{AdminArea, Locality, PostalCode, Geocodes, None}
for _, geoLocateType := range typeList {
f.lookup.Geolocation = geoLocateType
f.populate()
f.So(f.query.Get("geolocation"), should.Equal, string(geoLocateType))
}
}
func (f *LookupFixture) TestAdministrativeArea() {
f.lookup.AdministrativeArea = "Hello, World!"

f.populate()

f.So(f.query, should.HaveLength, 3)
f.So(f.query.Get("include_only_administrative_area"), should.Equal, "Hello, World!")
}
func (f *LookupFixture) TestLocality() {
f.lookup.Locality = "Hello, World!"

f.populate()

f.So(f.query, should.HaveLength, 3)
f.So(f.query, should.HaveLength, 2)
f.So(f.query.Get("include_only_locality"), should.Equal, "Hello, World!")
}
func (f *LookupFixture) TestPostalCode() {
f.lookup.PostalCode = "Hello, World!"

f.populate()

f.So(f.query, should.HaveLength, 3)
f.So(f.query, should.HaveLength, 2)
f.So(f.query.Get("include_only_postal_code"), should.Equal, "Hello, World!")
}
func (f *LookupFixture) TestLatitude() {
f.lookup.Latitude = 123.458757987986

f.populate()

f.So(f.query.Get("latitude"), should.Equal, "123.45875799") // Here we only care about 8 digits of accuracy
}
func (f *LookupFixture) TestLongitude() {
f.lookup.Longitude = -134.877532234

f.populate()

f.So(f.query.Get("longitude"), should.Equal, "-134.87753223") // Here we only care about 8 digits of accuracy
}
2 changes: 1 addition & 1 deletion wireup/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ func (b *clientBuilder) buildTransport() *http.Transport {

var (
defaultBaseURL_InternationalStreetAPI = &url.URL{Scheme: "https", Host: "international-street.api.smarty.com"}
defaultBaseURL_InternationalAutocompleteAPI = &url.URL{Scheme: "https", Host: "international-autocomplete.api.smarty.com"}
defaultBaseURL_InternationalAutocompleteAPI = &url.URL{Scheme: "https", Host: "international-autocomplete.api.smarty.com/v2"}
defaultBaseURL_USStreetAPI = &url.URL{Scheme: "https", Host: "us-street.api.smarty.com"}
defaultBaseURL_USZIPCodeAPI = &url.URL{Scheme: "https", Host: "us-zipcode.api.smarty.com"}
defaultBaseURL_USAutocompleteAPI = &url.URL{Scheme: "https", Host: "us-autocomplete.api.smarty.com"}
Expand Down

0 comments on commit c240dc9

Please sign in to comment.