@finos/legend-extension-dsl-data-space 10.4.248 → 10.4.249

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.
Files changed (37) hide show
  1. package/lib/components/DataSpaceLegendAIIntegration.d.ts +1 -18
  2. package/lib/components/DataSpaceLegendAIIntegration.d.ts.map +1 -1
  3. package/lib/components/DataSpaceLegendAIIntegration.js +9 -350
  4. package/lib/components/DataSpaceLegendAIIntegration.js.map +1 -1
  5. package/lib/components/DataSpaceLegendAISchema.d.ts +43 -0
  6. package/lib/components/DataSpaceLegendAISchema.d.ts.map +1 -0
  7. package/lib/components/DataSpaceLegendAISchema.js +397 -0
  8. package/lib/components/DataSpaceLegendAISchema.js.map +1 -0
  9. package/lib/components/index.d.ts +1 -0
  10. package/lib/components/index.d.ts.map +1 -1
  11. package/lib/components/index.js +1 -0
  12. package/lib/components/index.js.map +1 -1
  13. package/lib/graph-manager/action/analytics/DataSpaceAnalysis.d.ts +5 -0
  14. package/lib/graph-manager/action/analytics/DataSpaceAnalysis.d.ts.map +1 -1
  15. package/lib/graph-manager/action/analytics/DataSpaceAnalysis.js +5 -0
  16. package/lib/graph-manager/action/analytics/DataSpaceAnalysis.js.map +1 -1
  17. package/lib/graph-manager/protocol/pure/DSL_DataSpace_PureProtocolProcessorPlugin.d.ts.map +1 -1
  18. package/lib/graph-manager/protocol/pure/DSL_DataSpace_PureProtocolProcessorPlugin.js +10 -10
  19. package/lib/graph-manager/protocol/pure/DSL_DataSpace_PureProtocolProcessorPlugin.js.map +1 -1
  20. package/lib/graph-manager/protocol/pure/v1/V1_DSL_DataSpace_PureGraphManagerExtension.d.ts.map +1 -1
  21. package/lib/graph-manager/protocol/pure/v1/V1_DSL_DataSpace_PureGraphManagerExtension.js +18 -5
  22. package/lib/graph-manager/protocol/pure/v1/V1_DSL_DataSpace_PureGraphManagerExtension.js.map +1 -1
  23. package/lib/graph-manager/protocol/pure/v1/engine/analytics/V1_DataSpaceAnalysis.d.ts +8 -0
  24. package/lib/graph-manager/protocol/pure/v1/engine/analytics/V1_DataSpaceAnalysis.d.ts.map +1 -1
  25. package/lib/graph-manager/protocol/pure/v1/engine/analytics/V1_DataSpaceAnalysis.js +13 -3
  26. package/lib/graph-manager/protocol/pure/v1/engine/analytics/V1_DataSpaceAnalysis.js.map +1 -1
  27. package/lib/index.css +1 -1
  28. package/lib/package.json +1 -1
  29. package/package.json +6 -6
  30. package/src/components/DataSpaceLegendAIIntegration.tsx +18 -482
  31. package/src/components/DataSpaceLegendAISchema.ts +562 -0
  32. package/src/components/index.ts +1 -0
  33. package/src/graph-manager/action/analytics/DataSpaceAnalysis.ts +6 -0
  34. package/src/graph-manager/protocol/pure/DSL_DataSpace_PureProtocolProcessorPlugin.ts +16 -13
  35. package/src/graph-manager/protocol/pure/v1/V1_DSL_DataSpace_PureGraphManagerExtension.ts +35 -14
  36. package/src/graph-manager/protocol/pure/v1/engine/analytics/V1_DataSpaceAnalysis.ts +33 -17
  37. package/tsconfig.json +1 -0
@@ -16,59 +16,29 @@
16
16
 
17
17
  import { useState, useEffect, useMemo, useCallback } from 'react';
18
18
  import { observer } from 'mobx-react-lite';
