UNPKG

@finos/legend-application-marketplace

Version:
398 lines 20.9 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 { ActionState, assertErrorThrown, guaranteeNonNullable, isNonNullable, } from '@finos/legend-shared'; import { deserialize } from 'serializr'; import { V1_deserializeDataContractResponse, V1_entitlementsDataProductDetailsResponseToDataProductDetails, V1_liteDataContractWithUserStatusModelSchema, V1_pendingTasksResponseModelSchema, V1_TaskStatusChangeResponseModelSchema, V1_transformDataContractToLiteDatacontract, V1_deserializeDataRequestsWithWorkflowResponse, } from '@finos/legend-graph'; import { buildSyncErrorLayer, buildContractErrorsRoot, getContractAPGCoordinates, getUnverifiedIngestDefinitionsForAPG, } from '@finos/legend-extension-dsl-data-product'; import { makeObservable, flow, observable, flowResult, action, computed, } from 'mobx'; import { TEST_USER, } from './LakehouseEntitlementsStore.js'; export class ContractCreatedByUserDetails { contractResultLite; assignees = new Set(); members = new Map(); constructor(contract) { this.contractResultLite = contract; makeObservable(this, { assignees: observable, members: observable, sortedAssigneeIds: computed, sortedMemberIds: computed, addAssignees: action, addMember: action, }); } get sortedAssigneeIds() { return Array.from(this.assignees).toSorted(); } get sortedMemberIds() { return Array.from(this.members.keys()).toSorted(); } addAssignees(assignees) { assignees.forEach((assignee) => this.assignees.add(assignee)); } addMember(id, status) { this.members.set(id, status); } } export class EntitlementsDashboardState { lakehouseEntitlementsStore; pendingTasks; pendingTaskContractMap = new Map(); allContractsForUser; // The contracts createdBy user API returns an entry for each task, not just for each contract. // To consolidate this information, we store a map of contract ID to the contract details + the // consolidated user information from the tasks. allContractsCreatedByUserMap = new Map(); dataRequestsCreatedByUser; selectedTaskIds = new Set(); initializationState = ActionState.create(); fetchingPendingTasksState = ActionState.create(); fetchingContractsForUserState = ActionState.create(); fetchingContractsByUserState = ActionState.create(); fetchingDataRequestsCreatedByUserState = ActionState.create(); changingState = ActionState.create(); constructor(state) { this.lakehouseEntitlementsStore = state; makeObservable(this, { pendingTasks: observable, allContractsForUser: observable, allContractsCreatedByUserMap: observable, pendingTaskContractMap: observable, selectedTaskIds: observable, dataRequestsCreatedByUser: observable, pendingTaskContracts: computed, allContractsCreatedByUser: computed, setSelectedTaskIds: action, init: flow, approve: flow, deny: flow, fetchPendingTasks: flow, fetchPendingTaskContracts: flow, fetchContractsForUser: flow, fetchContractsCreatedByUser: flow, fetchContractDeploymentEnvironments: flow, updateContract: flow, fetchDataRequestsCreatedByUser: flow, }); } get pendingTaskContracts() { return Array.from(this.pendingTaskContractMap.values()); } get allContractsCreatedByUser() { return Array.from(this.allContractsCreatedByUserMap.values()); } setSelectedTaskIds(ids) { this.selectedTaskIds = ids; } *init(token) { this.initializationState.inProgress(); this.setSelectedTaskIds(new Set()); try { this.fetchingPendingTasksState.inProgress(); this.fetchingContractsForUserState.inProgress(); this.fetchingContractsByUserState.inProgress(); this.fetchingDataRequestsCreatedByUserState.inProgress(); const [pendingTasksData, contractsForUser, contractsCreatedByUserMap, dataRequestsCreatedByUser,] = (yield Promise.all([ (async () => { try { const tasks = await flowResult(this.fetchPendingTasks(token)); const taskContractMap = await flowResult(this.fetchPendingTaskContracts(token, tasks)); return { tasks, taskContractMap }; } catch (error) { assertErrorThrown(error); this.lakehouseEntitlementsStore.applicationStore.alertUnhandledError(error); return { tasks: [], taskContractMap: new Map(), }; } })(), flowResult(this.fetchContractsForUser(token)), flowResult(this.fetchContractsCreatedByUser(token)), flowResult(this.fetchDataRequestsCreatedByUser(token)), ])); const allContracts = [ ...Array.from(pendingTasksData.taskContractMap.values()), ...contractsForUser.map((c) => c.contractResultLite), ...Array.from(contractsCreatedByUserMap.values()).map((c) => c.contractResultLite), ]; const envMap = (yield flowResult(this.fetchContractDeploymentEnvironments(allContracts, token))); const { filteredTasks, filteredContractsForUser, filteredCreatedByUserMap, filteredDataRequests, } = this.filterByUserEnvironment(pendingTasksData, contractsForUser, contractsCreatedByUserMap, dataRequestsCreatedByUser, envMap); this.pendingTaskContractMap = pendingTasksData.taskContractMap; this.pendingTasks = filteredTasks; this.allContractsForUser = filteredContractsForUser; this.allContractsCreatedByUserMap = filteredCreatedByUserMap; this.dataRequestsCreatedByUser = filteredDataRequests; this.fetchingPendingTasksState.complete(); this.fetchingContractsForUserState.complete(); this.fetchingContractsByUserState.complete(); this.fetchingDataRequestsCreatedByUserState.complete(); } catch (error) { assertErrorThrown(error); this.lakehouseEntitlementsStore.applicationStore.alertUnhandledError(error); } finally { this.initializationState.complete(); } } *fetchPendingTasks(token) { try { const rawTasks = (yield this.lakehouseEntitlementsStore.marketplaceBaseStore.pendingTasksCache.fetch(TEST_USER, token)); const tasks = deserialize(V1_pendingTasksResponseModelSchema, rawTasks); return [...tasks.dataOwner, ...tasks.privilegeManager]; } catch (error) { assertErrorThrown(error); this.lakehouseEntitlementsStore.applicationStore.notificationService.notifyError(`Error fetching pending tasks: ${error.message}`); return []; } } *fetchPendingTaskContracts(token, pendingTasks) { const pendingTaskContractIds = Array.from(new Set(pendingTasks.map((t) => t.dataContractId))); const contractClient = this.lakehouseEntitlementsStore.lakehouseContractServerClient; const plugins = this.lakehouseEntitlementsStore.applicationStore.pluginManager.getPureProtocolProcessorPlugins(); const pendingTaskContracts = (yield Promise.all(pendingTaskContractIds.map(async (contractId) => { try { const rawContractResponse = await contractClient.getDataContract(contractId, false, token); const dataContract = V1_deserializeDataContractResponse(rawContractResponse, plugins)[0]?.dataContract; if (!dataContract) { return undefined; } return V1_transformDataContractToLiteDatacontract(dataContract); } catch (error) { assertErrorThrown(error); return undefined; } }))); const resultMap = new Map(); pendingTaskContractIds.forEach((contractId, idx) => { const contract = pendingTaskContracts[idx]; if (contract) { resultMap.set(contractId, contract); } }); return resultMap; } *fetchContractsForUser(token) { try { const rawContracts = (yield this.lakehouseEntitlementsStore.lakehouseContractServerClient.getContractsForUser(this.lakehouseEntitlementsStore.applicationStore.identityService .currentUser, token)); return rawContracts.map((rawContract) => deserialize(V1_liteDataContractWithUserStatusModelSchema(this.lakehouseEntitlementsStore.applicationStore.pluginManager.getPureProtocolProcessorPlugins()), rawContract)); } catch (error) { assertErrorThrown(error); this.lakehouseEntitlementsStore.applicationStore.notificationService.notifyError(`Error fetching data contracts for user: ${error.message}`); return []; } } *fetchContractsCreatedByUser(token) { try { const rawContracts = (yield this.lakehouseEntitlementsStore.lakehouseContractServerClient.getContractsCreatedByUser(this.lakehouseEntitlementsStore.applicationStore.identityService .currentUser, token)); const contracts = rawContracts.map((rawContract) => deserialize(V1_liteDataContractWithUserStatusModelSchema(this.lakehouseEntitlementsStore.applicationStore.pluginManager.getPureProtocolProcessorPlugins()), rawContract)); const resultMap = new Map(); contracts.forEach((contract) => { if (!resultMap.has(contract.contractResultLite.guid)) { resultMap.set(contract.contractResultLite.guid, new ContractCreatedByUserDetails(contract.contractResultLite)); } const entry = guaranteeNonNullable(resultMap.get(contract.contractResultLite.guid)); entry.addAssignees(contract.pendingTaskWithAssignees?.assignees ?? []); entry.addMember(contract.user, contract.status); }); return resultMap; } catch (error) { assertErrorThrown(error); this.lakehouseEntitlementsStore.applicationStore.notificationService.notifyError(`Error fetching data contracts created by user: ${error.message}`); return new Map(); } } *fetchDataRequestsCreatedByUser(token) { try { const raw = (yield this.lakehouseEntitlementsStore.lakehouseContractServerClient.getDataAccessRequestsCreatedBy(this.lakehouseEntitlementsStore.applicationStore.identityService .currentUser, token)); return V1_deserializeDataRequestsWithWorkflowResponse(raw, this.lakehouseEntitlementsStore.applicationStore.pluginManager.getPureProtocolProcessorPlugins()); } catch (error) { assertErrorThrown(error); this.lakehouseEntitlementsStore.applicationStore.notificationService.notifyError(`Error fetching data requests created by user: ${error.message}`); return []; } } *fetchContractDeploymentEnvironments(allContracts, token) { const uniqueDIDToDataProduct = new Map(); for (const contract of allContracts) { uniqueDIDToDataProduct.set(contract.deploymentId, contract.resourceId); } const didToEnvType = new Map(); const contractClient = this.lakehouseEntitlementsStore.lakehouseContractServerClient; yield Promise.all(Array.from(uniqueDIDToDataProduct.entries()).map(async ([deploymentId, resourceId]) => { try { const raw = await contractClient.getDataProductByIdAndDID(resourceId, deploymentId, token); const env = V1_entitlementsDataProductDetailsResponseToDataProductDetails(raw)[0]?.lakehouseEnvironment?.type; if (env) { didToEnvType.set(deploymentId, env); } } catch (error) { assertErrorThrown(error); } })); return didToEnvType; } async getContractIngestErrors(contractId, token) { const baseStore = this.lakehouseEntitlementsStore.marketplaceBaseStore; const plugins = this.lakehouseEntitlementsStore.applicationStore.pluginManager.getPureProtocolProcessorPlugins(); const apg = await getContractAPGCoordinates(contractId, baseStore.lakehouseContractServerClient, plugins, token); if (!apg) { return undefined; } const unverifiedIngestDefinitions = await getUnverifiedIngestDefinitionsForAPG(apg, baseStore, plugins, () => baseStore.createInitializedGraphManager(), token); if (unverifiedIngestDefinitions.length === 0) { return undefined; } return { title: `Ingest${unverifiedIngestDefinitions.length === 1 ? '' : 's'} Not Found:`, errorItems: unverifiedIngestDefinitions, }; } async getContractSyncErrors(contractId, token) { try { const response = (await this.lakehouseEntitlementsStore.lakehouseContractServerClient.getContractSyncStatus(contractId, token)); return buildSyncErrorLayer(response); } catch (error) { assertErrorThrown(error); return undefined; } } async getContractErrors(contractId, token, checkSyncStatus = false) { const [ingestErrorsLayer, syncErrorsLayer] = await Promise.all([ this.getContractIngestErrors(contractId, token), checkSyncStatus ? this.getContractSyncErrors(contractId, token) : Promise.resolve(undefined), ]); return buildContractErrorsRoot([ingestErrorsLayer, syncErrorsLayer]); } filterByUserEnvironment(pendingData, contractsForUser, contractsCreatedByUserMap, dataRequests, envMap) { const userEnv = this.lakehouseEntitlementsStore.marketplaceBaseStore.envState .lakehouseEnvironment; const envMatchesForDeploymentId = (deploymentId) => { const env = envMap.get(deploymentId); return !env || env === userEnv; }; const filteredTasks = pendingData.tasks.filter((task) => { const contract = pendingData.taskContractMap.get(task.dataContractId); return !contract || envMatchesForDeploymentId(contract.deploymentId); }); const filteredContractsForUser = contractsForUser.filter((c) => envMatchesForDeploymentId(c.contractResultLite.deploymentId)); const filteredCreatedByUserMap = new Map(); for (const [guid, details] of contractsCreatedByUserMap.entries()) { if (envMatchesForDeploymentId(details.contractResultLite.deploymentId)) { filteredCreatedByUserMap.set(guid, details); } } const filteredDataRequests = dataRequests.filter((dr) => { const envType = dr.dataRequest.resourceEnvType; return !envType || envType === userEnv; }); return { filteredTasks, filteredContractsForUser, filteredCreatedByUserMap, filteredDataRequests, }; } *updateContract(contractId, token) { const [newUserContracts, newCreatedByUserContracts] = (yield Promise.all([ (async () => { const rawContracts = await this.lakehouseEntitlementsStore.lakehouseContractServerClient.getContractsForUser(this.lakehouseEntitlementsStore.applicationStore.identityService .currentUser, token); return rawContracts.map((rawContract) => deserialize(V1_liteDataContractWithUserStatusModelSchema(this.lakehouseEntitlementsStore.applicationStore.pluginManager.getPureProtocolProcessorPlugins()), rawContract)); })(), (async () => { const rawContracts = await this.lakehouseEntitlementsStore.lakehouseContractServerClient.getContractsCreatedByUser(this.lakehouseEntitlementsStore.applicationStore.identityService .currentUser, token); return rawContracts.map((rawContract) => deserialize(V1_liteDataContractWithUserStatusModelSchema(this.lakehouseEntitlementsStore.applicationStore.pluginManager.getPureProtocolProcessorPlugins()), rawContract)); })(), ])); // Update the contract for the user this.allContractsForUser = this.allContractsForUser ?.map((contract) => contract.contractResultLite.guid === contractId ? newUserContracts.find((c) => c.contractResultLite.guid === contractId) : contract) .filter(isNonNullable); // Update the contract + all related data for contract created by the user this.allContractsCreatedByUserMap.delete(contractId); const updatedCreatedByUserContracts = newCreatedByUserContracts.filter((c) => c.contractResultLite.guid === contractId); updatedCreatedByUserContracts.forEach((contract) => { if (!this.allContractsCreatedByUserMap.has(contract.contractResultLite.guid)) { this.allContractsCreatedByUserMap.set(contract.contractResultLite.guid, new ContractCreatedByUserDetails(contract.contractResultLite)); } const entry = guaranteeNonNullable(this.allContractsCreatedByUserMap.get(contract.contractResultLite.guid)); entry.addAssignees(contract.pendingTaskWithAssignees?.assignees ?? []); entry.addMember(contract.user, contract.status); }); } *approve(task, token) { try { this.changingState.inProgress(); this.changingState.setMessage('Approving Task'); const response = (yield this.lakehouseEntitlementsStore.lakehouseContractServerClient.approveTask(task.taskId, token)); const change = deserialize(V1_TaskStatusChangeResponseModelSchema, response); if (change.errorMessage) { throw new Error(`Unable to approve task: ${task.taskId}: ${change.errorMessage}`); } task.status = change.status; this.pendingTasks = [...(this.pendingTasks ?? [])]; this.lakehouseEntitlementsStore.marketplaceBaseStore.pendingTasksCache.invalidate(); this.lakehouseEntitlementsStore.applicationStore.notificationService.notifySuccess(`Task has been Approved`); } finally { this.changingState.complete(); this.changingState.setMessage(undefined); } } *deny(task, token) { try { this.changingState.inProgress(); this.lakehouseEntitlementsStore.applicationStore.alertService.setBlockingAlert({ message: 'Denying Task', prompt: 'Denying task...', showLoading: true, }); const response = (yield this.lakehouseEntitlementsStore.lakehouseContractServerClient.denyTask(task.taskId, token)); const change = deserialize(V1_TaskStatusChangeResponseModelSchema, response); if (change.errorMessage) { throw new Error(`Unable to deny task: ${task.taskId}: ${change.errorMessage}`); } task.status = change.status; this.pendingTasks = [...(this.pendingTasks ?? [])]; this.lakehouseEntitlementsStore.marketplaceBaseStore.pendingTasksCache.invalidate(); this.lakehouseEntitlementsStore.applicationStore.notificationService.notifySuccess(`Task has been denied`); } finally { this.changingState.complete(); this.changingState.setMessage(undefined); this.lakehouseEntitlementsStore.applicationStore.alertService.setBlockingAlert(undefined); } } } //# sourceMappingURL=EntitlementsDashboardState.js.map