@finos/legend-application-marketplace 0.2.12 → 0.2.14
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/lib/components/MarketplaceCard/FieldSearchResultListItem.d.ts +1 -0
- package/lib/components/MarketplaceCard/FieldSearchResultListItem.d.ts.map +1 -1
- package/lib/components/MarketplaceCard/FieldSearchResultListItem.js +30 -12
- package/lib/components/MarketplaceCard/FieldSearchResultListItem.js.map +1 -1
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/package.json +1 -1
- package/lib/pages/Lakehouse/entitlements/EntitlementsClosedContractsDashboard.d.ts.map +1 -1
- package/lib/pages/Lakehouse/entitlements/EntitlementsClosedContractsDashboard.js +14 -3
- package/lib/pages/Lakehouse/entitlements/EntitlementsClosedContractsDashboard.js.map +1 -1
- package/lib/pages/Lakehouse/entitlements/EntitlementsPendingContractsDashboard.d.ts.map +1 -1
- package/lib/pages/Lakehouse/entitlements/EntitlementsPendingContractsDashboard.js +15 -3
- package/lib/pages/Lakehouse/entitlements/EntitlementsPendingContractsDashboard.js.map +1 -1
- package/lib/pages/Lakehouse/entitlements/EntitlementsPendingTasksDashboard.d.ts.map +1 -1
- package/lib/pages/Lakehouse/entitlements/EntitlementsPendingTasksDashboard.js +19 -4
- package/lib/pages/Lakehouse/entitlements/EntitlementsPendingTasksDashboard.js.map +1 -1
- package/lib/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.d.ts.map +1 -1
- package/lib/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.js +29 -23
- package/lib/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.js.map +1 -1
- package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.d.ts.map +1 -1
- package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.js +8 -1
- package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.js.map +1 -1
- package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.d.ts +26 -1
- package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.d.ts.map +1 -1
- package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.js +233 -14
- package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.js.map +1 -1
- package/lib/stores/lakehouse/fieldSearch/FieldSearchResultState.d.ts +4 -0
- package/lib/stores/lakehouse/fieldSearch/FieldSearchResultState.d.ts.map +1 -1
- package/lib/stores/lakehouse/fieldSearch/FieldSearchResultState.js +44 -7
- package/lib/stores/lakehouse/fieldSearch/FieldSearchResultState.js.map +1 -1
- package/package.json +8 -8
- package/src/components/MarketplaceCard/FieldSearchResultListItem.tsx +110 -32
- package/src/pages/Lakehouse/entitlements/EntitlementsClosedContractsDashboard.tsx +22 -2
- package/src/pages/Lakehouse/entitlements/EntitlementsPendingContractsDashboard.tsx +28 -4
- package/src/pages/Lakehouse/entitlements/EntitlementsPendingTasksDashboard.tsx +45 -5
- package/src/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.tsx +100 -25
- package/src/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.tsx +37 -0
- package/src/stores/lakehouse/entitlements/EntitlementsDashboardState.ts +371 -37
- package/src/stores/lakehouse/fieldSearch/FieldSearchResultState.ts +53 -4
|
@@ -18,13 +18,17 @@ import {
|
|
|
18
18
|
ActionState,
|
|
19
19
|
assertErrorThrown,
|
|
20
20
|
guaranteeNonNullable,
|
|
21
|
+
guaranteeType,
|
|
22
|
+
HttpStatus,
|
|
21
23
|
isNonNullable,
|
|
24
|
+
NetworkClientError,
|
|
22
25
|
type GeneratorFn,
|
|
23
26
|
type PlainObject,
|
|
24
27
|
} from '@finos/legend-shared';
|
|
25
28
|
import { deserialize } from 'serializr';
|
|
26
29
|
import {
|
|
27
|
-
type
|
|
30
|
+
type PureProtocolProcessorPlugin,
|
|
31
|
+
type V1_DataProduct,
|
|
28
32
|
type V1_EnrichedUserApprovalStatus,
|
|
29
33
|
type V1_LiteDataContract,
|
|
30
34
|
type V1_LiteDataContractWithUserStatus,
|
|
@@ -32,13 +36,23 @@ import {
|
|
|
32
36
|
type V1_TaskStatus,
|
|
33
37
|
type V1_ContractUserEventRecord,
|
|
34
38
|
type V1_TaskStatusChangeResponse,
|
|
35
|
-
|
|
39
|
+
RawLambda,
|
|
40
|
+
V1_DataProductAccessor,
|
|
41
|
+
V1_deserializeDataContractResponse,
|
|
36
42
|
V1_entitlementsDataProductDetailsResponseToDataProductDetails,
|
|
43
|
+
V1_IngestDefinitionAccessor,
|
|
44
|
+
V1_LakehouseAccessPoint,
|
|
37
45
|
V1_liteDataContractWithUserStatusModelSchema,
|
|
38
46
|
V1_pendingTasksResponseModelSchema,
|
|
47
|
+
V1_PureGraphManager,
|
|
48
|
+
V1_resolveAccessorsFromRawLambda,
|
|
49
|
+
V1_ResourceType,
|
|
50
|
+
V1_SdlcDeploymentDataProductOrigin,
|
|
39
51
|
V1_TaskStatusChangeResponseModelSchema,
|
|
40
52
|
V1_transformDataContractToLiteDatacontract,
|
|
41
53
|
} from '@finos/legend-graph';
|
|
54
|
+
import { DEFAULT_TAB_SIZE } from '@finos/legend-application';
|
|
55
|
+
import type { ContractErrorLayer } from '@finos/legend-extension-dsl-data-product';
|
|
42
56
|
import {
|
|
43
57
|
makeObservable,
|
|
44
58
|
flow,
|
|
@@ -51,6 +65,94 @@ import {
|
|
|
51
65
|
TEST_USER,
|
|
52
66
|
type LakehouseEntitlementsStore,
|
|
53
67
|
} from './LakehouseEntitlementsStore.js';
|
|
68
|
+
import { getDataProductFromDetails } from '../../../utils/LakehouseUtils.js';
|
|
69
|
+
|
|
70
|
+
export enum ContractSyncStatus {
|
|
71
|
+
NEVER_SYNCED = 'NEVER_SYNCED',
|
|
72
|
+
NOT_FULLY_SYNCED = 'NOT_FULLY_SYNCED',
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type LakehouseContractSyncStatusResponse = {
|
|
76
|
+
status: string;
|
|
77
|
+
unsyncedUsers?: { username: string }[];
|
|
78
|
+
unsyncedAccessPoints?: { accessPointName: string }[];
|
|
79
|
+
unsyncedTargetAccounts?: string[];
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const collectIngestSpecPathsFromOriginDp = (
|
|
83
|
+
rootDataProduct: V1_DataProduct,
|
|
84
|
+
accessPointGroupId: string,
|
|
85
|
+
graphManager: V1_PureGraphManager,
|
|
86
|
+
plugins: PureProtocolProcessorPlugin[],
|
|
87
|
+
): Set<string> => {
|
|
88
|
+
const dpPath = `${rootDataProduct.package}::${rootDataProduct.name}`;
|
|
89
|
+
const targetApg = rootDataProduct.accessPointGroups.find(
|
|
90
|
+
(apg) => apg.id === accessPointGroupId,
|
|
91
|
+
);
|
|
92
|
+
if (!targetApg) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
`Access point group '${accessPointGroupId}' not found in data product '${dpPath}'`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
const specs = new Set<string>();
|
|
98
|
+
const visited = new Set<string>();
|
|
99
|
+
const worklist: string[] = [accessPointGroupId];
|
|
100
|
+
|
|
101
|
+
const collectFromApg = (apgId: string): void => {
|
|
102
|
+
const apg = rootDataProduct.accessPointGroups.find((g) => g.id === apgId);
|
|
103
|
+
if (!apg) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
for (const accessPoint of apg.accessPoints) {
|
|
107
|
+
if (!(accessPoint instanceof V1_LakehouseAccessPoint)) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const visitKey = `${dpPath}::${accessPoint.id}`;
|
|
111
|
+
if (visited.has(visitKey)) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
visited.add(visitKey);
|
|
115
|
+
|
|
116
|
+
const rawLambda = new RawLambda(
|
|
117
|
+
accessPoint.func.parameters,
|
|
118
|
+
accessPoint.func.body,
|
|
119
|
+
);
|
|
120
|
+
const accessors =
|
|
121
|
+
V1_resolveAccessorsFromRawLambda(rawLambda, graphManager, plugins) ??
|
|
122
|
+
[];
|
|
123
|
+
for (const accessor of accessors) {
|
|
124
|
+
if (accessor instanceof V1_IngestDefinitionAccessor) {
|
|
125
|
+
const specPath = accessor.path[0];
|
|
126
|
+
if (specPath) {
|
|
127
|
+
specs.add(specPath);
|
|
128
|
+
}
|
|
129
|
+
} else if (accessor instanceof V1_DataProductAccessor) {
|
|
130
|
+
const refDpPath = accessor.path[0];
|
|
131
|
+
const refApId = accessor.path[1];
|
|
132
|
+
if (!refDpPath || !refApId) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (refDpPath !== dpPath) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const refApg = rootDataProduct.accessPointGroups.find((g) =>
|
|
139
|
+
g.accessPoints.some((ap) => ap.id === refApId),
|
|
140
|
+
);
|
|
141
|
+
if (refApg && !worklist.includes(refApg.id)) {
|
|
142
|
+
worklist.push(refApg.id);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
while (worklist.length > 0) {
|
|
150
|
+
const apgId = guaranteeNonNullable(worklist.shift());
|
|
151
|
+
collectFromApg(apgId);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return specs;
|
|
155
|
+
};
|
|
54
156
|
|
|
55
157
|
export class ContractCreatedByUserDetails {
|
|
56
158
|
readonly contractResultLite: V1_LiteDataContract;
|
|
@@ -246,30 +348,35 @@ export class EntitlementsDashboardState {
|
|
|
246
348
|
const pendingTaskContractIds = Array.from(
|
|
247
349
|
new Set(pendingTasks.map((t) => t.dataContractId)),
|
|
248
350
|
);
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
this.lakehouseEntitlementsStore.applicationStore.pluginManager.getPureProtocolProcessorPlugins(),
|
|
261
|
-
),
|
|
262
|
-
rawContractResponse,
|
|
351
|
+
const contractClient =
|
|
352
|
+
this.lakehouseEntitlementsStore.lakehouseContractServerClient;
|
|
353
|
+
const plugins =
|
|
354
|
+
this.lakehouseEntitlementsStore.applicationStore.pluginManager.getPureProtocolProcessorPlugins();
|
|
355
|
+
const pendingTaskContracts = (yield Promise.all(
|
|
356
|
+
pendingTaskContractIds.map(async (contractId) => {
|
|
357
|
+
try {
|
|
358
|
+
const rawContractResponse = await contractClient.getDataContract(
|
|
359
|
+
contractId,
|
|
360
|
+
false,
|
|
361
|
+
token,
|
|
263
362
|
);
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
363
|
+
const dataContract = V1_deserializeDataContractResponse(
|
|
364
|
+
rawContractResponse,
|
|
365
|
+
plugins,
|
|
366
|
+
)[0]?.dataContract;
|
|
367
|
+
if (!dataContract) {
|
|
368
|
+
return undefined;
|
|
369
|
+
}
|
|
370
|
+
return V1_transformDataContractToLiteDatacontract(dataContract);
|
|
371
|
+
} catch (error) {
|
|
372
|
+
assertErrorThrown(error);
|
|
373
|
+
return undefined;
|
|
374
|
+
}
|
|
375
|
+
}),
|
|
376
|
+
)) as (V1_LiteDataContract | undefined)[];
|
|
270
377
|
const resultMap = new Map<string, V1_LiteDataContract>();
|
|
271
|
-
pendingTaskContractIds.forEach((contractId) => {
|
|
272
|
-
const contract = pendingTaskContracts
|
|
378
|
+
pendingTaskContractIds.forEach((contractId, idx) => {
|
|
379
|
+
const contract = pendingTaskContracts[idx];
|
|
273
380
|
if (contract) {
|
|
274
381
|
resultMap.set(contractId, contract);
|
|
275
382
|
}
|
|
@@ -356,29 +463,26 @@ export class EntitlementsDashboardState {
|
|
|
356
463
|
}
|
|
357
464
|
|
|
358
465
|
const didToEnvType = new Map<number, string>();
|
|
466
|
+
const contractClient =
|
|
467
|
+
this.lakehouseEntitlementsStore.lakehouseContractServerClient;
|
|
359
468
|
yield Promise.all(
|
|
360
469
|
Array.from(uniqueDIDToDataProduct.entries()).map(
|
|
361
470
|
async ([deploymentId, resourceId]) => {
|
|
362
471
|
try {
|
|
363
|
-
const raw =
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
const details =
|
|
472
|
+
const raw = await contractClient.getDataProductByIdAndDID(
|
|
473
|
+
resourceId,
|
|
474
|
+
deploymentId,
|
|
475
|
+
token,
|
|
476
|
+
);
|
|
477
|
+
const env =
|
|
370
478
|
V1_entitlementsDataProductDetailsResponseToDataProductDetails(
|
|
371
479
|
raw,
|
|
372
|
-
);
|
|
373
|
-
const env = details[0]?.lakehouseEnvironment?.type;
|
|
480
|
+
)[0]?.lakehouseEnvironment?.type;
|
|
374
481
|
if (env) {
|
|
375
482
|
didToEnvType.set(deploymentId, env);
|
|
376
483
|
}
|
|
377
484
|
} catch (error) {
|
|
378
485
|
assertErrorThrown(error);
|
|
379
|
-
this.lakehouseEntitlementsStore.applicationStore.notificationService.notifyError(
|
|
380
|
-
`Error fetching deployment environment for deployment ${deploymentId}: ${error.message}`,
|
|
381
|
-
);
|
|
382
486
|
}
|
|
383
487
|
},
|
|
384
488
|
),
|
|
@@ -386,7 +490,237 @@ export class EntitlementsDashboardState {
|
|
|
386
490
|
return didToEnvType;
|
|
387
491
|
}
|
|
388
492
|
|
|
389
|
-
|
|
493
|
+
async getUnverifiedIngestDefinitions(
|
|
494
|
+
contractId: string,
|
|
495
|
+
token: string | undefined,
|
|
496
|
+
): Promise<string[]> {
|
|
497
|
+
const entitlementsStore = this.lakehouseEntitlementsStore;
|
|
498
|
+
const baseStore = entitlementsStore.marketplaceBaseStore;
|
|
499
|
+
const applicationStore = entitlementsStore.applicationStore;
|
|
500
|
+
const plugins =
|
|
501
|
+
applicationStore.pluginManager.getPureProtocolProcessorPlugins();
|
|
502
|
+
const contractClient = entitlementsStore.lakehouseContractServerClient;
|
|
503
|
+
|
|
504
|
+
const PROD_ENV = 'prod';
|
|
505
|
+
const SDLC_DEPLOYMENT = 'alloy-git';
|
|
506
|
+
|
|
507
|
+
try {
|
|
508
|
+
const liteContract = await (async () => {
|
|
509
|
+
try {
|
|
510
|
+
const rawContractResponse = await contractClient.getDataContract(
|
|
511
|
+
contractId,
|
|
512
|
+
false,
|
|
513
|
+
token,
|
|
514
|
+
);
|
|
515
|
+
const dataContract = V1_deserializeDataContractResponse(
|
|
516
|
+
rawContractResponse,
|
|
517
|
+
plugins,
|
|
518
|
+
)[0]?.dataContract;
|
|
519
|
+
if (!dataContract) {
|
|
520
|
+
return undefined;
|
|
521
|
+
}
|
|
522
|
+
return V1_transformDataContractToLiteDatacontract(dataContract);
|
|
523
|
+
} catch (error) {
|
|
524
|
+
assertErrorThrown(error);
|
|
525
|
+
return undefined;
|
|
526
|
+
}
|
|
527
|
+
})();
|
|
528
|
+
if (!liteContract) {
|
|
529
|
+
return [];
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const accessPointGroupId =
|
|
533
|
+
liteContract.resourceType === V1_ResourceType.ACCESS_POINT_GROUP
|
|
534
|
+
? (liteContract.accessPointGroup ?? undefined)
|
|
535
|
+
: undefined;
|
|
536
|
+
if (!accessPointGroupId) {
|
|
537
|
+
return [];
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const dpDetails = await (async () => {
|
|
541
|
+
try {
|
|
542
|
+
const raw = await contractClient.getDataProductByIdAndDID(
|
|
543
|
+
liteContract.resourceId,
|
|
544
|
+
liteContract.deploymentId,
|
|
545
|
+
token,
|
|
546
|
+
);
|
|
547
|
+
return V1_entitlementsDataProductDetailsResponseToDataProductDetails(
|
|
548
|
+
raw,
|
|
549
|
+
)[0];
|
|
550
|
+
} catch (error) {
|
|
551
|
+
assertErrorThrown(error);
|
|
552
|
+
return undefined;
|
|
553
|
+
}
|
|
554
|
+
})();
|
|
555
|
+
if (!dpDetails) {
|
|
556
|
+
return [];
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
if (!(dpDetails.origin instanceof V1_SdlcDeploymentDataProductOrigin)) {
|
|
560
|
+
return [];
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const graphManager = new V1_PureGraphManager(
|
|
564
|
+
applicationStore.pluginManager,
|
|
565
|
+
applicationStore.logService,
|
|
566
|
+
baseStore.remoteEngine,
|
|
567
|
+
);
|
|
568
|
+
await graphManager.initialize(
|
|
569
|
+
{
|
|
570
|
+
env: applicationStore.config.env,
|
|
571
|
+
tabSize: DEFAULT_TAB_SIZE,
|
|
572
|
+
clientConfig: {
|
|
573
|
+
baseUrl: applicationStore.config.engineServerUrl,
|
|
574
|
+
},
|
|
575
|
+
},
|
|
576
|
+
{ engine: baseStore.remoteEngine },
|
|
577
|
+
);
|
|
578
|
+
|
|
579
|
+
const v1DataProduct = await getDataProductFromDetails(
|
|
580
|
+
dpDetails,
|
|
581
|
+
graphManager,
|
|
582
|
+
baseStore,
|
|
583
|
+
);
|
|
584
|
+
if (!v1DataProduct) {
|
|
585
|
+
return [];
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const specs = collectIngestSpecPathsFromOriginDp(
|
|
589
|
+
v1DataProduct,
|
|
590
|
+
accessPointGroupId,
|
|
591
|
+
graphManager,
|
|
592
|
+
plugins,
|
|
593
|
+
);
|
|
594
|
+
if (specs.size === 0) {
|
|
595
|
+
return [];
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
const ingestEnvironment =
|
|
599
|
+
await baseStore.lakehouseDataProductService.getOrFetchEnvironmentForDID(
|
|
600
|
+
liteContract.deploymentId,
|
|
601
|
+
token,
|
|
602
|
+
);
|
|
603
|
+
const ingestServerUrl = ingestEnvironment?.ingestServerUrl;
|
|
604
|
+
if (ingestServerUrl === undefined) {
|
|
605
|
+
return [];
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const sdlcOrigin = guaranteeType(
|
|
609
|
+
dpDetails.origin,
|
|
610
|
+
V1_SdlcDeploymentDataProductOrigin,
|
|
611
|
+
);
|
|
612
|
+
const gav = `${sdlcOrigin.group}~${sdlcOrigin.artifact}`;
|
|
613
|
+
const specsToVerify = Array.from(specs, (specPath) => ({
|
|
614
|
+
specPath,
|
|
615
|
+
urn: `urn:lakehouse:${PROD_ENV}:ingest:definition:${SDLC_DEPLOYMENT}:${gav}~${specPath}`,
|
|
616
|
+
}));
|
|
617
|
+
|
|
618
|
+
const ingestClient = baseStore.lakehouseIngestServerClient;
|
|
619
|
+
const settled = await Promise.all(
|
|
620
|
+
specsToVerify.map(async (entry) => {
|
|
621
|
+
try {
|
|
622
|
+
await ingestClient.getIngestDefinitionDetail(
|
|
623
|
+
entry.urn,
|
|
624
|
+
ingestServerUrl,
|
|
625
|
+
token,
|
|
626
|
+
);
|
|
627
|
+
return undefined;
|
|
628
|
+
} catch (error) {
|
|
629
|
+
assertErrorThrown(error);
|
|
630
|
+
if (
|
|
631
|
+
error instanceof NetworkClientError &&
|
|
632
|
+
error.response.status === HttpStatus.NOT_FOUND
|
|
633
|
+
) {
|
|
634
|
+
return entry.specPath;
|
|
635
|
+
}
|
|
636
|
+
return undefined;
|
|
637
|
+
}
|
|
638
|
+
}),
|
|
639
|
+
);
|
|
640
|
+
return settled.filter(isNonNullable);
|
|
641
|
+
} catch (error) {
|
|
642
|
+
assertErrorThrown(error);
|
|
643
|
+
return [];
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
async getContractSyncErrors(
|
|
648
|
+
contractId: string,
|
|
649
|
+
token: string | undefined,
|
|
650
|
+
): Promise<ContractErrorLayer | undefined> {
|
|
651
|
+
try {
|
|
652
|
+
const response =
|
|
653
|
+
(await this.lakehouseEntitlementsStore.lakehouseContractServerClient.getContractSyncStatus(
|
|
654
|
+
contractId,
|
|
655
|
+
token,
|
|
656
|
+
)) as LakehouseContractSyncStatusResponse;
|
|
657
|
+
|
|
658
|
+
const status = response.status.toUpperCase();
|
|
659
|
+
|
|
660
|
+
if (status === ContractSyncStatus.NEVER_SYNCED) {
|
|
661
|
+
return { title: 'Sync Error: Contract Never Synced' };
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
if (status === ContractSyncStatus.NOT_FULLY_SYNCED) {
|
|
665
|
+
const unsyncedUsers =
|
|
666
|
+
response.unsyncedUsers?.map((user) => user.username) ?? [];
|
|
667
|
+
const unsyncedAccessPoints =
|
|
668
|
+
response.unsyncedAccessPoints?.map(
|
|
669
|
+
(accessPoint) => accessPoint.accessPointName,
|
|
670
|
+
) ?? [];
|
|
671
|
+
const unsyncedTargetAccounts = response.unsyncedTargetAccounts ?? [];
|
|
672
|
+
|
|
673
|
+
const syncGroupingLayers: ContractErrorLayer[] = [
|
|
674
|
+
{ title: 'Users', errorItems: unsyncedUsers },
|
|
675
|
+
{ title: 'Target Accounts', errorItems: unsyncedTargetAccounts },
|
|
676
|
+
{ title: 'Access Points', errorItems: unsyncedAccessPoints },
|
|
677
|
+
].filter((layer) => layer.errorItems.length > 0);
|
|
678
|
+
|
|
679
|
+
if (syncGroupingLayers.length === 0) {
|
|
680
|
+
return undefined;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
return {
|
|
684
|
+
title: 'Unsynced Entities',
|
|
685
|
+
childLayers: syncGroupingLayers,
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
return undefined;
|
|
690
|
+
} catch (error) {
|
|
691
|
+
assertErrorThrown(error);
|
|
692
|
+
return undefined;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
async getContractErrors(
|
|
697
|
+
contractId: string,
|
|
698
|
+
token: string | undefined,
|
|
699
|
+
checkSyncStatus = false,
|
|
700
|
+
): Promise<ContractErrorLayer | undefined> {
|
|
701
|
+
const [unverifiedIngestDefinitions, syncErrorsLayer] = await Promise.all([
|
|
702
|
+
this.getUnverifiedIngestDefinitions(contractId, token),
|
|
703
|
+
checkSyncStatus
|
|
704
|
+
? this.getContractSyncErrors(contractId, token)
|
|
705
|
+
: Promise.resolve(undefined),
|
|
706
|
+
]);
|
|
707
|
+
|
|
708
|
+
const childLayers: ContractErrorLayer[] = [
|
|
709
|
+
unverifiedIngestDefinitions.length > 0
|
|
710
|
+
? {
|
|
711
|
+
title: `Ingest${unverifiedIngestDefinitions.length === 1 ? '' : 's'} Not Found`,
|
|
712
|
+
errorItems: unverifiedIngestDefinitions,
|
|
713
|
+
}
|
|
714
|
+
: undefined,
|
|
715
|
+
syncErrorsLayer,
|
|
716
|
+
].filter(isNonNullable);
|
|
717
|
+
|
|
718
|
+
return childLayers.length > 0
|
|
719
|
+
? { title: 'Contract Errors', childLayers }
|
|
720
|
+
: undefined;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
filterByUserEnvironment(
|
|
390
724
|
pendingData: {
|
|
391
725
|
tasks: V1_ContractUserEventRecord[];
|
|
392
726
|
taskContractMap: Map<string, V1_LiteDataContract>;
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
type GroupedFieldSearchDataProduct,
|
|
20
20
|
type GroupedFieldSearchResultEntry,
|
|
21
21
|
} from '@finos/legend-server-marketplace';
|
|
22
|
+
import { hashArray } from '@finos/legend-shared';
|
|
22
23
|
import { DataProductTypeFilter } from '../LegendMarketplaceSearchResultsStore.js';
|
|
23
24
|
import { generateGAVCoordinates } from '@finos/legend-storage';
|
|
24
25
|
import {
|
|
@@ -26,6 +27,15 @@ import {
|
|
|
26
27
|
generateLegacyDataProductPath,
|
|
27
28
|
} from '../../../__lib__/LegendMarketplaceNavigation.js';
|
|
28
29
|
|
|
30
|
+
enum FieldSearchResultStateDefaultValue {
|
|
31
|
+
UNKNOWN_FIELD_TYPE = 'Unknown',
|
|
32
|
+
EMPTY_FIELD_DESCRIPTION = '-',
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
enum FieldSearchDataProductKey {
|
|
36
|
+
DISTINCT_SEPARATOR = '|',
|
|
37
|
+
}
|
|
38
|
+
|
|
29
39
|
const PRODUCT_TYPE_FILTER_MAP: Record<
|
|
30
40
|
DataProductSearchResultDetailsType,
|
|
31
41
|
DataProductTypeFilter
|
|
@@ -39,6 +49,28 @@ const PRODUCT_TYPE_FILTER_MAP: Record<
|
|
|
39
49
|
const getDataProductName = (path: string): string =>
|
|
40
50
|
path.split('::').at(-1) ?? path;
|
|
41
51
|
|
|
52
|
+
const generateFieldSearchResultId = (
|
|
53
|
+
fieldName: string,
|
|
54
|
+
fieldType: string,
|
|
55
|
+
fieldDescription: string,
|
|
56
|
+
): string => `${hashArray([fieldName, fieldType, fieldDescription])}`;
|
|
57
|
+
|
|
58
|
+
const getDistinctDataProducts = (
|
|
59
|
+
dataProducts: FieldSearchDataProductEntry[],
|
|
60
|
+
): FieldSearchDataProductEntry[] => {
|
|
61
|
+
const seen = new Set<string>();
|
|
62
|
+
return dataProducts.filter((dp) => {
|
|
63
|
+
// Dedupe primarily by owning data product path. Fallback to a stable
|
|
64
|
+
// composite key only when path is unavailable.
|
|
65
|
+
const dedupeKey = dp.path || dp.distinctKey;
|
|
66
|
+
if (seen.has(dedupeKey)) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
seen.add(dedupeKey);
|
|
70
|
+
return true;
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
|
|
42
74
|
const getOwningDataProductPath = (
|
|
43
75
|
dataProduct: GroupedFieldSearchDataProduct,
|
|
44
76
|
): string => {
|
|
@@ -71,6 +103,8 @@ const getOwningDataProductPath = (
|
|
|
71
103
|
export class FieldSearchDataProductEntry {
|
|
72
104
|
readonly name: string;
|
|
73
105
|
readonly datasetName: string | undefined;
|
|
106
|
+
readonly datasetDescription: string | undefined;
|
|
107
|
+
readonly executionContextKey: string | undefined;
|
|
74
108
|
readonly modelPath: string | undefined;
|
|
75
109
|
readonly path: string;
|
|
76
110
|
readonly entityPath: string;
|
|
@@ -80,6 +114,7 @@ export class FieldSearchDataProductEntry {
|
|
|
80
114
|
readonly artifactId: string | undefined;
|
|
81
115
|
readonly versionId: string | undefined;
|
|
82
116
|
readonly productType: DataProductTypeFilter | undefined;
|
|
117
|
+
readonly distinctKey: string;
|
|
83
118
|
|
|
84
119
|
constructor(dataProduct: GroupedFieldSearchDataProduct) {
|
|
85
120
|
const productType = PRODUCT_TYPE_FILTER_MAP[dataProduct.productType];
|
|
@@ -87,6 +122,8 @@ export class FieldSearchDataProductEntry {
|
|
|
87
122
|
|
|
88
123
|
this.name = dataProductName;
|
|
89
124
|
this.datasetName = dataProduct.datasetName;
|
|
125
|
+
this.datasetDescription = dataProduct.datasetDescription;
|
|
126
|
+
this.executionContextKey = dataProduct.defaultExecutionContext;
|
|
90
127
|
this.modelPath = dataProduct.modelPath;
|
|
91
128
|
this.path = getOwningDataProductPath(dataProduct);
|
|
92
129
|
this.entityPath = dataProduct.path;
|
|
@@ -96,6 +133,12 @@ export class FieldSearchDataProductEntry {
|
|
|
96
133
|
this.artifactId = dataProduct.artifactId;
|
|
97
134
|
this.versionId = dataProduct.versionId;
|
|
98
135
|
this.productType = productType;
|
|
136
|
+
this.distinctKey = [
|
|
137
|
+
this.path,
|
|
138
|
+
this.entityPath,
|
|
139
|
+
this.dataProductId,
|
|
140
|
+
this.name,
|
|
141
|
+
].join(FieldSearchDataProductKey.DISTINCT_SEPARATOR);
|
|
99
142
|
}
|
|
100
143
|
}
|
|
101
144
|
|
|
@@ -105,18 +148,24 @@ export class FieldSearchResultState {
|
|
|
105
148
|
readonly fieldType: string;
|
|
106
149
|
readonly fieldDescription: string;
|
|
107
150
|
readonly dataProducts: FieldSearchDataProductEntry[];
|
|
151
|
+
readonly distinctDataProducts: FieldSearchDataProductEntry[];
|
|
108
152
|
|
|
109
153
|
constructor(result: GroupedFieldSearchResultEntry) {
|
|
110
154
|
this.fieldName = result.fieldName;
|
|
111
|
-
this.fieldType =
|
|
112
|
-
|
|
113
|
-
this.
|
|
155
|
+
this.fieldType =
|
|
156
|
+
result.fieldType ?? FieldSearchResultStateDefaultValue.UNKNOWN_FIELD_TYPE;
|
|
157
|
+
this.fieldDescription =
|
|
158
|
+
result.fieldDescription ??
|
|
159
|
+
FieldSearchResultStateDefaultValue.EMPTY_FIELD_DESCRIPTION;
|
|
160
|
+
this.id = generateFieldSearchResultId(
|
|
114
161
|
this.fieldName,
|
|
115
162
|
this.fieldType,
|
|
116
163
|
this.fieldDescription,
|
|
117
|
-
|
|
164
|
+
);
|
|
118
165
|
this.dataProducts = result.dataProducts.map(
|
|
119
166
|
(dp) => new FieldSearchDataProductEntry(dp),
|
|
120
167
|
);
|
|
168
|
+
// Precompute once since dataProducts are immutable after construction.
|
|
169
|
+
this.distinctDataProducts = getDistinctDataProducts(this.dataProducts);
|
|
121
170
|
}
|
|
122
171
|
}
|