19
- import {
20
- type QueryExplicitExecutionContextInfo,
21
- type MappingModelCoverageAnalysisResult,
22
- Enumeration,
23
- PRIMITIVE_TYPE,
24
- RelationalDatabaseTableSpecification,
25
- } from '@finos/legend-graph';
19
+ import { type QueryExplicitExecutionContextInfo } from '@finos/legend-graph';
26
20
  import {
27
21
  LegendAIChat,
28
22
  LegendAIErrorBoundary,
29
23
  findLegendAIPlugin,
30
- extractParameterSchemas,
31
- buildPropertyDocIndex,
32
- enrichColumnsFromElementDocs,
33
- inferServiceRelationshipsFromAssociations,
34
- extractLambdaPreFilters,
35
- extractModelContext,
36
24
  LegendAIChatTelemetryEventType,
37
25
  bridgeLegendAIServices,
38
26
  useLegendAIChatTelemetryLogger,
39
27
  LegendAIMessageFeedbackRating,
40
- type TDSColumnSchema,
41
28
  type TDSServiceSchema,
42
29
  type LegendAIConfig,
43
- type LegendAIProductMetadata,
44
30
  type LegendAIOrchestratorDataProductCoordinates,
45
- type LegendAIServiceSummary,
46
31
  type LegendAIModelContext,
47
- type LegendAIModelEntity,
48
- type LegendAIModelProperty,
49
- type LegendAIModelAssociation,
50
- type LegendAIExecutableInfo,
51
- type LegendAIColumnPropertyMapping,
52
- type LegendAIParameterInfo,
53
32
  type LegendAIMessageFeedback,
54
33
  } from '@finos/legend-lego/legend-ai';
55
- import { type DiagramAnalysisResult } from '@finos/legend-extension-dsl-diagram';
56
- import { assertErrorThrown, LogEvent } from '@finos/legend-shared';
34
+ import { LogEvent } from '@finos/legend-shared';
57
35
  import type { DataSpaceViewerState } from '../stores/DataSpaceViewerState.js';
58
36
  import { DSL_DATASPACE_EVENT } from '../__lib__/DSL_DataSpace_Event.js';
59
37
  import {
60
- DataSpaceServiceExecutableInfo,
61
- DataSpaceMultiExecutionServiceExecutableInfo,
62
- DataSpaceExecutableTDSResult,
63
- type DataSpaceExecutableAnalysisResult,
64
- } from '../graph-manager/action/analytics/DataSpaceAnalysis.js';
65
- import {
66
- DataSpaceSupportEmail,
67
- DataSpaceSupportCombinedInfo,
68
- } from '../graph/metamodel/pure/model/packageableElements/dataSpace/DSL_DataSpace_DataSpace.js';
69
-
70
- const MAX_QUERY_SCAN_LENGTH = 5_000;
71
- const MAX_QUERY_TEMPLATE_LENGTH = 1000;
38
+ buildDataSpaceModelContext,
39
+ extractTDSServicesFromDataSpace,
40
+ extractMetadataFromDataSpace,
41
+ } from './DataSpaceLegendAISchema.js';
72
42
 
73
43
  const DATA_SPACE_TELEMETRY_EVENT_KEYS: Record<
74
44
  LegendAIChatTelemetryEventType,
@@ -97,387 +67,6 @@ const DATA_SPACE_TELEMETRY_EVENT_KEYS: Record<
97
67
  [LegendAIChatTelemetryEventType.OPEN_IN_DATACUBE_CLICKED]:
98
68
  DSL_DATASPACE_EVENT.LEGEND_AI_OPEN_IN_DATACUBE_CLICKED,
99
69
  };
