@mountainpass/addressr 2.1.1 → 2.1.2

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.
@@ -113,6 +113,9 @@ async function initIndex(esClient, clear, synonyms) {
113
113
  },
114
114
  confidence: {
115
115
  type: 'integer'
116
+ },
117
+ locality_pid: {
118
+ type: 'keyword'
116
119
  }
117
120
  }
118
121
  }
@@ -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: 0,
988
+ size: 100,
988
989
  query: {
989
990
  term: {
990
991
  postcodes: postcode
991
992
  }
992
993
  },
993
- aggs: {
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_);
@@ -47,10 +47,37 @@ function startRest2Server() {
47
47
  const {
48
48
  json,
49
49
  hash,
50
- statusCode
50
+ statusCode,
51
+ localityPid
51
52
  } = await (0, _addressService.getAddress)(pid);
53
+ const links = [];
54
+ if (localityPid) {
55
+ links.push({
56
+ rel: 'related',
57
+ uri: `/localities/${localityPid}`,
58
+ title: json.structured?.locality?.name || 'Locality'
59
+ });
60
+ }
61
+ if (json.structured) {
62
+ const s = json.structured;
63
+ if (s.postcode) {
64
+ links.push({
65
+ rel: 'related',
66
+ uri: `/postcodes/${s.postcode}`,
67
+ title: `Postcode ${s.postcode}`
68
+ });
69
+ }
70
+ if (s.state && s.state.abbreviation) {
71
+ links.push({
72
+ rel: 'related',
73
+ uri: `/states/${s.state.abbreviation}`,
74
+ title: s.state.name
75
+ });
76
+ }
77
+ }
52
78
  return {
53
79
  body: json,
80
+ links,
54
81
  headers: {
55
82
  etag: `"${_version.version}-${hash}"`,
56
83
  'cache-control': `public, max-age=${ONE_WEEK}`
@@ -114,9 +141,25 @@ function startRest2Server() {
114
141
  }) => {
115
142
  const resp = await (0, _addressService.getLocality)(pid);
116
143
  const source = resp.body._source;
144
+ const links = [];
145
+ if (source.primary_postcode) {
146
+ links.push({
147
+ rel: 'related',
148
+ uri: `/postcodes/${source.primary_postcode}`,
149
+ title: `Postcode ${source.primary_postcode}`
150
+ });
151
+ }
152
+ if (source.state_abbreviation) {
153
+ links.push({
154
+ rel: 'related',
155
+ uri: `/states/${source.state_abbreviation}`,
156
+ title: source.state_name
157
+ });
158
+ }
117
159
  const hash = _nodeCrypto.default.createHash('md5').update(JSON.stringify(source)).digest('hex');
118
160
  return {
119
161
  body: source,
162
+ links,
120
163
  headers: {
121
164
  etag: `"${_version.version}-${hash}"`,
122
165
  'cache-control': `public, max-age=${ONE_WEEK}`
@@ -182,8 +225,14 @@ function startRest2Server() {
182
225
  postcode
183
226
  }) => {
184
227
  const result = await (0, _addressService.getPostcode)(postcode);
185
- const localities = result.body.aggregations.localities.buckets.map(l => ({
186
- name: l.key
228
+ const hits = result.body.hits.hits;
229
+ const localities = hits.map(h => ({
230
+ name: h._source.locality_name
231
+ }));
232
+ const links = hits.map(h => ({
233
+ rel: 'related',
234
+ uri: `/localities/${h._source.locality_pid}`,
235
+ title: h._source.locality_name
187
236
  }));
188
237
  const body = {
189
238
  postcode,
@@ -192,6 +241,7 @@ function startRest2Server() {
192
241
  const hash = _nodeCrypto.default.createHash('md5').update(JSON.stringify(body)).digest('hex');
193
242
  return {
194
243
  body,
244
+ links,
195
245
  headers: {
196
246
  etag: `"${_version.version}-${hash}"`,
197
247
  'cache-control': `public, max-age=${ONE_WEEK}`
@@ -233,15 +283,9 @@ function startRest2Server() {
233
283
  }) => {
234
284
  const result = await (0, _addressService.getState)(abbreviation);
235
285
  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
286
  const body = {
241
287
  abbreviation: abbreviation.toUpperCase(),
242
- name: stateName,
243
- localities,
244
- postcodes
288
+ name: stateName
245
289
  };
246
290
  const hash = _nodeCrypto.default.createHash('md5').update(JSON.stringify(body)).digest('hex');
247
291
  return {
@@ -293,12 +337,199 @@ function startRest2Server() {
293
337
  };
294
338
  }
295
339
  });
340
+ waycharter.registerResourceType({
341
+ path: '/api-docs',
342
+ loader: async () => {
343
+ const spec = {
344
+ openapi: '3.0.3',
345
+ info: {
346
+ title: 'Addressr by Mountain Pass',
347
+ description: 'Free Australian Address Validation, Search and Autocomplete. This OpenAPI spec is supplementary — the HATEOAS link-driven API is the authoritative contract.',
348
+ version: _version.version
349
+ },
350
+ paths: {
351
+ '/addresses': {
352
+ get: {
353
+ summary: 'Search Addresses',
354
+ operationId: 'searchAddresses',
355
+ parameters: [{
356
+ name: 'q',
357
+ in: 'query',
358
+ required: true,
359
+ schema: {
360
+ type: 'string',
361
+ minLength: 3
362
+ },
363
+ description: 'Address search query'
364
+ }],
365
+ responses: {
366
+ 200: {
367
+ description: 'List of matching addresses'
368
+ }
369
+ }
370
+ }
371
+ },
372
+ '/addresses/{pid}': {
373
+ get: {
374
+ summary: 'Get Address',
375
+ operationId: 'getAddress',
376
+ parameters: [{
377
+ name: 'pid',
378
+ in: 'path',
379
+ required: true,
380
+ schema: {
381
+ type: 'string'
382
+ },
383
+ description: 'Address persistent identifier'
384
+ }],
385
+ responses: {
386
+ 200: {
387
+ description: 'Address details with structured data'
388
+ }
389
+ }
390
+ }
391
+ },
392
+ '/localities': {
393
+ get: {
394
+ summary: 'Search Localities',
395
+ operationId: 'searchLocalities',
396
+ parameters: [{
397
+ name: 'q',
398
+ in: 'query',
399
+ required: true,
400
+ schema: {
401
+ type: 'string',
402
+ minLength: 2
403
+ },
404
+ description: 'Locality/suburb name search query'
405
+ }],
406
+ responses: {
407
+ 200: {
408
+ description: 'List of matching localities'
409
+ }
410
+ }
411
+ }
412
+ },
413
+ '/localities/{pid}': {
414
+ get: {
415
+ summary: 'Get Locality',
416
+ operationId: 'getLocality',
417
+ parameters: [{
418
+ name: 'pid',
419
+ in: 'path',
420
+ required: true,
421
+ schema: {
422
+ type: 'string'
423
+ },
424
+ description: 'Locality persistent identifier'
425
+ }],
426
+ responses: {
427
+ 200: {
428
+ description: 'Locality details'
429
+ }
430
+ }
431
+ }
432
+ },
433
+ '/postcodes': {
434
+ get: {
435
+ summary: 'Search Postcodes',
436
+ operationId: 'searchPostcodes',
437
+ parameters: [{
438
+ name: 'q',
439
+ in: 'query',
440
+ required: false,
441
+ schema: {
442
+ type: 'string'
443
+ },
444
+ description: 'Postcode prefix search query. Omit to list all postcodes.'
445
+ }],
446
+ responses: {
447
+ 200: {
448
+ description: 'List of matching postcodes with associated localities'
449
+ }
450
+ }
451
+ }
452
+ },
453
+ '/postcodes/{postcode}': {
454
+ get: {
455
+ summary: 'Get Postcode',
456
+ operationId: 'getPostcode',
457
+ parameters: [{
458
+ name: 'postcode',
459
+ in: 'path',
460
+ required: true,
461
+ schema: {
462
+ type: 'string'
463
+ },
464
+ description: 'Australian postcode'
465
+ }],
466
+ responses: {
467
+ 200: {
468
+ description: 'Postcode details with associated localities'
469
+ }
470
+ }
471
+ }
472
+ },
473
+ '/states': {
474
+ get: {
475
+ summary: 'Search States',
476
+ operationId: 'searchStates',
477
+ parameters: [{
478
+ name: 'q',
479
+ in: 'query',
480
+ required: false,
481
+ schema: {
482
+ type: 'string'
483
+ },
484
+ description: 'State name or abbreviation search. Omit to list all states.'
485
+ }],
486
+ responses: {
487
+ 200: {
488
+ description: 'List of matching states and territories'
489
+ }
490
+ }
491
+ }
492
+ },
493
+ '/states/{abbreviation}': {
494
+ get: {
495
+ summary: 'Get State',
496
+ operationId: 'getState',
497
+ parameters: [{
498
+ name: 'abbreviation',
499
+ in: 'path',
500
+ required: true,
501
+ schema: {
502
+ type: 'string'
503
+ },
504
+ description: 'State/territory abbreviation (e.g., NSW, VIC)'
505
+ }],
506
+ responses: {
507
+ 200: {
508
+ description: 'State/territory details'
509
+ }
510
+ }
511
+ }
512
+ }
513
+ }
514
+ };
515
+ return {
516
+ body: spec,
517
+ headers: {
518
+ 'cache-control': `public, max-age=${ONE_WEEK}`,
519
+ 'content-type': 'application/json'
520
+ }
521
+ };
522
+ }
523
+ });
296
524
  waycharter.registerResourceType({
297
525
  path: '/',
298
526
  loader: async () => {
299
527
  return {
300
528
  body: {},
301
529
  links: [...addressesType.additionalPaths, ...localitiesType.additionalPaths, ...postcodesType.additionalPaths, ...statesType.additionalPaths, {
530
+ rel: 'https://addressr.io/rels/api-docs',
531
+ uri: '/api-docs'
532
+ }, {
302
533
  rel: 'https://addressr.io/rels/health',
303
534
  uri: '/health'
304
535
  }],
package/lib/version.js CHANGED
@@ -5,4 +5,4 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.version = void 0;
7
7
  // Generated by genversion.
8
- const version = exports.version = '2.1.1';
8
+ const version = exports.version = '2.1.2';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mountainpass/addressr",
3
- "version": "2.1.1",
3
+ "version": "2.1.2",
4
4
  "description": "Australian Address Validation, Search and Autocomplete",
5
5
  "author": {
6
6
  "name": "Mountain Pass",