@mountainpass/addressr 2.1.1 → 2.1.3
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/lib/client/elasticsearch.js +3 -0
- package/lib/service/address-service.js +7 -23
- package/lib/src/waycharter-server.js +702 -10
- package/lib/version.js +1 -1
- package/package.json +1 -1
|
@@ -737,7 +737,8 @@ async function loadAddressDetails(file, expectedCount, context, {
|
|
|
737
737
|
sla,
|
|
738
738
|
ssla,
|
|
739
739
|
structured,
|
|
740
|
-
confidence: structured.structured.confidence
|
|
740
|
+
confidence: structured.structured.confidence,
|
|
741
|
+
locality_pid: row.LOCALITY_PID
|
|
741
742
|
});
|
|
742
743
|
}
|
|
743
744
|
if (indexingBody.length > 0) {
|
|
@@ -984,20 +985,13 @@ async function getPostcode(postcode) {
|
|
|
984
985
|
const searchResp = await globalThis.esClient.search({
|
|
985
986
|
index: _elasticsearch.ES_LOCALITY_INDEX_NAME,
|
|
986
987
|
body: {
|
|
987
|
-
size:
|
|
988
|
+
size: 100,
|
|
988
989
|
query: {
|
|
989
990
|
term: {
|
|
990
991
|
postcodes: postcode
|
|
991
992
|
}
|
|
992
993
|
},
|
|
993
|
-
|
|
994
|
-
localities: {
|
|
995
|
-
terms: {
|
|
996
|
-
field: 'locality_name.raw',
|
|
997
|
-
size: 100
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
994
|
+
_source: ['locality_name', 'locality_pid']
|
|
1001
995
|
}
|
|
1002
996
|
});
|
|
1003
997
|
return searchResp;
|
|
@@ -1018,18 +1012,6 @@ async function getState(abbreviation) {
|
|
|
1018
1012
|
field: 'state_name',
|
|
1019
1013
|
size: 1
|
|
1020
1014
|
}
|
|
1021
|
-
},
|
|
1022
|
-
localities: {
|
|
1023
|
-
terms: {
|
|
1024
|
-
field: 'locality_name.raw',
|
|
1025
|
-
size: 1000
|
|
1026
|
-
}
|
|
1027
|
-
},
|
|
1028
|
-
postcodes: {
|
|
1029
|
-
terms: {
|
|
1030
|
-
field: 'postcodes',
|
|
1031
|
-
size: 1000
|
|
1032
|
-
}
|
|
1033
1015
|
}
|
|
1034
1016
|
}
|
|
1035
1017
|
}
|
|
@@ -1599,10 +1581,12 @@ async function getAddress(addressId) {
|
|
|
1599
1581
|
});
|
|
1600
1582
|
// TODO: store hash in address
|
|
1601
1583
|
const hash = _nodeCrypto.default.createHash('md5').update(JSON.stringify(json)).digest('hex');
|
|
1584
|
+
const localityPid = jsonX.body._source.locality_pid;
|
|
1602
1585
|
return {
|
|
1603
1586
|
link,
|
|
1604
1587
|
json,
|
|
1605
|
-
hash
|
|
1588
|
+
hash,
|
|
1589
|
+
localityPid
|
|
1606
1590
|
};
|
|
1607
1591
|
} catch (error_) {
|
|
1608
1592
|
error('error getting record from elastic search', error_);
|
|
@@ -18,6 +18,638 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
18
18
|
var app = (0, _express.default)();
|
|
19
19
|
const ONE_DAY = 60 * 60 * 24;
|
|
20
20
|
const ONE_WEEK = ONE_DAY * 7;
|
|
21
|
+
function buildOpenApiSpec(apiVersion) {
|
|
22
|
+
const schemas = {
|
|
23
|
+
AddressSearchResult: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
sla: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
description: 'Single line address',
|
|
29
|
+
example: 'UNIT 1, 19 MURRAY RD, CHRISTMAS ISLAND OT 6798'
|
|
30
|
+
},
|
|
31
|
+
ssla: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
description: 'Short single line address (for addresses with flats)',
|
|
34
|
+
example: '1/19 MURRAY RD, CHRISTMAS ISLAND OT 6798'
|
|
35
|
+
},
|
|
36
|
+
highlight: {
|
|
37
|
+
type: 'object',
|
|
38
|
+
description: 'Search term highlights in the address',
|
|
39
|
+
properties: {
|
|
40
|
+
sla: {
|
|
41
|
+
type: 'string'
|
|
42
|
+
},
|
|
43
|
+
ssla: {
|
|
44
|
+
type: 'string'
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
score: {
|
|
49
|
+
type: 'number',
|
|
50
|
+
description: 'Search relevance score',
|
|
51
|
+
example: 5.43
|
|
52
|
+
},
|
|
53
|
+
pid: {
|
|
54
|
+
type: 'string',
|
|
55
|
+
description: 'Persistent identifier for the address',
|
|
56
|
+
example: 'GAOT_717882967'
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
Address: {
|
|
61
|
+
type: 'object',
|
|
62
|
+
properties: {
|
|
63
|
+
sla: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
example: 'UNIT 1, 19 MURRAY RD, CHRISTMAS ISLAND OT 6798'
|
|
66
|
+
},
|
|
67
|
+
structured: {
|
|
68
|
+
type: 'object',
|
|
69
|
+
properties: {
|
|
70
|
+
confidence: {
|
|
71
|
+
type: 'integer',
|
|
72
|
+
example: 2
|
|
73
|
+
},
|
|
74
|
+
flat: {
|
|
75
|
+
type: 'object',
|
|
76
|
+
properties: {
|
|
77
|
+
number: {
|
|
78
|
+
type: 'integer',
|
|
79
|
+
example: 1
|
|
80
|
+
},
|
|
81
|
+
type: {
|
|
82
|
+
type: 'object',
|
|
83
|
+
properties: {
|
|
84
|
+
code: {
|
|
85
|
+
type: 'string',
|
|
86
|
+
example: 'UNIT'
|
|
87
|
+
},
|
|
88
|
+
name: {
|
|
89
|
+
type: 'string',
|
|
90
|
+
example: 'UNIT'
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
number: {
|
|
97
|
+
type: 'object',
|
|
98
|
+
properties: {
|
|
99
|
+
number: {
|
|
100
|
+
type: 'integer',
|
|
101
|
+
example: 19
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
street: {
|
|
106
|
+
type: 'object',
|
|
107
|
+
properties: {
|
|
108
|
+
name: {
|
|
109
|
+
type: 'string',
|
|
110
|
+
example: 'MURRAY'
|
|
111
|
+
},
|
|
112
|
+
type: {
|
|
113
|
+
type: 'object',
|
|
114
|
+
properties: {
|
|
115
|
+
code: {
|
|
116
|
+
type: 'string',
|
|
117
|
+
example: 'ROAD'
|
|
118
|
+
},
|
|
119
|
+
name: {
|
|
120
|
+
type: 'string',
|
|
121
|
+
example: 'RD'
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
locality: {
|
|
128
|
+
type: 'object',
|
|
129
|
+
properties: {
|
|
130
|
+
name: {
|
|
131
|
+
type: 'string',
|
|
132
|
+
example: 'CHRISTMAS ISLAND'
|
|
133
|
+
},
|
|
134
|
+
class: {
|
|
135
|
+
type: 'object',
|
|
136
|
+
properties: {
|
|
137
|
+
code: {
|
|
138
|
+
type: 'string',
|
|
139
|
+
example: 'U'
|
|
140
|
+
},
|
|
141
|
+
name: {
|
|
142
|
+
type: 'string',
|
|
143
|
+
example: 'UNOFFICIAL SUBURB'
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
postcode: {
|
|
150
|
+
type: 'string',
|
|
151
|
+
example: '6798'
|
|
152
|
+
},
|
|
153
|
+
state: {
|
|
154
|
+
type: 'object',
|
|
155
|
+
properties: {
|
|
156
|
+
abbreviation: {
|
|
157
|
+
type: 'string',
|
|
158
|
+
example: 'OT'
|
|
159
|
+
},
|
|
160
|
+
name: {
|
|
161
|
+
type: 'string',
|
|
162
|
+
example: 'OTHER TERRITORIES'
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
LocalitySearchResult: {
|
|
171
|
+
type: 'object',
|
|
172
|
+
properties: {
|
|
173
|
+
name: {
|
|
174
|
+
type: 'string',
|
|
175
|
+
example: 'LILYDALE'
|
|
176
|
+
},
|
|
177
|
+
state: {
|
|
178
|
+
type: 'object',
|
|
179
|
+
properties: {
|
|
180
|
+
name: {
|
|
181
|
+
type: 'string',
|
|
182
|
+
example: 'VICTORIA'
|
|
183
|
+
},
|
|
184
|
+
abbreviation: {
|
|
185
|
+
type: 'string',
|
|
186
|
+
example: 'VIC'
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
class: {
|
|
191
|
+
type: 'object',
|
|
192
|
+
properties: {
|
|
193
|
+
code: {
|
|
194
|
+
type: 'string',
|
|
195
|
+
description: 'Classification code (G=Gazetted, U=Unofficial, T=Topographic, I=Informal)',
|
|
196
|
+
example: 'G'
|
|
197
|
+
},
|
|
198
|
+
name: {
|
|
199
|
+
type: 'string',
|
|
200
|
+
example: 'GAZETTED LOCALITY'
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
postcode: {
|
|
205
|
+
type: 'string',
|
|
206
|
+
description: 'Primary postcode for this locality',
|
|
207
|
+
example: '3140'
|
|
208
|
+
},
|
|
209
|
+
score: {
|
|
210
|
+
type: 'number',
|
|
211
|
+
example: 5.23
|
|
212
|
+
},
|
|
213
|
+
pid: {
|
|
214
|
+
type: 'string',
|
|
215
|
+
example: 'loc1234567890ab'
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
Locality: {
|
|
220
|
+
type: 'object',
|
|
221
|
+
properties: {
|
|
222
|
+
locality_name: {
|
|
223
|
+
type: 'string',
|
|
224
|
+
example: 'CHRISTMAS ISLAND'
|
|
225
|
+
},
|
|
226
|
+
locality_class_code: {
|
|
227
|
+
type: 'string',
|
|
228
|
+
example: 'U'
|
|
229
|
+
},
|
|
230
|
+
locality_class_name: {
|
|
231
|
+
type: 'string',
|
|
232
|
+
example: 'UNOFFICIAL SUBURB'
|
|
233
|
+
},
|
|
234
|
+
primary_postcode: {
|
|
235
|
+
type: 'string',
|
|
236
|
+
example: '6798'
|
|
237
|
+
},
|
|
238
|
+
postcodes: {
|
|
239
|
+
type: 'array',
|
|
240
|
+
items: {
|
|
241
|
+
type: 'string'
|
|
242
|
+
},
|
|
243
|
+
example: ['6798']
|
|
244
|
+
},
|
|
245
|
+
state_abbreviation: {
|
|
246
|
+
type: 'string',
|
|
247
|
+
example: 'OT'
|
|
248
|
+
},
|
|
249
|
+
state_name: {
|
|
250
|
+
type: 'string',
|
|
251
|
+
example: 'OTHER TERRITORIES'
|
|
252
|
+
},
|
|
253
|
+
locality_pid: {
|
|
254
|
+
type: 'string',
|
|
255
|
+
example: 'loc9984d8beb142'
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
PostcodeSearchResult: {
|
|
260
|
+
type: 'object',
|
|
261
|
+
properties: {
|
|
262
|
+
postcode: {
|
|
263
|
+
type: 'string',
|
|
264
|
+
example: '3140'
|
|
265
|
+
},
|
|
266
|
+
localities: {
|
|
267
|
+
type: 'array',
|
|
268
|
+
items: {
|
|
269
|
+
type: 'object',
|
|
270
|
+
properties: {
|
|
271
|
+
name: {
|
|
272
|
+
type: 'string',
|
|
273
|
+
example: 'LILYDALE'
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
PostcodeDetail: {
|
|
281
|
+
type: 'object',
|
|
282
|
+
properties: {
|
|
283
|
+
postcode: {
|
|
284
|
+
type: 'string',
|
|
285
|
+
example: '6798'
|
|
286
|
+
},
|
|
287
|
+
localities: {
|
|
288
|
+
type: 'array',
|
|
289
|
+
description: 'Locality names. Individual locality resources are linked via related Link headers.',
|
|
290
|
+
items: {
|
|
291
|
+
type: 'object',
|
|
292
|
+
properties: {
|
|
293
|
+
name: {
|
|
294
|
+
type: 'string',
|
|
295
|
+
example: 'CHRISTMAS ISLAND'
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
State: {
|
|
303
|
+
type: 'object',
|
|
304
|
+
properties: {
|
|
305
|
+
abbreviation: {
|
|
306
|
+
type: 'string',
|
|
307
|
+
example: 'NSW'
|
|
308
|
+
},
|
|
309
|
+
name: {
|
|
310
|
+
type: 'string',
|
|
311
|
+
example: 'NEW SOUTH WALES'
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
Health: {
|
|
316
|
+
type: 'object',
|
|
317
|
+
properties: {
|
|
318
|
+
status: {
|
|
319
|
+
type: 'string',
|
|
320
|
+
example: 'healthy'
|
|
321
|
+
},
|
|
322
|
+
version: {
|
|
323
|
+
type: 'string',
|
|
324
|
+
example: '2.1.2'
|
|
325
|
+
},
|
|
326
|
+
timestamp: {
|
|
327
|
+
type: 'string',
|
|
328
|
+
format: 'date-time',
|
|
329
|
+
example: '2026-04-14T11:17:54.637Z'
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
return {
|
|
335
|
+
openapi: '3.0.3',
|
|
336
|
+
info: {
|
|
337
|
+
title: 'Addressr by Mountain Pass',
|
|
338
|
+
description: 'Free Australian Address Validation, Search and Autocomplete. This OpenAPI spec is supplementary — the HATEOAS link-driven API is the authoritative contract. Follow `related` Link headers to navigate between addresses, localities, postcodes and states.',
|
|
339
|
+
version: apiVersion
|
|
340
|
+
},
|
|
341
|
+
servers: [{
|
|
342
|
+
url: 'https://addressr.p.rapidapi.com',
|
|
343
|
+
description: 'RapidAPI'
|
|
344
|
+
}],
|
|
345
|
+
paths: {
|
|
346
|
+
'/addresses': {
|
|
347
|
+
get: {
|
|
348
|
+
summary: 'Search Addresses',
|
|
349
|
+
description: 'Search Australian addresses by any component — street, suburb, postcode, state. Supports fuzzy and prefix matching.',
|
|
350
|
+
operationId: 'searchAddresses',
|
|
351
|
+
tags: ['Addresses'],
|
|
352
|
+
parameters: [{
|
|
353
|
+
name: 'q',
|
|
354
|
+
in: 'query',
|
|
355
|
+
required: true,
|
|
356
|
+
schema: {
|
|
357
|
+
type: 'string',
|
|
358
|
+
minLength: 3
|
|
359
|
+
},
|
|
360
|
+
example: 'UNIT 1, 19 MURRAY RD, CHRISTMAS ISLAND',
|
|
361
|
+
description: 'Address search query (min 3 characters)'
|
|
362
|
+
}, {
|
|
363
|
+
name: 'page',
|
|
364
|
+
in: 'query',
|
|
365
|
+
required: false,
|
|
366
|
+
schema: {
|
|
367
|
+
type: 'integer',
|
|
368
|
+
minimum: 0
|
|
369
|
+
},
|
|
370
|
+
example: 0,
|
|
371
|
+
description: 'Zero-based page number for pagination'
|
|
372
|
+
}],
|
|
373
|
+
responses: {
|
|
374
|
+
200: {
|
|
375
|
+
description: 'List of matching addresses',
|
|
376
|
+
content: {
|
|
377
|
+
'application/json': {
|
|
378
|
+
schema: {
|
|
379
|
+
type: 'array',
|
|
380
|
+
items: {
|
|
381
|
+
$ref: '#/components/schemas/AddressSearchResult'
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
'/addresses/{pid}': {
|
|
391
|
+
get: {
|
|
392
|
+
summary: 'Get Address',
|
|
393
|
+
description: 'Get full structured details for a specific address. Response includes Link headers with `related` rels to the locality, postcode, and state.',
|
|
394
|
+
operationId: 'getAddress',
|
|
395
|
+
tags: ['Addresses'],
|
|
396
|
+
parameters: [{
|
|
397
|
+
name: 'pid',
|
|
398
|
+
in: 'path',
|
|
399
|
+
required: true,
|
|
400
|
+
schema: {
|
|
401
|
+
type: 'string'
|
|
402
|
+
},
|
|
403
|
+
example: 'GAOT_717882967',
|
|
404
|
+
description: 'Address persistent identifier (G-NAF PID)'
|
|
405
|
+
}],
|
|
406
|
+
responses: {
|
|
407
|
+
200: {
|
|
408
|
+
description: 'Address details with structured data',
|
|
409
|
+
content: {
|
|
410
|
+
'application/json': {
|
|
411
|
+
schema: {
|
|
412
|
+
$ref: '#/components/schemas/Address'
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
'/localities': {
|
|
421
|
+
get: {
|
|
422
|
+
summary: 'Search Localities',
|
|
423
|
+
description: 'Search Australian suburbs and localities by name. Supports fuzzy and prefix matching. Returns localities with state, postcode, and classification.',
|
|
424
|
+
operationId: 'searchLocalities',
|
|
425
|
+
tags: ['Localities'],
|
|
426
|
+
parameters: [{
|
|
427
|
+
name: 'q',
|
|
428
|
+
in: 'query',
|
|
429
|
+
required: true,
|
|
430
|
+
schema: {
|
|
431
|
+
type: 'string',
|
|
432
|
+
minLength: 2
|
|
433
|
+
},
|
|
434
|
+
example: 'lilydale',
|
|
435
|
+
description: 'Locality/suburb name search query (min 2 characters)'
|
|
436
|
+
}],
|
|
437
|
+
responses: {
|
|
438
|
+
200: {
|
|
439
|
+
description: 'List of matching localities',
|
|
440
|
+
content: {
|
|
441
|
+
'application/json': {
|
|
442
|
+
schema: {
|
|
443
|
+
type: 'array',
|
|
444
|
+
items: {
|
|
445
|
+
$ref: '#/components/schemas/LocalitySearchResult'
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
},
|
|
454
|
+
'/localities/{pid}': {
|
|
455
|
+
get: {
|
|
456
|
+
summary: 'Get Locality',
|
|
457
|
+
description: 'Get details for a specific locality. Response includes Link headers with `related` rels to the postcode and state.',
|
|
458
|
+
operationId: 'getLocality',
|
|
459
|
+
tags: ['Localities'],
|
|
460
|
+
parameters: [{
|
|
461
|
+
name: 'pid',
|
|
462
|
+
in: 'path',
|
|
463
|
+
required: true,
|
|
464
|
+
schema: {
|
|
465
|
+
type: 'string'
|
|
466
|
+
},
|
|
467
|
+
example: 'loc9984d8beb142',
|
|
468
|
+
description: 'Locality persistent identifier'
|
|
469
|
+
}],
|
|
470
|
+
responses: {
|
|
471
|
+
200: {
|
|
472
|
+
description: 'Locality details',
|
|
473
|
+
content: {
|
|
474
|
+
'application/json': {
|
|
475
|
+
schema: {
|
|
476
|
+
$ref: '#/components/schemas/Locality'
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
'/postcodes': {
|
|
485
|
+
get: {
|
|
486
|
+
summary: 'Search Postcodes',
|
|
487
|
+
description: 'Search Australian postcodes by prefix. Returns matching postcodes with their associated localities. Omit `q` to list all postcodes in ascending order.',
|
|
488
|
+
operationId: 'searchPostcodes',
|
|
489
|
+
tags: ['Postcodes'],
|
|
490
|
+
parameters: [{
|
|
491
|
+
name: 'q',
|
|
492
|
+
in: 'query',
|
|
493
|
+
required: false,
|
|
494
|
+
schema: {
|
|
495
|
+
type: 'string'
|
|
496
|
+
},
|
|
497
|
+
example: '314',
|
|
498
|
+
description: 'Postcode prefix search query (0+ characters). Omit to list all postcodes.'
|
|
499
|
+
}],
|
|
500
|
+
responses: {
|
|
501
|
+
200: {
|
|
502
|
+
description: 'List of matching postcodes with associated localities',
|
|
503
|
+
content: {
|
|
504
|
+
'application/json': {
|
|
505
|
+
schema: {
|
|
506
|
+
type: 'array',
|
|
507
|
+
items: {
|
|
508
|
+
$ref: '#/components/schemas/PostcodeSearchResult'
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
'/postcodes/{postcode}': {
|
|
518
|
+
get: {
|
|
519
|
+
summary: 'Get Postcode',
|
|
520
|
+
description: 'Get details for a specific postcode including all associated localities. Each locality is linked via a `related` Link header.',
|
|
521
|
+
operationId: 'getPostcode',
|
|
522
|
+
tags: ['Postcodes'],
|
|
523
|
+
parameters: [{
|
|
524
|
+
name: 'postcode',
|
|
525
|
+
in: 'path',
|
|
526
|
+
required: true,
|
|
527
|
+
schema: {
|
|
528
|
+
type: 'string'
|
|
529
|
+
},
|
|
530
|
+
example: '6798',
|
|
531
|
+
description: 'Australian postcode'
|
|
532
|
+
}],
|
|
533
|
+
responses: {
|
|
534
|
+
200: {
|
|
535
|
+
description: 'Postcode details with associated localities',
|
|
536
|
+
content: {
|
|
537
|
+
'application/json': {
|
|
538
|
+
schema: {
|
|
539
|
+
$ref: '#/components/schemas/PostcodeDetail'
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
},
|
|
547
|
+
'/states': {
|
|
548
|
+
get: {
|
|
549
|
+
summary: 'Search States',
|
|
550
|
+
description: 'Search Australian states and territories by name or abbreviation. Omit `q` to list all states alphabetically.',
|
|
551
|
+
operationId: 'searchStates',
|
|
552
|
+
tags: ['States'],
|
|
553
|
+
parameters: [{
|
|
554
|
+
name: 'q',
|
|
555
|
+
in: 'query',
|
|
556
|
+
required: false,
|
|
557
|
+
schema: {
|
|
558
|
+
type: 'string'
|
|
559
|
+
},
|
|
560
|
+
example: 'New',
|
|
561
|
+
description: 'State name or abbreviation search (0+ characters). Omit to list all states.'
|
|
562
|
+
}],
|
|
563
|
+
responses: {
|
|
564
|
+
200: {
|
|
565
|
+
description: 'List of matching states and territories',
|
|
566
|
+
content: {
|
|
567
|
+
'application/json': {
|
|
568
|
+
schema: {
|
|
569
|
+
type: 'array',
|
|
570
|
+
items: {
|
|
571
|
+
$ref: '#/components/schemas/State'
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
},
|
|
580
|
+
'/states/{abbreviation}': {
|
|
581
|
+
get: {
|
|
582
|
+
summary: 'Get State',
|
|
583
|
+
description: 'Get details for a specific state or territory. Use `/localities?q=` or `/postcodes?q=` to search within a state.',
|
|
584
|
+
operationId: 'getState',
|
|
585
|
+
tags: ['States'],
|
|
586
|
+
parameters: [{
|
|
587
|
+
name: 'abbreviation',
|
|
588
|
+
in: 'path',
|
|
589
|
+
required: true,
|
|
590
|
+
schema: {
|
|
591
|
+
type: 'string',
|
|
592
|
+
enum: ['ACT', 'NSW', 'NT', 'QLD', 'SA', 'TAS', 'VIC', 'WA', 'OT']
|
|
593
|
+
},
|
|
594
|
+
example: 'NSW',
|
|
595
|
+
description: 'State/territory abbreviation'
|
|
596
|
+
}],
|
|
597
|
+
responses: {
|
|
598
|
+
200: {
|
|
599
|
+
description: 'State/territory details',
|
|
600
|
+
content: {
|
|
601
|
+
'application/json': {
|
|
602
|
+
schema: {
|
|
603
|
+
$ref: '#/components/schemas/State'
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
'/health': {
|
|
612
|
+
get: {
|
|
613
|
+
summary: 'Health Check',
|
|
614
|
+
description: 'Check API service status. Returns version, timestamp, and health status.',
|
|
615
|
+
operationId: 'healthCheck',
|
|
616
|
+
tags: ['System'],
|
|
617
|
+
responses: {
|
|
618
|
+
200: {
|
|
619
|
+
description: 'API is healthy',
|
|
620
|
+
content: {
|
|
621
|
+
'application/json': {
|
|
622
|
+
schema: {
|
|
623
|
+
$ref: '#/components/schemas/Health'
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
},
|
|
632
|
+
components: {
|
|
633
|
+
schemas
|
|
634
|
+
},
|
|
635
|
+
tags: [{
|
|
636
|
+
name: 'Addresses',
|
|
637
|
+
description: 'Search and retrieve addresses'
|
|
638
|
+
}, {
|
|
639
|
+
name: 'Localities',
|
|
640
|
+
description: 'Search and retrieve suburbs/localities'
|
|
641
|
+
}, {
|
|
642
|
+
name: 'Postcodes',
|
|
643
|
+
description: 'Search and retrieve postcodes'
|
|
644
|
+
}, {
|
|
645
|
+
name: 'States',
|
|
646
|
+
description: 'Search and retrieve states/territories'
|
|
647
|
+
}, {
|
|
648
|
+
name: 'System',
|
|
649
|
+
description: 'System endpoints'
|
|
650
|
+
}]
|
|
651
|
+
};
|
|
652
|
+
}
|
|
21
653
|
var serverPort = process.env.PORT || 8080;
|
|
22
654
|
var logger = (0, _debug.default)('api');
|
|
23
655
|
var error = (0, _debug.default)('error');
|
|
@@ -47,10 +679,37 @@ function startRest2Server() {
|
|
|
47
679
|
const {
|
|
48
680
|
json,
|
|
49
681
|
hash,
|
|
50
|
-
statusCode
|
|
682
|
+
statusCode,
|
|
683
|
+
localityPid
|
|
51
684
|
} = await (0, _addressService.getAddress)(pid);
|
|
685
|
+
const links = [];
|
|
686
|
+
if (localityPid) {
|
|
687
|
+
links.push({
|
|
688
|
+
rel: 'related',
|
|
689
|
+
uri: `/localities/${localityPid}`,
|
|
690
|
+
title: json.structured?.locality?.name || 'Locality'
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
if (json.structured) {
|
|
694
|
+
const s = json.structured;
|
|
695
|
+
if (s.postcode) {
|
|
696
|
+
links.push({
|
|
697
|
+
rel: 'related',
|
|
698
|
+
uri: `/postcodes/${s.postcode}`,
|
|
699
|
+
title: `Postcode ${s.postcode}`
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
if (s.state && s.state.abbreviation) {
|
|
703
|
+
links.push({
|
|
704
|
+
rel: 'related',
|
|
705
|
+
uri: `/states/${s.state.abbreviation}`,
|
|
706
|
+
title: s.state.name
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
}
|
|
52
710
|
return {
|
|
53
711
|
body: json,
|
|
712
|
+
links,
|
|
54
713
|
headers: {
|
|
55
714
|
etag: `"${_version.version}-${hash}"`,
|
|
56
715
|
'cache-control': `public, max-age=${ONE_WEEK}`
|
|
@@ -114,9 +773,25 @@ function startRest2Server() {
|
|
|
114
773
|
}) => {
|
|
115
774
|
const resp = await (0, _addressService.getLocality)(pid);
|
|
116
775
|
const source = resp.body._source;
|
|
776
|
+
const links = [];
|
|
777
|
+
if (source.primary_postcode) {
|
|
778
|
+
links.push({
|
|
779
|
+
rel: 'related',
|
|
780
|
+
uri: `/postcodes/${source.primary_postcode}`,
|
|
781
|
+
title: `Postcode ${source.primary_postcode}`
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
if (source.state_abbreviation) {
|
|
785
|
+
links.push({
|
|
786
|
+
rel: 'related',
|
|
787
|
+
uri: `/states/${source.state_abbreviation}`,
|
|
788
|
+
title: source.state_name
|
|
789
|
+
});
|
|
790
|
+
}
|
|
117
791
|
const hash = _nodeCrypto.default.createHash('md5').update(JSON.stringify(source)).digest('hex');
|
|
118
792
|
return {
|
|
119
793
|
body: source,
|
|
794
|
+
links,
|
|
120
795
|
headers: {
|
|
121
796
|
etag: `"${_version.version}-${hash}"`,
|
|
122
797
|
'cache-control': `public, max-age=${ONE_WEEK}`
|
|
@@ -182,8 +857,14 @@ function startRest2Server() {
|
|
|
182
857
|
postcode
|
|
183
858
|
}) => {
|
|
184
859
|
const result = await (0, _addressService.getPostcode)(postcode);
|
|
185
|
-
const
|
|
186
|
-
|
|
860
|
+
const hits = result.body.hits.hits;
|
|
861
|
+
const localities = hits.map(h => ({
|
|
862
|
+
name: h._source.locality_name
|
|
863
|
+
}));
|
|
864
|
+
const links = hits.map(h => ({
|
|
865
|
+
rel: 'related',
|
|
866
|
+
uri: `/localities/${h._source.locality_pid}`,
|
|
867
|
+
title: h._source.locality_name
|
|
187
868
|
}));
|
|
188
869
|
const body = {
|
|
189
870
|
postcode,
|
|
@@ -192,6 +873,7 @@ function startRest2Server() {
|
|
|
192
873
|
const hash = _nodeCrypto.default.createHash('md5').update(JSON.stringify(body)).digest('hex');
|
|
193
874
|
return {
|
|
194
875
|
body,
|
|
876
|
+
links,
|
|
195
877
|
headers: {
|
|
196
878
|
etag: `"${_version.version}-${hash}"`,
|
|
197
879
|
'cache-control': `public, max-age=${ONE_WEEK}`
|
|
@@ -233,15 +915,9 @@ function startRest2Server() {
|
|
|
233
915
|
}) => {
|
|
234
916
|
const result = await (0, _addressService.getState)(abbreviation);
|
|
235
917
|
const stateName = result.body.aggregations.state_name.buckets[0]?.key || abbreviation.toUpperCase();
|
|
236
|
-
const localities = result.body.aggregations.localities.buckets.map(l => ({
|
|
237
|
-
name: l.key
|
|
238
|
-
}));
|
|
239
|
-
const postcodes = result.body.aggregations.postcodes.buckets.map(p => p.key);
|
|
240
918
|
const body = {
|
|
241
919
|
abbreviation: abbreviation.toUpperCase(),
|
|
242
|
-
name: stateName
|
|
243
|
-
localities,
|
|
244
|
-
postcodes
|
|
920
|
+
name: stateName
|
|
245
921
|
};
|
|
246
922
|
const hash = _nodeCrypto.default.createHash('md5').update(JSON.stringify(body)).digest('hex');
|
|
247
923
|
return {
|
|
@@ -293,12 +969,28 @@ function startRest2Server() {
|
|
|
293
969
|
};
|
|
294
970
|
}
|
|
295
971
|
});
|
|
972
|
+
waycharter.registerResourceType({
|
|
973
|
+
path: '/api-docs',
|
|
974
|
+
loader: async () => {
|
|
975
|
+
const spec = buildOpenApiSpec(_version.version);
|
|
976
|
+
return {
|
|
977
|
+
body: spec,
|
|
978
|
+
headers: {
|
|
979
|
+
'cache-control': `public, max-age=${ONE_WEEK}`,
|
|
980
|
+
'content-type': 'application/json'
|
|
981
|
+
}
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
});
|
|
296
985
|
waycharter.registerResourceType({
|
|
297
986
|
path: '/',
|
|
298
987
|
loader: async () => {
|
|
299
988
|
return {
|
|
300
989
|
body: {},
|
|
301
990
|
links: [...addressesType.additionalPaths, ...localitiesType.additionalPaths, ...postcodesType.additionalPaths, ...statesType.additionalPaths, {
|
|
991
|
+
rel: 'https://addressr.io/rels/api-docs',
|
|
992
|
+
uri: '/api-docs'
|
|
993
|
+
}, {
|
|
302
994
|
rel: 'https://addressr.io/rels/health',
|
|
303
995
|
uri: '/health'
|
|
304
996
|
}],
|
package/lib/version.js
CHANGED