@finos/legend-application-marketplace 0.2.11 → 0.2.13

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 (43) hide show
  1. package/lib/application/LegendMarketplaceWebApplication.d.ts.map +1 -1
  2. package/lib/application/LegendMarketplaceWebApplication.js +2 -2
  3. package/lib/application/LegendMarketplaceWebApplication.js.map +1 -1
  4. package/lib/components/MarketplaceCard/FieldSearchResultListItem.d.ts +1 -0
  5. package/lib/components/MarketplaceCard/FieldSearchResultListItem.d.ts.map +1 -1
  6. package/lib/components/MarketplaceCard/FieldSearchResultListItem.js +30 -12
  7. package/lib/components/MarketplaceCard/FieldSearchResultListItem.js.map +1 -1
  8. package/lib/index.css +2 -2
  9. package/lib/index.css.map +1 -1
  10. package/lib/package.json +1 -1
  11. package/lib/pages/Lakehouse/entitlements/EntitlementsClosedContractsDashboard.d.ts.map +1 -1
  12. package/lib/pages/Lakehouse/entitlements/EntitlementsClosedContractsDashboard.js +16 -2
  13. package/lib/pages/Lakehouse/entitlements/EntitlementsClosedContractsDashboard.js.map +1 -1
  14. package/lib/pages/Lakehouse/entitlements/EntitlementsPendingContractsDashboard.d.ts.map +1 -1
  15. package/lib/pages/Lakehouse/entitlements/EntitlementsPendingContractsDashboard.js +19 -3
  16. package/lib/pages/Lakehouse/entitlements/EntitlementsPendingContractsDashboard.js.map +1 -1
  17. package/lib/pages/Lakehouse/entitlements/EntitlementsPendingTasksDashboard.d.ts.map +1 -1
  18. package/lib/pages/Lakehouse/entitlements/EntitlementsPendingTasksDashboard.js +23 -4
  19. package/lib/pages/Lakehouse/entitlements/EntitlementsPendingTasksDashboard.js.map +1 -1
  20. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.d.ts.map +1 -1
  21. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.js +29 -23
  22. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.js.map +1 -1
  23. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.d.ts.map +1 -1
  24. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.js +8 -1
  25. package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.js.map +1 -1
  26. package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.d.ts +1 -0
  27. package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.d.ts.map +1 -1
  28. package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.js +175 -14
  29. package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.js.map +1 -1
  30. package/lib/stores/lakehouse/fieldSearch/FieldSearchResultState.d.ts +4 -0
  31. package/lib/stores/lakehouse/fieldSearch/FieldSearchResultState.d.ts.map +1 -1
  32. package/lib/stores/lakehouse/fieldSearch/FieldSearchResultState.js +44 -7
  33. package/lib/stores/lakehouse/fieldSearch/FieldSearchResultState.js.map +1 -1
  34. package/package.json +9 -9
  35. package/src/application/LegendMarketplaceWebApplication.tsx +11 -6
  36. package/src/components/MarketplaceCard/FieldSearchResultListItem.tsx +110 -32
  37. package/src/pages/Lakehouse/entitlements/EntitlementsClosedContractsDashboard.tsx +25 -3
  38. package/src/pages/Lakehouse/entitlements/EntitlementsPendingContractsDashboard.tsx +34 -5
  39. package/src/pages/Lakehouse/entitlements/EntitlementsPendingTasksDashboard.tsx +51 -6
  40. package/src/pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.tsx +100 -25
  41. package/src/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.tsx +37 -0
  42. package/src/stores/lakehouse/entitlements/EntitlementsDashboardState.ts +276 -36
  43. package/src/stores/lakehouse/fieldSearch/FieldSearchResultState.ts +53 -4
@@ -20,20 +20,22 @@ import { flowResult } from 'mobx';
20
20
  import { useCallback, useEffect, useRef } from 'react';
21
21
  import { useSyncStateAndSearchParam } from '@finos/legend-application';
