UNPKG

@finos/legend-application-marketplace

Version:
299 lines (279 loc) 10.4 kB
/** * Copyright (c) 2020-present, Goldman Sachs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { NAVIGATION_ZONE_SEPARATOR, type GenericLegendApplicationStore, type NavigationZone, } from '@finos/legend-application'; import { type DataProductArtifactGeneration, type GraphData, type GraphManagerState, type V1_AccessPointGroup, type V1_CreateContractPayload, type V1_DataContract, type V1_DataContractsRecord, type V1_DataProduct, V1_AdhocTeam, V1_AppDirLevel, V1_AppDirNode, V1_AppDirNodeModelSchema, V1_createContractPayloadModelSchema, V1_DataContractsRecordModelSchemaToContracts, V1_ResourceType, V1_User, V1_UserType, } from '@finos/legend-graph'; import type { VersionedProjectData } from '@finos/legend-server-depot'; import { action, computed, flow, makeObservable, observable } from 'mobx'; import { DATA_PRODUCT_WIKI_PAGE_SECTIONS, DataProductLayoutState, } from './DataProductLayoutState.js'; import { DATA_PRODUCT_VIEWER_ACTIVITY_MODE, generateAnchorForActivity, } from './DataProductViewerNavigation.js'; import { DataProductDataAccessState } from './DataProductDataAccessState.js'; import { ActionState, assertErrorThrown, guaranteeNonEmptyString, guaranteeNonNullable, type GeneratorFn, type PlainObject, } from '@finos/legend-shared'; import { serialize } from 'serializr'; import { dataContractContainsDataProduct } from './LakehouseUtils.js'; import type { LakehouseContractServerClient } from '@finos/legend-server-marketplace'; import type { MarketplaceLakehouseStore } from './MarketplaceLakehouseStore.js'; const buildAdhocUser = (user: string): V1_AdhocTeam => { const _user = new V1_User(); _user.name = user; _user.userType = V1_UserType.WORKFORCE_USER; const _adhocTeam = new V1_AdhocTeam(); _adhocTeam.users = [_user]; return _adhocTeam; }; export class DataProductViewerState { readonly applicationStore: GenericLegendApplicationStore; readonly lakehouseStore: MarketplaceLakehouseStore; readonly graphManagerState: GraphManagerState; readonly layoutState: DataProductLayoutState; readonly product: V1_DataProduct; readonly isSandboxProduct: boolean; readonly project: VersionedProjectData; readonly retrieveGraphData: () => GraphData; readonly viewSDLCProject: (path: string | undefined) => Promise<void>; readonly viewIngestEnvironment?: (() => void) | undefined; readonly onZoneChange?: | ((zone: NavigationZone | undefined) => void) | undefined; // we may want to move this out eventually readonly lakeServerClient: LakehouseContractServerClient; currentActivity = DATA_PRODUCT_VIEWER_ACTIVITY_MODE.DESCRIPTION; accessState: DataProductDataAccessState; generation: DataProductArtifactGeneration | undefined; associatedContracts: V1_DataContract[] | undefined; dataContractAccessPointGroup: V1_AccessPointGroup | undefined; dataContract: V1_DataContract | undefined; // actions creatingContractState = ActionState.create(); constructor( applicationStore: GenericLegendApplicationStore, lakehouseStore: MarketplaceLakehouseStore, graphManagerState: GraphManagerState, lakeServerClient: LakehouseContractServerClient, project: VersionedProjectData, product: V1_DataProduct, isSandboxProduct: boolean, generation: DataProductArtifactGeneration | undefined, actions: { retrieveGraphData: () => GraphData; viewSDLCProject: (path: string | undefined) => Promise<void>; viewIngestEnvironment?: (() => void) | undefined; onZoneChange?: ((zone: NavigationZone | undefined) => void) | undefined; }, ) { makeObservable(this, { currentActivity: observable, setCurrentActivity: action, isVerified: computed, accessState: observable, fetchContracts: flow, associatedContracts: observable, dataContractAccessPointGroup: observable, setDataContractAccessPointGroup: action, dataContract: observable, setDataContract: action, setAssociatedContracts: action, createContract: flow, creatingContractState: observable, }); this.applicationStore = applicationStore; this.lakehouseStore = lakehouseStore; this.graphManagerState = graphManagerState; this.project = project; this.product = product; this.isSandboxProduct = isSandboxProduct; this.generation = generation; this.retrieveGraphData = actions.retrieveGraphData; this.viewSDLCProject = actions.viewSDLCProject; this.viewIngestEnvironment = actions.viewIngestEnvironment; this.onZoneChange = actions.onZoneChange; this.layoutState = new DataProductLayoutState(this); this.accessState = new DataProductDataAccessState(this); this.lakeServerClient = lakeServerClient; } setAssociatedContracts(val: V1_DataContract[] | undefined): void { this.associatedContracts = val; } setDataContractAccessPointGroup(val: V1_AccessPointGroup | undefined) { this.dataContractAccessPointGroup = val; } setDataContract(val: V1_DataContract | undefined) { this.dataContract = val; } *fetchContracts(token: string | undefined): GeneratorFn<void> { try { this.accessState.accessGroupStates.forEach((e) => e.fetchingAccessState.inProgress(), ); const did = guaranteeNonEmptyString( this.generation?.dataProduct.deploymentId, 'did required to get contracts', ); const didNode = new V1_AppDirNode(); didNode.appDirId = Number(did); didNode.level = V1_AppDirLevel.DEPLOYMENT; const _contracts = (yield this.lakeServerClient.getDataContractsFromDID( [serialize(V1_AppDirNodeModelSchema, didNode)], token, )) as PlainObject<V1_DataContractsRecord>; const dataProductContracts = V1_DataContractsRecordModelSchemaToContracts( _contracts, ).filter((_contract) => dataContractContainsDataProduct( this.product, this.deploymentId, _contract, ), ); this.setAssociatedContracts(dataProductContracts); this.accessState.accessGroupStates.forEach((e) => e.handleDataProductContracts(dataProductContracts), ); } catch (error) { assertErrorThrown(error); this.accessState.viewerState.applicationStore.notificationService.notifyError( `${error.message}`, ); } finally { this.accessState.accessGroupStates.forEach((e) => e.fetchingAccessState.complete(), ); } } *createContract( userId: string, description: string, group: V1_AccessPointGroup, token: string | undefined, ): GeneratorFn<void> { try { this.creatingContractState.inProgress(); const request = serialize(V1_createContractPayloadModelSchema, { description, resourceId: this.product.name, resourceType: V1_ResourceType.ACCESS_POINT_GROUP, deploymentId: guaranteeNonNullable( this.generation?.dataProduct.deploymentId, 'Cannot create contract. Data product generation is missing deployment ID', ), accessPointGroup: group.id, consumer: buildAdhocUser(userId), } satisfies V1_CreateContractPayload) as PlainObject<V1_CreateContractPayload>; const contracts = V1_DataContractsRecordModelSchemaToContracts( (yield this.lakeServerClient.createContract( request, token, )) as unknown as PlainObject<V1_DataContractsRecord>, ); const associatedContract = contracts[0]; // Only if the user has requested a contract for themself do we update the associated contract. if ( associatedContract?.consumer instanceof V1_AdhocTeam && associatedContract.consumer.users.some( (u) => u.name === this.applicationStore.identityService.currentUser, ) ) { const groupAccessState = this.accessState.accessGroupStates.find( (e) => e.group === group, ); groupAccessState?.setAssociatedContract(associatedContract); } this.setDataContractAccessPointGroup(undefined); this.setDataContract(associatedContract); this.applicationStore.notificationService.notifySuccess( `Contract created, please go to contract view for pending tasks`, ); } catch (error) { assertErrorThrown(error); this.accessState.viewerState.applicationStore.notificationService.notifyError( `${error.message}`, ); } finally { this.creatingContractState.complete(); } } setCurrentActivity(val: DATA_PRODUCT_VIEWER_ACTIVITY_MODE): void { this.currentActivity = val; } get isVerified(): boolean { // TODO what does it mean if data product is vertified ? return true; } get deploymentId(): string | undefined { return this.generation?.dataProduct.deploymentId; } changeZone(zone: NavigationZone, force = false): void { if (force) { this.layoutState.setCurrentNavigationZone(''); } if (zone !== this.layoutState.currentNavigationZone) { const zoneChunks = zone.split(NAVIGATION_ZONE_SEPARATOR); const activityChunk = zoneChunks[0]; const matchingActivity = Object.values( DATA_PRODUCT_VIEWER_ACTIVITY_MODE, ).find( (activity) => generateAnchorForActivity(activity) === activityChunk, ); if (activityChunk && matchingActivity) { if (DATA_PRODUCT_WIKI_PAGE_SECTIONS.includes(matchingActivity)) { this.layoutState.setWikiPageAnchorToNavigate({ anchor: zone, }); } this.setCurrentActivity(matchingActivity); this.onZoneChange?.(zone); this.layoutState.setCurrentNavigationZone(zone); } else { this.setCurrentActivity(DATA_PRODUCT_VIEWER_ACTIVITY_MODE.DESCRIPTION); this.layoutState.setCurrentNavigationZone(''); } } } }