@mountainpass/addressr 2.4.2 → 2.4.4
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/service/address-service.js +14 -58
- package/lib/service/gnaf-package-fetch.js +115 -0
- package/lib/version.js +1 -1
- package/package.json +1 -1
|
@@ -31,8 +31,7 @@ var _elasticsearch = require("../client/elasticsearch");
|
|
|
31
31
|
var _streamDown = _interopRequireDefault(require("../utils/stream-down"));
|
|
32
32
|
var _setLinkOptions = require("./set-link-options");
|
|
33
33
|
var _rangeExpansion = require("./range-expansion");
|
|
34
|
-
var
|
|
35
|
-
var _keyvFile = require("keyv-file");
|
|
34
|
+
var _gnafPackageFetch = require("./gnaf-package-fetch");
|
|
36
35
|
var _nodeCrypto = _interopRequireDefault(require("node:crypto"));
|
|
37
36
|
var _glob = require("glob");
|
|
38
37
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -44,21 +43,17 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
44
43
|
const fsp = _nodeFs.default.promises;
|
|
45
44
|
var logger = (0, _debug.default)('api');
|
|
46
45
|
var error = (0, _debug.default)('error');
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
|
|
47
|
+
// `cache`, `ONE_DAY_MS`, and `THIRTY_DAYS_MS` previously lived here for
|
|
48
|
+
// fetchPackageData. Moved to ./gnaf-package-fetch.js along with that
|
|
49
|
+
// function — see the comment above its `import` and P033.
|
|
50
|
+
|
|
52
51
|
const PAGE_SIZE = process.env.PAGE_SIZE || 8;
|
|
53
52
|
function getCoveredStates() {
|
|
54
53
|
const covered = process.env.COVERED_STATES || '';
|
|
55
54
|
return covered == '' ? [] : covered.split(',');
|
|
56
55
|
}
|
|
57
56
|
const COVERED_STATES = getCoveredStates();
|
|
58
|
-
const ONE_DAY_S = 60 /*sec*/ * 60 /*min*/ * 24; /*hours*/
|
|
59
|
-
|
|
60
|
-
const ONE_DAY_MS = 1000 * ONE_DAY_S;
|
|
61
|
-
const THIRTY_DAYS_MS = ONE_DAY_MS * 30;
|
|
62
57
|
const ES_INDEX_NAME = process.env.ES_INDEX_NAME || 'addressr';
|
|
63
58
|
async function dropIndex() {
|
|
64
59
|
await (0, _elasticsearch.dropIndex)(globalThis.esClient);
|
|
@@ -98,56 +93,17 @@ async function setAddresses(addr) {
|
|
|
98
93
|
//logger(await searchForAddress('657 The Entrance Road')); //'2/25 TOTTERDE'; // 'UNT 2, BELCONNEN';);
|
|
99
94
|
}
|
|
100
95
|
|
|
101
|
-
//
|
|
96
|
+
// fetchPackageData and the GNAF_PACKAGE_URL constant moved to
|
|
97
|
+
// ./gnaf-package-fetch.js so the data.gov.au CKAN fetch is testable in
|
|
98
|
+
// raw Node ESM (this file uses babel-only bare imports). See P033 for the
|
|
99
|
+
// source-inspection anti-pattern that motivated the extraction. The new
|
|
100
|
+
// module also adds a Mozilla-prefixed compatible-mode User-Agent header
|
|
101
|
+
// required by data.gov.au's CloudFront WAF (without it, fetch returns
|
|
102
|
+
// HTTP 403 — surfaced by run 25032179791).
|
|
102
103
|
|
|
103
|
-
// SEE https://data.gov.au/data/dataset/19432f89-dc3a-4ef3-b943-5326ef1dbecc
|
|
104
|
-
const GNAF_PACKAGE_URL = process.env.GNAF_PACKAGE_URL || 'https://data.gov.au/data/api/3/action/package_show?id=19432f89-dc3a-4ef3-b943-5326ef1dbecc';
|
|
105
|
-
async function fetchPackageData() {
|
|
106
|
-
const packageUrl = GNAF_PACKAGE_URL;
|
|
107
|
-
// See if we have the value in cache
|
|
108
|
-
const cachedResponse = await cache.get(packageUrl);
|
|
109
|
-
logger('cached gnaf package data', cachedResponse);
|
|
110
|
-
let age = 0;
|
|
111
|
-
if (cachedResponse !== undefined) {
|
|
112
|
-
cachedResponse.headers['x-cache'] = 'HIT';
|
|
113
|
-
const created = new Date(cachedResponse.headers.date);
|
|
114
|
-
logger('created', created);
|
|
115
|
-
age = Date.now() - created;
|
|
116
|
-
if (age <= ONE_DAY_MS) {
|
|
117
|
-
return cachedResponse;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
// cached value was older than one day, so go fetch
|
|
121
|
-
try {
|
|
122
|
-
const fetchResponse = await fetch(packageUrl);
|
|
123
|
-
const body = await fetchResponse.text();
|
|
124
|
-
const headers = Object.fromEntries(fetchResponse.headers.entries());
|
|
125
|
-
logger('fresh gnaf package data', {
|
|
126
|
-
body,
|
|
127
|
-
headers
|
|
128
|
-
});
|
|
129
|
-
await cache.set(packageUrl, {
|
|
130
|
-
body,
|
|
131
|
-
headers
|
|
132
|
-
});
|
|
133
|
-
headers['x-cache'] = 'MISS';
|
|
134
|
-
return {
|
|
135
|
-
body,
|
|
136
|
-
headers
|
|
137
|
-
};
|
|
138
|
-
} catch (error_) {
|
|
139
|
-
// we were unable to fetch. if we have cached value that isn't stale, return in
|
|
140
|
-
if (cachedResponse !== undefined && age < THIRTY_DAYS_MS) {
|
|
141
|
-
cachedResponse.headers['warning'] = '110 custom/1.0 "Response is Stale"';
|
|
142
|
-
return cachedResponse;
|
|
143
|
-
}
|
|
144
|
-
// otherwise, throw the original network error
|
|
145
|
-
throw error_;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
104
|
const GNAF_DIR = process.env.GNAF_DIR || `target/gnaf`;
|
|
149
105
|
async function fetchGnafFile() {
|
|
150
|
-
const response = await fetchPackageData();
|
|
106
|
+
const response = await (0, _gnafPackageFetch.fetchPackageData)();
|
|
151
107
|
const pack = JSON.parse(response.body);
|
|
152
108
|
// id as of 16/07 for zip is 4b084096-65e4-4c8e-abbe-5e54ff85f42f
|
|
153
109
|
const dataResource = pack.result.resources.find(r => r.state === 'active' && r.mimetype === 'application/zip');
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.LOADER_USER_AGENT = exports.GNAF_PACKAGE_URL = void 0;
|
|
7
|
+
exports.fetchPackageData = fetchPackageData;
|
|
8
|
+
var _keyv = require("keyv");
|
|
9
|
+
var _keyvFile = require("keyv-file");
|
|
10
|
+
var _debug = _interopRequireDefault(require("debug"));
|
|
11
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
// @jtbd JTBD-400 (Ship Releases Reliably From Trunk)
|
|
13
|
+
//
|
|
14
|
+
// Extracted from service/address-service.js so the loader's data.gov.au CKAN
|
|
15
|
+
// fetch is testable behaviourally. service/address-service.js uses babel-only
|
|
16
|
+
// bare imports that raw Node ESM cannot resolve; this module uses clean ESM
|
|
17
|
+
// with `.js` extensions throughout, so behavioural tests can import it
|
|
18
|
+
// directly. See P033 for the source-inspection anti-pattern this avoids.
|
|
19
|
+
//
|
|
20
|
+
// fetchPackageData accepts `fetch` and `cache` via dependency injection, with
|
|
21
|
+
// defaults to globalThis.fetch and a Keyv-file cache backed by
|
|
22
|
+
// target/keyv-file.msgpack (cwd-relative, NOT module-relative — must match
|
|
23
|
+
// the address-service.js call site's process.cwd()).
|
|
24
|
+
|
|
25
|
+
const logger = (0, _debug.default)('api');
|
|
26
|
+
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
|
|
27
|
+
const THIRTY_DAYS_MS = 30 * ONE_DAY_MS;
|
|
28
|
+
|
|
29
|
+
// Dataset registration on data.gov.au:
|
|
30
|
+
// https://data.gov.au/data/dataset/19432f89-dc3a-4ef3-b943-5326ef1dbecc
|
|
31
|
+
const GNAF_PACKAGE_URL = exports.GNAF_PACKAGE_URL = process.env.GNAF_PACKAGE_URL || 'https://data.gov.au/data/api/3/action/package_show?id=19432f89-dc3a-4ef3-b943-5326ef1dbecc';
|
|
32
|
+
|
|
33
|
+
// data.gov.au's CloudFront WAF returns HTTP 403 on Node-default User-Agent
|
|
34
|
+
// (the CKAN package_show response is HTML "<!DOCTYPE…" instead of JSON).
|
|
35
|
+
// A Mozilla-prefixed compatible-mode UA that identifies the tool gets
|
|
36
|
+
// through. Verified 2026-04-28 via curl: same URL with no UA → 403; with
|
|
37
|
+
// this UA → 200. Surfaced by the failed ADR 029 Phase 1 step 5 v2 populate
|
|
38
|
+
// (run 25032179791).
|
|
39
|
+
const LOADER_USER_AGENT = exports.LOADER_USER_AGENT = 'Mozilla/5.0 (compatible; addressr-loader; +https://github.com/mountain-pass/addressr)';
|
|
40
|
+
|
|
41
|
+
// Default cache instance — same Keyv-file backend service/address-service.js
|
|
42
|
+
// originally created. Path is cwd-relative by design (resolves against
|
|
43
|
+
// process.cwd() at runtime, matching the addressr-loader CLI's working dir).
|
|
44
|
+
const defaultCache = new _keyv.Keyv({
|
|
45
|
+
store: new _keyvFile.KeyvFile({
|
|
46
|
+
filename: 'target/keyv-file.msgpack'
|
|
47
|
+
})
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Fetch the data.gov.au CKAN package_show response for the G-NAF dataset.
|
|
52
|
+
*
|
|
53
|
+
* On cache hit (entry < 1 day old): returns the cached response with
|
|
54
|
+
* `x-cache: HIT` annotation, no network call.
|
|
55
|
+
*
|
|
56
|
+
* On cache miss: fetches with the LOADER_USER_AGENT header, persists the
|
|
57
|
+
* response to cache, returns with `x-cache: MISS`.
|
|
58
|
+
*
|
|
59
|
+
* On fetch failure: returns the cached response with a stale `warning`
|
|
60
|
+
* header if one exists and is younger than 30 days; otherwise rethrows.
|
|
61
|
+
*
|
|
62
|
+
* @param {object} [deps] dependency injection for testing
|
|
63
|
+
* @param {typeof globalThis.fetch} [deps.fetch] fetch implementation (default: globalThis.fetch)
|
|
64
|
+
* @param {{ get: (k: string) => Promise<any>, set: (k: string, v: any) => Promise<any> }} [deps.cache] Keyv-shaped cache (default: target/keyv-file.msgpack)
|
|
65
|
+
* @returns {Promise<{ body: string, headers: Record<string, string> }>}
|
|
66
|
+
*/
|
|
67
|
+
async function fetchPackageData({
|
|
68
|
+
fetch: fetchFunction = globalThis.fetch,
|
|
69
|
+
cache = defaultCache
|
|
70
|
+
} = {}) {
|
|
71
|
+
const packageUrl = GNAF_PACKAGE_URL;
|
|
72
|
+
const cachedResponse = await cache.get(packageUrl);
|
|
73
|
+
logger('cached gnaf package data', cachedResponse);
|
|
74
|
+
let age = 0;
|
|
75
|
+
if (cachedResponse !== undefined) {
|
|
76
|
+
cachedResponse.headers['x-cache'] = 'HIT';
|
|
77
|
+
const created = new Date(cachedResponse.headers.date);
|
|
78
|
+
logger('created', created);
|
|
79
|
+
age = Date.now() - created;
|
|
80
|
+
if (age <= ONE_DAY_MS) {
|
|
81
|
+
return cachedResponse;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Cached value was older than one day, so go fetch.
|
|
85
|
+
try {
|
|
86
|
+
const fetchResponse = await fetchFunction(packageUrl, {
|
|
87
|
+
headers: {
|
|
88
|
+
'User-Agent': LOADER_USER_AGENT
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
const body = await fetchResponse.text();
|
|
92
|
+
const headers = Object.fromEntries(fetchResponse.headers.entries());
|
|
93
|
+
logger('fresh gnaf package data', {
|
|
94
|
+
body,
|
|
95
|
+
headers
|
|
96
|
+
});
|
|
97
|
+
await cache.set(packageUrl, {
|
|
98
|
+
body,
|
|
99
|
+
headers
|
|
100
|
+
});
|
|
101
|
+
headers['x-cache'] = 'MISS';
|
|
102
|
+
return {
|
|
103
|
+
body,
|
|
104
|
+
headers
|
|
105
|
+
};
|
|
106
|
+
} catch (error_) {
|
|
107
|
+
// Network unreachable: serve stale-up-to-30-days from cache rather
|
|
108
|
+
// than fail the load entirely.
|
|
109
|
+
if (cachedResponse !== undefined && age < THIRTY_DAYS_MS) {
|
|
110
|
+
cachedResponse.headers['warning'] = '110\tcustom/1.0 "Response is Stale"';
|
|
111
|
+
return cachedResponse;
|
|
112
|
+
}
|
|
113
|
+
throw error_;
|
|
114
|
+
}
|
|
115
|
+
}
|
package/lib/version.js
CHANGED