From fa05eb55f483432a4e9678f52443317164c4a4d8 Mon Sep 17 00:00:00 2001 From: Matty Evans Date: Thu, 1 Sep 2022 09:51:31 +1000 Subject: [PATCH] feat(timezone): added interface to fetch Timezone info via gmaps api (#6) --- .travis.yml | 3 +- README.md | 61 +++++++++++------- abode.go | 111 ++++++++++++++++++++++++--------- abode_test.go | 169 +++++++++++++++++++++++++++++++++----------------- go.mod | 15 ++++- go.sum | 91 ++++----------------------- 6 files changed, 256 insertions(+), 194 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4f4e0af..b8d732d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,4 @@ sudo: false language: go go: - - 1.16.x - - tip + - 1.19.x diff --git a/README.md b/README.md index c6b602f..539833c 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,11 @@ [![Go Report Card](https://goreportcard.com/badge/github.com/mattevans/abode)](https://goreportcard.com/report/github.com/mattevans/abode) [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/mattevans/abode/blob/master/LICENSE) -Explode one-line address strings using Golang. - -This package uses the [Google Maps API](https://console.developers.google.com/apis/credentials) to geocode the address. -Specifically you will require the [Geocoding API](https://console.developers.google.com/apis/library/geocoding-backend.googleapis.com) -enabled to translate address strings to detailed address objects. - -Don't forget to set your `GOOGLE_MAPS_API_KEY` environment variable. +- Geocode one-line addresses. +- Determine timezone information for a given address. +- This package uses the [Google Maps Web Services](https://developers.google.com/maps/web-services) to geocode the address. +- You will require the [Geocoding API](https://developers.google.com/maps/documentation) enabled, and optionally the [Timezone API] if you wish to also use `Timezone()`. +- Remember to set your `GOOGLE_MAPS_API_KEY` environment variable. Installation ----------------- @@ -21,39 +19,54 @@ Installation Example ------------- -Explode your one-line address... +### Geocode an address: ```go -yourAddress := "193 Rogers Ave, Brooklyn, New York" +addr := "193 Rogers Ave, Brooklyn, New York" -// Explode our one-line address into components. -address, err := abode.Explode(yourAddress) +address, err := abode.ExplodeWithContext(ctx, addr) if err != nil { return err } ``` -Which will give you... +Returns... ```go abode.Address{ - AddressLine1: "193 Rogers Avenue", - AddressLine2: "Brooklyn" - AddressCity: nil, - AddressState: "New York" - AddressCountry: "United States" - AddressZip: "11216" - AddressLat: 40.6706073, - AddressLng: -73.9530182, - FormattedAddress: "193 Rogers Ave, Brooklyn, NY 11216, USA", + AddressLine1: "193 Rogers Avenue", + AddressLine2: "Brooklyn" + AddressCity: nil, + AddressState: "New York" + AddressCountry: "United States" + AddressZip: "11216" + AddressLat: 40.6706073, + AddressLng: -73.9530182, + FormattedAddress: "193 Rogers Ave, Brooklyn, NY 11216, USA", } ``` -Configuration -------------- +### Timezone information for an address: -Each `abode.Address{}` component can be tailored to meet your needs. Simply adjust the mapping of the Google Maps Address Components [here](https://github.com/mattevans/abode/blob/master/component.go#L31). +```go +addr := "193 Rogers Ave, Brooklyn, New York" +address, err := abode.Timezone(ctx, addr) +if err != nil { + return err +} +``` + +Returns... + +```go +abode.Location{ + DstOffset: 0, + RawOffset: -17762, + TimeZoneId: "GMT-04:56:02", + TimeZoneName: "America/New_York" +} +``` Disclaimer ------------- diff --git a/abode.go b/abode.go index b9511d3..a6f4ab5 100644 --- a/abode.go +++ b/abode.go @@ -10,8 +10,10 @@ import ( "googlemaps.github.io/maps" ) -var client *maps.Client -var addressLineDelimiter = "," +var ( + client *maps.Client + addressLineDelimiter = "," +) // Address represents a response Address from abode. type Address struct { @@ -27,47 +29,62 @@ type Address struct { FormattedAddress *string `json:"formatted_address"` } -// initClient will initalize a Google Maps API client. -func initClient() error { - var err error - key := os.Getenv("GOOGLE_MAPS_API_KEY") +type Location struct { + DstOffset int `json:"dst_offset"` + RawOffset int `json:"raw_offset"` + TimeZoneId string `json:"time_zone_id"` + TimeZoneName string `json:"time_zone_name"` +} + +// client will initialize a Google Maps API client. +func mapsClient() error { + var ( + err error + key = os.Getenv("GOOGLE_MAPS_API_KEY") + ) + if key == "" { - return errors.New("please configure a `GOOGLE_MAPS_API_KEY`") + return errors.New("missing `GOOGLE_MAPS_API_KEY`") } + client, err = maps.NewClient(maps.WithAPIKey(key)) return err } -// Explode takes a one-line address string, explodes it and returns an *Address +// Explode takes a one-line address string, explodes it using gmaps.Geocode() and returns an *Address. +// Deprecated: Use ExplodeWithContext instead. func Explode(address string) (*Address, error) { + return ExplodeWithContext(context.Background(), address) +} + +// ExplodeWithContext takes a one-line address string, explodes it using gmaps.Geocode() and returns an *Address. +func ExplodeWithContext(ctx context.Context, address string) (*Address, error) { if client == nil { - if err := initClient(); err != nil { + if err := mapsClient(); err != nil { return nil, err } } - // Build the API request. - req := &maps.GeocodingRequest{ + rsp, err := client.Geocode(ctx, &maps.GeocodingRequest{ Address: address, - } - - // Execute the request. - resp, err := client.Geocode(context.Background(), req) - if len(resp) < 1 { + }) + if err != nil { return nil, err } - if err != nil { + + if len(rsp) < 1 { return nil, err } - // Using the first match in our response, grab the values we need. - components := resp[0].AddressComponents - formattedAddress := resp[0].FormattedAddress - lat := resp[0].Geometry.Location.Lat - lng := resp[0].Geometry.Location.Lng + var ( + geocodeResult = rsp[0] + components = geocodeResult.AddressComponents + formattedAddress = geocodeResult.FormattedAddress + lat = geocodeResult.Geometry.Location.Lat + lng = geocodeResult.Geometry.Location.Lng + ) - // Construct the return *Address{} - response := &Address{ + return &Address{ AddressLine1: compose(addressLine1Composition, "", components, false), AddressLine2: compose(addressLine2Composition, addressLineDelimiter, components, false), AddressCity: compose(addressCityComposition, addressLineDelimiter, components, false), @@ -78,28 +95,64 @@ func Explode(address string) (*Address, error) { AddressLat: &lat, AddressLng: &lng, FormattedAddress: &formattedAddress, + }, err +} + +// Timezone takes a one-line address string, and determine timezone/location data for it using gmaps.Timezone(). +func Timezone(ctx context.Context, address string) (*Location, error) { + if client == nil { + if err := mapsClient(); err != nil { + return nil, err + } } - return response, err + geocodeResult, err := ExplodeWithContext(ctx, address) + if err != nil { + return nil, err + } + + if geocodeResult.AddressLat == nil || geocodeResult.AddressLng == nil { + return nil, errors.New("unable to determine latitude and longitude for address") + } + + resp, err := client.Timezone(ctx, &maps.TimezoneRequest{ + Location: &maps.LatLng{Lat: *geocodeResult.AddressLat, Lng: *geocodeResult.AddressLng}, + }) + if err != nil { + return nil, err + } + + return &Location{ + DstOffset: resp.DstOffset, + RawOffset: resp.RawOffset, + TimeZoneId: resp.TimeZoneID, + TimeZoneName: resp.TimeZoneName, + }, err } func compose(composition []string, delimiter string, components []maps.AddressComponent, useShortName bool) *string { var str string + for _, element := range composition { component := getComponentByType(components, element) if useShortName { if component != nil && !strings.Contains(str, component.ShortName) { str = fmt.Sprintf("%s %s%s", str, component.ShortName, delimiter) } - } else { - if component != nil && !strings.Contains(str, component.LongName) { - str = fmt.Sprintf("%s %s%s", str, component.LongName, delimiter) - } + + continue + } + + if component != nil && !strings.Contains(str, component.LongName) { + str = fmt.Sprintf("%s %s%s", str, component.LongName, delimiter) } } + if str == "" { return nil } + str = strings.TrimPrefix(strings.TrimSuffix(str, delimiter), " ") + return &str } diff --git a/abode_test.go b/abode_test.go index c16ca6e..29eb083 100644 --- a/abode_test.go +++ b/abode_test.go @@ -1,6 +1,7 @@ package abode import ( + "context" "reflect" "testing" @@ -8,92 +9,146 @@ import ( . "github.com/onsi/gomega" ) -func TestExplodeUSAddress(t *testing.T) { - // Register the test. +func TestExplode_US(t *testing.T) { RegisterTestingT(t) - // Init client if client == nil { - err := initClient() - if err != nil { + if err := mapsClient(); err != nil { t.Errorf("Unexpected error: %s", err) } } - line1 := "193 Rogers Avenue" - line2 := "Brooklyn" - state := "New York" - country := "United States" - countryCode := "US" - zip := "11216" - lat := 40.6706252 - lng := -73.9530545 - formatted := "193 Rogers Ave, Brooklyn, NY 11216, USA" - - expected := &Address{ - AddressLine1: &line1, - AddressLine2: &line2, - AddressCity: nil, - AddressState: &state, - AddressCountry: &country, - AddressCountryCode: &countryCode, - AddressZip: &zip, - AddressLat: &lat, - AddressLng: &lng, - FormattedAddress: &formatted, + var ( + line1 = "193 Rogers Avenue" + line2 = "Brooklyn" + state = "New York" + country = "United States" + countryCode = "US" + zip = "11216" + lat = 40.6706252 + lng = -73.9530545 + formatted = "193 Rogers Ave, Brooklyn, NY 11216, USA" + + expected = &Address{ + AddressLine1: &line1, + AddressLine2: &line2, + AddressCity: nil, + AddressState: &state, + AddressCountry: &country, + AddressCountryCode: &countryCode, + AddressZip: &zip, + AddressLat: &lat, + AddressLng: &lng, + FormattedAddress: &formatted, + } + ) + + result, err := ExplodeWithContext(context.Background(), "193 Rogers Ave, Brooklyn, New York") + if err != nil { + t.Errorf("Unexpected error: %s", err) + } + + if !reflect.DeepEqual(result, expected) { + t.Errorf("Unexpected Results\n%s", pretty.Compare(result, expected)) + } +} + +func TestExplode_International(t *testing.T) { + RegisterTestingT(t) + + if client == nil { + if err := mapsClient(); err != nil { + t.Errorf("Unexpected error: %s", err) + } } - address1 := "193 Rogers Ave, Brooklyn, New York" - result, err := Explode(address1) + var ( + line1 = "1 4 Abercrombie Street" + line2 = "Howick" + city = "Auckland" + state = "Auckland" + country = "New Zealand" + countryCode = "NZ" + zip = "2014" + lat = -36.8990751 + lng = 174.9334851 + formatted = "1/4 Abercrombie Street, Howick, Auckland 2014, New Zealand" + + expected = &Address{ + AddressLine1: &line1, + AddressLine2: &line2, + AddressCity: &city, + AddressState: &state, + AddressCountry: &country, + AddressCountryCode: &countryCode, + AddressZip: &zip, + AddressLat: &lat, + AddressLng: &lng, + FormattedAddress: &formatted, + } + ) + + result, err := ExplodeWithContext(context.Background(), "1/4 Abercrombie Street, Auckland") if err != nil { t.Errorf("Unexpected error: %s", err) } + if !reflect.DeepEqual(result, expected) { t.Errorf("Unexpected Results\n%s", pretty.Compare(result, expected)) } } -func TestExplodeInternationalAddress(t *testing.T) { - // Register the test. +func TestTimezone_US(t *testing.T) { RegisterTestingT(t) - // Init client if client == nil { - err := initClient() - if err != nil { + if err := mapsClient(); err != nil { t.Errorf("Unexpected error: %s", err) } } - line1 := "1 4 Abercrombie Street" - line2 := "Howick" - city := "Auckland" - state := "Auckland" - country := "New Zealand" - countryCode := "NZ" - zip := "2014" - lat := -36.8990751 - lng := 174.9334851 - formatted := "1/4 Abercrombie Street, Howick, Auckland 2014, New Zealand" - - expected := &Address{ - AddressLine1: &line1, - AddressLine2: &line2, - AddressCity: &city, - AddressState: &state, - AddressCountry: &country, - AddressCountryCode: &countryCode, - AddressZip: &zip, - AddressLat: &lat, - AddressLng: &lng, - FormattedAddress: &formatted, + var ( + expected = &Location{ + DstOffset: 0, + RawOffset: -17762, + TimeZoneId: "America/New_York", + TimeZoneName: "GMT-04:56:02", + } + ) + + result, err := Timezone(context.Background(), "193 Rogers Ave, Brooklyn, New York") + if err != nil { + t.Errorf("Unexpected error: %s", err) + } + + if !reflect.DeepEqual(result, expected) { + t.Errorf("Unexpected Results\n%s", pretty.Compare(result, expected)) + } +} + +func TestTimezone_International(t *testing.T) { + RegisterTestingT(t) + + if client == nil { + if err := mapsClient(); err != nil { + t.Errorf("Unexpected error: %s", err) + } } - address1 := "1/4 Abercrombie Street, Auckland" - result, err := Explode(address1) + var ( + expected = &Location{ + DstOffset: 0, + RawOffset: 41944, + TimeZoneId: "Pacific/Auckland", + TimeZoneName: "GMT+11:39:04", + } + ) + + result, err := Timezone(context.Background(), "1/4 Abercrombie Street, Auckland") if err != nil { t.Errorf("Unexpected error: %s", err) } + if !reflect.DeepEqual(result, expected) { t.Errorf("Unexpected Results\n%s", pretty.Compare(result, expected)) } diff --git a/go.mod b/go.mod index a02f9e6..d23bdfe 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,19 @@ module github.com/mattevans/abode -go 1.16 +go 1.19 require ( github.com/kylelemons/godebug v1.1.0 - github.com/onsi/gomega v1.16.0 - golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect + github.com/onsi/gomega v1.20.2 googlemaps.github.io/maps v1.3.2 ) + +require ( + github.com/google/go-cmp v0.5.8 // indirect + github.com/google/uuid v1.1.1 // indirect + go.opencensus.io v0.22.3 // indirect + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index 98d2185..13b13e1 100644 --- a/go.sum +++ b/go.sum @@ -4,32 +4,17 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -38,115 +23,63 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c= -github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= +github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY= +github.com/onsi/gomega v1.20.2/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= googlemaps.github.io/maps v1.3.2 h1:3YfYdVWFTFi7lVdCdrDYW3dqHvfCSUdC7/x8pbMOuKQ= googlemaps.github.io/maps v1.3.2/go.mod h1:cCq0JKYAnnCRSdiaBi7Ex9CW15uxIAk7oPi8V/xEh6s= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=