100
- const ENTITY_PATH_PATTERN =
101
- /(?<entityPath>[A-Za-z_]\w{0,63}(?:::[A-Za-z_]\w{0,63}){0,10})\.all\(\)/g;
102
-
103
- // ────────────────────────────────────────────────────────────────────────────
104
- // Diagram-based model context extraction (fallback when elementDocs is sparse)
105
- // ────────────────────────────────────────────────────────────────────────────
106
-
107
- /**
108
- * Extracts a {@link LegendAIModelContext} from diagram metamodel objects.
109
- * Diagrams always contain the actual Class metamodel references so this
110
- * works even when elementDocs documentation is empty or incomplete.
111
- */
112
- export function extractModelContextFromDiagrams(
113
- diagrams: DiagramAnalysisResult[],
114
- ): LegendAIModelContext {
115
- const entityMap = new Map<string, LegendAIModelEntity>();
116
- const associations: LegendAIModelAssociation[] = [];
117
- const enumPaths = new Map<
118
- string,
119
- { path: string; name: string; values: string[] }
120
- >();
121
-
122
- for (const { diagram } of diagrams) {
123
- for (const classView of diagram.classViews) {
124
- const cls = classView.class.value;
125
- if (entityMap.has(cls.path)) {
126
- continue;
127
- }
128
- const properties: LegendAIModelProperty[] = cls.properties.map((prop) => {
129
- const rawType = prop.genericType.value.rawType;
130
- const typePath = rawType.path;
131
- if (rawType instanceof Enumeration && !enumPaths.has(typePath)) {
132
- enumPaths.set(typePath, {
133
- path: typePath,
134
- name: rawType.name,
135
- values: rawType.values.map((v) => v.name),
136
- });
137
- }
138
- return {
139
- name: prop.name,
140
- type: typePath,
141
- isCollection:
142
- prop.multiplicity.upperBound === undefined ||
143
- prop.multiplicity.upperBound > 1,
144
- isOptional: prop.multiplicity.lowerBound === 0,
145
- };
146
- });
147
- const entity: LegendAIModelEntity = {
148
- path: cls.path,
149
- name: cls.name,
150
- properties,
151
- };
152
- entityMap.set(cls.path, entity);
153
- }
154
-
155
- for (const assocView of diagram.associationViews) {
156
- const assoc = assocView.association.value;
157
- const [propA, propB] = assoc.properties;
158
- associations.push({
159
- name: assoc.name,
160
- leftEntity: assocView.from.classView.value.class.value.path,
161
- leftProperty: propA.name,
162
- rightEntity: assocView.to.classView.value.class.value.path,
163
- rightProperty: propB.name,
164
- });
165
- }
166
- }
167
-
168
- const result: LegendAIModelContext = {
169
- entities: Array.from(entityMap.values()),
170
- associations,
171
- };
172
- if (enumPaths.size > 0) {
173
- result.enumerations = Array.from(enumPaths.values());
174
- }
175
- return result;
176
- }
177
-
178
- /**
179
- * Enriches a {@link LegendAIModelContext} with mapping coverage data.
180
- * Marks entities that are root-mapped (directly queryable by the orchestrator)
181
- * so entity resolution can prefer them.
182
- */
183
- export function enrichModelContextWithMappingCoverage(
184
- modelContext: LegendAIModelContext,
185
- mappingCoverage: MappingModelCoverageAnalysisResult | undefined,
186
- ): void {
187
- if (!mappingCoverage) {
188
- return;
189
- }
190
- for (const entity of modelContext.entities) {
191
- const mapped = mappingCoverage.mappedEntities.find(
192
- (me) => me.path === entity.path,
193
- );
194
- if (mapped?.info?.isRootEntity) {
195
- entity.isRootMapped = true;
196
- }
197
- }
198
- }
199
-
200
- /**
201
- * Builds column-to-property mappings by matching TDS result column names
202
- * to model property names on the root entity.
203
- * Uses case-insensitive, whitespace/underscore-normalized comparison.
204
- */
205
- function buildColumnPropertyMappings(
206
- columns: string[],
207
- rootEntity: LegendAIModelEntity | undefined,
208
- ): LegendAIColumnPropertyMapping[] | undefined {
209
- if (!rootEntity || rootEntity.properties.length === 0) {
210
- return undefined;
211
- }
212
-
213
- const normalizeName = (name: string): string =>
214
- name.toLowerCase().replaceAll(/[\s_]/g, '');
215
-
216
- const propLookup = new Map<string, string>();
217
- for (const prop of rootEntity.properties) {
218
- propLookup.set(normalizeName(prop.name), prop.name);
219
- }
220
-
221
- const mappings: LegendAIColumnPropertyMapping[] = [];
222
- for (const colName of columns) {
223
- const propName = propLookup.get(normalizeName(colName));
224
- if (propName && propName !== colName) {
225
- mappings.push({ columnName: colName, propertyPath: propName });
226
- }
227
- }
228
-
229
- return mappings.length > 0 ? mappings : undefined;
230
- }
231
-
232
- function collectReferencedEntityPaths(queryStr: string): string[] {
233
- const safeQuery = queryStr.slice(0, MAX_QUERY_SCAN_LENGTH);
234
- // Reset lastIndex defensively — the regex is module-scoped and reused.
235
- ENTITY_PATH_PATTERN.lastIndex = 0;
236
- const paths: string[] = [];
237
- const seen = new Set<string>();
238
- let match: RegExpExecArray | null;
239
- while ((match = ENTITY_PATH_PATTERN.exec(safeQuery)) !== null) {
240
- const path = match.groups?.entityPath;
241
- if (path && !seen.has(path)) {
242
- seen.add(path);
243
- paths.push(path);
244
- }
245
- }
246
- return paths;
247
- }
248
-
249
- function buildExecutableInfo(
250
- exec: DataSpaceExecutableAnalysisResult,
251
- entityMap: Map<string, LegendAIModelEntity>,
252
- serviceParameters: Map<string, LegendAIParameterInfo[]>,
253
- ): LegendAIExecutableInfo | undefined {
254
- if (
255
- !(
256
- exec.info instanceof DataSpaceServiceExecutableInfo ||
257
- exec.info instanceof DataSpaceMultiExecutionServiceExecutableInfo
258
- )
259
- ) {
260
- return undefined;
261
- }
262
- const queryStr = exec.info.query;
263
- const referencedEntityPaths = collectReferencedEntityPaths(queryStr);
264
- const rootEntityPath = referencedEntityPaths[0];
265
- if (!rootEntityPath) {
266
- return undefined;
267
- }
268
- const info: LegendAIExecutableInfo = {
269
- title: exec.title,
270
- rootEntityPath,
271
- queryTemplate:
272
- queryStr.length > MAX_QUERY_TEMPLATE_LENGTH
273
- ? `${queryStr.slice(0, MAX_QUERY_TEMPLATE_LENGTH)}...`
274
- : queryStr,
275
- };
276
- if (referencedEntityPaths.length > 1) {
277
- info.referencedEntityPaths = referencedEntityPaths;
278
- }
279
- if (exec.description !== undefined) {
280
- info.description = exec.description;
281
- }
282
- const reqParams = serviceParameters.get(exec.title);
283
- if (reqParams && reqParams.length > 0) {
284
- info.requiredParameters = reqParams;
285
- }
286
- if (exec.result instanceof DataSpaceExecutableTDSResult) {
287
- const columnNames = exec.result.columns.map((c) => c.name);
288
- const colMappings = buildColumnPropertyMappings(
289
- columnNames,
290
- entityMap.get(rootEntityPath),
291
- );
292
- if (colMappings) {
293
- info.columnPropertyMappings = colMappings;
294
- }
295
- }
296
- return info;
297
- }
298
-
299
- export function extractExecutableInfo(
300
- executables: DataSpaceExecutableAnalysisResult[],
301
- modelContext: LegendAIModelContext,
302
- services: TDSServiceSchema[],
303
- ): LegendAIExecutableInfo[] {
304
- const entityMap = new Map(modelContext.entities.map((e) => [e.path, e]));
305
- const serviceParameters = new Map<string, LegendAIParameterInfo[]>();
306
- for (const svc of services) {
307
- if (svc.parameterSchemas && svc.parameterSchemas.length > 0) {
308
- serviceParameters.set(
309
- svc.title,
310
- svc.parameterSchemas.map((p) => ({
311
- name: p.name,
312
- type: p.type ?? PRIMITIVE_TYPE.STRING,
313
- })),
314
- );
315
- }
316
- }
317
- const result: LegendAIExecutableInfo[] = [];
318
- for (const exec of executables) {
319
- const info = buildExecutableInfo(exec, entityMap, serviceParameters);
320
- if (!info) {
321
- continue;
322
- }
323
- result.push(info);
324
- const queryableEntityPaths = info.referencedEntityPaths ?? [
325
- info.rootEntityPath,
326
- ];
327
- for (const entityPath of queryableEntityPaths) {
328
- const entity = entityMap.get(entityPath);
329
- if (entity) {
330
- entity.isQueryable = true;
331
- }
332
- }
333
- }
334
- return result;
335
- }
336
-
337
- export async function extractTDSServicesFromDataSpace(
338
- viewerState: DataSpaceViewerState,
339
- ): Promise<TDSServiceSchema[]> {
340
- const executables = viewerState.dataSpaceAnalysisResult.executables;
341
- const graphManager = viewerState.graphManagerState.graphManager;
342
-
343
- const nested = await Promise.all(
344
- executables.map(async (exec): Promise<TDSServiceSchema[]> => {
345
- if (
346
- !(
347
- exec.info instanceof DataSpaceServiceExecutableInfo ||
348
- exec.info instanceof DataSpaceMultiExecutionServiceExecutableInfo
349
- ) ||
350
- !(exec.result instanceof DataSpaceExecutableTDSResult)
351
- ) {
352
- return [];
353
- }
354
- const info = exec.info;
355
- const tdsResult = exec.result;
356
-
357
- const { parameters, parameterSchemas, parameterExtractionFailed } =
358
- await extractParameterSchemas(
359
- info.query,
360
- graphManager,
361
- viewerState.graphManagerState,
362
- );
363
-
364
- // Extract hardcoded pre-filter constraints from the lambda
365
- let preFilters: TDSServiceSchema['preFilters'];
366
- try {
367
- const rawLambda = await graphManager.pureCodeToLambda(info.query);
368
- const extracted = extractLambdaPreFilters(rawLambda.body);
369
- if (extracted.length > 0) {
370
- preFilters = extracted;
371
- }
372
- } catch (error) {
373
- assertErrorThrown(error);
374
- // pre-filter extraction is best-effort — a service without pre-filters
375
- // still works, the AI just won't know about hardcoded constraints.
376
- viewerState.applicationStore.logService.debug(
377
- LogEvent.create(DSL_DATASPACE_EVENT.ERROR_EXTRACT_LEGEND_AI_SERVICES),
378
- error,
379
- );
380
- }
381
-
382
- const entry: TDSServiceSchema = {
383
- title: exec.title,
384
- pattern: info.pattern,
385
- columns: tdsResult.columns.map((col) => {
386
- const column: TDSColumnSchema = { name: col.name };
387
- if (col.type !== undefined) {
388
- column.type = col.type;
389
- }
390
- if (col.relationalType !== undefined) {
391
- column.relationalType = col.relationalType;
392
- }
393
- if (col.documentation !== undefined) {
394
- column.documentation = col.documentation;
395
- }
396
- if (col.sampleValues !== undefined) {
397
- column.sampleValues = col.sampleValues;
398
- }
399
- return column;
400
- }),
401
- parameters,
402
- ...(parameterSchemas.length > 0 ? { parameterSchemas } : {}),
403
- ...(parameterExtractionFailed
404
- ? { parameterExtractionFailed: true }
405
- : {}),
406
- ...(preFilters ? { preFilters } : {}),
407
- };
408
- if (exec.description !== undefined) {
409
- entry.description = exec.description;
410
- }
411
- return [entry];
412
- }),
413
- );
414
- const services = nested.flat();
415
-
416
- // Enrich columns with documentation and nullability from elementDocs
417
- const propIndex = buildPropertyDocIndex(
418
- viewerState.dataSpaceAnalysisResult.elementDocs,
419
- );
420
- if (propIndex.size > 0) {
421
- for (const svc of services) {
422
- enrichColumnsFromElementDocs(svc.columns, propIndex);
423
- }
424
- }
425
-
426
- return services;
427
- }
428
-
429
- function extractMetadataFromDataSpace(
430
- viewerState: DataSpaceViewerState,
431
- coordinates: string,
432
- services: TDSServiceSchema[],
433
- ): LegendAIProductMetadata {
434
- const result = viewerState.dataSpaceAnalysisResult;
435
-
436
- let supportInfoText: string | undefined;
437
- if (result.supportInfo instanceof DataSpaceSupportEmail) {
438
- supportInfoText = result.supportInfo.address;
439
- } else if (result.supportInfo instanceof DataSpaceSupportCombinedInfo) {
440
- supportInfoText = (result.supportInfo.emails ?? []).join(', ');
441
- }
442
-
443
- const metadata: LegendAIProductMetadata = {
444
- name: result.title ?? result.name,
445
- coordinates,
446
- serviceSummaries: result.executables.map((exec) => {
447
- const summary: LegendAIServiceSummary = {
448
- title: exec.title,
449
- };
450
- if (exec.description !== undefined) {
451
- summary.description = exec.description;
452
- }
453
- if (exec.result instanceof DataSpaceExecutableTDSResult) {
454
- summary.columnNames = exec.result.columns.map((col) => col.name);
455
- }
456
- return summary;
457
- }),
458
- tags: result.taggedValues.map((tv) => ({
459
- profile: tv.profile,
460
- value: tv.value,
461
- })),
462
- };
463
- if (result.description !== undefined) {
464
- metadata.description = result.description;
465
- }
466
- if (supportInfoText !== undefined) {
467
- metadata.supportInfo = supportInfoText;
468
- }
469
- if (services.length >= 2) {
470
- const serviceRels = inferServiceRelationshipsFromAssociations(
471
- services,
472
- result.elementDocs,
473
- );
474
- if (serviceRels.length > 0) {
475
- metadata.serviceRelationships = serviceRels;
476
- }
477
- }
478
- return metadata;
479
- }
480
-
481
70
  const DataSpaceLegendAIIntegrationInner = observer(
482
71
  (props: {
483
72
  dataSpaceViewerState: DataSpaceViewerState;
@@ -550,71 +139,18 @@ const DataSpaceLegendAIIntegrationInner = observer(
550
139
  [dataSpaceViewerState, coordinates, services],
551
140
  );
552
141
 
553
- const modelContext: LegendAIModelContext | undefined = useMemo(() => {
554
- const result = dataSpaceViewerState.dataSpaceAnalysisResult;
555
- let ctx: LegendAIModelContext | undefined;
556
- if (result.elementDocs.length > 0) {
557
- const docsCtx = extractModelContext(result.elementDocs);
558
- if (docsCtx.entities.length > 0) {
559
- ctx = docsCtx;
560
- }
561
- }
562
-
563
- // Fallback: extract from diagram metamodel objects (always available)
564
- if (!ctx && result.diagrams.length > 0) {
565
- const diagramCtx = extractModelContextFromDiagrams(result.diagrams);
566
- if (diagramCtx.entities.length > 0) {
567
- ctx = diagramCtx;
568
- }
569
- }
570
-
571
- if (!ctx) {
572
- return undefined;
573
- }
574
-
575
- const mappingPath =
576
- dataSpaceViewerState.currentExecutionContext.mapping.path;
577
- const mappingCoverage =
578
- result.mappingToMappingCoverageResult?.get(mappingPath);
579
- enrichModelContextWithMappingCoverage(ctx, mappingCoverage);
580
-
581
- if (result.executables.length > 0) {
582
- const execInfos = extractExecutableInfo(
583
- result.executables,
584
- ctx,
585
- services,
586
- );
587
- if (execInfos.length > 0) {
588
- ctx.executables = execInfos;
589
- }
590
- }
591
-
592
- const physicalDatasets =
593
- dataSpaceViewerState.currentExecutionContext.datasets;
594
- if (physicalDatasets.length > 0) {
595
- ctx.datasets = physicalDatasets.map((ds) =>
596
- ds instanceof RelationalDatabaseTableSpecification
597
- ? {
598
- name: ds.name,
599
- database: ds.database,
600
- schema: ds.schema,
601
- table: ds.table,
602
- }
603
- : { name: ds.name },
604
- );
605
- }
606
-
607
- if (result.description) {
608
- ctx.dataspaceDescription = result.description;
609
- }
610
-
611
- return ctx;
612
- }, [
613
- dataSpaceViewerState.dataSpaceAnalysisResult,
614
- dataSpaceViewerState.currentExecutionContext.mapping,
615
- dataSpaceViewerState.currentExecutionContext.datasets,
616
- services,
617
- ]);
142
+ const modelContext: LegendAIModelContext | undefined = useMemo(
143
+ () => buildDataSpaceModelContext(dataSpaceViewerState, services),
144
+ // Deps are the observable inputs the builder reads; the viewer state
145
+ // identity itself is stable for the lifetime of the panel.
146
+ // eslint-disable-next-line react-hooks/exhaustive-deps
147
+ [
148
+ dataSpaceViewerState.dataSpaceAnalysisResult,
149
+ dataSpaceViewerState.currentExecutionContext.mapping,
150
+ dataSpaceViewerState.currentExecutionContext.datasets,
151
+ services,
152
+ ],
153
+ );
618
154
 
619
155
  const telemetryService =
620
156
  dataSpaceViewerState.applicationStore.telemetryService;