dataconv-client-sdk-ts 0.3.2 → 0.4.1
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 +337 -188
- package/dist/DataConvClient.d.ts +16 -2
- package/dist/DataConvClient.d.ts.map +1 -1
- package/dist/DataConvClient.js +167 -13
- package/dist/DataConvClient.js.map +1 -1
- package/dist/cli.js +808 -131
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +117 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/workbook-inspection.d.ts +4 -0
- package/dist/workbook-inspection.d.ts.map +1 -0
- package/dist/workbook-inspection.js +43 -0
- package/dist/workbook-inspection.js.map +1 -0
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -2,14 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
TypeScript SDK for consuming the `adapter-ingestion-py` pre-conversion API.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
-
|
|
5
|
+
## Table of contents
|
|
6
|
+
|
|
7
|
+
- [DataConv Client SDK for TypeScript](#dataconv-client-sdk-for-typescript)
|
|
8
|
+
- [Table of contents](#table-of-contents)
|
|
9
|
+
- [Features](#features)
|
|
10
|
+
- [Installation](#installation)
|
|
11
|
+
- [Configuration](#configuration)
|
|
12
|
+
- [Discover form fields](#discover-form-fields)
|
|
13
|
+
- [Field selection tracking (UI dedup)](#field-selection-tracking-ui-dedup)
|
|
14
|
+
- [React example: multi-dropdown dedup](#react-example-multi-dropdown-dedup)
|
|
15
|
+
- [End-to-end flow](#end-to-end-flow)
|
|
16
|
+
- [Multipart / local file upload](#multipart--local-file-upload)
|
|
17
|
+
- [Backend initialization](#backend-initialization)
|
|
18
|
+
- [CLI](#cli)
|
|
19
|
+
- [Recommended local setup (no IP/DID flags in commands)](#recommended-local-setup-no-ipdid-flags-in-commands)
|
|
20
|
+
- [Command helper and endpoint-resolution conventions](#command-helper-and-endpoint-resolution-conventions)
|
|
21
|
+
- [One-shot evidence script](#one-shot-evidence-script)
|
|
22
|
+
- [Notes](#notes)
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- Discover frontend field descriptors from `/.well-known/api-config.json`
|
|
29
|
+
- Track which fields have already been selected across UI dropdowns
|
|
30
|
+
- Tenant/software configuration creation and polling
|
|
8
31
|
- Excel/XLSX upload via DIDComm attachment or `multipart/form-data`
|
|
9
32
|
- `_upload-response` polling
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
33
|
+
- Promotion through `Composition/_patch` and `Patient/_batch`
|
|
34
|
+
- Tenant-scoped dataset search under `/publisher/.../dataset/{resourceType}/_search`
|
|
35
|
+
- Helpers to read the converted `Bundle` and keep the last received response
|
|
36
|
+
|
|
37
|
+
---
|
|
13
38
|
|
|
14
39
|
## Installation
|
|
15
40
|
|
|
@@ -23,11 +48,161 @@ npm install dataconv-client-sdk-ts
|
|
|
23
48
|
DATACONV_BASE_URL=http://localhost:8080
|
|
24
49
|
```
|
|
25
50
|
|
|
26
|
-
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Discover form fields
|
|
54
|
+
|
|
55
|
+
The client reads the API discovery document published by the server and returns frontend-ready field descriptors.
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { DataConvClient } from 'dataconv-client-sdk-ts';
|
|
59
|
+
|
|
60
|
+
const client = new DataConvClient({
|
|
61
|
+
issuerDid: 'did:web:clinic.example:employee:loader',
|
|
62
|
+
tenantId: 'VATES-B00000000',
|
|
63
|
+
jurisdiction: 'ES',
|
|
64
|
+
crypto: globalThis.crypto
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const apiConfig = await client.getWellKnownApiConfig();
|
|
68
|
+
|
|
69
|
+
console.log(apiConfig.language); // "es"
|
|
70
|
+
console.log(apiConfig.fields);
|
|
71
|
+
// [
|
|
72
|
+
// { code: 'section', display: 'Departamento o sección: ...' },
|
|
73
|
+
// { code: 'coverage_insurer', display: 'Identificador o nombre de la aseguradora' }
|
|
74
|
+
// ]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The returned object includes:
|
|
78
|
+
|
|
79
|
+
| Property | Type | Description |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| `language` | `string` | Language code of the API config (`"es"`, ...) |
|
|
82
|
+
| `fields` | `{ code, display }[]` | Ready-to-use options for dropdowns |
|
|
83
|
+
| `supportedFields` | `Record<string, string>` | Raw `code → display` map |
|
|
84
|
+
| `endpoints` | `Record<string, string>` | Endpoint paths for `create`, `upload`, etc. |
|
|
85
|
+
|
|
86
|
+
The recommended frontend flow is:
|
|
87
|
+
|
|
88
|
+
1. Read `fields` from `getWellKnownApiConfig()`.
|
|
89
|
+
2. Let the user map spreadsheet columns to those field codes.
|
|
90
|
+
3. Submit `mappingConfig.fieldMap` using those same codes.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
### Field selection tracking (UI dedup)
|
|
95
|
+
|
|
96
|
+
The SDK keeps a per-session set of already-selected field codes so the UI can prevent a user from assigning the same field to two different dropdowns.
|
|
97
|
+
|
|
98
|
+
| Method | Returns | Description |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| `selectField(code, mappedTo?)` | `boolean` | `true` if added, `false` if already selected |
|
|
101
|
+
| `unselectField(code)` | `boolean` | `true` if removed, `false` if not present |
|
|
102
|
+
| `isFieldSelected(code)` | `boolean` | Whether the code is currently selected |
|
|
103
|
+
| `getSelectedFieldCodes()` | `string[]` | All currently selected codes |
|
|
104
|
+
| `getSelectedFieldMappings()` | `Record<string, string>` | Map of selected field code → mapped source column |
|
|
105
|
+
| `getSelectedMappingForField(code)` | `string \| undefined` | Source column currently associated to the selected code |
|
|
106
|
+
| `clearSelectedFields()` | `void` | Reset selection state |
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
client.selectField('section'); // true
|
|
110
|
+
client.selectField('section'); // false → already selected
|
|
111
|
+
client.selectField('concept', 'CONCEPTO');
|
|
112
|
+
client.getSelectedMappingForField('concept'); // 'CONCEPTO'
|
|
113
|
+
client.isFieldSelected('section'); // true
|
|
114
|
+
client.unselectField('section'); // true
|
|
115
|
+
client.getSelectedFieldCodes(); // []
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
27
119
|
|
|
28
|
-
|
|
120
|
+
### React example: multi-dropdown dedup
|
|
29
121
|
|
|
30
|
-
|
|
122
|
+
```tsx
|
|
123
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
124
|
+
import { DataConvClient } from 'dataconv-client-sdk-ts';
|
|
125
|
+
|
|
126
|
+
const client = new DataConvClient({
|
|
127
|
+
issuerDid: 'did:web:clinic.example:employee:loader',
|
|
128
|
+
tenantId: 'VATES-B00000000',
|
|
129
|
+
jurisdiction: 'ES',
|
|
130
|
+
crypto: globalThis.crypto
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
type FieldOption = { code: string; display: string };
|
|
134
|
+
|
|
135
|
+
export function FieldMappingForm() {
|
|
136
|
+
const [options, setOptions] = useState<FieldOption[]>([]);
|
|
137
|
+
const [mapping, setMapping] = useState<Record<string, string>>({
|
|
138
|
+
colA: '',
|
|
139
|
+
colB: '',
|
|
140
|
+
colC: ''
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
let mounted = true;
|
|
145
|
+
client.getSupportedFields().then((fields) => {
|
|
146
|
+
if (mounted) setOptions(fields);
|
|
147
|
+
});
|
|
148
|
+
return () => {
|
|
149
|
+
mounted = false;
|
|
150
|
+
client.clearSelectedFields();
|
|
151
|
+
};
|
|
152
|
+
}, []);
|
|
153
|
+
|
|
154
|
+
const selectedSet = useMemo(() => new Set(client.getSelectedFieldCodes()), [mapping]);
|
|
155
|
+
|
|
156
|
+
const onChangeField = (columnKey: string, newCode: string) => {
|
|
157
|
+
const previousCode = mapping[columnKey];
|
|
158
|
+
if (previousCode) {
|
|
159
|
+
client.unselectField(previousCode);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (newCode && !client.selectField(newCode, columnKey)) {
|
|
163
|
+
const mappedTo = client.getSelectedMappingForField(newCode);
|
|
164
|
+
if (previousCode) {
|
|
165
|
+
client.selectField(previousCode);
|
|
166
|
+
}
|
|
167
|
+
alert(`El campo ${newCode} ya ha sido seleccionado${mappedTo ? ` en ${mappedTo}` : ''}.`);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
setMapping((current) => ({ ...current, [columnKey]: newCode }));
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
return (
|
|
175
|
+
<>
|
|
176
|
+
{Object.keys(mapping).map((columnKey) => (
|
|
177
|
+
<select
|
|
178
|
+
key={columnKey}
|
|
179
|
+
value={mapping[columnKey]}
|
|
180
|
+
onChange={(event) => onChangeField(columnKey, event.target.value)}
|
|
181
|
+
>
|
|
182
|
+
<option value="">Selecciona un campo</option>
|
|
183
|
+
{options.map((field) => {
|
|
184
|
+
const selectedInAnotherDropdown =
|
|
185
|
+
selectedSet.has(field.code) && mapping[columnKey] !== field.code;
|
|
186
|
+
return (
|
|
187
|
+
<option key={field.code} value={field.code} disabled={selectedInAnotherDropdown}>
|
|
188
|
+
{field.display}
|
|
189
|
+
</option>
|
|
190
|
+
);
|
|
191
|
+
})}
|
|
192
|
+
</select>
|
|
193
|
+
))}
|
|
194
|
+
</>
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## End-to-end flow
|
|
202
|
+
|
|
203
|
+
Minimum steps to go from field discovery to promoted resources:
|
|
204
|
+
|
|
205
|
+
1. Discover fields and initialize the client.
|
|
31
206
|
2. Create the tenant/software configuration.
|
|
32
207
|
3. Wait for `_create-response`.
|
|
33
208
|
4. Upload the Excel file.
|
|
@@ -52,8 +227,6 @@ client.setVpToken('<vp_token>');
|
|
|
52
227
|
|
|
53
228
|
// 1. Discover frontend field descriptors from the API.
|
|
54
229
|
const apiConfig = await client.getWellKnownApiConfig();
|
|
55
|
-
|
|
56
|
-
// Example UI options:
|
|
57
230
|
const fieldOptions = apiConfig.fields;
|
|
58
231
|
// [
|
|
59
232
|
// { code: 'section', display: 'Departamento o sección: ...' },
|
|
@@ -135,6 +308,13 @@ const searchResponse = await client.searchResources({
|
|
|
135
308
|
});
|
|
136
309
|
```
|
|
137
310
|
|
|
311
|
+
For DIDComm polling responses, the SDK also exposes:
|
|
312
|
+
|
|
313
|
+
- `getMainDiagnosticInfoByResponse(response)` — reads `OperationOutcome.issue[0].diagnostics` from a response.
|
|
314
|
+
- `getMainDiagnosticInfo()` — reads it from the last stored config/conversion response.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
138
318
|
## Multipart / local file upload
|
|
139
319
|
|
|
140
320
|
```ts
|
|
@@ -145,231 +325,200 @@ await client.uploadSpreadsheetMultipart({
|
|
|
145
325
|
});
|
|
146
326
|
```
|
|
147
327
|
|
|
148
|
-
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## Backend initialization
|
|
149
331
|
|
|
150
|
-
|
|
332
|
+
If the backend instantiates the SDK after `Organization/_activate`, it can inject `axios` or `fetch`:
|
|
151
333
|
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
334
|
+
```ts
|
|
335
|
+
import axios from 'axios';
|
|
336
|
+
import { DataConvClient } from 'dataconv-client-sdk-ts';
|
|
337
|
+
|
|
338
|
+
const client = new DataConvClient({
|
|
339
|
+
issuerDid: activatedOrganizationDid,
|
|
340
|
+
tenantId: tenantAlternateName,
|
|
341
|
+
jurisdiction: 'ES',
|
|
342
|
+
httpClient: axios.create({ baseURL: process.env.DATACONV_BASE_URL }),
|
|
343
|
+
crypto: globalThis.crypto
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
client.setVpToken(vpTokenFromActivation);
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Also works with `fetch`:
|
|
350
|
+
|
|
351
|
+
```ts
|
|
352
|
+
const client = new DataConvClient({
|
|
353
|
+
issuerDid: activatedOrganizationDid,
|
|
354
|
+
tenantId: tenantAlternateName,
|
|
355
|
+
jurisdiction: 'ES',
|
|
356
|
+
fetch,
|
|
357
|
+
crypto: globalThis.crypto
|
|
358
|
+
});
|
|
163
359
|
```
|
|
164
360
|
|
|
165
|
-
|
|
361
|
+
---
|
|
166
362
|
|
|
167
|
-
|
|
168
|
-
- the Excel path and size in KB
|
|
169
|
-
- the `Location` header returned by `_upload`
|
|
170
|
-
- the `thid`
|
|
171
|
-
- the exact polling URL used
|
|
172
|
-
- the output JSON file path
|
|
173
|
-
- the main diagnostic text from the final DIDComm response
|
|
363
|
+
## CLI
|
|
174
364
|
|
|
175
|
-
|
|
365
|
+
The package exposes a CLI that can first create mapping config, then upload a file, wait for `_upload-response`, save the full DIDComm response to JSON, and print the main outcome summary.
|
|
176
366
|
|
|
177
|
-
|
|
367
|
+
### Local setup
|
|
178
368
|
|
|
179
|
-
|
|
180
|
-
- or defining `DATACONV_SERVICE_DID_MAP` as a JSON object, for example:
|
|
369
|
+
Copy `.env.example` to `.env.local`, fill in your values, and source it before running commands:
|
|
181
370
|
|
|
182
371
|
```bash
|
|
183
|
-
|
|
372
|
+
cp .env.example .env.local
|
|
373
|
+
# edit .env.local: tenantId, issuerDid, base URL, DATACONV_ID_TOKEN
|
|
374
|
+
source .env.local
|
|
184
375
|
```
|
|
185
376
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
## Discover form fields from `/.well-known/api-config.json`
|
|
377
|
+
The helper script `scripts/evidencia-publicacion.sh` loads `.env.local` automatically (falls back to `.env.example`).
|
|
189
378
|
|
|
190
|
-
|
|
379
|
+
For advanced DID document-based endpoint resolution and full command reference, see [docs/cli-reference.md](docs/cli-reference.md).
|
|
191
380
|
|
|
192
|
-
```
|
|
193
|
-
|
|
381
|
+
```bash
|
|
382
|
+
# 1) login against your IdP (store OIDC id_token locally)
|
|
383
|
+
dataconv login --id-token "$DATACONV_ID_TOKEN"
|
|
194
384
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
// [
|
|
198
|
-
// { code: 'section', display: 'Departamento o sección: ...' },
|
|
199
|
-
// { code: 'coverage_insurer', display: 'Identificador o nombre de la aseguradora' }
|
|
200
|
-
// ]
|
|
385
|
+
# 2) exchange Bearer access token
|
|
386
|
+
dataconv exchange --scope "excel/_upload Subject/_search ChargeItem/_search DocumentReference/_search"
|
|
201
387
|
|
|
202
|
-
|
|
388
|
+
# 3) optional tenant-admin step: create a constrained API key
|
|
389
|
+
dataconv api-key-create --email ops@example.com --target "publisher/cds-es/v1/animal-care/vates-a00000001/dataset/*/*/_upload"
|
|
203
390
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
208
|
-
if (!client.selectField('section')) {
|
|
209
|
-
alert('No puedes seleccionar el mismo campo dos veces');
|
|
210
|
-
}
|
|
391
|
+
# 4) upload + automatic polling
|
|
392
|
+
dataconv upload ./examples/example-api-config.xlsx \
|
|
393
|
+
--output-json ./artifacts/upload-response.json
|
|
211
394
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
{
|
|
215
|
-
softwareId: 'api-config',
|
|
216
|
-
resourceType: 'Composition',
|
|
217
|
-
fileName: 'AppMyPets-api-config.xlsx'
|
|
218
|
-
}
|
|
219
|
-
);
|
|
395
|
+
# 5) optional: patch/batch after review, then search with Bearer token
|
|
396
|
+
dataconv search --resource-type DocumentReference --params '{"_count": 5}'
|
|
220
397
|
|
|
221
|
-
|
|
222
|
-
|
|
398
|
+
# optional alternative to patch
|
|
399
|
+
dataconv batch --thid "<thid-obtenido-de-upload>"
|
|
223
400
|
```
|
|
224
401
|
|
|
225
|
-
|
|
402
|
+
Output includes:
|
|
226
403
|
|
|
227
|
-
-
|
|
228
|
-
-
|
|
229
|
-
- `
|
|
230
|
-
- `
|
|
404
|
+
- exact public upload URL used
|
|
405
|
+
- Excel path and size in KB
|
|
406
|
+
- `Location` header from `_upload`
|
|
407
|
+
- `thid`
|
|
408
|
+
- exact polling URL used
|
|
409
|
+
- output JSON file path
|
|
410
|
+
- main `OperationOutcome.issue[0].description` when available (fallback: `diagnostics`)
|
|
231
411
|
|
|
232
|
-
|
|
412
|
+
When `--mapping-json` is provided to `upload`, CLI creates tenant mapping config first and polls `config/_create-response` before submitting the spreadsheet.
|
|
233
413
|
|
|
234
|
-
|
|
235
|
-
- `getMainDiagnosticInfo()` to read it from the last stored config/conversion response
|
|
414
|
+
The CLI prints evidence-style process logs, for example:
|
|
236
415
|
|
|
237
|
-
|
|
416
|
+
- authentication/exchange against configured dataspace name
|
|
417
|
+
- upload accepted with `thid`
|
|
418
|
+
- automatic `_upload-response` polling
|
|
419
|
+
- final summary and JSON artifact path
|
|
238
420
|
|
|
239
|
-
|
|
240
|
-
2. Let the user map spreadsheet columns to those field codes.
|
|
241
|
-
3. Submit `mappingConfig.fieldMap` using those same codes.
|
|
421
|
+
### Command helper and endpoint-resolution conventions
|
|
242
422
|
|
|
243
|
-
|
|
423
|
+
Use per-command help to see expected conventions:
|
|
244
424
|
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
|
|
425
|
+
```bash
|
|
426
|
+
dataconv help exchange
|
|
427
|
+
dataconv help upload
|
|
428
|
+
dataconv search --help
|
|
429
|
+
```
|
|
248
430
|
|
|
249
|
-
|
|
250
|
-
issuerDid: 'did:web:clinic.example:employee:loader',
|
|
251
|
-
tenantId: 'VATES-B00000000',
|
|
252
|
-
jurisdiction: 'ES',
|
|
253
|
-
crypto: globalThis.crypto
|
|
254
|
-
});
|
|
431
|
+
Service IDs used as CLI resolution metadata:
|
|
255
432
|
|
|
256
|
-
|
|
433
|
+
- `exchange`: `#identity:openid:token:_exchange`
|
|
434
|
+
- `upload` (update): `#dataset:{softwareId}:{resourceType}:_upload`
|
|
435
|
+
- `patch` (publish): `#dataset:{softwareId}:{resourceType}:_patch`
|
|
436
|
+
- `batch` (publish): `#dataset:{softwareId}:{resourceType}:_batch`
|
|
437
|
+
- `search`: `#dataset:api:{resourceType}:_search`
|
|
257
438
|
|
|
258
|
-
|
|
259
|
-
const [options, setOptions] = useState<FieldOption[]>([]);
|
|
260
|
-
const [mapping, setMapping] = useState<Record<string, string>>({
|
|
261
|
-
colA: '',
|
|
262
|
-
colB: '',
|
|
263
|
-
colC: ''
|
|
264
|
-
});
|
|
439
|
+
Fallback env vars for localhost testing:
|
|
265
440
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
return () => {
|
|
272
|
-
mounted = false;
|
|
273
|
-
client.clearSelectedFields();
|
|
274
|
-
};
|
|
275
|
-
}, []);
|
|
441
|
+
- `PUBLISHER_OPENID_EXCHANGE`
|
|
442
|
+
- `PUBLISHER_DATASET_UPDATE`
|
|
443
|
+
- `PUBLISHER_DATASET_PATCH`
|
|
444
|
+
- `PUBLISHER_DATASET_BATCH`
|
|
445
|
+
- `PUBLISHER_DATASET_SEARCH`
|
|
276
446
|
|
|
277
|
-
|
|
447
|
+
Important contract note:
|
|
278
448
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
if (previousCode) {
|
|
282
|
-
client.unselectField(previousCode);
|
|
283
|
-
}
|
|
449
|
+
- `--organization-did` and `--service-id` are stored as CLI-side endpoint-resolution context.
|
|
450
|
+
- The `/exchange` request body remains OpenAPI-compatible (no extra payload fields derived from service-id/fallback metadata).
|
|
284
451
|
|
|
285
|
-
|
|
286
|
-
if (previousCode) {
|
|
287
|
-
client.selectField(previousCode);
|
|
288
|
-
}
|
|
289
|
-
alert(`El campo ${newCode} ya ha sido seleccionado anteriormente.`);
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
452
|
+
### One-shot evidence script
|
|
292
453
|
|
|
293
|
-
|
|
294
|
-
};
|
|
454
|
+
Use the included helper to run login/exchange/upload/search in one shot:
|
|
295
455
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
{options.map((field) => {
|
|
306
|
-
const selectedInAnotherDropdown =
|
|
307
|
-
selectedSet.has(field.code) && mapping[columnKey] !== field.code;
|
|
308
|
-
return (
|
|
309
|
-
<option key={field.code} value={field.code} disabled={selectedInAnotherDropdown}>
|
|
310
|
-
{field.display}
|
|
311
|
-
</option>
|
|
312
|
-
);
|
|
313
|
-
})}
|
|
314
|
-
</select>
|
|
315
|
-
))}
|
|
316
|
-
</>
|
|
317
|
-
);
|
|
318
|
-
}
|
|
456
|
+
```bash
|
|
457
|
+
chmod +x ./scripts/publish-dataset.sh
|
|
458
|
+
./scripts/evidencia-publicacion.sh
|
|
459
|
+
|
|
460
|
+
# opcional: forzar mapping JSON externo + promoción por batch
|
|
461
|
+
DATACONV_PUBLICACION_MAPPING_JSON=./examples/mappings/qvet-v1.json \
|
|
462
|
+
DATACONV_PUBLICACION_HEADER_ROW_INDEX=1 \
|
|
463
|
+
DATACONV_PROMOTION_MODE=batch \
|
|
464
|
+
./scripts/evidencia-publicacion.sh
|
|
319
465
|
```
|
|
320
466
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
- `selectField(code)` returns `false` when the code was already used.
|
|
324
|
-
- `unselectField(code)` frees a code when a dropdown changes value.
|
|
325
|
-
- `getSelectedFieldCodes()` helps disable options already selected elsewhere.
|
|
326
|
-
|
|
327
|
-
## Backend initialization
|
|
467
|
+
Generated files:
|
|
328
468
|
|
|
329
|
-
|
|
469
|
+
- `./artifacts/datasets/upload-response.json`
|
|
470
|
+
- `./artifacts/datasets/search-subject.json`
|
|
471
|
+
- `./artifacts/datasets/search-documentreference.json`
|
|
472
|
+
- `./artifacts/datasets/dcat-files.json`
|
|
330
473
|
|
|
331
|
-
|
|
332
|
-
import axios from 'axios';
|
|
333
|
-
import { DataConvClient } from 'dataconv-client-sdk-ts';
|
|
474
|
+
Notes on scope model and flow:
|
|
334
475
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
476
|
+
- You can request endpoint-action scopes (recommended for evaluator logs), e.g. `excel/_upload` or `DocumentReference/_search`.
|
|
477
|
+
- Backend accepts these action scopes as equivalent to coarse scopes (`dataconv.upload` / `dataconv.read`).
|
|
478
|
+
- `tenantId`, `jurisdiction`, `sector`, and `softwareId` are taken from env/profile defaults (recommended tenant format: `VATES-<NIF>`).
|
|
479
|
+
- API keys do **not** mint `id_token`s. The `id_token` always comes from the external IdP/login step.
|
|
480
|
+
- API keys are used at `/exchange` time to constrain or delegate scopes. The resulting Bearer `access_token` is what you use for config, upload, patch/batch, and search.
|
|
481
|
+
- For explicit exceptional non-confidential mode, send `api_key_profile=api-key-exception.v1` in `/exchange` payload (server must allow it).
|
|
482
|
+
- Recommended API key authorization model is atomic:
|
|
483
|
+
- one rule entry (`data[].resource`) = one consent-like authorization rule = one ODRL policy object.
|
|
484
|
+
- include `scope` (mandatory), and preferably `target` + `instrument` (ODRL).
|
|
338
485
|
|
|
339
|
-
|
|
340
|
-
issuerDid: activatedOrganizationDid,
|
|
341
|
-
tenantId: tenantAlternateName,
|
|
342
|
-
jurisdiction: 'ES',
|
|
343
|
-
httpClient,
|
|
344
|
-
crypto: globalThis.crypto
|
|
345
|
-
});
|
|
486
|
+
When `--service-did` is used, resolve it locally with either `--resolved-base-url` or the `DATACONV_SERVICE_DID_MAP` env variable:
|
|
346
487
|
|
|
347
|
-
|
|
488
|
+
```bash
|
|
489
|
+
export DATACONV_SERVICE_DID_MAP='{"did:web:dataconv-api.example.org":"http://127.0.0.1:8080"}'
|
|
348
490
|
```
|
|
349
491
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
```
|
|
492
|
+
> **DEMO_MODE** — If the backend runs with `DEMO_MODE=true`, generate a test `id_token` without a real IdP:
|
|
493
|
+
>
|
|
494
|
+
> ```bash
|
|
495
|
+
> # genera un id_token demo (alg:none, solo requiere campo email en el payload)
|
|
496
|
+
> export DATACONV_ID_TOKEN=$(node -e '
|
|
497
|
+
> const h = Buffer.from(JSON.stringify({alg:"none",typ:"JWT"})).toString("base64url");
|
|
498
|
+
> const p = Buffer.from(JSON.stringify({email:"admin@example.com"})).toString("base64url");
|
|
499
|
+
> console.log(`${h}.${p}.`);
|
|
500
|
+
> ')
|
|
501
|
+
> # eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIn0.
|
|
502
|
+
> ```
|
|
503
|
+
>
|
|
504
|
+
> Use the token with `--authorization-bearer "$DATACONV_ID_TOKEN"`. No signature verification is performed in demo mode; only the `email` claim is required.
|
|
505
|
+
|
|
506
|
+
---
|
|
361
507
|
|
|
362
508
|
## Notes
|
|
363
509
|
|
|
364
|
-
-
|
|
365
|
-
- `sector` is
|
|
510
|
+
- `tenantId` is the operational tenant identifier, typically the organization's VAT/taxId.
|
|
511
|
+
- `sector` is part of the public route for publisher config, dataset publication, and dataset search.
|
|
366
512
|
- `patchConversion()` defaults to `Composition/_patch`.
|
|
367
513
|
- `batchPromotion()` defaults to `Patient/_batch`.
|
|
368
|
-
- `searchResources()` resolves to `/
|
|
369
|
-
-
|
|
370
|
-
-
|
|
371
|
-
-
|
|
372
|
-
-
|
|
373
|
-
-
|
|
374
|
-
- `
|
|
375
|
-
|
|
514
|
+
- `searchResources()` resolves to `/publisher/cds-{jurisdiction}/v1/{sector}/{tenantId}/dataset/{resourceType}/_search`.
|
|
515
|
+
- Search parameter names are normalized to lowercase (`userSelected` → `userselected`).
|
|
516
|
+
- Search comparators are prefixed in the value: `ge2026-01-01`, `gt...`, `le...`, `lt...`.
|
|
517
|
+
- The backend may require `vp_token` and/or `id_token` depending on `PRECONV_AUTH_MODE`.
|
|
518
|
+
- CSV upload is modeled in the SDK but the current backend only accepts Excel/XLSX.
|
|
519
|
+
- `gdc-common-utils-ts` is an npm dependency; this SDK adds concrete pre-conversion types on top of those DIDComm helpers.
|
|
520
|
+
- ICA VCs and `controller.publicKeyJwk` belong to the `_activate` onboarding flow. `DataConvClient` is used afterwards, once the tenant is already activated.
|
|
521
|
+
|
|
522
|
+
## Roadmap and Briefing
|
|
523
|
+
- `BRIEFING_DATASPACE_EN.md`
|
|
524
|
+
- `TODO_ROADMAP.md`
|
package/dist/DataConvClient.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DataConvBatchOptions, ConversionResultEntry, ConvertedBundleResource, CreateTenantConfigOptions, DataConvClientConfig, DataConvConversionPollOptions, DataConvCreateResult, DataConvDidCommResponse, DataConvMultipartUploadOptions, DataConvOperationOutcome, DataConvPatchOptions, DataConvPatchResponse, DataConvSearchBundle, DataConvSearchOptions, DataConvTenantConfigPollOptions, DataConvUploadDidCommOptions, DataConvUploadResult, DataConvSupportedField, DataConvWellKnownApiConfig, TenantAdapterConfigEntry, TenantAdapterConfigResource } from './types.js';
|
|
1
|
+
import type { DataConvBatchOptions, ConversionResultEntry, ConvertedBundleResource, CreateTenantConfigOptions, DataConvClientConfig, DataConvConversionPollOptions, DataConvCreateResult, DataConvDidCommResponse, DataConvApiKeyCreateActionsOptions, DataConvApiKeyCreateActionsResult, DataConvApiKeyAuthorizationRule, DataConvApiKeyLifecycleOptions, DataConvApiKeyLifecycleResult, DataConvExchangeTokenOptions, DataConvExchangeTokenResult, DataConvMultipartUploadOptions, DataConvOperationOutcome, DataConvOrganizationTenantActivationOptions, DataConvOrganizationTenantActivationResult, DataConvPatchOptions, DataConvPatchResponse, DataConvSearchBundle, DataConvSearchOptions, DataConvTenantConfigPollOptions, DataConvUploadDidCommOptions, DataConvUploadResult, DataConvSupportedField, DataConvWellKnownApiConfig, TenantAdapterConfigEntry, TenantAdapterConfigResource } from './types.js';
|
|
2
2
|
export declare class DataConvClient {
|
|
3
3
|
private readonly config;
|
|
4
4
|
private readonly httpClient?;
|
|
@@ -13,24 +13,38 @@ export declare class DataConvClient {
|
|
|
13
13
|
private lastTenantConfigResponse?;
|
|
14
14
|
private lastConversionResponse?;
|
|
15
15
|
private selectedFieldCodes;
|
|
16
|
+
private selectedFieldMappings;
|
|
16
17
|
constructor(config: DataConvClientConfig);
|
|
17
18
|
setIdToken(idToken: string): void;
|
|
18
19
|
setVpToken(vpToken: string): void;
|
|
20
|
+
activateOrganizationTenant(options: DataConvOrganizationTenantActivationOptions): Promise<DataConvOrganizationTenantActivationResult>;
|
|
19
21
|
getLastTenantConfigResponse(): DataConvDidCommResponse<TenantAdapterConfigResource> | undefined;
|
|
20
22
|
getLastConversionResponse(): DataConvDidCommResponse<ConvertedBundleResource> | undefined;
|
|
21
23
|
clearStoredResponses(): void;
|
|
24
|
+
exchangeToken(options: DataConvExchangeTokenOptions): Promise<DataConvExchangeTokenResult>;
|
|
25
|
+
createTenantApiKeyActions(options: DataConvApiKeyCreateActionsOptions): Promise<DataConvApiKeyCreateActionsResult>;
|
|
26
|
+
createTenantApiKeyRules(options: Omit<DataConvApiKeyCreateActionsOptions, 'actions'> & {
|
|
27
|
+
rules: DataConvApiKeyAuthorizationRule[];
|
|
28
|
+
}): Promise<DataConvApiKeyCreateActionsResult>;
|
|
29
|
+
disableTenantApiKeyActions(options: DataConvApiKeyLifecycleOptions): Promise<DataConvApiKeyLifecycleResult>;
|
|
30
|
+
removeTenantApiKeyActions(options: DataConvApiKeyLifecycleOptions): Promise<DataConvApiKeyLifecycleResult>;
|
|
31
|
+
private updateTenantApiKeyActions;
|
|
22
32
|
getTenantConfigEntries(response?: DataConvDidCommResponse<TenantAdapterConfigResource> | undefined): Array<TenantAdapterConfigEntry<TenantAdapterConfigResource>>;
|
|
23
33
|
getSuccessfulTenantConfigs(response?: DataConvDidCommResponse<TenantAdapterConfigResource> | undefined): TenantAdapterConfigResource[];
|
|
24
34
|
getConversionEntry(response?: DataConvDidCommResponse<ConvertedBundleResource> | undefined): ConversionResultEntry | undefined;
|
|
25
35
|
getConvertedBundle(response?: DataConvDidCommResponse<ConvertedBundleResource> | undefined): ConvertedBundleResource | undefined;
|
|
26
36
|
getResponseIssues<TResource>(response: DataConvDidCommResponse<TResource> | undefined): DataConvOperationOutcome | undefined;
|
|
27
37
|
getMainDiagnosticInfoByResponse<TResource>(response: DataConvDidCommResponse<TResource> | undefined): string | undefined;
|
|
38
|
+
getMainIssueDescriptionByResponse<TResource>(response: DataConvDidCommResponse<TResource> | undefined): string | undefined;
|
|
28
39
|
getSelectedFieldCodes(): string[];
|
|
40
|
+
getSelectedFieldMappings(): Record<string, string>;
|
|
41
|
+
getSelectedMappingForField(code: string): string | undefined;
|
|
29
42
|
isFieldSelected(code: string): boolean;
|
|
30
|
-
selectField(code: string): boolean;
|
|
43
|
+
selectField(code: string, mappedTo?: string): boolean;
|
|
31
44
|
unselectField(code: string): boolean;
|
|
32
45
|
clearSelectedFields(): void;
|
|
33
46
|
getMainDiagnosticInfo(): string | undefined;
|
|
47
|
+
getMainIssueDescription(): string | undefined;
|
|
34
48
|
getWellKnownApiConfig(): Promise<DataConvWellKnownApiConfig>;
|
|
35
49
|
getSupportedFields(): Promise<DataConvSupportedField[]>;
|
|
36
50
|
createTenantConfig(options: CreateTenantConfigOptions): Promise<DataConvCreateResult>;
|