gdc-sdk-client-ts 1.0.4 → 1.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 +33 -9
- package/dist/ClientSDK.d.ts +7 -1
- package/dist/ClientSDK.js +69 -16
- package/dist/frontend-services/BaseApiService.d.ts +1 -0
- package/dist/frontend-services/BaseApiService.js +23 -2
- package/dist/frontend-services/org-admin/OrgAdminService.d.ts +8 -4
- package/dist/frontend-services/org-admin/OrgAdminService.js +25 -9
- package/dist/interfaces/others.d.ts +16 -0
- package/dist/utils/entitlements.d.ts +10 -0
- package/dist/utils/entitlements.js +21 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -141,14 +141,14 @@ The `SmartTokenManager` within the SDK handles this flow automatically. API serv
|
|
|
141
141
|
|
|
142
142
|
1. **The `API_INTEGRATORS_GUIDE.md` is the Source of Truth:** All API method implementations, service selectors, and payload structures **must** be based on the official documentation available at `https://raw.githubusercontent.com/Global-DataCare/docs/refs/heads/main/API_INTEGRATORS_GUIDE.md`. The SDK should not invent or assume logic.
|
|
143
143
|
2. **Test Data is the Second Source of Truth:** The mock data files in `client-sdk-ts/__tests__/data/` are sourced from the backend's own test suite and documentation. They represent the ground truth for API responses and **must** be used for all Jest tests and for the in-app `DEMO_MODE`.
|
|
144
|
-
3. **Specific Methods are Simple, The Engine is Smart:** A specific API method (e.g., `
|
|
144
|
+
3. **Specific Methods are Simple, The Engine is Smart:** A specific API method (e.g., `startOrganizationRegistration`) is responsible for knowing the "what" (the endpoint selector, the payload). The `BaseApiService` engine (`resolveAndExecute`) is responsible for the "how" (DID resolution, message construction, job submission).
|
|
145
145
|
|
|
146
146
|
## End-to-End Onboarding Flow
|
|
147
147
|
|
|
148
148
|
The SDK is designed to follow a specific, multi-step business process for onboarding a new organization.
|
|
149
149
|
|
|
150
|
-
1. **`orgAdmin.admin.
|
|
151
|
-
2. **`orgAdmin.admin.
|
|
150
|
+
1. **`orgAdmin.admin.startOrganizationRegistration()`**: A legal representative, authenticated with an external OIDC `id_token`, submits the organization's details. The SDK automatically resolves and targets the operator host DID for this onboarding call. The async response will contain an `Offer`.
|
|
151
|
+
2. **`orgAdmin.admin.confirmOrganizationRegistration()`**: The representative accepts the offer by submitting an order. The SDK again resolves the operator host DID automatically. The async response will contain a set of license codes.
|
|
152
152
|
3. **Payment (External)**: If required, the user completes the payment flow.
|
|
153
153
|
4. **`common.auth.activateDevice()`**: The representative (or another employee) uses a valid license code to register their device. This is a DCR (Dynamic Client Registration) call made to the **Gateway's (Tenant's) DID**. This call creates the `client_id` (`did:web`) for the device and marks it as active.
|
|
154
154
|
5. **`smartToken` Acquisition**: From this point forward, all API calls are authorized (Post-DCR) and must acquire a `smartToken`. The SDK handles this automatically using the device's identity (via `private_key_jwt`) and the user's `id_token`.
|
|
@@ -264,6 +264,31 @@ const sdk = useMemo(() => {
|
|
|
264
264
|
}, []);
|
|
265
265
|
```
|
|
266
266
|
|
|
267
|
+
### API Config Discovery For Frontend Forms
|
|
268
|
+
|
|
269
|
+
When a provider API publishes `/.well-known/api-config.json`, the SDK can fetch it and return the form fields in a UI-friendly shape.
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
const apiConfig = await sdk.fetchWellKnownApiConfig('http://34.8.62.21');
|
|
273
|
+
// or: await sdk.fetchWellKnownApiConfig('did:web:api.example.com');
|
|
274
|
+
|
|
275
|
+
console.log(apiConfig.language); // "es"
|
|
276
|
+
console.log(apiConfig.fields);
|
|
277
|
+
// [
|
|
278
|
+
// { code: 'section', display: 'Departamento o sección: ...' },
|
|
279
|
+
// { code: 'coverage_insurer', display: 'Identificador o nombre de la aseguradora' },
|
|
280
|
+
// ]
|
|
281
|
+
|
|
282
|
+
const supportedFields = await sdk.fetchSupportedFields('http://34.8.62.21');
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
The resolved object includes:
|
|
286
|
+
|
|
287
|
+
- `language`
|
|
288
|
+
- `supportedFields` as the raw `code -> display` map
|
|
289
|
+
- `fields` as `{ code, display }[]` ready for selects/tables
|
|
290
|
+
- `endpoints` with backend route templates such as `create`, `createResponse`, `upload`, and `uploadResponse`
|
|
291
|
+
|
|
267
292
|
### Constructing the Provider DID
|
|
268
293
|
|
|
269
294
|
Before initializing a session, your application must determine the canonical DID of the provider (the organization or host) the user wants to connect to. The SDK supports two scenarios:
|
|
@@ -316,16 +341,15 @@ const session = await sdk.initializeSession({
|
|
|
316
341
|
const orgAdminService = session.orgAdmin.admin;
|
|
317
342
|
|
|
318
343
|
// --- Step 2: Create the Organization ---
|
|
319
|
-
//
|
|
320
|
-
const hostDid = 'did:web:provider.example.com';
|
|
344
|
+
// The SDK derives the operator host DID from `providerDid`.
|
|
321
345
|
const orgClaims = { /* ... organization registration data ... */ };
|
|
322
|
-
const { thid: createOrgThid } = await orgAdminService.
|
|
346
|
+
const { thid: createOrgThid } = await orgAdminService.startOrganizationRegistration(orgClaims, legalRepIdToken);
|
|
323
347
|
// Poll for the result, which will contain an Offer...
|
|
324
348
|
|
|
325
349
|
// --- Step 3: Confirm the Order ---
|
|
326
|
-
// The user accepts the offer.
|
|
350
|
+
// The user accepts the offer. The SDK keeps targeting the operator host DID internally.
|
|
327
351
|
const offerId = '...'; // From the result of the previous step
|
|
328
|
-
const { thid: confirmOrderThid } = await orgAdminService.
|
|
352
|
+
const { thid: confirmOrderThid } = await orgAdminService.confirmOrganizationRegistration(offerId, legalRepIdToken);
|
|
329
353
|
// Poll for the result, which will contain a license code...
|
|
330
354
|
|
|
331
355
|
// --- Step 4: Activate the First Device ---
|
|
@@ -337,7 +361,7 @@ const { thid: activateThid } = await commonAuthService.activateDevice(licenseCod
|
|
|
337
361
|
```
|
|
338
362
|
|
|
339
363
|
### Note on Production vs. Test Onboarding
|
|
340
|
-
The self-service `
|
|
364
|
+
The self-service `startOrganizationRegistration` flow detailed above is intended for the **`test-network`**. Onboarding a new organization in the **production** environment involves a manual verification process by the host provider to ensure the legitimacy of the legal entity.
|
|
341
365
|
|
|
342
366
|
## Testing
|
|
343
367
|
|
package/dist/ClientSDK.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { IWallet } from 'gdc-common-utils-ts/interfaces/IWallet';
|
|
2
2
|
import { IVaultRepository } from './interfaces/IVaultRepository';
|
|
3
3
|
import { ProfileManager } from './ProfileManager';
|
|
4
|
-
import { SdkConfig } from './interfaces/others';
|
|
4
|
+
import { SdkConfig, WellKnownApiConfigResolved, WellKnownApiSupportedField } from './interfaces/others';
|
|
5
5
|
import { DidDocument } from 'gdc-common-utils-ts/models/did';
|
|
6
6
|
import { IVerifier } from './interfaces/ITrustRegistry';
|
|
7
7
|
import { AppInfo } from './interfaces/others';
|
|
@@ -15,6 +15,7 @@ export interface InitializeSessionParams {
|
|
|
15
15
|
role: string;
|
|
16
16
|
providerDid: string;
|
|
17
17
|
appType?: AppInfo['appType'];
|
|
18
|
+
familyId?: string;
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
20
21
|
* @class ClientSDK
|
|
@@ -30,6 +31,10 @@ export declare class ClientSDK {
|
|
|
30
31
|
currentSession: ProfileManager | null;
|
|
31
32
|
profileRegistry: ProfileRegistry | null;
|
|
32
33
|
get operationMode(): string;
|
|
34
|
+
private resolveApiConfigBaseUrl;
|
|
35
|
+
private normalizeWellKnownApiConfig;
|
|
36
|
+
fetchWellKnownApiConfig(source: string): Promise<WellKnownApiConfigResolved>;
|
|
37
|
+
fetchSupportedFields(source: string): Promise<WellKnownApiSupportedField[]>;
|
|
33
38
|
/**
|
|
34
39
|
* Dynamically adds a mock DID Document to the SDK's configuration for DEMO mode.
|
|
35
40
|
* This allows the UI to inject mock data at runtime to bypass network failures.
|
|
@@ -49,6 +54,7 @@ export declare class ClientSDK {
|
|
|
49
54
|
*/
|
|
50
55
|
initializeProfileRegistry(createVault: (registryId: string) => IVaultRepository, registryId?: string): Promise<ProfileRegistry>;
|
|
51
56
|
shutdownSession(): void;
|
|
57
|
+
private normalizeRoleCodeForDid;
|
|
52
58
|
private constructEmployeeDid;
|
|
53
59
|
private constructIndividualDid;
|
|
54
60
|
}
|
package/dist/ClientSDK.js
CHANGED
|
@@ -11,6 +11,55 @@ export class ClientSDK {
|
|
|
11
11
|
get operationMode() {
|
|
12
12
|
return this.sdkConfig.api.operationMode;
|
|
13
13
|
}
|
|
14
|
+
resolveApiConfigBaseUrl(source) {
|
|
15
|
+
const normalized = String(source || '').trim();
|
|
16
|
+
if (!normalized) {
|
|
17
|
+
throw new Error('fetchWellKnownApiConfig requires a provider did:web or a base URL.');
|
|
18
|
+
}
|
|
19
|
+
if (normalized.startsWith('did:web:')) {
|
|
20
|
+
return getBaseUrlFromDidWeb(normalizeDidWeb(normalized));
|
|
21
|
+
}
|
|
22
|
+
return normalized.endsWith('/') ? normalized : `${normalized}/`;
|
|
23
|
+
}
|
|
24
|
+
normalizeWellKnownApiConfig(payload) {
|
|
25
|
+
const raw = (payload && typeof payload === 'object') ? payload : {};
|
|
26
|
+
const supportedFieldsSource = raw.supportedFields && typeof raw.supportedFields === 'object'
|
|
27
|
+
? raw.supportedFields
|
|
28
|
+
: {};
|
|
29
|
+
const endpointsSource = raw.endpoints && typeof raw.endpoints === 'object'
|
|
30
|
+
? raw.endpoints
|
|
31
|
+
: {};
|
|
32
|
+
const supportedFields = Object.fromEntries(Object.entries(supportedFieldsSource)
|
|
33
|
+
.filter(([key, value]) => String(key || '').trim() && String(value || '').trim())
|
|
34
|
+
.map(([key, value]) => [String(key).trim(), String(value).trim()]));
|
|
35
|
+
const endpoints = Object.fromEntries(Object.entries(endpointsSource)
|
|
36
|
+
.filter(([key, value]) => String(key || '').trim() && String(value || '').trim())
|
|
37
|
+
.map(([key, value]) => [String(key).trim(), String(value).trim()]));
|
|
38
|
+
const fields = Object.entries(supportedFields).map(([code, display]) => ({
|
|
39
|
+
code,
|
|
40
|
+
display,
|
|
41
|
+
}));
|
|
42
|
+
return {
|
|
43
|
+
language: String(raw.language || '').trim(),
|
|
44
|
+
supportedFields,
|
|
45
|
+
endpoints,
|
|
46
|
+
fields,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
async fetchWellKnownApiConfig(source) {
|
|
50
|
+
const baseUrl = this.resolveApiConfigBaseUrl(source);
|
|
51
|
+
const url = new URL('.well-known/api-config.json', baseUrl).href;
|
|
52
|
+
const response = await this._fetch(url);
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
throw new Error(`Failed to fetch API config from ${url}`);
|
|
55
|
+
}
|
|
56
|
+
const payload = await response.json();
|
|
57
|
+
return this.normalizeWellKnownApiConfig(payload);
|
|
58
|
+
}
|
|
59
|
+
async fetchSupportedFields(source) {
|
|
60
|
+
const config = await this.fetchWellKnownApiConfig(source);
|
|
61
|
+
return config.fields;
|
|
62
|
+
}
|
|
14
63
|
/**
|
|
15
64
|
* Dynamically adds a mock DID Document to the SDK's configuration for DEMO mode.
|
|
16
65
|
* This allows the UI to inject mock data at runtime to bypass network failures.
|
|
@@ -130,7 +179,7 @@ export class ClientSDK {
|
|
|
130
179
|
const finalProfileId = params.profileId || this.sdkConfig.crypto.randomUUID();
|
|
131
180
|
const userDid = appType === 'Organization'
|
|
132
181
|
? this.constructEmployeeDid(providerDid, email, role)
|
|
133
|
-
: this.constructIndividualDid(providerDid, finalProfileId);
|
|
182
|
+
: await this.constructIndividualDid(providerDid, finalProfileId, email, role, params.familyId);
|
|
134
183
|
const publicKeys = await this.wallet.provisionKeys(finalProfileId);
|
|
135
184
|
const newProfile = {
|
|
136
185
|
id: finalProfileId,
|
|
@@ -174,25 +223,29 @@ export class ClientSDK {
|
|
|
174
223
|
this.currentSession = null;
|
|
175
224
|
}
|
|
176
225
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
226
|
+
normalizeRoleCodeForDid(role, fallback = 'unknown-role') {
|
|
227
|
+
const raw = (role || '').trim();
|
|
228
|
+
if (!raw)
|
|
229
|
+
return fallback;
|
|
230
|
+
const parts = raw.split('|');
|
|
231
|
+
if (parts.length === 2) {
|
|
232
|
+
return (parts[1] || '').trim() || fallback;
|
|
183
233
|
}
|
|
184
|
-
|
|
185
|
-
|
|
234
|
+
return raw;
|
|
235
|
+
}
|
|
236
|
+
constructEmployeeDid(connectorDid, email, role) {
|
|
237
|
+
const roleCode = this.normalizeRoleCodeForDid(role, 'invalid-role');
|
|
238
|
+
const rawDid = `${connectorDid}:employee:${email}:${roleCode}`;
|
|
186
239
|
// Normalize it to ensure canonical representation
|
|
187
240
|
return normalizeDidWeb(rawDid);
|
|
188
241
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const
|
|
195
|
-
const rawDid = `${connectorDid}:
|
|
242
|
+
async constructIndividualDid(connectorDid, profileId, email, role, familyId) {
|
|
243
|
+
const normalizedEmail = (email || '').trim().toLowerCase();
|
|
244
|
+
const emailHash = await this.sdkConfig.crypto.digestString(normalizedEmail, 'SHA-256');
|
|
245
|
+
const multibaseEmailHash = `z${emailHash}`;
|
|
246
|
+
const normalizedFamilyId = (familyId || `profile-${profileId}`).trim();
|
|
247
|
+
const normalizedRole = this.normalizeRoleCodeForDid(role, 'ONESELF').toUpperCase();
|
|
248
|
+
const rawDid = `${connectorDid}:family:${normalizedFamilyId}:${multibaseEmailHash}:${normalizedRole}`;
|
|
196
249
|
return normalizeDidWeb(rawDid);
|
|
197
250
|
}
|
|
198
251
|
}
|
|
@@ -34,6 +34,7 @@ export declare abstract class BaseApiService {
|
|
|
34
34
|
protected readonly appInfo: AppInfo;
|
|
35
35
|
protected readonly cryptoHelper: ICryptoHelper;
|
|
36
36
|
constructor(context: ServiceContext);
|
|
37
|
+
private withRetryPolicy;
|
|
37
38
|
/**
|
|
38
39
|
* The generic "engine" for all API calls. It takes the endpoint ingredients,
|
|
39
40
|
* builds the selector, finds the service, constructs the message, and executes the job.
|
|
@@ -25,6 +25,26 @@ export class BaseApiService {
|
|
|
25
25
|
this.appInfo = context.appInfo; // Initialize AppInfo
|
|
26
26
|
this.cryptoHelper = context.cryptoHelper;
|
|
27
27
|
}
|
|
28
|
+
async withRetryPolicy(operationKey, fn) {
|
|
29
|
+
var _a, _b, _c, _d;
|
|
30
|
+
const policy = (_b = (_a = this.sdkConfig.api).getRetryPolicy) === null || _b === void 0 ? void 0 : _b.call(_a, operationKey);
|
|
31
|
+
const retries = Math.max(0, (_c = policy === null || policy === void 0 ? void 0 : policy.retries) !== null && _c !== void 0 ? _c : 0);
|
|
32
|
+
const delayMs = Math.max(0, (_d = policy === null || policy === void 0 ? void 0 : policy.delayMs) !== null && _d !== void 0 ? _d : 0);
|
|
33
|
+
const attempts = retries + 1;
|
|
34
|
+
let lastError;
|
|
35
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
36
|
+
try {
|
|
37
|
+
return await fn();
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
lastError = error;
|
|
41
|
+
if (attempt < attempts && delayMs > 0) {
|
|
42
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
throw lastError instanceof Error ? lastError : new Error(`Operation failed: ${operationKey}`);
|
|
47
|
+
}
|
|
28
48
|
/**
|
|
29
49
|
* The generic "engine" for all API calls. It takes the endpoint ingredients,
|
|
30
50
|
* builds the selector, finds the service, constructs the message, and executes the job.
|
|
@@ -69,16 +89,17 @@ export class BaseApiService {
|
|
|
69
89
|
jti: this.jobManager.generateId(),
|
|
70
90
|
thid: thid,
|
|
71
91
|
};
|
|
92
|
+
const operationKey = `${serviceSelector.section || 'default'}/${serviceSelector.format}/${serviceSelector.resourceType}/${serviceSelector.action}`;
|
|
72
93
|
// 6. Call the appropriate execution method.
|
|
73
94
|
if (isPublic) {
|
|
74
95
|
if (!idToken)
|
|
75
96
|
throw new Error("idToken is required for public jobs.");
|
|
76
|
-
await this.executePublicJob(endpointUrl, idToken, didcommMessage, serviceSelector);
|
|
97
|
+
await this.withRetryPolicy(operationKey, () => this.executePublicJob(endpointUrl, idToken, didcommMessage, serviceSelector));
|
|
77
98
|
}
|
|
78
99
|
else {
|
|
79
100
|
if (!requiredScope || !idToken)
|
|
80
101
|
throw new Error("requiredScope and idToken are required for authorized jobs.");
|
|
81
|
-
await this.executeAuthorizedJob(targetDid, endpointUrl, requiredScope, idToken, didcommMessage, serviceSelector);
|
|
102
|
+
await this.withRetryPolicy(operationKey, () => this.executeAuthorizedJob(targetDid, endpointUrl, requiredScope, idToken, didcommMessage, serviceSelector));
|
|
82
103
|
}
|
|
83
104
|
return { thid };
|
|
84
105
|
}
|
|
@@ -3,15 +3,20 @@ import { BaseApiService } from "../BaseApiService";
|
|
|
3
3
|
* Service class for handling organization-related administrative actions.
|
|
4
4
|
*/
|
|
5
5
|
export declare class OrgAdminService extends BaseApiService {
|
|
6
|
+
/**
|
|
7
|
+
* Resolves the operator host DID from the current session provider DID.
|
|
8
|
+
* For hosted organizations (`did:web:host:tenant:...`) this returns `did:web:host`.
|
|
9
|
+
* For self-hosted organizations (`did:web:org.example.com`) this returns the same DID.
|
|
10
|
+
*/
|
|
11
|
+
private resolveOperatorDidFromSession;
|
|
6
12
|
/**
|
|
7
13
|
* Registers a new organization with the gateway using an external OIDC token.
|
|
8
14
|
*
|
|
9
15
|
* @param dataClaims The organization's registration form data.
|
|
10
|
-
* @param gatewayDid The DID of the target gateway/host.
|
|
11
16
|
* @param idToken The OIDC token of the legal representative.
|
|
12
17
|
* @returns The thread ID (thid) of the submitted job for polling.
|
|
13
18
|
*/
|
|
14
|
-
|
|
19
|
+
startOrganizationRegistration(dataClaims: object, idToken: string, targetNetwork?: string): Promise<{
|
|
15
20
|
thid: string;
|
|
16
21
|
}>;
|
|
17
22
|
/**
|
|
@@ -19,11 +24,10 @@ export declare class OrgAdminService extends BaseApiService {
|
|
|
19
24
|
* This is the second step in the organization onboarding flow.
|
|
20
25
|
*
|
|
21
26
|
* @param acceptedOfferId The URN identifier of the offer being accepted.
|
|
22
|
-
* @param gatewayDid The DID of the target gateway/host.
|
|
23
27
|
* @param idToken The OIDC token of the legal representative.
|
|
24
28
|
* @returns The thread ID (thid) of the submitted job for polling.
|
|
25
29
|
*/
|
|
26
|
-
|
|
30
|
+
confirmOrganizationRegistration(acceptedOfferId: string, idToken: string, targetNetwork?: string): Promise<{
|
|
27
31
|
thid: string;
|
|
28
32
|
}>;
|
|
29
33
|
/**
|
|
@@ -1,21 +1,37 @@
|
|
|
1
1
|
// Copyright 2025 Antifraud Services Inc. under the Apache License, Version 2.0.
|
|
2
2
|
// File: client-sdk-ts/src/services/org-admin/OrgAdminService.ts
|
|
3
3
|
import { BaseApiService } from "../BaseApiService.js";
|
|
4
|
-
import { EndpointKey, ENDPOINT_REGISTRY } from "../../serviceSelectorRegistry.js";
|
|
4
|
+
import { EndpointKey, ENDPOINT_REGISTRY, defaultNetworkIdentifier } from "../../serviceSelectorRegistry.js";
|
|
5
5
|
/**
|
|
6
6
|
* Service class for handling organization-related administrative actions.
|
|
7
7
|
*/
|
|
8
8
|
export class OrgAdminService extends BaseApiService {
|
|
9
|
+
/**
|
|
10
|
+
* Resolves the operator host DID from the current session provider DID.
|
|
11
|
+
* For hosted organizations (`did:web:host:tenant:...`) this returns `did:web:host`.
|
|
12
|
+
* For self-hosted organizations (`did:web:org.example.com`) this returns the same DID.
|
|
13
|
+
*/
|
|
14
|
+
resolveOperatorDidFromSession() {
|
|
15
|
+
const providerDid = this.profile.providerDid;
|
|
16
|
+
if (!providerDid || !providerDid.startsWith('did:web:')) {
|
|
17
|
+
throw new Error(`[OrgAdminService] Invalid provider DID in session: '${providerDid}'.`);
|
|
18
|
+
}
|
|
19
|
+
const parts = providerDid.split(':');
|
|
20
|
+
if (parts.length < 3) {
|
|
21
|
+
throw new Error(`[OrgAdminService] Invalid provider DID format: '${providerDid}'.`);
|
|
22
|
+
}
|
|
23
|
+
return `did:web:${parts[2]}`;
|
|
24
|
+
}
|
|
9
25
|
/**
|
|
10
26
|
* Registers a new organization with the gateway using an external OIDC token.
|
|
11
27
|
*
|
|
12
28
|
* @param dataClaims The organization's registration form data.
|
|
13
|
-
* @param gatewayDid The DID of the target gateway/host.
|
|
14
29
|
* @param idToken The OIDC token of the legal representative.
|
|
15
30
|
* @returns The thread ID (thid) of the submitted job for polling.
|
|
16
31
|
*/
|
|
17
|
-
async
|
|
18
|
-
console.log(`[OrgAdminService] Calling
|
|
32
|
+
async startOrganizationRegistration(dataClaims, idToken, targetNetwork = defaultNetworkIdentifier) {
|
|
33
|
+
console.log(`[OrgAdminService] Calling startOrganizationRegistration on network: ${targetNetwork}.`);
|
|
34
|
+
const operatorDid = this.resolveOperatorDidFromSession();
|
|
19
35
|
const serviceSelector = Object.assign({}, ENDPOINT_REGISTRY[EndpointKey.REGISTER_TENANT]);
|
|
20
36
|
serviceSelector.sector = targetNetwork;
|
|
21
37
|
const payloadBody = {
|
|
@@ -24,7 +40,7 @@ export class OrgAdminService extends BaseApiService {
|
|
|
24
40
|
meta: { claims: dataClaims },
|
|
25
41
|
}],
|
|
26
42
|
};
|
|
27
|
-
return this.resolveAndExecute(
|
|
43
|
+
return this.resolveAndExecute(operatorDid, serviceSelector, payloadBody, true, // isPublic
|
|
28
44
|
undefined, idToken);
|
|
29
45
|
}
|
|
30
46
|
/**
|
|
@@ -32,12 +48,12 @@ export class OrgAdminService extends BaseApiService {
|
|
|
32
48
|
* This is the second step in the organization onboarding flow.
|
|
33
49
|
*
|
|
34
50
|
* @param acceptedOfferId The URN identifier of the offer being accepted.
|
|
35
|
-
* @param gatewayDid The DID of the target gateway/host.
|
|
36
51
|
* @param idToken The OIDC token of the legal representative.
|
|
37
52
|
* @returns The thread ID (thid) of the submitted job for polling.
|
|
38
53
|
*/
|
|
39
|
-
async
|
|
40
|
-
console.log(`[OrgAdminService] Calling
|
|
54
|
+
async confirmOrganizationRegistration(acceptedOfferId, idToken, targetNetwork = defaultNetworkIdentifier) {
|
|
55
|
+
console.log(`[OrgAdminService] Calling confirmOrganizationRegistration for offer: ${acceptedOfferId} on network: ${targetNetwork}.`);
|
|
56
|
+
const operatorDid = this.resolveOperatorDidFromSession();
|
|
41
57
|
const serviceSelector = Object.assign({}, ENDPOINT_REGISTRY[EndpointKey.CONFIRM_ORDER]);
|
|
42
58
|
serviceSelector.sector = targetNetwork;
|
|
43
59
|
const payloadBody = {
|
|
@@ -51,7 +67,7 @@ export class OrgAdminService extends BaseApiService {
|
|
|
51
67
|
}
|
|
52
68
|
}],
|
|
53
69
|
};
|
|
54
|
-
return this.resolveAndExecute(
|
|
70
|
+
return this.resolveAndExecute(operatorDid, serviceSelector, payloadBody, true, // isPublic
|
|
55
71
|
undefined, idToken);
|
|
56
72
|
}
|
|
57
73
|
/**
|
|
@@ -39,6 +39,22 @@ export interface INetwork {
|
|
|
39
39
|
export interface IApiConfig {
|
|
40
40
|
operationMode: 'DEMO' | 'FAPI';
|
|
41
41
|
legacyFhirEnabled: boolean;
|
|
42
|
+
getRetryPolicy?: (operationKey: string) => {
|
|
43
|
+
retries: number;
|
|
44
|
+
delayMs: number;
|
|
45
|
+
} | undefined;
|
|
46
|
+
}
|
|
47
|
+
export interface WellKnownApiSupportedField {
|
|
48
|
+
code: string;
|
|
49
|
+
display: string;
|
|
50
|
+
}
|
|
51
|
+
export interface WellKnownApiConfigDocument {
|
|
52
|
+
language: string;
|
|
53
|
+
supportedFields: Record<string, string>;
|
|
54
|
+
endpoints: Record<string, string>;
|
|
55
|
+
}
|
|
56
|
+
export interface WellKnownApiConfigResolved extends WellKnownApiConfigDocument {
|
|
57
|
+
fields: WellKnownApiSupportedField[];
|
|
42
58
|
}
|
|
43
59
|
/**
|
|
44
60
|
* Defines the set of optional parameters for enabling the SDK's mock mode.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type LicenseClass = 'member' | 'employee';
|
|
2
|
+
export type OfferEntitlement = {
|
|
3
|
+
offerId?: string;
|
|
4
|
+
checkoutUrl?: string;
|
|
5
|
+
quantity?: number;
|
|
6
|
+
unitPrice?: string;
|
|
7
|
+
totalPrice?: string;
|
|
8
|
+
taxes?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare const parseOfferEntitlementFromClaims: (claims: Record<string, any> | undefined) => OfferEntitlement;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const findClaimBySuffix = (claims, suffix) => {
|
|
2
|
+
if (!claims)
|
|
3
|
+
return undefined;
|
|
4
|
+
for (const [key, value] of Object.entries(claims)) {
|
|
5
|
+
if (typeof value === 'string' && key.endsWith(suffix))
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
8
|
+
return undefined;
|
|
9
|
+
};
|
|
10
|
+
export const parseOfferEntitlementFromClaims = (claims) => {
|
|
11
|
+
const quantityRaw = findClaimBySuffix(claims, 'Offer.eligibleQuantity.value');
|
|
12
|
+
const quantity = typeof quantityRaw === 'string' && quantityRaw.trim().length > 0 ? Number(quantityRaw) : undefined;
|
|
13
|
+
return {
|
|
14
|
+
offerId: findClaimBySuffix(claims, 'Offer.identifier'),
|
|
15
|
+
checkoutUrl: findClaimBySuffix(claims, 'Offer.checkoutPageURLTemplate'),
|
|
16
|
+
quantity: Number.isFinite(quantity) ? quantity : undefined,
|
|
17
|
+
unitPrice: findClaimBySuffix(claims, 'Offer.unitPrice.value'),
|
|
18
|
+
totalPrice: findClaimBySuffix(claims, 'Offer.price.value'),
|
|
19
|
+
taxes: findClaimBySuffix(claims, 'Offer.tax.value'),
|
|
20
|
+
};
|
|
21
|
+
};
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdc-sdk-client-ts",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@noble/hashes": "^1.8.0",
|
|
8
|
-
"gdc-common-utils-ts": "^1.0
|
|
8
|
+
"gdc-common-utils-ts": "^1.1.0"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "jest",
|