geocodio-library-node 2.0.0 → 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 +60 -5
- package/lib/__tests__/geocodioLibraryNode.test.js +26 -4
- package/lib/__tests__/warnings.test.js +316 -0
- package/lib/index.d.ts +58 -7
- package/lib/index.js +11 -0
- 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)
|
|
@@ -125,6 +126,7 @@ To batch geocode, simply pass an array of addresses or coordinates instead of a
|
|
|
125
126
|
geocoder.geocode([
|
|
126
127
|
'1109 N Highland St, Arlington VA',
|
|
127
128
|
'525 University Ave, Toronto, ON, Canada',
|
|
129
|
+
'10 Downing St, London, United Kingdom',
|
|
128
130
|
'4410 S Highway 17 92, Casselberry FL',
|
|
129
131
|
'15000 NE 24th Street, Redmond WA',
|
|
130
132
|
'17015 Walnut Grove Drive, Morgan Hill CA'
|
|
@@ -145,9 +147,10 @@ geocoder.reverse([
|
|
|
145
147
|
geocoder.geocode({
|
|
146
148
|
'MyId1': '1109 N Highland St, Arlington VA',
|
|
147
149
|
'MyId2': '525 University Ave, Toronto, ON, Canada',
|
|
148
|
-
'MyId3': '
|
|
149
|
-
'MyId4': '
|
|
150
|
-
'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'
|
|
151
154
|
})
|
|
152
155
|
.then(response => { ... })
|
|
153
156
|
.catch(err => { ... });
|
|
@@ -155,7 +158,7 @@ geocoder.geocode({
|
|
|
155
158
|
|
|
156
159
|
### Field appends
|
|
157
160
|
|
|
158
|
-
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).
|
|
159
162
|
|
|
160
163
|
To request additional fields, simply supply them as an array as the second parameter
|
|
161
164
|
|
|
@@ -173,6 +176,12 @@ geocoder.geocode(
|
|
|
173
176
|
geocoder.reverse('38.9002898,-76.9990361', ['census2010'])
|
|
174
177
|
.then(response => { ... })
|
|
175
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 => { ... });
|
|
176
185
|
```
|
|
177
186
|
|
|
178
187
|
### Address components
|
|
@@ -201,6 +210,12 @@ geocoder.geocode([
|
|
|
201
210
|
state_province: 'ON',
|
|
202
211
|
country: 'Canada',
|
|
203
212
|
},
|
|
213
|
+
{
|
|
214
|
+
street: '10 Downing St',
|
|
215
|
+
city: 'London',
|
|
216
|
+
postal_code: 'SW1A 2AA',
|
|
217
|
+
country: 'United Kingdom',
|
|
218
|
+
},
|
|
204
219
|
])
|
|
205
220
|
.then(response => { ... })
|
|
206
221
|
.catch(err => { ... });
|
|
@@ -222,6 +237,46 @@ geocoder.reverse('38.9002898,-76.9990361', ['timezone'], 5)
|
|
|
222
237
|
.catch(err => { ... });
|
|
223
238
|
```
|
|
224
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
|
+
|
|
225
280
|
### Distance calculations
|
|
226
281
|
|
|
227
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 {
|
|
@@ -346,6 +360,15 @@ declare module 'geocodio-library-node' {
|
|
|
346
360
|
exact_match: boolean;
|
|
347
361
|
}
|
|
348
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
|
+
|
|
349
372
|
export interface Fields {
|
|
350
373
|
congressional_districts?: CongressionalDistrict[];
|
|
351
374
|
state_legislative_districts?: StateLegislativeDistricts;
|
|
@@ -353,6 +376,9 @@ declare module 'geocodio-library-node' {
|
|
|
353
376
|
timezone?: Timezone;
|
|
354
377
|
census?: Census;
|
|
355
378
|
zip4?: Zip4;
|
|
379
|
+
uk_westminster?: UKLegislativeDistrict[];
|
|
380
|
+
uk_devolved?: UKLegislativeDistrict[];
|
|
381
|
+
uk_local?: UKLegislativeDistrict[];
|
|
356
382
|
[key: string]: unknown;
|
|
357
383
|
}
|
|
358
384
|
|
|
@@ -364,6 +390,7 @@ declare module 'geocodio-library-node' {
|
|
|
364
390
|
accuracy_type: GeocodeAccuracyType;
|
|
365
391
|
source?: string;
|
|
366
392
|
fields?: Fields;
|
|
393
|
+
_warnings?: Warnings;
|
|
367
394
|
}
|
|
368
395
|
|
|
369
396
|
export type FieldOption =
|
|
@@ -390,6 +417,12 @@ declare module 'geocodio-library-node' {
|
|
|
390
417
|
| 'census2020'
|
|
391
418
|
| 'provriding'
|
|
392
419
|
| 'riding'
|
|
420
|
+
| 'uk-westminster'
|
|
421
|
+
| 'uk-westminster-next'
|
|
422
|
+
| 'uk-devolved'
|
|
423
|
+
| 'uk-devolved-next'
|
|
424
|
+
| 'uk-local'
|
|
425
|
+
| 'uk-local-next'
|
|
393
426
|
| 'zip4'
|
|
394
427
|
| 'acs-demographics'
|
|
395
428
|
| 'acs-economics'
|
|
@@ -399,7 +432,7 @@ declare module 'geocodio-library-node' {
|
|
|
399
432
|
|
|
400
433
|
export interface SingleGeocodeResponse {
|
|
401
434
|
results: GeocodedAddress[];
|
|
402
|
-
_warnings?:
|
|
435
|
+
_warnings?: Warnings;
|
|
403
436
|
}
|
|
404
437
|
|
|
405
438
|
export interface BatchGeocodeResponse<Q extends string | AddressInputComponents, T extends Array<Q> | Record<string, Q>> {
|
|
@@ -413,7 +446,7 @@ declare module 'geocodio-library-node' {
|
|
|
413
446
|
|
|
414
447
|
export interface ReverseGeocodeResponse {
|
|
415
448
|
results: GeocodedAddress[];
|
|
416
|
-
_warnings?:
|
|
449
|
+
_warnings?: Warnings;
|
|
417
450
|
}
|
|
418
451
|
|
|
419
452
|
export interface BatchReverseGeocodeResponse<Q extends string | [number, number], T extends Array<Q> | Record<string, Q>> {
|
|
@@ -431,6 +464,24 @@ declare module 'geocodio-library-node' {
|
|
|
431
464
|
file: {
|
|
432
465
|
filename: string;
|
|
433
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;
|
|
434
485
|
}
|
|
435
486
|
|
|
436
487
|
export default class Geocodio {
|
|
@@ -490,16 +541,16 @@ declare module 'geocodio-library-node' {
|
|
|
490
541
|
|
|
491
542
|
downloadDistanceMatrixJob(id: string | number, filePath: string): Promise<void>;
|
|
492
543
|
|
|
493
|
-
deleteDistanceMatrixJob(id: string | number): Promise<
|
|
544
|
+
deleteDistanceMatrixJob(id: string | number): Promise<DeleteResponse>;
|
|
494
545
|
|
|
495
546
|
// List API
|
|
496
547
|
list: {
|
|
497
548
|
create(filename: string, direction: string, format: string, callback: string): Promise<ListResponse>;
|
|
498
|
-
status(listId: number): Promise<
|
|
499
|
-
all(): Promise<
|
|
549
|
+
status(listId: number): Promise<ListStatusResponse>;
|
|
550
|
+
all(): Promise<ListsResponse>;
|
|
500
551
|
download(listId: number, output: string): Promise<unknown>;
|
|
501
|
-
delete(listId: number): Promise<
|
|
502
|
-
deleteList(listId: number): Promise<
|
|
552
|
+
delete(listId: number): Promise<DeleteResponse>;
|
|
553
|
+
deleteList(listId: number): Promise<DeleteResponse>; // Alias for delete
|
|
503
554
|
};
|
|
504
555
|
}
|
|
505
556
|
}
|
package/lib/index.js
CHANGED
|
@@ -535,6 +535,9 @@ class Geocodio {
|
|
|
535
535
|
|
|
536
536
|
const decoratedError = new Error(errorMessage);
|
|
537
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);
|
|
538
541
|
|
|
539
542
|
throw decoratedError;
|
|
540
543
|
} else {
|
|
@@ -543,6 +546,14 @@ class Geocodio {
|
|
|
543
546
|
});
|
|
544
547
|
}
|
|
545
548
|
|
|
549
|
+
extractWarnings(data) {
|
|
550
|
+
if (data && Array.isArray(data._warnings)) {
|
|
551
|
+
return data._warnings;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
return [];
|
|
555
|
+
}
|
|
556
|
+
|
|
546
557
|
formatUrl(endpoint) {
|
|
547
558
|
return `https://${this.hostname}/${this.apiVersion}/${endpoint}`;
|
|
548
559
|
}
|