Skip to content

Commit 21d6c55

Browse files
committed
Refactor client to pass context
1 parent 857cfed commit 21d6c55

File tree

8 files changed

+171
-88
lines changed

8 files changed

+171
-88
lines changed

elevation.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package geonames
2+
3+
type ElevationSRTM1 struct {
4+
Srtm1 int `json:"srtm1"`
5+
Lng float64 `json:"lng"`
6+
Lat float64 `json:"lat"`
7+
}
8+
9+
type Elevation struct {
10+
Type string
11+
Lat float64
12+
Lng float64
13+
}
14+
15+
func GetElevationSRTM1() Elevation {
16+
17+
return Elevation{}
18+
}

elevation_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package geonames_test
2+
3+
import (
4+
"bytes"
5+
"io"
6+
"net/http"
7+
"net/http/httptest"
8+
"testing"
9+
10+
"github.com/qba73/geonames"
11+
)
12+
13+
// ts is a helper func that creates a test server with embedded URI validation.
14+
var testServer = func(reader io.Reader, wantURI string, t *testing.T) *httptest.Server {
15+
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
16+
gotReqURI := r.RequestURI
17+
verifyURIs(wantURI, gotReqURI, t)
18+
_, err := io.Copy(rw, reader)
19+
if err != nil {
20+
t.Fatal(err)
21+
}
22+
}))
23+
return ts
24+
}
25+
26+
func TestGetElevationReturnsDataOnValidInput(t *testing.T) {
27+
t.Parallel()
28+
29+
resp := new(bytes.Buffer)
30+
resp.Read([]byte(`23`))
31+
32+
ts := testServer(&resp)
33+
34+
}
35+
36+
func TestGetElevationValidInput(t *testing.T) {
37+
t.Parallel()
38+
39+
wantReqURI := "/srtm1JSON?lat=50&lng=50&username=DummyUser"
40+
ts := newTestServer(testFile, wantReqURI, t)
41+
defer ts.Close()
42+
43+
client, err := geonames.NewClient("DummyUser", geonames.WithBaseURL(ts.URL))
44+
if err != nil {
45+
t.Fatal(err)
46+
}
47+
48+
client.GetElevation()
49+
}

geonames.go

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func WithHTTPClient(h *http.Client) option {
2323
if h == nil {
2424
return errors.New("nil http Client")
2525
}
26-
c.httpClient = h
26+
c.HTTPClient = h
2727
return nil
2828
}
2929
}
@@ -34,7 +34,7 @@ func WithBaseURL(url string) option {
3434
if url == "" {
3535
return errors.New("nil baseURL")
3636
}
37-
c.baseURL = url
37+
c.BaseURL = url
3838
return nil
3939
}
4040
}
@@ -45,40 +45,51 @@ func WithHTTPHeaders(header http.Header) option {
4545
if header == nil {
4646
return errors.New("nil HTTP headers")
4747
}
48-
c.headers = header
48+
c.Headers = header
4949
return nil
5050
}
5151
}
5252

5353
// Client holds data required for communicating with the Geonames Web Services.
5454
type Client struct {
55-
userName string
56-
userAgent string
57-
baseURL string
58-
httpClient *http.Client
55+
UserName string
56+
UserAgent string
57+
BaseURL string
58+
HTTPClient *http.Client
5959

6060
// Optional HTTP headers to set for each API request.
61-
headers map[string][]string
61+
Headers map[string][]string
62+
}
63+
64+
var DefaultClient = Client{
65+
UserName: "demo",
66+
BaseURL: "http://api.geonames.org",
67+
HTTPClient: http.DefaultClient,
68+
Headers: map[string][]string{"Content-Type": {"application/json"}},
69+
}
70+
71+
// NewDemoClient creates a demo client. Demo client
72+
// does not require username registration at geonames.org website.
73+
func NewDemoClient() *Client {
74+
return &DefaultClient
6275
}
6376

