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