@startinblox/components-ds4go 3.0.2 → 3.1.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/.storybook/preview.ts +1 -0
- package/biome.json +1 -1
- package/dist/custom-getter-ZPFnoSjt-BCNOlbJZ-B4tuxA42.js +338 -0
- package/dist/en-BySYJZMr-CWZl5AwU-CWZl5AwU.js +14 -0
- package/dist/fr-BZZDTsmw-CNDWt66j-CNDWt66j.js +14 -0
- package/dist/index-BSwVRtNS.js +104980 -0
- package/dist/index.js +1 -3032
- package/dist/quill.snow-C_A_QkE8-D-uedtvC-D-uedtvC.js +13 -0
- package/dist/slimselect-NFLzJMfV-DZ7j6Vsj-DZ7j6Vsj.js +5 -0
- package/package.json +9 -8
- package/src/components/cards/ds4go-card-catalog.ts +132 -0
- package/src/components/catalog/ds4go-catalog-filter-holder.ts +459 -0
- package/src/components/catalog/ds4go-customer-holder.ts +162 -0
- package/src/components/catalog/ds4go-fact-bundle-holder.ts +7 -7
- package/src/components/modal/ds4go-customer-modal.ts +134 -0
- package/src/components/modal/ds4go-fact-bundle-modal.ts +2 -2
- package/src/components/solid-customer-list.ts +195 -0
- package/src/components/solid-dsif-explorer-poc.ts +8 -8
- package/src/components/solid-dsp-connector.ts +12 -4
- package/src/components/solid-fact-bundle-creation.ts +266 -169
- package/src/components/solid-fact-bundle.ts +9 -4
- package/src/helpers/components/orbitComponent.ts +12 -13
- package/src/helpers/i18n/configureLocalization.ts +12 -5
- package/src/helpers/index.ts +0 -2
- package/src/styles/cards/ds4go-card-catalog.scss +149 -0
- package/src/styles/fact-bundle-creation.scss +6 -2
- package/src/styles/modal/ds4go-customer-modal.scss +91 -0
- package/src/styles/modal/ds4go-fact-bundle-modal.scss +1 -1
- package/vite.config.ts +7 -7
- package/src/components/solid-boilerplate.ts +0 -76
- package/src/helpers/components/ResourceMapper.ts +0 -469
- package/src/helpers/components/orbitDspComponent.ts +0 -250
- package/src/helpers/mappings/dsp-mapping-config.ts +0 -545
|
@@ -1,469 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Resource Mapper Service
|
|
3
|
-
*
|
|
4
|
-
* Provides unified, configurable mapping from source data formats (FC self-descriptions,
|
|
5
|
-
* DSP datasets, etc.) to TEMS-compatible resource objects with LDP containers.
|
|
6
|
-
*
|
|
7
|
-
* Architecture:
|
|
8
|
-
* - Stores (sib-core): Fetch raw data
|
|
9
|
-
* - ResourceMapper (solid-tems): Transform raw data to TEMS format
|
|
10
|
-
* - Components (solid-tems): Configure mapper and render UI
|
|
11
|
-
*
|
|
12
|
-
* Usage:
|
|
13
|
-
* ```typescript
|
|
14
|
-
* const mapper = new ResourceMapper(fcMappingConfig);
|
|
15
|
-
* const temsResource = mapper.map(sourceData, context);
|
|
16
|
-
* ```
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import type { Resource, UnknownResource } from "@src/component";
|
|
20
|
-
|
|
21
|
-
// import type { Resource } from '@startinblox/core';
|
|
22
|
-
|
|
23
|
-
// ============================================================================
|
|
24
|
-
// Types and Interfaces
|
|
25
|
-
// ============================================================================
|
|
26
|
-
|
|
27
|
-
export interface ResourceMapperConfig {
|
|
28
|
-
/**
|
|
29
|
-
* Base field mappings (simple property extractions)
|
|
30
|
-
*/
|
|
31
|
-
baseFields: Record<string, FieldMapping>;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* LDP Container field mappings (arrays wrapped in ldp:Container)
|
|
35
|
-
*/
|
|
36
|
-
containerFields?: Record<string, ContainerFieldMapping>;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Nested object mappings
|
|
40
|
-
*/
|
|
41
|
-
nestedObjects?: Record<string, NestedObjectMapping>;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Contract negotiation field mappings
|
|
45
|
-
*/
|
|
46
|
-
contractFields?: Record<string, FieldMapping>;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Optional custom post-processing function
|
|
50
|
-
*/
|
|
51
|
-
postProcess?: (
|
|
52
|
-
resource: Resource,
|
|
53
|
-
source: any,
|
|
54
|
-
context: MappingContext,
|
|
55
|
-
) => Resource;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export interface FieldMapping {
|
|
59
|
-
/**
|
|
60
|
-
* Path to source value (array of keys to traverse)
|
|
61
|
-
*/
|
|
62
|
-
source: string[];
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Optional transform function
|
|
66
|
-
*/
|
|
67
|
-
transform?: (value: any, source: any, context: MappingContext) => any;
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Optional fallback if source is not found
|
|
71
|
-
*/
|
|
72
|
-
fallback?: string | ((source: any, context: MappingContext) => any);
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Optional default value if source and fallback are not found
|
|
76
|
-
*/
|
|
77
|
-
defaultValue?: any;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export interface ContainerFieldMapping {
|
|
81
|
-
/**
|
|
82
|
-
* Path to source array
|
|
83
|
-
*/
|
|
84
|
-
source: string[];
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* LDP container type (e.g., 'ldp:Container')
|
|
88
|
-
*/
|
|
89
|
-
containerType: string;
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Item type for contained objects (e.g., 'tems:Category')
|
|
93
|
-
*/
|
|
94
|
-
itemType: string;
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Field mappings for each item
|
|
98
|
-
*/
|
|
99
|
-
itemFields: Record<
|
|
100
|
-
string,
|
|
101
|
-
(item: any, index: number, context: MappingContext) => any
|
|
102
|
-
>;
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Optional filter function to include/exclude items
|
|
106
|
-
*/
|
|
107
|
-
filter?: (item: any, index: number, context: MappingContext) => boolean;
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Optional transform for the entire array before item mapping
|
|
111
|
-
*/
|
|
112
|
-
transform?: (value: any, source: any, context: MappingContext) => any[];
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export interface NestedObjectMapping {
|
|
116
|
-
/**
|
|
117
|
-
* Path to source object
|
|
118
|
-
*/
|
|
119
|
-
source: string[];
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Field mappings for the nested object
|
|
123
|
-
*/
|
|
124
|
-
fields: Record<string, string[] | FieldMapping>;
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Optional type for the nested object
|
|
128
|
-
*/
|
|
129
|
-
type?: string;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export interface MappingContext {
|
|
133
|
-
/**
|
|
134
|
-
* Base URL for generating @id values
|
|
135
|
-
*/
|
|
136
|
-
temsServiceBase?: string;
|
|
137
|
-
temsCategoryBase?: string;
|
|
138
|
-
temsImageBase?: string;
|
|
139
|
-
temsProviderBase?: string;
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Additional context data (provider info, etc.)
|
|
143
|
-
*/
|
|
144
|
-
[key: string]: any;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// ============================================================================
|
|
148
|
-
// Transform Functions
|
|
149
|
-
// ============================================================================
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Strips URN prefixes from IDs
|
|
153
|
-
*/
|
|
154
|
-
export function stripUrnPrefix(value: string | undefined): string {
|
|
155
|
-
if (!value) return "";
|
|
156
|
-
return value.replace(/^urn:uuid:/i, "").replace(/^urn:tems:/i, "");
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Processes ODRL policy: strips URN prefixes and adds target field
|
|
161
|
-
*/
|
|
162
|
-
export function processPolicyTransform(
|
|
163
|
-
policy: any,
|
|
164
|
-
source: any,
|
|
165
|
-
_context: MappingContext,
|
|
166
|
-
): any {
|
|
167
|
-
if (!policy) return null;
|
|
168
|
-
|
|
169
|
-
const processedPolicy = JSON.parse(JSON.stringify(policy)); // Deep clone
|
|
170
|
-
|
|
171
|
-
// Strip URN prefix from policy @id
|
|
172
|
-
if (processedPolicy["@id"]) {
|
|
173
|
-
processedPolicy["@id"] = stripUrnPrefix(processedPolicy["@id"]);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Add target field (asset ID)
|
|
177
|
-
const assetId = getNestedValue(source, ["@id"]) || "";
|
|
178
|
-
processedPolicy.target = stripUrnPrefix(assetId);
|
|
179
|
-
|
|
180
|
-
return processedPolicy;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Extracts first value from array or returns the value itself
|
|
185
|
-
*/
|
|
186
|
-
export function firstOrSelf(value: any): any {
|
|
187
|
-
return Array.isArray(value) && value.length > 0 ? value[0] : value;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Generates unique ID based on name
|
|
192
|
-
*/
|
|
193
|
-
export function generateIdFromName(name: string, base: string): string {
|
|
194
|
-
const slug = name
|
|
195
|
-
.toLowerCase()
|
|
196
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
197
|
-
.replace(/^-+|-+$/g, "");
|
|
198
|
-
return `${base}${slug}`;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// ============================================================================
|
|
202
|
-
// Helper Functions
|
|
203
|
-
// ============================================================================
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Gets nested value from object using path array
|
|
207
|
-
*/
|
|
208
|
-
export function getNestedValue(obj: any, path: string[]): any {
|
|
209
|
-
// If path is empty, return undefined (don't return the entire object)
|
|
210
|
-
// Empty paths are used when the field should be computed via transform or use defaultValue
|
|
211
|
-
if (path.length === 0) {
|
|
212
|
-
return undefined;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
let current = obj;
|
|
216
|
-
for (const key of path) {
|
|
217
|
-
if (current === null || current === undefined) {
|
|
218
|
-
return undefined;
|
|
219
|
-
}
|
|
220
|
-
current = current[key];
|
|
221
|
-
}
|
|
222
|
-
return current;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Sets nested value in object using path array
|
|
227
|
-
*/
|
|
228
|
-
export function setNestedValue(obj: any, path: string[], value: any): void {
|
|
229
|
-
if (path.length === 0) return;
|
|
230
|
-
|
|
231
|
-
let current = obj;
|
|
232
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
233
|
-
const key = path[i];
|
|
234
|
-
if (!(key in current)) {
|
|
235
|
-
current[key] = {};
|
|
236
|
-
}
|
|
237
|
-
current = current[key];
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
current[path[path.length - 1]] = value;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// ============================================================================
|
|
244
|
-
// ResourceMapper Class
|
|
245
|
-
// ============================================================================
|
|
246
|
-
|
|
247
|
-
export class ResourceMapper {
|
|
248
|
-
private config: ResourceMapperConfig;
|
|
249
|
-
|
|
250
|
-
constructor(config: ResourceMapperConfig) {
|
|
251
|
-
this.config = config;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Maps source data to TEMS-compatible resource
|
|
256
|
-
*/
|
|
257
|
-
map(source: any, context: MappingContext = {}): Resource {
|
|
258
|
-
const resource: UnknownResource = {};
|
|
259
|
-
|
|
260
|
-
// Map base fields
|
|
261
|
-
for (const [destKey, mapping] of Object.entries(this.config.baseFields)) {
|
|
262
|
-
const value = this.mapField(source, mapping, context);
|
|
263
|
-
if (value !== undefined) {
|
|
264
|
-
resource[destKey] = value;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Map container fields
|
|
269
|
-
if (this.config.containerFields) {
|
|
270
|
-
for (const [destKey, mapping] of Object.entries(
|
|
271
|
-
this.config.containerFields,
|
|
272
|
-
)) {
|
|
273
|
-
const container = this.mapContainerField(source, mapping, context);
|
|
274
|
-
if (container) {
|
|
275
|
-
resource[destKey] = container;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Map nested objects
|
|
281
|
-
if (this.config.nestedObjects) {
|
|
282
|
-
for (const [destKey, mapping] of Object.entries(
|
|
283
|
-
this.config.nestedObjects,
|
|
284
|
-
)) {
|
|
285
|
-
const nestedObj = this.mapNestedObject(source, mapping, context);
|
|
286
|
-
if (nestedObj) {
|
|
287
|
-
resource[destKey] = nestedObj;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Map contract fields
|
|
293
|
-
if (this.config.contractFields) {
|
|
294
|
-
for (const [destKey, mapping] of Object.entries(
|
|
295
|
-
this.config.contractFields,
|
|
296
|
-
)) {
|
|
297
|
-
const value = this.mapField(source, mapping, context);
|
|
298
|
-
if (value !== undefined) {
|
|
299
|
-
resource[destKey] = value;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Apply post-processing
|
|
305
|
-
if (this.config.postProcess) {
|
|
306
|
-
return this.config.postProcess((resource as Resource), source, context);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
return (resource as Resource);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Maps a single field
|
|
314
|
-
*/
|
|
315
|
-
private mapField(
|
|
316
|
-
source: any,
|
|
317
|
-
mapping: FieldMapping,
|
|
318
|
-
context: MappingContext,
|
|
319
|
-
): any {
|
|
320
|
-
// Get source value
|
|
321
|
-
let value = getNestedValue(source, mapping.source);
|
|
322
|
-
|
|
323
|
-
// Apply transform
|
|
324
|
-
if (mapping.transform) {
|
|
325
|
-
value = mapping.transform(value, source, context);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Apply fallback
|
|
329
|
-
if (value === undefined || value === null || value === "") {
|
|
330
|
-
if (mapping.fallback) {
|
|
331
|
-
if (typeof mapping.fallback === "function") {
|
|
332
|
-
value = mapping.fallback(source, context);
|
|
333
|
-
} else {
|
|
334
|
-
// Fallback is a path to another field
|
|
335
|
-
value = getNestedValue(source, [mapping.fallback]);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Apply default value
|
|
341
|
-
if (value === undefined || value === null || value === "") {
|
|
342
|
-
value = mapping.defaultValue;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
return value;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
* Maps a container field (array wrapped in LDP container)
|
|
350
|
-
*/
|
|
351
|
-
private mapContainerField(
|
|
352
|
-
source: any,
|
|
353
|
-
mapping: ContainerFieldMapping,
|
|
354
|
-
context: MappingContext,
|
|
355
|
-
): any {
|
|
356
|
-
// Get source array
|
|
357
|
-
let sourceArray = getNestedValue(source, mapping.source);
|
|
358
|
-
|
|
359
|
-
if (!sourceArray) {
|
|
360
|
-
return null;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// Apply array-level transform
|
|
364
|
-
if (mapping.transform) {
|
|
365
|
-
sourceArray = mapping.transform(sourceArray, source, context);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
// Ensure it's an array
|
|
369
|
-
if (!Array.isArray(sourceArray)) {
|
|
370
|
-
sourceArray = [sourceArray];
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// Filter items
|
|
374
|
-
if (mapping.filter) {
|
|
375
|
-
sourceArray = sourceArray.filter((item: any, index: number) =>
|
|
376
|
-
mapping.filter!(item, index, context),
|
|
377
|
-
);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// Map items
|
|
381
|
-
const items = sourceArray.map((item: any, index: number) => {
|
|
382
|
-
const mappedItem: any = {
|
|
383
|
-
"@type": mapping.itemType,
|
|
384
|
-
};
|
|
385
|
-
|
|
386
|
-
for (const [fieldKey, fieldFn] of Object.entries(mapping.itemFields)) {
|
|
387
|
-
const fieldValue = fieldFn(item, index, context);
|
|
388
|
-
if (fieldValue !== undefined) {
|
|
389
|
-
mappedItem[fieldKey] = fieldValue;
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
return mappedItem;
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
// Return LDP container
|
|
397
|
-
return {
|
|
398
|
-
"@type": mapping.containerType,
|
|
399
|
-
"ldp:contains": items,
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
/**
|
|
404
|
-
* Maps a nested object
|
|
405
|
-
*/
|
|
406
|
-
private mapNestedObject(
|
|
407
|
-
source: any,
|
|
408
|
-
mapping: NestedObjectMapping,
|
|
409
|
-
context: MappingContext,
|
|
410
|
-
): any {
|
|
411
|
-
// Get source object
|
|
412
|
-
const sourceObj = getNestedValue(source, mapping.source);
|
|
413
|
-
|
|
414
|
-
if (!sourceObj) {
|
|
415
|
-
return null;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
const nestedObj: any = {};
|
|
419
|
-
|
|
420
|
-
// Add type if specified
|
|
421
|
-
if (mapping.type) {
|
|
422
|
-
nestedObj["@type"] = mapping.type;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// Map fields
|
|
426
|
-
for (const [destKey, fieldMapping] of Object.entries(mapping.fields)) {
|
|
427
|
-
let value: any;
|
|
428
|
-
|
|
429
|
-
if (Array.isArray(fieldMapping)) {
|
|
430
|
-
// Simple path mapping
|
|
431
|
-
value = getNestedValue(sourceObj, fieldMapping);
|
|
432
|
-
} else {
|
|
433
|
-
// Full FieldMapping
|
|
434
|
-
value = this.mapField(sourceObj, fieldMapping, context);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
if (value !== undefined) {
|
|
438
|
-
nestedObj[destKey] = value;
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
return Object.keys(nestedObj).length > (mapping.type ? 1 : 0)
|
|
443
|
-
? nestedObj
|
|
444
|
-
: null;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
/**
|
|
448
|
-
* Unwraps LDP containers (converts ldp:contains arrays to plain arrays)
|
|
449
|
-
*
|
|
450
|
-
* This is typically called by components after mapping to simplify data structure
|
|
451
|
-
* for rendering.
|
|
452
|
-
*/
|
|
453
|
-
static unwrapLdpContainers(resource: Resource): Resource {
|
|
454
|
-
const unwrapped: Resource = { ...resource };
|
|
455
|
-
|
|
456
|
-
for (const [key, value] of Object.entries(unwrapped)) {
|
|
457
|
-
if (
|
|
458
|
-
value &&
|
|
459
|
-
typeof value === "object" &&
|
|
460
|
-
value["@type"] === "ldp:Container" &&
|
|
461
|
-
value["ldp:contains"]
|
|
462
|
-
) {
|
|
463
|
-
unwrapped[key] = value["ldp:contains"];
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
return unwrapped;
|
|
468
|
-
}
|
|
469
|
-
}
|
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
StoreService,
|
|
3
|
-
StoreType,
|
|
4
|
-
} from "https://cdn.jsdelivr.net/npm/@startinblox/core@beta/+esm";
|
|
5
|
-
import { OrbitComponent, setupComponentSubscriptions } from "@helpers";
|
|
6
|
-
import { ResourceMapper } from "@helpers/components/ResourceMapper";
|
|
7
|
-
import { dspMappingConfig } from "@helpers/mappings/dsp-mapping-config";
|
|
8
|
-
import type { DSPComponentParameters, Resource } from "@src/component.d.ts";
|
|
9
|
-
import type { PropertyValues } from "lit";
|
|
10
|
-
import { property, state } from "lit/decorators.js";
|
|
11
|
-
|
|
12
|
-
export interface DSPProviderConfig {
|
|
13
|
-
name: string;
|
|
14
|
-
address: string;
|
|
15
|
-
color?: string;
|
|
16
|
-
participantId?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export default class extends OrbitComponent {
|
|
20
|
-
@property({ attribute: "participant-connector-uri", reflect: true })
|
|
21
|
-
participantConnectorUri?: string;
|
|
22
|
-
|
|
23
|
-
@property({ attribute: "participant-id", reflect: true })
|
|
24
|
-
participantId?: string;
|
|
25
|
-
|
|
26
|
-
@property({ attribute: "participant-api-key", reflect: true })
|
|
27
|
-
participantApiKey?: string;
|
|
28
|
-
|
|
29
|
-
@property({ attribute: "api-gateway-config", reflect: true })
|
|
30
|
-
apiGatewayConfig?: string;
|
|
31
|
-
|
|
32
|
-
@state()
|
|
33
|
-
storeService?: StoreService;
|
|
34
|
-
|
|
35
|
-
@state()
|
|
36
|
-
dspStoreService?: StoreService;
|
|
37
|
-
|
|
38
|
-
@state()
|
|
39
|
-
_apiGatewayConfigParsed?: any;
|
|
40
|
-
|
|
41
|
-
@property({ type: Array })
|
|
42
|
-
providers: DSPProviderConfig[] = [];
|
|
43
|
-
|
|
44
|
-
willUpdate(_changedProperties: PropertyValues<this>) {
|
|
45
|
-
if (
|
|
46
|
-
(!this.providers || this.providers.length === 0) &&
|
|
47
|
-
this.component?.parameters?.providers
|
|
48
|
-
) {
|
|
49
|
-
this.providers = this.component.parameters.providers || [];
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
protected async _attach(
|
|
54
|
-
defaultRoute: boolean | string,
|
|
55
|
-
setupSubscriptions: boolean,
|
|
56
|
-
ignoreRouter: boolean,
|
|
57
|
-
) {
|
|
58
|
-
if (!this.orbit) {
|
|
59
|
-
if (window.orbit) {
|
|
60
|
-
this.orbit = window.orbit;
|
|
61
|
-
if (setupSubscriptions) {
|
|
62
|
-
setupComponentSubscriptions({
|
|
63
|
-
component: this,
|
|
64
|
-
defaultRoute: defaultRoute,
|
|
65
|
-
ignoreRouter: ignoreRouter,
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
if (this.route) {
|
|
69
|
-
this.component = this.orbit.getComponentFromRoute(this.route);
|
|
70
|
-
if (this.component) {
|
|
71
|
-
for (const c of this.orbit.components) {
|
|
72
|
-
if (c.uniq === this.component.uniq) {
|
|
73
|
-
c.instance = this;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Get connector configuration from attributes or component params
|
|
80
|
-
if (!this.participantConnectorUri) {
|
|
81
|
-
const params = this.component?.parameters as DSPComponentParameters;
|
|
82
|
-
if (params?.["participant-connector-uri"]) {
|
|
83
|
-
this.participantConnectorUri =
|
|
84
|
-
params["participant-connector-uri"];
|
|
85
|
-
}
|
|
86
|
-
if (params?.["participant-api-key"]) {
|
|
87
|
-
this.participantApiKey = params["participant-api-key"];
|
|
88
|
-
}
|
|
89
|
-
if (params?.providers) {
|
|
90
|
-
this.providers = params.providers;
|
|
91
|
-
}
|
|
92
|
-
if (params?.["participant-id"]) {
|
|
93
|
-
this.participantId = params["participant-id"];
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Parse API Gateway configuration (OPTIONAL)
|
|
98
|
-
const params = this.component?.parameters as DSPComponentParameters;
|
|
99
|
-
|
|
100
|
-
if (params?.["api-gateway-config"]) {
|
|
101
|
-
this.apiGatewayConfig = params["api-gateway-config"];
|
|
102
|
-
try {
|
|
103
|
-
this._apiGatewayConfigParsed =
|
|
104
|
-
typeof this.apiGatewayConfig === "string"
|
|
105
|
-
? JSON.parse(this.apiGatewayConfig)
|
|
106
|
-
: this.apiGatewayConfig;
|
|
107
|
-
} catch (_error) {
|
|
108
|
-
this._apiGatewayConfigParsed = null;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Initialize DataspaceConnectorStore
|
|
113
|
-
if (!this.storeService && this.participantConnectorUri) {
|
|
114
|
-
try {
|
|
115
|
-
// Use unique store name based on component ID to avoid singleton conflicts
|
|
116
|
-
const storeName = `dsp-connector-${this.component.uniq}`;
|
|
117
|
-
|
|
118
|
-
const config = {
|
|
119
|
-
type: StoreType.DataspaceConnector,
|
|
120
|
-
endpoint: this.participantConnectorUri,
|
|
121
|
-
catalogEndpoint: `${this.participantConnectorUri}/management/v3/catalog/request`,
|
|
122
|
-
contractNegotiationEndpoint: `${this.participantConnectorUri}/management/v3/contractnegotiations`,
|
|
123
|
-
transferProcessEndpoint: `${this.participantConnectorUri}/management/v3/transferprocesses`,
|
|
124
|
-
edrsEndpoint: `${this.participantConnectorUri}/management/v3/edrs`,
|
|
125
|
-
authMethod: "dsp-api-key",
|
|
126
|
-
dspApiKey: this.participantApiKey || "password",
|
|
127
|
-
participantId: this.participantId || "stbx-consumer",
|
|
128
|
-
retryAttempts: 10,
|
|
129
|
-
timeout: 30000,
|
|
130
|
-
// Include API Gateway configuration ONLY if available
|
|
131
|
-
...(this._apiGatewayConfigParsed && {
|
|
132
|
-
apiGatewayConfig: this._apiGatewayConfigParsed,
|
|
133
|
-
}),
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
this.storeService = StoreService.addStore(storeName, config);
|
|
137
|
-
// Also set dspStoreService for contract negotiation in tems-modal
|
|
138
|
-
this.dspStoreService = this.storeService;
|
|
139
|
-
// Expose globally for FederatedIndexManager auto-negotiation
|
|
140
|
-
if (!window.dspStore) {
|
|
141
|
-
window.dspStore = this.storeService;
|
|
142
|
-
}
|
|
143
|
-
} catch (error) {
|
|
144
|
-
console.error("DSP Store initialization error:", error);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
await this._afterAttach();
|
|
148
|
-
|
|
149
|
-
this.ready = true;
|
|
150
|
-
this.dispatchEvent(
|
|
151
|
-
new CustomEvent("component-ready", {
|
|
152
|
-
detail: {
|
|
153
|
-
component: this.component,
|
|
154
|
-
},
|
|
155
|
-
}),
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
return Promise.resolve(true);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
return Promise.resolve(false);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Fetch catalog from multiple DSP providers
|
|
167
|
-
*/
|
|
168
|
-
async fetchFederatedCatalog(): Promise<Resource[]> {
|
|
169
|
-
if (!this.storeService || !this.providers || this.providers.length === 0) {
|
|
170
|
-
console.warn("DSP store or providers not configured");
|
|
171
|
-
return [];
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
try {
|
|
175
|
-
// FIXME: Avoid magic strings
|
|
176
|
-
// Use default base URLs for TEMS resources
|
|
177
|
-
const baseURL = "https://api.tems.example.com/services/";
|
|
178
|
-
|
|
179
|
-
// Initialize ResourceMapper with DSP configuration
|
|
180
|
-
const mapper = new ResourceMapper(dspMappingConfig);
|
|
181
|
-
|
|
182
|
-
const catalogPromises = this.providers.map(async (provider) => {
|
|
183
|
-
try {
|
|
184
|
-
const catalog = await this.storeService.getCatalog(provider.address);
|
|
185
|
-
|
|
186
|
-
if (catalog?.["dcat:dataset"]) {
|
|
187
|
-
// Normalize datasets to array - catalog may return single object if only one dataset
|
|
188
|
-
const rawDatasets = catalog["dcat:dataset"];
|
|
189
|
-
const datasets = Array.isArray(rawDatasets)
|
|
190
|
-
? rawDatasets
|
|
191
|
-
: [rawDatasets];
|
|
192
|
-
// IMPORTANT: Prefer config's participantId over catalog response
|
|
193
|
-
// This ensures assets from different providers are distinguishable
|
|
194
|
-
// even if EDC catalogs return the same participantId (misconfiguration)
|
|
195
|
-
const catalogResponseParticipantId =
|
|
196
|
-
catalog.participantId ||
|
|
197
|
-
catalog["edc:participantId"] ||
|
|
198
|
-
catalog["https://w3id.org/edc/v0.0.1/ns/participantId"];
|
|
199
|
-
|
|
200
|
-
// Use config's participantId as authoritative (user configured it intentionally)
|
|
201
|
-
// Only fall back to catalog response if config doesn't have participantId
|
|
202
|
-
const effectiveParticipantId =
|
|
203
|
-
provider.participantId || catalogResponseParticipantId;
|
|
204
|
-
|
|
205
|
-
// Map each dataset using ResourceMapper
|
|
206
|
-
return datasets.map((dataset: Resource) => {
|
|
207
|
-
const context = {
|
|
208
|
-
temsServiceBase: baseURL,
|
|
209
|
-
temsCategoryBase: baseURL.replace(
|
|
210
|
-
"/services/",
|
|
211
|
-
"/providers/categories/",
|
|
212
|
-
),
|
|
213
|
-
temsImageBase: baseURL.replace(
|
|
214
|
-
"/services/",
|
|
215
|
-
"/objects/images/",
|
|
216
|
-
),
|
|
217
|
-
temsProviderBase: baseURL.replace("/services/", "/providers/"),
|
|
218
|
-
// Provider context for mapping - use config's participantId as authoritative
|
|
219
|
-
providerName: provider.name,
|
|
220
|
-
providerAddress: provider.address,
|
|
221
|
-
providerColor: provider.color,
|
|
222
|
-
providerParticipantId: effectiveParticipantId,
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
return mapper.map(dataset, context);
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return [];
|
|
230
|
-
} catch (_error) {
|
|
231
|
-
return [];
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
const results = await Promise.all(catalogPromises);
|
|
236
|
-
const allDatasets = results.flat();
|
|
237
|
-
|
|
238
|
-
return allDatasets;
|
|
239
|
-
} catch (_error) {
|
|
240
|
-
return [];
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Get current DataspaceConnectorStore instance
|
|
246
|
-
*/
|
|
247
|
-
getStoreService() {
|
|
248
|
-
return this.storeService;
|
|
249
|
-
}
|
|
250
|
-
}
|