6477
// NewClient creates a new client for GeoNames Web service.
6578
//
6679
// The username has to be registered at the GeoNames.org website.
6780
// HTTP requests without a valid username will return 403 HTTP errors.
6881
func NewClient(username string, options ...option) (*Client, error) {
69-
if username == "" {
70-
return nil, errors.New("nil geonames user")
71-
}
7282
c := Client{
73-
userName: username,
74-
userAgent: userAgent,
75-
baseURL: "http://api.geonames.org",
76-
httpClient: &http.Client{
83+
UserName: username,
84+
UserAgent: userAgent,
85+
BaseURL: "http://api.geonames.org",
86+
HTTPClient: &http.Client{
7787
Timeout: time.Second * 5,
7888
},
79-
headers: map[string][]string{
89+
Headers: map[string][]string{
8090
"User-Agent": {userAgent},
81-
"Content-Type": {"application/json"}},
91+
"Content-Type": {"application/json"},
92+
},
8293
}
8394
for _, opt := range options {
8495
if err := opt(&c); err != nil {
@@ -88,15 +99,12 @@ func NewClient(username string, options ...option) (*Client, error) {
8899
return &c, nil
89100
}
90101

91-
func (c Client) get(url string, data interface{}) error {
92-
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
93-
defer cancel()
94-
102+
func (c Client) get(ctx context.Context, url string, data any) error {
95103
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
96104
if err != nil {
97105
return fmt.Errorf("creating request: %w", err)
98106
}
99-
res, err := c.httpClient.Do(req)
107+
res, err := c.HTTPClient.Do(req)
100108
if err != nil {
101109
return fmt.Errorf("sending GET request: %w", err)
102110
}
@@ -117,7 +125,8 @@ func (c Client) get(url string, data interface{}) error {
117125
return nil
118126
}
119127

128+
// Position holds information about Lat and Long.
120129
type Position struct {
121-
Lat float64
122-
Long float64
130+
Lat float64
131+
Lng float64
123132
}

geonames_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package geonames_test
22

33
import (
4+
"context"
45
"io"
56
"net/http"
67
"net/http/httptest"
@@ -74,5 +75,5 @@ func TestWikipediaResolvesGeoNameOnValidInput(t *testing.T) {
7475
t.Fatal(err)
7576
}
7677

77-
client.GetPlace(name, country, 1)
78+
client.GetPlace(context.Background(), name, country, 1)
7879
}

postal.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package geonames
22

33
import (
4+
"context"
45
"fmt"
56
"net/url"
67
)
@@ -28,13 +29,13 @@ type PostalCode struct {
2829
}
2930

3031
// GetPostalCode retrieves postal codes for the given place name and the country code.
31-
func (c Client) GetPostCode(place, country string) ([]PostalCode, error) {
32+
func (c Client) GetPostCode(ctx context.Context, place, country string) ([]PostalCode, error) {
3233
url, err := c.buildPostalURL(place, country)
3334
if err != nil {
3435
return nil, err
3536
}
3637
var pr postalResponse
37-
if err := c.get(url, &pr); err != nil {
38+
if err := c.get(ctx, url, &pr); err != nil {
3839
return nil, err
3940
}
4041

@@ -44,8 +45,8 @@ func (c Client) GetPostCode(place, country string) ([]PostalCode, error) {
4445
PlaceName: pc.PlaceName,
4546
AdminName1: pc.AdminName1,
4647
Position: Position{
47-
Lat: pc.Lat,
48-
Long: pc.Lng,
48+
Lat: pc.Lat,
49+
Lng: pc.Lng,
4950
},
5051
PostalCode: pc.PostalCode,
5152
CountryCode: pc.CountryCode,
@@ -60,9 +61,9 @@ func (c Client) buildPostalURL(placeName, countryCode string) (string, error) {
6061
params := url.Values{
6162
"placename": {placeName},
6263
"country": {countryCode},
63-
"username": {c.userName},
64+
"username": {c.UserName},
6465
}
65-
basePostal := fmt.Sprintf("%s/postalCodeSearchJSON", c.baseURL)
66+
basePostal := fmt.Sprintf("%s/postalCodeSearchJSON", c.BaseURL)
6667

6768
u, err := url.Parse(basePostal)
6869
if err != nil {

0 commit comments

Comments
 (0)