22
22
  import { useSearchParams } from '@finos/legend-application/browser';
23
- import { isNonEmptyString } from '@finos/legend-shared';
23
+ import { isNonEmptyString, LogEvent } from '@finos/legend-shared';
24
24
  import {
25
25
  CubesLoadingIndicator,
26
26
  CubesLoadingIndicatorIcon,
27
27
  } from '@finos/legend-art';
28
- import { DATAPRODUCT_TYPE } from '@finos/legend-extension-dsl-data-product';
29
28
  import {
30
29
  LEGEND_MARKETPLACE_FIELD_SEARCH_RESULTS_QUERY_PARAM_TOKEN,
31
30
  generateLakehouseSearchResultsRoute,
31
+ EXTERNAL_APPLICATION_NAVIGATION__generateDataSpaceQueryEditorUrl,
32
32
  } from '../../../__lib__/LegendMarketplaceNavigation.js';
33
33
  import {
34
34
  LEGEND_MARKETPLACE_PAGE,
35
35
  LegendMarketplaceTelemetryHelper,
36
36
  } from '../../../__lib__/LegendMarketplaceTelemetryHelper.js';
37
+
38
+ import { LEGEND_MARKETPLACE_APP_EVENT } from '../../../__lib__/LegendMarketplaceAppEvent.js';
37
39
  import {
38
40
  useLegendMarketplaceFieldSearchResultsStore,
39
41
  withLegendMarketplaceFieldSearchResultsStore,
@@ -54,6 +56,9 @@ const FieldSearchResultsContent = observer(
54
56
  handleItemsPerPageChange: (itemsPerPage: number) => void;
55
57
  handleToggleExpandRow: (rowId: string) => void;
56
58
  handleOpenDataProduct: (dataProduct: FieldSearchDataProductEntry) => void;
59
+ handleOpenDatasetInQuery: (
60
+ dataProduct: FieldSearchDataProductEntry,
61
+ ) => void;
57
62
  }) => {
58
63
  const {
59
64
  fieldSearchResultsStore,
@@ -61,6 +66,7 @@ const FieldSearchResultsContent = observer(
61
66
  handleItemsPerPageChange,
62
67
  handleToggleExpandRow,
63
68
  handleOpenDataProduct,
69
+ handleOpenDatasetInQuery,
64
70
  } = props;
65
71
 
66
72
  if (fieldSearchResultsStore.isLoading) {
@@ -160,6 +166,9 @@ const FieldSearchResultsContent = observer(
160
166
  <Typography className="marketplace-lakehouse-field-search-results__list-header-cell">
161
167
  Data Products
162
168
  </Typography>
169
+ <Typography className="marketplace-lakehouse-field-search-results__list-header-cell">
170
+ Datasets
171
+ </Typography>
163
172
  </div>
164
173
  <div className="marketplace-lakehouse-field-search-results__list-body">
165
174
  {fieldSearchResultsStore.tableRows.map((row) => (
@@ -169,6 +178,7 @@ const FieldSearchResultsContent = observer(
169
178
  expanded={fieldSearchResultsStore.isRowExpanded(row.id)}
170
179
  onToggleExpanded={handleToggleExpandRow}
171
180
  onOpenDataProduct={handleOpenDataProduct}
181
+ onOpenDatasetInQuery={handleOpenDatasetInQuery}
172
182
  />
173
183
  ))}
174
184
  </div>
@@ -283,29 +293,45 @@ const LegendMarketplaceFieldSearchResultsPage = observer(() => {
283
293
  [applicationStore, fieldSearchResultsStore],
284
294
  );
285
295
 
286
- const handleOpenDataProduct = useCallback(
287
- (dataProduct: FieldSearchDataProductEntry) => {
288
- LegendMarketplaceTelemetryHelper.logEvent_ClickingDataProductCard(
289
- applicationStore.telemetryService,
290
- {
291
- origin:
292
- dataProduct.productType === DataProductTypeFilter.LEGACY
293
- ? {
294
- type: DATAPRODUCT_TYPE.SDLC,
295
- groupId: dataProduct.groupId,
296
- artifactId: dataProduct.artifactId,
297
- versionId: dataProduct.versionId,
298
- path: dataProduct.entityPath,
299
- }
300
- : {
301
- type: DATAPRODUCT_TYPE.ADHOC,
302
- },
303
- dataProductId: dataProduct.dataProductId,
304
- name: dataProduct.name,
305
- deploymentId: dataProduct.deploymentId,
306
- },
307
- LEGEND_MARKETPLACE_PAGE.SEARCH_RESULTS_PAGE,
308
- );
296
+ const openInLegendQuery = useCallback(
297
+ (
298
+ dataProduct: FieldSearchDataProductEntry,
299
+ modelPath: string | undefined,
300
+ ) => {
301
+ const isLegacyDataProduct =
302
+ dataProduct.productType === DataProductTypeFilter.LEGACY;
303
+ const canOpenLegacyQuery =
304
+ dataProduct.groupId &&
305
+ dataProduct.artifactId &&
306
+ dataProduct.versionId &&
307
+ dataProduct.executionContextKey;
308
+
309
+ if (isLegacyDataProduct && canOpenLegacyQuery) {
310
+ applicationStore.navigationService.navigator.visitAddress(
311
+ EXTERNAL_APPLICATION_NAVIGATION__generateDataSpaceQueryEditorUrl(
312
+ applicationStore.config.queryApplicationUrl,
313
+ dataProduct.groupId,
314
+ dataProduct.artifactId,
315
+ dataProduct.versionId,
316
+ dataProduct.entityPath,
317
+ dataProduct.executionContextKey,
318
+ undefined,
319
+ modelPath,
320
+ ),
321
+ );
322
+ return;
323
+ }
324
+
325
+ if (isLegacyDataProduct && !canOpenLegacyQuery) {
326
+ applicationStore.logService.warn(
327
+ LogEvent.create(
328
+ LEGEND_MARKETPLACE_APP_EVENT.CLICK_QUERY_DATA_PRODUCT,
329
+ ),
330
+ `Falling back to marketplace navigation for legacy data product ${dataProduct.entityPath} due to missing query context`,
331
+ );
332
+ }
333
+
334
+ // Fallback to marketplace data product page for adhoc/lakehouse products
309
335
  applicationStore.navigationService.navigator.visitAddress(
310
336
  applicationStore.navigationService.navigator.generateAddress(
311
337
  dataProduct.path,
@@ -315,6 +341,20 @@ const LegendMarketplaceFieldSearchResultsPage = observer(() => {
315
341
  [applicationStore],
316
342
  );
317
343
 
344
+ const handleOpenDataProduct = useCallback(
345
+ (dataProduct: FieldSearchDataProductEntry) => {
346
+ openInLegendQuery(dataProduct, undefined);
347
+ },
348
+ [openInLegendQuery],
349
+ );
350
+
351
+ const handleOpenDatasetInQuery = useCallback(
352
+ (dataProduct: FieldSearchDataProductEntry) => {
353
+ openInLegendQuery(dataProduct, dataProduct.modelPath);
354
+ },
355
+ [openInLegendQuery],
356
+ );
357
+
318
358
  const handleToggleExpandRow = useCallback(
319
359
  (rowId: string) => {
320
360
  fieldSearchResultsStore.toggleExpandRow(rowId);
@@ -322,6 +362,15 @@ const LegendMarketplaceFieldSearchResultsPage = observer(() => {
322
362
  [fieldSearchResultsStore],
323
363
  );
324
364
 
365
+ const handleOpenDataProductsTab = useCallback(() => {
366
+ applicationStore.navigationService.navigator.goToLocation(
367
+ generateLakehouseSearchResultsRoute(
368
+ fieldSearchResultsStore.searchQuery,
369
+ false,
370
+ ),
371
+ );
372
+ }, [applicationStore, fieldSearchResultsStore.searchQuery]);
373
+
325
374
  return (
326
375
  <LegendMarketplacePage className="marketplace-lakehouse-search-results marketplace-lakehouse-field-search-results">
327
376
  <div ref={pageRef} />
@@ -345,6 +394,31 @@ const LegendMarketplaceFieldSearchResultsPage = observer(() => {
345
394
  >
346
395
  {fieldSearchResultsStore.totalFieldMatches} Fields
347
396
  </Typography>
397
+ <div
398
+ className="legend-marketplace-search-results__search-type-tabs"
399
+ role="tablist"
400
+ aria-label="Search result type"
401
+ >
402
+ <button
403
+ type="button"
404
+ role="tab"
405
+ aria-selected={false}
406
+ tabIndex={-1}
407
+ className="legend-marketplace-search-results__search-type-tab"
408
+ onClick={handleOpenDataProductsTab}
409
+ >
410
+ Data Products
411
+ </button>
412
+ <button
413
+ type="button"
414
+ role="tab"
415
+ aria-selected={true}
416
+ tabIndex={0}
417
+ className="legend-marketplace-search-results__search-type-tab legend-marketplace-search-results__search-type-tab--active"
418
+ >
419
+ Data Fields
420
+ </button>
421
+ </div>
348
422
  </div>
349
423
  </div>
350
424
  <Container
@@ -366,6 +440,7 @@ const LegendMarketplaceFieldSearchResultsPage = observer(() => {
366
440
  handleItemsPerPageChange={handleItemsPerPageChange}
367
441
  handleToggleExpandRow={handleToggleExpandRow}
368
442
  handleOpenDataProduct={handleOpenDataProduct}
443
+ handleOpenDatasetInQuery={handleOpenDatasetInQuery}
369
444
  />
370
445
  </div>
371
446
  </div>
@@ -382,6 +382,43 @@ export const LegendMarketplaceSearchResults =
382
382
  ? `${searchResultsStore.filterSortProducts?.length ?? 0} Products`
383
383
  : `${searchResultsStore.totalItems} Products`}
384
384
  </Typography>
385
+ <div
386
+ className="legend-marketplace-search-results__search-type-tabs"
387
+ role="tablist"
388
+ aria-label="Search result type"
389
+ >
390
+ <button
391
+ type="button"
392
+ role="tab"
393
+ aria-selected={true}
394
+ tabIndex={0}
395
+ className="legend-marketplace-search-results__search-type-tab legend-marketplace-search-results__search-type-tab--active"
396
+ >
397
+ Data Products
398
+ </button>
399
+ <button
400
+ type="button"
401
+ role="tab"
402
+ aria-selected={false}
403
+ tabIndex={-1}
404
+ className="legend-marketplace-search-results__search-type-tab"
405
+ onClick={() => {
406
+ if (isNonEmptyString(searchResultsStore.searchQuery)) {
407
+ applicationStore.navigationService.navigator.goToLocation(
408
+ generateFieldSearchResultsRoute(
409
+ searchResultsStore.searchQuery,
410
+ ),
411
+ );
412
+ } else {
413
+ applicationStore.navigationService.navigator.goToLocation(
414
+ generateFieldSearchResultsRoute(undefined),
415
+ );
416
+ }
417
+ }}
418
+ >
419
+ Data Fields
420
+ </button>
421
+ </div>
385
422
  <div className="legend-marketplace-search-results__sort-bar__controls">
386
423
  <div className="legend-marketplace-search-results__view-toggle">
387
424
  <div
@@ -18,27 +18,40 @@ import {
18
18
  ActionState,
19
19
  assertErrorThrown,
20
20
  guaranteeNonNullable,
21
+ guaranteeType,
21
22
  isNonNullable,
22
23
  type GeneratorFn,
23
24
  type PlainObject,
24
25
  } from '@finos/legend-shared';
25
26
  import { deserialize } from 'serializr';
26
27
  import {
27
- type V1_DataContract,
28
+ type PureProtocolProcessorPlugin,
29
+ type V1_DataProduct,
28
30
  type V1_EnrichedUserApprovalStatus,
31
+ type V1_EntitlementsDataProductDetails,
29
32
  type V1_LiteDataContract,
30
33
  type V1_LiteDataContractWithUserStatus,
31
34
  type V1_PendingTasksResponse,
32
35
  type V1_TaskStatus,
33
36
  type V1_ContractUserEventRecord,
34
37
  type V1_TaskStatusChangeResponse,
35
- V1_dataContractsResponseModelSchema,
38
+ RawLambda,
39
+ V1_DataProductAccessor,
40
+ V1_deserializeDataContractResponse,
36
41
  V1_entitlementsDataProductDetailsResponseToDataProductDetails,
42
+ V1_IngestDefinitionAccessor,
43
+ V1_LakehouseAccessPoint,
37
44
  V1_liteDataContractWithUserStatusModelSchema,
38
45
  V1_pendingTasksResponseModelSchema,
46
+ V1_PureGraphManager,
47
+ V1_resolveAccessorsFromRawLambda,
48
+ V1_ResourceType,
49
+ V1_SdlcDeploymentDataProductOrigin,
39
50
  V1_TaskStatusChangeResponseModelSchema,
40
51
  V1_transformDataContractToLiteDatacontract,
41
52
  } from '@finos/legend-graph';
53
+ import { DEFAULT_TAB_SIZE } from '@finos/legend-application';
54
+ import { type IngestDeploymentServerConfig } from '@finos/legend-server-lakehouse';
42
55
  import {
43
56
  makeObservable,
44
57
  flow,
@@ -51,6 +64,82 @@ import {
51
64
  TEST_USER,
52
65
  type LakehouseEntitlementsStore,
53
66
  } from './LakehouseEntitlementsStore.js';
67
+ import { getDataProductFromDetails } from '../../../utils/LakehouseUtils.js';
68
+
69
+ const collectIngestSpecPathsFromOriginDp = (
70
+ rootDataProduct: V1_DataProduct,
71
+ accessPointGroupId: string,
72
+ graphManager: V1_PureGraphManager,
73
+ plugins: PureProtocolProcessorPlugin[],
74
+ ): Set<string> => {
75
+ const dpPath = `${rootDataProduct.package}::${rootDataProduct.name}`;
76
+ const targetApg = rootDataProduct.accessPointGroups.find(
77
+ (apg) => apg.id === accessPointGroupId,
78
+ );
79
+ if (!targetApg) {
80
+ throw new Error(
81
+ `Access point group '${accessPointGroupId}' not found in data product '${dpPath}'`,
82
+ );
83
+ }
84
+ const specs = new Set<string>();
85
+ const visited = new Set<string>();
86
+ const worklist: string[] = [accessPointGroupId];
87
+
88
+ const collectFromApg = (apgId: string): void => {
89
+ const apg = rootDataProduct.accessPointGroups.find((g) => g.id === apgId);
90
+ if (!apg) {
91
+ return;
92
+ }
93
+ for (const accessPoint of apg.accessPoints) {
94
+ if (!(accessPoint instanceof V1_LakehouseAccessPoint)) {
95
+ continue;
96
+ }
97
+ const visitKey = `${dpPath}::${accessPoint.id}`;
98
+ if (visited.has(visitKey)) {
99
+ continue;
100
+ }
101
+ visited.add(visitKey);
102
+
103
+ const rawLambda = new RawLambda(
104
+ accessPoint.func.parameters,
105
+ accessPoint.func.body,
106
+ );
107
+ const accessors =
108
+ V1_resolveAccessorsFromRawLambda(rawLambda, graphManager, plugins) ??
109
+ [];
110
+ for (const accessor of accessors) {
111
+ if (accessor instanceof V1_IngestDefinitionAccessor) {
112
+ const specPath = accessor.path[0];
113
+ if (specPath) {
114
+ specs.add(specPath);
115
+ }
116
+ } else if (accessor instanceof V1_DataProductAccessor) {
117
+ const refDpPath = accessor.path[0];
118
+ const refApId = accessor.path[1];
119
+ if (!refDpPath || !refApId) {
120
+ continue;
121
+ }
122
+ if (refDpPath !== dpPath) {
123
+ continue;
124
+ }
125
+ const refApg = rootDataProduct.accessPointGroups.find((g) =>
126
+ g.accessPoints.some((ap) => ap.id === refApId),
127
+ );
128
+ if (refApg && !worklist.includes(refApg.id)) {
129
+ worklist.push(refApg.id);
130
+ }
131
+ }
132
+ }
133
+ }
134
+ };
135
+
136
+ while (worklist.length > 0) {
137
+ const apgId = guaranteeNonNullable(worklist.shift());
138
+ collectFromApg(apgId);
139
+ }
140
+
141
+ return specs;
142
+ };
54
143
 
55
144
  export class ContractCreatedByUserDetails {
56
145
  readonly contractResultLite: V1_LiteDataContract;
@@ -125,6 +214,7 @@ export class EntitlementsDashboardState {
125
214
  fetchContractsForUser: flow,
126
215
  fetchContractsCreatedByUser: flow,
127
216
  fetchContractDeploymentEnvironments: flow,
217
+ getUnverifiedIngestDefinitions: flow,
128
218
  updateContract: flow,
129
219
  });
130
220
  }
@@ -246,30 +336,35 @@ export class EntitlementsDashboardState {
246
336
  const pendingTaskContractIds = Array.from(
247
337
  new Set(pendingTasks.map((t) => t.dataContractId)),
248
338
  );
249
- const pendingTaskContracts = (
250
- (yield Promise.all(
251
- pendingTaskContractIds.map(async (contractId) => {
252
- const rawContractResponse =
253
- await this.lakehouseEntitlementsStore.lakehouseContractServerClient.getDataContract(
254
- contractId,
255
- false,
256
- token,
257
- );
258
- const contractResponse = deserialize(
259
- V1_dataContractsResponseModelSchema(
260
- this.lakehouseEntitlementsStore.applicationStore.pluginManager.getPureProtocolProcessorPlugins(),
261
- ),
262
- rawContractResponse,
339
+ const contractClient =
340
+ this.lakehouseEntitlementsStore.lakehouseContractServerClient;
341
+ const plugins =
342
+ this.lakehouseEntitlementsStore.applicationStore.pluginManager.getPureProtocolProcessorPlugins();
343
+ const pendingTaskContracts = (yield Promise.all(
344
+ pendingTaskContractIds.map(async (contractId) => {
345
+ try {
346
+ const rawContractResponse = await contractClient.getDataContract(
347
+ contractId,
348
+ false,
349
+ token,
263
350
  );
264
- return contractResponse.dataContracts?.[0]?.dataContract;
265
- }),
266
- )) as (V1_DataContract | undefined)[]
267
- )
268
- .filter(isNonNullable)
269
- .map(V1_transformDataContractToLiteDatacontract);
351
+ const dataContract = V1_deserializeDataContractResponse(
352
+ rawContractResponse,
353
+ plugins,
354
+ )[0]?.dataContract;
355
+ if (!dataContract) {
356
+ return undefined;
357
+ }
358
+ return V1_transformDataContractToLiteDatacontract(dataContract);
359
+ } catch (error) {
360
+ assertErrorThrown(error);
361
+ return undefined;
362
+ }
363
+ }),
364
+ )) as (V1_LiteDataContract | undefined)[];
270
365
  const resultMap = new Map<string, V1_LiteDataContract>();
271
- pendingTaskContractIds.forEach((contractId) => {
272
- const contract = pendingTaskContracts.find((c) => c.guid === contractId);
366
+ pendingTaskContractIds.forEach((contractId, idx) => {
367
+ const contract = pendingTaskContracts[idx];
273
368
  if (contract) {
274
369
  resultMap.set(contractId, contract);
275
370
  }
@@ -356,29 +451,26 @@ export class EntitlementsDashboardState {
356
451
  }
357
452
 
358
453
  const didToEnvType = new Map<number, string>();
454
+ const contractClient =
455
+ this.lakehouseEntitlementsStore.lakehouseContractServerClient;
359
456
  yield Promise.all(
360
457
  Array.from(uniqueDIDToDataProduct.entries()).map(
361
458
  async ([deploymentId, resourceId]) => {
362
459
  try {
363
- const raw =
364
- await this.lakehouseEntitlementsStore.lakehouseContractServerClient.getDataProductByIdAndDID(
365
- resourceId,
366
- deploymentId,
367
- token,
368
- );
369
- const details =
460
+ const raw = await contractClient.getDataProductByIdAndDID(
461
+ resourceId,
462
+ deploymentId,
463
+ token,
464
+ );
465
+ const env =
370
466
  V1_entitlementsDataProductDetailsResponseToDataProductDetails(
371
467
  raw,
372
- );
373
- const env = details[0]?.lakehouseEnvironment?.type;
468
+ )[0]?.lakehouseEnvironment?.type;
374
469
  if (env) {
375
470
  didToEnvType.set(deploymentId, env);
376
471
  }
377
472
  } catch (error) {
378
473
  assertErrorThrown(error);
379
- this.lakehouseEntitlementsStore.applicationStore.notificationService.notifyError(
380
- `Error fetching deployment environment for deployment ${deploymentId}: ${error.message}`,
381
- );
382
474
  }
383
475
  },
384
476
  ),
@@ -386,6 +478,154 @@ export class EntitlementsDashboardState {
386
478
  return didToEnvType;
387
479
  }
388
480
 
481
+ *getUnverifiedIngestDefinitions(
482
+ contractId: string,
483
+ token: string | undefined,
484
+ ): GeneratorFn<string[] | undefined> {
485
+ const entitlementsStore = this.lakehouseEntitlementsStore;
486
+ const baseStore = entitlementsStore.marketplaceBaseStore;
487
+ const applicationStore = entitlementsStore.applicationStore;
488
+ const plugins =
489
+ applicationStore.pluginManager.getPureProtocolProcessorPlugins();
490
+ const contractClient = entitlementsStore.lakehouseContractServerClient;
491
+
492
+ const PROD_ENV = 'prod';
493
+ const SDLC_DEPLOYMENT = 'alloy-git';
494
+
495
+ try {
496
+ const liteContract = (yield (async () => {
497
+ try {
498
+ const rawContractResponse = await contractClient.getDataContract(
499
+ contractId,
500
+ false,
501
+ token,
502
+ );
503
+ const dataContract = V1_deserializeDataContractResponse(
504
+ rawContractResponse,
505
+ plugins,
506
+ )[0]?.dataContract;
507
+ if (!dataContract) {
508
+ return undefined;
509
+ }
510
+ return V1_transformDataContractToLiteDatacontract(dataContract);
511
+ } catch (error) {
512
+ assertErrorThrown(error);
513
+ return undefined;
514
+ }
515
+ })()) as V1_LiteDataContract | undefined;
516
+ if (!liteContract) {
517
+ return [];
518
+ }
519
+
520
+ const accessPointGroupId =
521
+ liteContract.resourceType === V1_ResourceType.ACCESS_POINT_GROUP
522
+ ? (liteContract.accessPointGroup ?? undefined)
523
+ : undefined;
524
+ if (!accessPointGroupId) {
525
+ return [];
526
+ }
527
+
528
+ const dpDetails = (yield (async () => {
529
+ try {
530
+ const raw = await contractClient.getDataProductByIdAndDID(
531
+ liteContract.resourceId,
532
+ liteContract.deploymentId,
533
+ token,
534
+ );
535
+ return V1_entitlementsDataProductDetailsResponseToDataProductDetails(
536
+ raw,
537
+ )[0];
538
+ } catch (error) {
539
+ assertErrorThrown(error);
540
+ return undefined;
541
+ }
542
+ })()) as V1_EntitlementsDataProductDetails | undefined;
543
+ if (!dpDetails) {
544
+ return [];
545
+ }
546
+
547
+ if (!(dpDetails.origin instanceof V1_SdlcDeploymentDataProductOrigin)) {
548
+ return [];
549
+ }
550
+
551
+ const graphManager = new V1_PureGraphManager(
552
+ applicationStore.pluginManager,
553
+ applicationStore.logService,
554
+ baseStore.remoteEngine,
555
+ );
556
+ yield graphManager.initialize(
557
+ {
558
+ env: applicationStore.config.env,
559
+ tabSize: DEFAULT_TAB_SIZE,
560
+ clientConfig: {
561
+ baseUrl: applicationStore.config.engineServerUrl,
562
+ },
563
+ },
564
+ { engine: baseStore.remoteEngine },
565
+ );
566
+
567
+ const v1DataProduct = (yield getDataProductFromDetails(
568
+ dpDetails,
569
+ graphManager,
570
+ baseStore,
571
+ )) as V1_DataProduct | undefined;
572
+ if (!v1DataProduct) {
573
+ return [];
574
+ }
575
+
576
+ const specs = collectIngestSpecPathsFromOriginDp(
577
+ v1DataProduct,
578
+ accessPointGroupId,
579
+ graphManager,
580
+ plugins,
581
+ );
582
+ if (specs.size === 0) {
583
+ return [];
584
+ }
585
+
586
+ const ingestEnvironment =
587
+ (yield baseStore.lakehouseDataProductService.getOrFetchEnvironmentForDID(
588
+ liteContract.deploymentId,
589
+ token,
590
+ )) as IngestDeploymentServerConfig | undefined;
591
+ const ingestServerUrl = ingestEnvironment?.ingestServerUrl;
592
+ if (ingestServerUrl === undefined) {
593
+ return [];
594
+ }
595
+
596
+ const sdlcOrigin = guaranteeType(
597
+ dpDetails.origin,
598
+ V1_SdlcDeploymentDataProductOrigin,
599
+ );
600
+ const gav = `${sdlcOrigin.group}~${sdlcOrigin.artifact}`;
601
+ const specsToVerify = Array.from(specs, (specPath) => ({
602
+ specPath,
603
+ urn: `urn:lakehouse:${PROD_ENV}:ingest:definition:${SDLC_DEPLOYMENT}:${gav}~${specPath}`,
604
+ }));
605
+
606
+ const ingestClient = baseStore.lakehouseIngestServerClient;
607
+ const settled = (yield Promise.all(
608
+ specsToVerify.map(async (entry) => {
609
+ try {
610
+ await ingestClient.getIngestDefinitionDetail(
611
+ entry.urn,
612
+ ingestServerUrl,
613
+ token,
614
+ );
615
+ return undefined;
616
+ } catch (error) {
617
+ assertErrorThrown(error);
618
+ return entry.specPath;
619
+ }
620
+ }),
621
+ )) as (string | undefined)[];
622
+ return settled.filter(isNonNullable);
623
+ } catch (error) {
624
+ assertErrorThrown(error);
625
+ return undefined;
626
+ }
627
+ }
628
+
389
629
  private filterByUserEnvironment(
390
630
  pendingData: {
391
631
  tasks: V1_ContractUserEventRecord[];