gdc-sdk-node-ts 0.6.3 → 0.6.6
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
CHANGED
|
@@ -77,6 +77,88 @@ Open these tests when you want to see exact method calls and exact inputs:
|
|
|
77
77
|
Portal-style `default-first` discovery with simple `getHosts(...)`,
|
|
78
78
|
`getIndexProviders(...)`, and `getDigitalTwinProviders(...)` calls.
|
|
79
79
|
|
|
80
|
+
## Live GW CORE Flow
|
|
81
|
+
|
|
82
|
+
Use [tests/live-gw-node-runtime.e2e.test.mjs](tests/live-gw-node-runtime.e2e.test.mjs)
|
|
83
|
+
as the canonical runtime flow.
|
|
84
|
+
|
|
85
|
+
Teaching rule:
|
|
86
|
+
|
|
87
|
+
- defaults come from `gdc-common-utils-ts/examples`
|
|
88
|
+
- override with env vars only when your tenant, bearer, or route is different
|
|
89
|
+
- local GW default is `http://127.0.0.1:3000`
|
|
90
|
+
- Docker-exposed GW can be overridden with `BASE_URL=http://127.0.0.1:8000`
|
|
91
|
+
|
|
92
|
+
Current live flow covered by the test suite:
|
|
93
|
+
|
|
94
|
+
1. bootstrap tenant / legal organization
|
|
95
|
+
2. bootstrap doctor or controller employee
|
|
96
|
+
3. bootstrap individual and grant consent for the doctor
|
|
97
|
+
4. ingest two IPS `Communication` bundles, each with one `MedicationStatement`
|
|
98
|
+
5. read the IPS/clinical index and verify both medications are present
|
|
99
|
+
6. persist audit/debug traces in `test-results/*.jsonl`
|
|
100
|
+
|
|
101
|
+
Shared example source of truth:
|
|
102
|
+
|
|
103
|
+
- tenant/route/controller/professional defaults:
|
|
104
|
+
[gdc-common-utils-ts/src/examples/shared.ts](https://github.com/Global-DataCare/gdc-common-utils-ts/blob/main/src/examples/shared.ts)
|
|
105
|
+
- live employee defaults:
|
|
106
|
+
[gdc-common-utils-ts/src/examples/organization-controller.ts](https://github.com/Global-DataCare/gdc-common-utils-ts/blob/main/src/examples/organization-controller.ts)
|
|
107
|
+
- live consent defaults:
|
|
108
|
+
[gdc-common-utils-ts/src/examples/individual-controller.ts](https://github.com/Global-DataCare/gdc-common-utils-ts/blob/main/src/examples/individual-controller.ts)
|
|
109
|
+
|
|
110
|
+
The two medication defaults used by the live test are intentionally reusable:
|
|
111
|
+
|
|
112
|
+
- `Ibuprofen 400 mg`
|
|
113
|
+
- `Paracetamol 600 mg`
|
|
114
|
+
- both every `8` hours
|
|
115
|
+
- both `PRN` / `dosage-asneeded = true`
|
|
116
|
+
- note text keeps the `4` hour gap in English
|
|
117
|
+
|
|
118
|
+
Run the full live runtime baseline:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
npm run test:e2e:live-gw
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Run the IPS ingestion/search branch as well:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
RUN_LIVE_GW_E2E_IPS_INGESTION=1 \
|
|
128
|
+
npm run test:e2e:live-gw
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Common overrides:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
BASE_URL=http://127.0.0.1:3000 \
|
|
135
|
+
AUTH_BEARER=... \
|
|
136
|
+
TENANT_ID=VATES-B00112233 \
|
|
137
|
+
TENANT_ROUTE_ID=acme-live \
|
|
138
|
+
JURISDICTION=ES \
|
|
139
|
+
SECTOR=health-care \
|
|
140
|
+
SUBJECT_DID=did:web:api.acme.org:individual:123 \
|
|
141
|
+
RUN_LIVE_GW_E2E_IPS_INGESTION=1 \
|
|
142
|
+
LIVE_GW_NODE_E2E_DEBUG=1 \
|
|
143
|
+
npm run test:e2e:live-gw
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Docker-exposed GW example:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
BASE_URL=http://127.0.0.1:8000 \
|
|
150
|
+
RUN_LIVE_GW_E2E_IPS_INGESTION=1 \
|
|
151
|
+
npm run test:e2e:live-gw
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Documentation consistency rule for this repo family:
|
|
155
|
+
|
|
156
|
+
- scripts, README examples, Swagger examples, and internal tests must reuse the
|
|
157
|
+
same example data and flow order
|
|
158
|
+
- if a new request/response example is added, add it first to
|
|
159
|
+
`gdc-common-utils-ts/examples` and consume it from there instead of
|
|
160
|
+
re-hardcoding values locally
|
|
161
|
+
|
|
80
162
|
## Dataspace Discovery Quick Map
|
|
81
163
|
|
|
82
164
|
Use the Node discovery layer when your backend or BFF needs to:
|
|
@@ -35,6 +35,8 @@ export declare class DefaultFirstDataspaceDiscovery {
|
|
|
35
35
|
getDigitalTwinProviders(input: SimpleDataspaceDiscoveryRequest): Promise<PublishedProviderMatch[]>;
|
|
36
36
|
private getProviders;
|
|
37
37
|
private createResolver;
|
|
38
|
+
private buildBootstrapPlan;
|
|
39
|
+
private resolveDefaultPublishedProviders;
|
|
38
40
|
}
|
|
39
41
|
/**
|
|
40
42
|
* Convenience factory for the default-first portal/backend discovery facade.
|
|
@@ -1,9 +1,57 @@
|
|
|
1
1
|
// Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
|
|
2
|
-
import { DataspaceDiscoverySourceMode, ServiceCapabilityToken, createDataspaceDiscoveryDefaultsRegistry, DataspaceProtocolVersions, } from 'gdc-common-utils-ts';
|
|
2
|
+
import { DataspaceCoverageScope, DataspaceDiscoverySourceMode, ServiceCapabilityToken, createDataspaceDiscoveryDefaultsRegistry, DataspaceProtocolVersions, inferCoverageScopeFromCountryCode, isProviderServiceCapability, normalizeCountryCode, } from 'gdc-common-utils-ts';
|
|
3
3
|
import { HttpDataspaceResolver } from './HttpDataspaceResolver.js';
|
|
4
4
|
function normalizeString(value) {
|
|
5
5
|
return typeof value === 'string' ? value.trim() : '';
|
|
6
6
|
}
|
|
7
|
+
function equalsIgnoreCase(left, right) {
|
|
8
|
+
return left.trim().toLowerCase() === right.trim().toLowerCase();
|
|
9
|
+
}
|
|
10
|
+
function splitTokens(value) {
|
|
11
|
+
if (Array.isArray(value)) {
|
|
12
|
+
return value.map((entry) => normalizeString(entry)).filter(Boolean);
|
|
13
|
+
}
|
|
14
|
+
const raw = normalizeString(value);
|
|
15
|
+
if (!raw)
|
|
16
|
+
return [];
|
|
17
|
+
return raw.split(',').map((entry) => entry.trim()).filter(Boolean);
|
|
18
|
+
}
|
|
19
|
+
function buildCoverageTokens(areaServed, filterJurisdiction) {
|
|
20
|
+
const tokens = new Set(splitTokens(areaServed).map((token) => token.toUpperCase()));
|
|
21
|
+
const normalizedJurisdiction = normalizeCountryCode(filterJurisdiction);
|
|
22
|
+
if (normalizedJurisdiction) {
|
|
23
|
+
const inferred = inferCoverageScopeFromCountryCode(normalizedJurisdiction);
|
|
24
|
+
if (inferred)
|
|
25
|
+
tokens.add(inferred.toUpperCase());
|
|
26
|
+
}
|
|
27
|
+
return tokens;
|
|
28
|
+
}
|
|
29
|
+
function matchesProviderCoverage(provider, jurisdiction, coverageScope) {
|
|
30
|
+
const normalizedJurisdiction = normalizeCountryCode(jurisdiction);
|
|
31
|
+
const normalizedCoverageScope = normalizeString(coverageScope).toUpperCase();
|
|
32
|
+
if (!normalizedJurisdiction && !normalizedCoverageScope)
|
|
33
|
+
return true;
|
|
34
|
+
const tokens = buildCoverageTokens(provider.areaServed, jurisdiction);
|
|
35
|
+
if (normalizedJurisdiction && tokens.has(normalizedJurisdiction))
|
|
36
|
+
return true;
|
|
37
|
+
if (normalizedCoverageScope && tokens.has(normalizedCoverageScope))
|
|
38
|
+
return true;
|
|
39
|
+
if (normalizedCoverageScope === DataspaceCoverageScope.EuropeanUnion) {
|
|
40
|
+
return Array.from(tokens).some((token) => inferCoverageScopeFromCountryCode(token) === DataspaceCoverageScope.EuropeanUnion);
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
function matchesDefaultPublishedProvider(provider, input, providerCapability) {
|
|
45
|
+
if (!equalsIgnoreCase(provider.category, input.sector))
|
|
46
|
+
return false;
|
|
47
|
+
if (!isProviderServiceCapability(provider.serviceType))
|
|
48
|
+
return false;
|
|
49
|
+
if (!equalsIgnoreCase(provider.serviceType, providerCapability))
|
|
50
|
+
return false;
|
|
51
|
+
if (!matchesProviderCoverage(provider, input.jurisdiction, input.coverageScope))
|
|
52
|
+
return false;
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
7
55
|
function isDefaultsRegistry(value) {
|
|
8
56
|
return Boolean(value)
|
|
9
57
|
&& typeof value === 'object'
|
|
@@ -60,7 +108,15 @@ export class DefaultFirstDataspaceDiscovery {
|
|
|
60
108
|
return this.getProviders(input, ServiceCapabilityToken.DigitalTwinProvider);
|
|
61
109
|
}
|
|
62
110
|
async getProviders(input, providerCapability) {
|
|
63
|
-
const
|
|
111
|
+
const bootstrapPlan = this.buildBootstrapPlan(input, [providerCapability]);
|
|
112
|
+
const defaultPublishedProviders = this.resolveDefaultPublishedProviders(bootstrapPlan.hostingOperators, input, providerCapability);
|
|
113
|
+
if (defaultPublishedProviders.length > 0) {
|
|
114
|
+
return defaultPublishedProviders;
|
|
115
|
+
}
|
|
116
|
+
const resolver = new HttpDataspaceResolver({
|
|
117
|
+
hostingOperators: bootstrapPlan.hostingOperators,
|
|
118
|
+
fetcher: this.fetcher,
|
|
119
|
+
});
|
|
64
120
|
return resolver.resolvePublishedProviders({
|
|
65
121
|
sector: input.sector,
|
|
66
122
|
jurisdiction: input.jurisdiction,
|
|
@@ -69,7 +125,14 @@ export class DefaultFirstDataspaceDiscovery {
|
|
|
69
125
|
});
|
|
70
126
|
}
|
|
71
127
|
createResolver(input, requiredCapabilities) {
|
|
72
|
-
const bootstrapPlan = this.
|
|
128
|
+
const bootstrapPlan = this.buildBootstrapPlan(input, requiredCapabilities);
|
|
129
|
+
return new HttpDataspaceResolver({
|
|
130
|
+
hostingOperators: bootstrapPlan.hostingOperators,
|
|
131
|
+
fetcher: this.fetcher,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
buildBootstrapPlan(input, requiredCapabilities) {
|
|
135
|
+
return this.defaultsRegistry.buildBootstrapPlan({
|
|
73
136
|
jurisdiction: input.jurisdiction,
|
|
74
137
|
version: this.version,
|
|
75
138
|
networkType: this.networkType,
|
|
@@ -78,10 +141,18 @@ export class DefaultFirstDataspaceDiscovery {
|
|
|
78
141
|
requiredCapabilities,
|
|
79
142
|
sourceMode: DataspaceDiscoverySourceMode.DefaultFirst,
|
|
80
143
|
});
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
144
|
+
}
|
|
145
|
+
resolveDefaultPublishedProviders(hosts, input, providerCapability) {
|
|
146
|
+
return hosts.flatMap((host) => (host.publishedProviders || [])
|
|
147
|
+
.filter((provider) => matchesDefaultPublishedProvider(provider, input, providerCapability))
|
|
148
|
+
.map((provider) => ({
|
|
149
|
+
providerDid: provider.providerDid,
|
|
150
|
+
record: provider,
|
|
151
|
+
hostingOperator: host.record,
|
|
152
|
+
hostingOperatorDid: host.operatorDid,
|
|
153
|
+
discoveryUrl: provider.discoveryUrl || host.discoveryUrl,
|
|
154
|
+
catalogUrl: provider.catalogUrl || host.catalogUrl,
|
|
155
|
+
})));
|
|
85
156
|
}
|
|
86
157
|
}
|
|
87
158
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdc-sdk-node-ts",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.6",
|
|
4
4
|
"description": "Next-generation Node runtime package for the GDC SDK family",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Antifraud Services Inc.",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"test:e2e:live-gw": "npm run build && RUN_LIVE_GW_E2E=1 node --test tests/live-gw-node-runtime.e2e.test.mjs"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"gdc-common-utils-ts": "^1.14.
|
|
21
|
-
"gdc-sdk-core-ts": "^0.6.
|
|
20
|
+
"gdc-common-utils-ts": "^1.14.10",
|
|
21
|
+
"gdc-sdk-core-ts": "^0.6.9"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^20.14.10",
|