geocodio-library-node 1.15.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -25
- package/lib/__tests__/geocodioLibraryNode.test.js +26 -4
- package/lib/__tests__/warnings.test.js +316 -0
- package/lib/index.d.ts +69 -17
- package/lib/index.js +14 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# geocod.io Node library [![NPM version][npm-image]][npm-url]
|
|
2
|
-
> Library for performing forward and reverse address geocoding for addresses or coordinates in the US and
|
|
2
|
+
> Library for performing forward and reverse address geocoding for addresses or coordinates in the US, Canada, Mexico and the UK, with support for distance calculations.
|
|
3
3
|
|
|
4
4
|
<!-- toc -->
|
|
5
5
|
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* [Field appends](#field-appends)
|
|
11
11
|
* [Address components](#address-components)
|
|
12
12
|
* [Limit results](#limit-results)
|
|
13
|
+
* [Warnings](#warnings)
|
|
13
14
|
* [Distance calculation](#distance-calculation)
|
|
14
15
|
* [Distance matrix](#distance-matrix)
|
|
15
16
|
* [Async distance jobs](#async-distance-jobs)
|
|
@@ -54,19 +55,6 @@ geocoder
|
|
|
54
55
|
})
|
|
55
56
|
/*
|
|
56
57
|
response => {
|
|
57
|
-
"input": {
|
|
58
|
-
"address_components": {
|
|
59
|
-
"number": "1109",
|
|
60
|
-
"predirectional": "N",
|
|
61
|
-
"street": "Highland",
|
|
62
|
-
"suffix": "St",
|
|
63
|
-
"formatted_street": "N Highland St",
|
|
64
|
-
"city": "Arlington",
|
|
65
|
-
"state": "VA",
|
|
66
|
-
"country": "US"
|
|
67
|
-
},
|
|
68
|
-
"formatted_address": "1109 N Highland St, Arlington, VA"
|
|
69
|
-
},
|
|
70
58
|
"results": [
|
|
71
59
|
{
|
|
72
60
|
"address_components": {
|
|
@@ -77,8 +65,8 @@ geocoder
|
|
|
77
65
|
"formatted_street": "N Highland St",
|
|
78
66
|
"city": "Arlington",
|
|
79
67
|
"county": "Arlington County",
|
|
80
|
-
"
|
|
81
|
-
"
|
|
68
|
+
"state_province": "VA",
|
|
69
|
+
"postal_code": "22201",
|
|
82
70
|
"country": "US"
|
|
83
71
|
},
|
|
84
72
|
"formatted_address": "1109 N Highland St, Arlington, VA 22201",
|
|
@@ -99,8 +87,8 @@ geocoder
|
|
|
99
87
|
"formatted_street": "N Highland St",
|
|
100
88
|
"city": "Arlington",
|
|
101
89
|
"county": "Arlington County",
|
|
102
|
-
"
|
|
103
|
-
"
|
|
90
|
+
"state_province": "VA",
|
|
91
|
+
"postal_code": "22201",
|
|
104
92
|
"country": "US"
|
|
105
93
|
},
|
|
106
94
|
"formatted_address": "1109 N Highland St, Arlington, VA 22201",
|
|
@@ -138,6 +126,7 @@ To batch geocode, simply pass an array of addresses or coordinates instead of a
|
|
|
138
126
|
geocoder.geocode([
|
|
139
127
|
'1109 N Highland St, Arlington VA',
|
|
140
128
|
'525 University Ave, Toronto, ON, Canada',
|
|
129
|
+
'10 Downing St, London, United Kingdom',
|
|
141
130
|
'4410 S Highway 17 92, Casselberry FL',
|
|
142
131
|
'15000 NE 24th Street, Redmond WA',
|
|
143
132
|
'17015 Walnut Grove Drive, Morgan Hill CA'
|
|
@@ -158,9 +147,10 @@ geocoder.reverse([
|
|
|
158
147
|
geocoder.geocode({
|
|
159
148
|
'MyId1': '1109 N Highland St, Arlington VA',
|
|
160
149
|
'MyId2': '525 University Ave, Toronto, ON, Canada',
|
|
161
|
-
'MyId3': '
|
|
162
|
-
'MyId4': '
|
|
163
|
-
'MyId5': '
|
|
150
|
+
'MyId3': '10 Downing St, London, United Kingdom',
|
|
151
|
+
'MyId4': '4410 S Highway 17 92, Casselberry FL',
|
|
152
|
+
'MyId5': '15000 NE 24th Street, Redmond WA',
|
|
153
|
+
'MyId6': '17015 Walnut Grove Drive, Morgan Hill CA'
|
|
164
154
|
})
|
|
165
155
|
.then(response => { ... })
|
|
166
156
|
.catch(err => { ... });
|
|
@@ -168,7 +158,7 @@ geocoder.geocode({
|
|
|
168
158
|
|
|
169
159
|
### Field appends
|
|
170
160
|
|
|
171
|
-
Geocodio allows you to append additional data points such as congressional districts, census codes, timezone, ACS survey results and [much much more](https://www.geocod.io/docs/#fields).
|
|
161
|
+
Geocodio allows you to append additional data points such as congressional districts, census codes, timezone, ACS survey results, UK constituencies and wards, and [much much more](https://www.geocod.io/docs/#fields).
|
|
172
162
|
|
|
173
163
|
To request additional fields, simply supply them as an array as the second parameter
|
|
174
164
|
|
|
@@ -186,6 +176,12 @@ geocoder.geocode(
|
|
|
186
176
|
geocoder.reverse('38.9002898,-76.9990361', ['census2010'])
|
|
187
177
|
.then(response => { ... })
|
|
188
178
|
.catch(err => { ... });
|
|
179
|
+
|
|
180
|
+
// United Kingdom addresses support UK-specific appends such as Westminster and
|
|
181
|
+
// devolved parliament constituencies, and local authority wards
|
|
182
|
+
geocoder.geocode('10 Downing St, London, United Kingdom', ['uk-westminster', 'uk-local'])
|
|
183
|
+
.then(response => { ... })
|
|
184
|
+
.catch(err => { ... });
|
|
189
185
|
```
|
|
190
186
|
|
|
191
187
|
### Address components
|
|
@@ -196,7 +192,7 @@ For forward geocoding requests it is possible to supply [individual address comp
|
|
|
196
192
|
geocoder.geocode({
|
|
197
193
|
street: '1109 N Highland St',
|
|
198
194
|
city: 'Arlington',
|
|
199
|
-
|
|
195
|
+
state_province: 'VA',
|
|
200
196
|
postal_code: '22201'
|
|
201
197
|
})
|
|
202
198
|
.then(response => { ... })
|
|
@@ -206,14 +202,20 @@ geocoder.geocode([
|
|
|
206
202
|
{
|
|
207
203
|
street: '1109 N Highland St',
|
|
208
204
|
city: 'Arlington',
|
|
209
|
-
|
|
205
|
+
state_province: 'VA'
|
|
210
206
|
},
|
|
211
207
|
{
|
|
212
208
|
street: '525 University Ave',
|
|
213
209
|
city: 'Toronto',
|
|
214
|
-
|
|
210
|
+
state_province: 'ON',
|
|
215
211
|
country: 'Canada',
|
|
216
212
|
},
|
|
213
|
+
{
|
|
214
|
+
street: '10 Downing St',
|
|
215
|
+
city: 'London',
|
|
216
|
+
postal_code: 'SW1A 2AA',
|
|
217
|
+
country: 'United Kingdom',
|
|
218
|
+
},
|
|
217
219
|
])
|
|
218
220
|
.then(response => { ... })
|
|
219
221
|
.catch(err => { ... });
|
|
@@ -235,6 +237,46 @@ geocoder.reverse('38.9002898,-76.9990361', ['timezone'], 5)
|
|
|
235
237
|
.catch(err => { ... });
|
|
236
238
|
```
|
|
237
239
|
|
|
240
|
+
### Warnings
|
|
241
|
+
|
|
242
|
+
The API reports non-fatal advisories under a `_warnings` key — a misspelled field name, an unexpected query parameter, a superseded API version, or an append that had to be skipped. The request still succeeds, so nothing is thrown; the warnings simply ride along with the response.
|
|
243
|
+
|
|
244
|
+
Responses are resolved decoded and verbatim, so warnings are read straight off the response. The key is only present when there is at least one warning, so always fall back to an empty array:
|
|
245
|
+
|
|
246
|
+
```javascript
|
|
247
|
+
geocoder.geocode('1109 N Highland St, Arlington, VA', ['congress'])
|
|
248
|
+
.then(response => {
|
|
249
|
+
(response._warnings || []).forEach(warning => console.warn(warning));
|
|
250
|
+
// "The field congress is not recognized. Did you mean cd?"
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Warnings show up in a few places, depending on what raised them:
|
|
255
|
+
|
|
256
|
+
| Where | Applies to |
|
|
257
|
+
| -- | -- |
|
|
258
|
+
| `response._warnings` | Single `geocode(...)` and `reverse(...)`, and the `list` and distance matrix job methods |
|
|
259
|
+
| `response.results[i].response._warnings` | Batch `geocode(...)` and `reverse(...)` — warnings are attached per address |
|
|
260
|
+
| `response.results[i]._warnings` | An individual geocoding result, e.g. an `ffiec` append skipped because the match is not street-level |
|
|
261
|
+
|
|
262
|
+
All of these are typed as an optional `_warnings?: Warnings` (a `string[]`) in the TypeScript definitions.
|
|
263
|
+
|
|
264
|
+
Warnings are also attached to error responses, where they are available on the thrown error as `warnings` (always an array, empty when the API sent none):
|
|
265
|
+
|
|
266
|
+
```javascript
|
|
267
|
+
geocoder.geocode('1109 N Highland St', ['congress'])
|
|
268
|
+
.catch(err => {
|
|
269
|
+
console.error(err.message); // "Could not geocode address. Postal code or city required."
|
|
270
|
+
console.error(err.code); // 422
|
|
271
|
+
|
|
272
|
+
err.warnings.forEach(warning => console.warn(warning));
|
|
273
|
+
// "The field congress is not recognized. Did you mean cd?"
|
|
274
|
+
});
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
> [!TIP]
|
|
278
|
+
> Warnings are worth logging during development — they are how the API tells you a field append was silently skipped, which otherwise looks like missing data.
|
|
279
|
+
|
|
238
280
|
### Distance calculations
|
|
239
281
|
|
|
240
282
|
Calculate distances from a single origin to multiple destinations, or compute full distance matrices.
|
|
@@ -48,12 +48,13 @@ describe("geocoder", () => {
|
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
it("Can batch forward geocode", () => {
|
|
51
|
-
expect.assertions(
|
|
51
|
+
expect.assertions(3);
|
|
52
52
|
|
|
53
53
|
return geocoder
|
|
54
54
|
.geocode([
|
|
55
55
|
"1109 N Highland St, Arlington VA",
|
|
56
|
-
"525 University Ave, Toronto, ON, Canada"
|
|
56
|
+
"525 University Ave, Toronto, ON, Canada",
|
|
57
|
+
"10 Downing St, London, United Kingdom"
|
|
57
58
|
])
|
|
58
59
|
.then(response => {
|
|
59
60
|
expect(
|
|
@@ -62,6 +63,9 @@ describe("geocoder", () => {
|
|
|
62
63
|
expect(
|
|
63
64
|
response.results[1].response.results[0].formatted_address
|
|
64
65
|
).toEqual("525 University Ave, Toronto, ON M5G");
|
|
66
|
+
expect(
|
|
67
|
+
response.results[2].response.results[0].formatted_address
|
|
68
|
+
).toEqual("10 Downing St, London SW1A 2AA");
|
|
65
69
|
});
|
|
66
70
|
});
|
|
67
71
|
|
|
@@ -116,7 +120,7 @@ describe("geocoder", () => {
|
|
|
116
120
|
.then(response => {
|
|
117
121
|
expect(
|
|
118
122
|
response.results[0].response.results[0].formatted_address
|
|
119
|
-
).toEqual("
|
|
123
|
+
).toEqual("100 E Washington St, Nashville, NC 27856");
|
|
120
124
|
expect(
|
|
121
125
|
response.results[1].response.results[0].formatted_address
|
|
122
126
|
).toEqual("3026 S 1st St, Garland, TX 75041");
|
|
@@ -134,7 +138,7 @@ describe("geocoder", () => {
|
|
|
134
138
|
.then(response => {
|
|
135
139
|
expect(
|
|
136
140
|
response.results[0].response.results[0].formatted_address
|
|
137
|
-
).toEqual("
|
|
141
|
+
).toEqual("100 E Washington St, Nashville, NC 27856");
|
|
138
142
|
expect(
|
|
139
143
|
response.results[1].response.results[0].formatted_address
|
|
140
144
|
).toEqual("3026 S 1st St, Garland, TX 75041");
|
|
@@ -185,6 +189,24 @@ describe("geocoder", () => {
|
|
|
185
189
|
});
|
|
186
190
|
});
|
|
187
191
|
|
|
192
|
+
it("Can append UK-specific fields", () => {
|
|
193
|
+
expect.assertions(2);
|
|
194
|
+
|
|
195
|
+
return geocoder
|
|
196
|
+
.geocode("10 Downing St, London, United Kingdom", [
|
|
197
|
+
"uk-westminster",
|
|
198
|
+
"uk-local"
|
|
199
|
+
])
|
|
200
|
+
.then(response => {
|
|
201
|
+
expect(response.results[0].fields.uk_westminster[0].name).toEqual(
|
|
202
|
+
"Cities of London and Westminster"
|
|
203
|
+
);
|
|
204
|
+
expect(response.results[0].fields.uk_local[0].district_type).toEqual(
|
|
205
|
+
"ward"
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
188
210
|
it("Can limit results", () => {
|
|
189
211
|
expect.assertions(1);
|
|
190
212
|
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
const axios = require("axios");
|
|
2
|
+
const Geocodio = require("../index.js");
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Warnings passthrough
|
|
6
|
+
*
|
|
7
|
+
* The API reports non-fatal advisories under a `_warnings` key -- an
|
|
8
|
+
* unrecognized field name, a superseded API version, an append that was
|
|
9
|
+
* skipped. The library resolves with the decoded response body verbatim, so
|
|
10
|
+
* the key is already available to callers. These tests lock that in: they
|
|
11
|
+
* fail if a future refactor drops the key on any response shape.
|
|
12
|
+
*
|
|
13
|
+
* The shapes are the ones the OpenAPI specification models (see the
|
|
14
|
+
* `Warnings` schema): top-level on single geocode/reverse, per result,
|
|
15
|
+
* per batch item, and on the lists and distance-jobs responses.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const respondWith = (method, data) =>
|
|
19
|
+
jest.spyOn(axios, method).mockResolvedValue({ status: 200, data });
|
|
20
|
+
|
|
21
|
+
const rejectWith = (method, status, data) => {
|
|
22
|
+
const error = new Error(`Request failed with status code ${status}`);
|
|
23
|
+
error.response = { status, data };
|
|
24
|
+
|
|
25
|
+
return jest.spyOn(axios, method).mockRejectedValue(error);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
describe("warnings", () => {
|
|
29
|
+
const geocoder = new Geocodio("test-key");
|
|
30
|
+
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
jest.restoreAllMocks();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe("geocoding responses", () => {
|
|
36
|
+
it("preserves top-level warnings on a single forward geocode", async () => {
|
|
37
|
+
respondWith("get", {
|
|
38
|
+
results: [
|
|
39
|
+
{ formatted_address: "1109 N Highland St, Arlington, VA 22201" }
|
|
40
|
+
],
|
|
41
|
+
_warnings: ["The field congress is not recognized. Did you mean cd?"]
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const response = await geocoder.geocode(
|
|
45
|
+
"1109 N Highland St, Arlington VA",
|
|
46
|
+
["congress"]
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
expect(response._warnings).toEqual([
|
|
50
|
+
"The field congress is not recognized. Did you mean cd?"
|
|
51
|
+
]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("preserves top-level warnings on a single reverse geocode", async () => {
|
|
55
|
+
respondWith("get", {
|
|
56
|
+
results: [
|
|
57
|
+
{ formatted_address: "1109 N Highland St, Arlington, VA 22201" }
|
|
58
|
+
],
|
|
59
|
+
_warnings: [
|
|
60
|
+
"Ignoring parameter zipcode as it was not expected. Did you mean postal_code?"
|
|
61
|
+
]
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const response = await geocoder.reverse("38.886665,-77.094733");
|
|
65
|
+
|
|
66
|
+
expect(response._warnings).toEqual([
|
|
67
|
+
"Ignoring parameter zipcode as it was not expected. Did you mean postal_code?"
|
|
68
|
+
]);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("preserves per-result warnings", async () => {
|
|
72
|
+
respondWith("get", {
|
|
73
|
+
results: [
|
|
74
|
+
{
|
|
75
|
+
formatted_address: "Arlington, VA 22201",
|
|
76
|
+
accuracy_type: "place",
|
|
77
|
+
_warnings: [
|
|
78
|
+
"ffiec field was skipped since result is not street-level"
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const response = await geocoder.geocode("22201", ["ffiec"]);
|
|
85
|
+
|
|
86
|
+
expect(response.results[0]._warnings).toEqual([
|
|
87
|
+
"ffiec field was skipped since result is not street-level"
|
|
88
|
+
]);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("preserves per-item warnings on a batch forward geocode", async () => {
|
|
92
|
+
const item = query => ({
|
|
93
|
+
query,
|
|
94
|
+
response: {
|
|
95
|
+
results: [{ formatted_address: query }],
|
|
96
|
+
_warnings: ["The field congress is not recognized. Did you mean cd?"]
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
respondWith("post", {
|
|
101
|
+
results: [
|
|
102
|
+
item("1109 N Highland St, Arlington VA"),
|
|
103
|
+
item("525 University Ave, Toronto, ON, Canada")
|
|
104
|
+
]
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const response = await geocoder.geocode(
|
|
108
|
+
[
|
|
109
|
+
"1109 N Highland St, Arlington VA",
|
|
110
|
+
"525 University Ave, Toronto, ON, Canada"
|
|
111
|
+
],
|
|
112
|
+
["congress"]
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
expect(response.results[0].response._warnings).toEqual([
|
|
116
|
+
"The field congress is not recognized. Did you mean cd?"
|
|
117
|
+
]);
|
|
118
|
+
expect(response.results[1].response._warnings).toEqual([
|
|
119
|
+
"The field congress is not recognized. Did you mean cd?"
|
|
120
|
+
]);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("preserves per-item warnings on a batch reverse geocode", async () => {
|
|
124
|
+
respondWith("post", {
|
|
125
|
+
results: [
|
|
126
|
+
{
|
|
127
|
+
query: "35.9746000,-77.9658000",
|
|
128
|
+
response: {
|
|
129
|
+
results: [
|
|
130
|
+
{
|
|
131
|
+
formatted_address: "101 W Washington St, Nashville, NC 27856"
|
|
132
|
+
}
|
|
133
|
+
],
|
|
134
|
+
_warnings: [
|
|
135
|
+
"There is a newer API version available, please consider upgrading to v2."
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const response = await geocoder.reverse(["35.9746000,-77.9658000"]);
|
|
143
|
+
|
|
144
|
+
expect(response.results[0].response._warnings).toEqual([
|
|
145
|
+
"There is a newer API version available, please consider upgrading to v2."
|
|
146
|
+
]);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("does not invent a warnings key when the API sends none", async () => {
|
|
150
|
+
respondWith("get", {
|
|
151
|
+
results: [
|
|
152
|
+
{ formatted_address: "1109 N Highland St, Arlington, VA 22201" }
|
|
153
|
+
]
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const response = await geocoder.geocode(
|
|
157
|
+
"1109 N Highland St, Arlington VA"
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
expect(response).not.toHaveProperty("_warnings");
|
|
161
|
+
expect(response._warnings || []).toEqual([]);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe("lists responses", () => {
|
|
166
|
+
it("preserves warnings on list status", async () => {
|
|
167
|
+
respondWith("get", {
|
|
168
|
+
id: 42,
|
|
169
|
+
status: { state: "COMPLETED" },
|
|
170
|
+
_warnings: [
|
|
171
|
+
"The fields parameter should contain a comma-separated list of fields instead of an array"
|
|
172
|
+
]
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const response = await geocoder.list.status(42);
|
|
176
|
+
|
|
177
|
+
expect(response._warnings).toEqual([
|
|
178
|
+
"The fields parameter should contain a comma-separated list of fields instead of an array"
|
|
179
|
+
]);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("preserves warnings when listing all lists", async () => {
|
|
183
|
+
respondWith("get", {
|
|
184
|
+
data: [],
|
|
185
|
+
total: 0,
|
|
186
|
+
_warnings: [
|
|
187
|
+
"The fields parameter should contain a comma-separated list of fields instead of an array"
|
|
188
|
+
]
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const response = await geocoder.list.all();
|
|
192
|
+
|
|
193
|
+
expect(response._warnings).toEqual([
|
|
194
|
+
"The fields parameter should contain a comma-separated list of fields instead of an array"
|
|
195
|
+
]);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("preserves warnings when deleting a list", async () => {
|
|
199
|
+
respondWith("delete", {
|
|
200
|
+
success: true,
|
|
201
|
+
_warnings: [
|
|
202
|
+
"There is a newer API version available, please consider upgrading to v2."
|
|
203
|
+
]
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const response = await geocoder.list.delete(42);
|
|
207
|
+
|
|
208
|
+
expect(response._warnings).toEqual([
|
|
209
|
+
"There is a newer API version available, please consider upgrading to v2."
|
|
210
|
+
]);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe("distance matrix job responses", () => {
|
|
215
|
+
it("preserves warnings when creating a job", async () => {
|
|
216
|
+
respondWith("post", {
|
|
217
|
+
identifier: "dmj_abc123",
|
|
218
|
+
status: "ENQUEUED",
|
|
219
|
+
_warnings: [
|
|
220
|
+
"There is a newer API version available, please consider upgrading to v2."
|
|
221
|
+
]
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const response = await geocoder.createDistanceMatrixJob(
|
|
225
|
+
"Store coverage",
|
|
226
|
+
[[38.886665, -77.094733]],
|
|
227
|
+
[[38.897675, -77.036547]]
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
expect(response._warnings).toEqual([
|
|
231
|
+
"There is a newer API version available, please consider upgrading to v2."
|
|
232
|
+
]);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("preserves warnings on job status", async () => {
|
|
236
|
+
respondWith("get", {
|
|
237
|
+
data: { identifier: "dmj_abc123", status: "COMPLETED" },
|
|
238
|
+
_warnings: [
|
|
239
|
+
"The fields parameter should contain a comma-separated list of fields instead of an array"
|
|
240
|
+
]
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
const response = await geocoder.distanceMatrixJobStatus("dmj_abc123");
|
|
244
|
+
|
|
245
|
+
expect(response._warnings).toEqual([
|
|
246
|
+
"The fields parameter should contain a comma-separated list of fields instead of an array"
|
|
247
|
+
]);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it("preserves warnings when listing jobs", async () => {
|
|
251
|
+
respondWith("get", {
|
|
252
|
+
data: [],
|
|
253
|
+
total: 0,
|
|
254
|
+
_warnings: [
|
|
255
|
+
"The fields parameter should contain a comma-separated list of fields instead of an array"
|
|
256
|
+
]
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
const response = await geocoder.distanceMatrixJobs();
|
|
260
|
+
|
|
261
|
+
expect(response._warnings).toEqual([
|
|
262
|
+
"The fields parameter should contain a comma-separated list of fields instead of an array"
|
|
263
|
+
]);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("preserves warnings when deleting a job", async () => {
|
|
267
|
+
respondWith("delete", {
|
|
268
|
+
success: true,
|
|
269
|
+
_warnings: [
|
|
270
|
+
"There is a newer API version available, please consider upgrading to v2."
|
|
271
|
+
]
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const response = await geocoder.deleteDistanceMatrixJob("dmj_abc123");
|
|
275
|
+
|
|
276
|
+
expect(response._warnings).toEqual([
|
|
277
|
+
"There is a newer API version available, please consider upgrading to v2."
|
|
278
|
+
]);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
describe("error responses", () => {
|
|
283
|
+
it("exposes warnings attached to an error response", async () => {
|
|
284
|
+
expect.assertions(3);
|
|
285
|
+
|
|
286
|
+
rejectWith("get", 422, {
|
|
287
|
+
error: "Could not geocode address. Postal code or city required.",
|
|
288
|
+
_warnings: ["The field congress is not recognized. Did you mean cd?"]
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
await geocoder.geocode("1109 N Highland St", ["congress"]);
|
|
293
|
+
} catch (err) {
|
|
294
|
+
expect(err.message).toEqual(
|
|
295
|
+
"Could not geocode address. Postal code or city required."
|
|
296
|
+
);
|
|
297
|
+
expect(err.code).toEqual(422);
|
|
298
|
+
expect(err.warnings).toEqual([
|
|
299
|
+
"The field congress is not recognized. Did you mean cd?"
|
|
300
|
+
]);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it("reports no warnings when the error response carries none", async () => {
|
|
305
|
+
expect.assertions(1);
|
|
306
|
+
|
|
307
|
+
rejectWith("get", 403, { error: "Invalid API key" });
|
|
308
|
+
|
|
309
|
+
try {
|
|
310
|
+
await geocoder.geocode("1109 N Highland St, Arlington VA");
|
|
311
|
+
} catch (err) {
|
|
312
|
+
expect(err.warnings).toEqual([]);
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
});
|
package/lib/index.d.ts
CHANGED
|
@@ -38,6 +38,17 @@ declare module 'geocodio-library-node' {
|
|
|
38
38
|
toObject(): { lat: number; lng: number; id?: string };
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// Non-fatal advisories the API returns under the `_warnings` key, e.g. an
|
|
42
|
+
// unrecognized field name, a superseded API version, or a skipped append.
|
|
43
|
+
// The key is only present when at least one warning was raised.
|
|
44
|
+
export type Warnings = string[];
|
|
45
|
+
|
|
46
|
+
// Errors thrown for non-2xx API responses
|
|
47
|
+
export interface GeocodioError extends Error {
|
|
48
|
+
code: number;
|
|
49
|
+
warnings: Warnings;
|
|
50
|
+
}
|
|
51
|
+
|
|
41
52
|
// Coordinate input types (accept multiple formats)
|
|
42
53
|
export type CoordinateInput =
|
|
43
54
|
| Coordinate
|
|
@@ -101,6 +112,7 @@ declare module 'geocodio-library-node' {
|
|
|
101
112
|
origins_count: number;
|
|
102
113
|
destinations_count: number;
|
|
103
114
|
total_calculations: number;
|
|
115
|
+
_warnings?: Warnings;
|
|
104
116
|
}
|
|
105
117
|
|
|
106
118
|
export interface DistanceJobStatusResponse {
|
|
@@ -114,6 +126,7 @@ declare module 'geocodio-library-node' {
|
|
|
114
126
|
total_calculations: number;
|
|
115
127
|
calculations_completed: number;
|
|
116
128
|
};
|
|
129
|
+
_warnings?: Warnings;
|
|
117
130
|
}
|
|
118
131
|
|
|
119
132
|
export interface DistanceJobsListResponse {
|
|
@@ -138,6 +151,7 @@ declare module 'geocodio-library-node' {
|
|
|
138
151
|
prev_page_url: string | null;
|
|
139
152
|
to: number;
|
|
140
153
|
total: number;
|
|
154
|
+
_warnings?: Warnings;
|
|
141
155
|
}
|
|
142
156
|
|
|
143
157
|
export interface DistanceJobOptions extends DistanceOptions {
|
|
@@ -178,18 +192,23 @@ declare module 'geocodio-library-node' {
|
|
|
178
192
|
street?: string;
|
|
179
193
|
suffix?: string;
|
|
180
194
|
postdirectional?: string;
|
|
181
|
-
|
|
182
|
-
|
|
195
|
+
unit_type?: string;
|
|
196
|
+
unit_number?: string;
|
|
183
197
|
formatted_street?: string;
|
|
184
198
|
city?: string;
|
|
185
199
|
county?: string;
|
|
186
|
-
|
|
187
|
-
|
|
200
|
+
state_province?: string;
|
|
201
|
+
postal_code?: string;
|
|
188
202
|
country?: string;
|
|
189
|
-
postal_code?: string; // Alternative to zip used in some API calls
|
|
190
203
|
}
|
|
191
204
|
|
|
192
|
-
|
|
205
|
+
// Address input components accepted by the geocode endpoint.
|
|
206
|
+
// `state` is still accepted by the API for backwards compatibility,
|
|
207
|
+
// but `state_province` is the v2 field name.
|
|
208
|
+
export type AddressInputComponents =
|
|
209
|
+
Pick<AddressComponents, "street" | "city" | "county" | "state_province" | "postal_code" | "country"> & {
|
|
210
|
+
state?: string;
|
|
211
|
+
}
|
|
193
212
|
|
|
194
213
|
export type GeocodeAccuracyType =
|
|
195
214
|
| 'rooftop'
|
|
@@ -341,6 +360,15 @@ declare module 'geocodio-library-node' {
|
|
|
341
360
|
exact_match: boolean;
|
|
342
361
|
}
|
|
343
362
|
|
|
363
|
+
export interface UKLegislativeDistrict {
|
|
364
|
+
district_type: string;
|
|
365
|
+
gss_code: string;
|
|
366
|
+
ocd_id: string;
|
|
367
|
+
name: string;
|
|
368
|
+
is_upcoming_district: boolean;
|
|
369
|
+
source: string;
|
|
370
|
+
}
|
|
371
|
+
|
|
344
372
|
export interface Fields {
|
|
345
373
|
congressional_districts?: CongressionalDistrict[];
|
|
346
374
|
state_legislative_districts?: StateLegislativeDistricts;
|
|
@@ -348,6 +376,9 @@ declare module 'geocodio-library-node' {
|
|
|
348
376
|
timezone?: Timezone;
|
|
349
377
|
census?: Census;
|
|
350
378
|
zip4?: Zip4;
|
|
379
|
+
uk_westminster?: UKLegislativeDistrict[];
|
|
380
|
+
uk_devolved?: UKLegislativeDistrict[];
|
|
381
|
+
uk_local?: UKLegislativeDistrict[];
|
|
351
382
|
[key: string]: unknown;
|
|
352
383
|
}
|
|
353
384
|
|
|
@@ -359,6 +390,7 @@ declare module 'geocodio-library-node' {
|
|
|
359
390
|
accuracy_type: GeocodeAccuracyType;
|
|
360
391
|
source?: string;
|
|
361
392
|
fields?: Fields;
|
|
393
|
+
_warnings?: Warnings;
|
|
362
394
|
}
|
|
363
395
|
|
|
364
396
|
export type FieldOption =
|
|
@@ -385,6 +417,12 @@ declare module 'geocodio-library-node' {
|
|
|
385
417
|
| 'census2020'
|
|
386
418
|
| 'provriding'
|
|
387
419
|
| 'riding'
|
|
420
|
+
| 'uk-westminster'
|
|
421
|
+
| 'uk-westminster-next'
|
|
422
|
+
| 'uk-devolved'
|
|
423
|
+
| 'uk-devolved-next'
|
|
424
|
+
| 'uk-local'
|
|
425
|
+
| 'uk-local-next'
|
|
388
426
|
| 'zip4'
|
|
389
427
|
| 'acs-demographics'
|
|
390
428
|
| 'acs-economics'
|
|
@@ -393,12 +431,8 @@ declare module 'geocodio-library-node' {
|
|
|
393
431
|
| 'acs-social';
|
|
394
432
|
|
|
395
433
|
export interface SingleGeocodeResponse {
|
|
396
|
-
input: {
|
|
397
|
-
address_components: AddressComponents;
|
|
398
|
-
formatted_address: string;
|
|
399
|
-
};
|
|
400
434
|
results: GeocodedAddress[];
|
|
401
|
-
_warnings?:
|
|
435
|
+
_warnings?: Warnings;
|
|
402
436
|
}
|
|
403
437
|
|
|
404
438
|
export interface BatchGeocodeResponse<Q extends string | AddressInputComponents, T extends Array<Q> | Record<string, Q>> {
|
|
@@ -412,7 +446,7 @@ declare module 'geocodio-library-node' {
|
|
|
412
446
|
|
|
413
447
|
export interface ReverseGeocodeResponse {
|
|
414
448
|
results: GeocodedAddress[];
|
|
415
|
-
_warnings?:
|
|
449
|
+
_warnings?: Warnings;
|
|
416
450
|
}
|
|
417
451
|
|
|
418
452
|
export interface BatchReverseGeocodeResponse<Q extends string | [number, number], T extends Array<Q> | Record<string, Q>> {
|
|
@@ -430,6 +464,24 @@ declare module 'geocodio-library-node' {
|
|
|
430
464
|
file: {
|
|
431
465
|
filename: string;
|
|
432
466
|
};
|
|
467
|
+
_warnings?: Warnings;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export interface ListStatusResponse {
|
|
471
|
+
id: number;
|
|
472
|
+
_warnings?: Warnings;
|
|
473
|
+
[key: string]: unknown;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export interface ListsResponse {
|
|
477
|
+
data: unknown[];
|
|
478
|
+
_warnings?: Warnings;
|
|
479
|
+
[key: string]: unknown;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
export interface DeleteResponse {
|
|
483
|
+
success: boolean;
|
|
484
|
+
_warnings?: Warnings;
|
|
433
485
|
}
|
|
434
486
|
|
|
435
487
|
export default class Geocodio {
|
|
@@ -489,16 +541,16 @@ declare module 'geocodio-library-node' {
|
|
|
489
541
|
|
|
490
542
|
downloadDistanceMatrixJob(id: string | number, filePath: string): Promise<void>;
|
|
491
543
|
|
|
492
|
-
deleteDistanceMatrixJob(id: string | number): Promise<
|
|
544
|
+
deleteDistanceMatrixJob(id: string | number): Promise<DeleteResponse>;
|
|
493
545
|
|
|
494
546
|
// List API
|
|
495
547
|
list: {
|
|
496
548
|
create(filename: string, direction: string, format: string, callback: string): Promise<ListResponse>;
|
|
497
|
-
status(listId: number): Promise<
|
|
498
|
-
all(): Promise<
|
|
549
|
+
status(listId: number): Promise<ListStatusResponse>;
|
|
550
|
+
all(): Promise<ListsResponse>;
|
|
499
551
|
download(listId: number, output: string): Promise<unknown>;
|
|
500
|
-
delete(listId: number): Promise<
|
|
501
|
-
deleteList(listId: number): Promise<
|
|
552
|
+
delete(listId: number): Promise<DeleteResponse>;
|
|
553
|
+
deleteList(listId: number): Promise<DeleteResponse>; // Alias for delete
|
|
502
554
|
};
|
|
503
555
|
}
|
|
504
556
|
}
|
package/lib/index.js
CHANGED
|
@@ -129,13 +129,13 @@ class Geocodio {
|
|
|
129
129
|
this.apiKey = apiKey || process.env.GEOCODIO_API_KEY || null;
|
|
130
130
|
this.hostname =
|
|
131
131
|
hostname || process.env.GEOCODIO_HOSTNAME || "api.geocod.io";
|
|
132
|
-
this.apiVersion = apiVersion || process.env.GEOCODIO_API_VERSION || "
|
|
132
|
+
this.apiVersion = apiVersion || process.env.GEOCODIO_API_VERSION || "v2";
|
|
133
133
|
|
|
134
134
|
this.SINGLE_TIMEOUT_MS = 5000;
|
|
135
135
|
this.BATCH_TIMEOUT_MS = 30 * 60 * 1000;
|
|
136
136
|
|
|
137
137
|
this.HTTP_HEADERS = {
|
|
138
|
-
"User-Agent": "geocodio-library-node/
|
|
138
|
+
"User-Agent": "geocodio-library-node/2.0.0",
|
|
139
139
|
Authorization: `Bearer ${this.apiKey}`
|
|
140
140
|
};
|
|
141
141
|
|
|
@@ -143,6 +143,7 @@ class Geocodio {
|
|
|
143
143
|
"street",
|
|
144
144
|
"city",
|
|
145
145
|
"state",
|
|
146
|
+
"state_province",
|
|
146
147
|
"postal_code",
|
|
147
148
|
"country"
|
|
148
149
|
];
|
|
@@ -534,6 +535,9 @@ class Geocodio {
|
|
|
534
535
|
|
|
535
536
|
const decoratedError = new Error(errorMessage);
|
|
536
537
|
decoratedError.code = code;
|
|
538
|
+
// Warnings ride along on error responses too, e.g. a misspelled
|
|
539
|
+
// field name in a request that failed for an unrelated reason
|
|
540
|
+
decoratedError.warnings = this.extractWarnings(error.response.data);
|
|
537
541
|
|
|
538
542
|
throw decoratedError;
|
|
539
543
|
} else {
|
|
@@ -542,6 +546,14 @@ class Geocodio {
|
|
|
542
546
|
});
|
|
543
547
|
}
|
|
544
548
|
|
|
549
|
+
extractWarnings(data) {
|
|
550
|
+
if (data && Array.isArray(data._warnings)) {
|
|
551
|
+
return data._warnings;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
return [];
|
|
555
|
+
}
|
|
556
|
+
|
|
545
557
|
formatUrl(endpoint) {
|
|
546
558
|
return `https://${this.hostname}/${this.apiVersion}/${endpoint}`;
|
|
547
559
|
}
|