UNPKG

@finos/legend-extension-dsl-data-space-studio

Version:
402 lines 21.3 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 {} from '@finos/legend-graph'; import { StoreProjectData, ProjectDependencyCoordinates, ProjectVersionEntities, MASTER_SNAPSHOT_ALIAS, } from '@finos/legend-server-depot'; import { ActionState, assertErrorThrown, guaranteeNonNullable, LogEvent, } from '@finos/legend-shared'; import {} from '@finos/legend-storage'; import { fetchProjectConfigurationStatus, generateEditorRoute, LEGEND_STUDIO_APP_EVENT, EditorStore, generateReviewRoute, } from '@finos/legend-application-studio'; import { DEFAULT_TAB_SIZE, ActionAlertType, ActionAlertActionType, } from '@finos/legend-application'; import { makeObservable, observable, computed, action, flow, flowResult, } from 'mobx'; import { Project, WorkspaceType, Workspace, ProjectConfiguration, EntityChangeType, } from '@finos/legend-server-sdlc'; import { DATA_SPACE_ELEMENT_CLASSIFIER_PATH, DSL_DataSpace_getGraphManagerExtension, } from '@finos/legend-extension-dsl-data-space/graph'; import { generateDataSpaceTemplateQueryPromotionRoute } from '@finos/legend-extension-dsl-data-space/application'; const projectDependencyToProjectCoordinates = (projectDependency) => new ProjectDependencyCoordinates(guaranteeNonNullable(projectDependency.groupId), guaranteeNonNullable(projectDependency.artifactId), projectDependency.versionId); const DEFAULT_WORKSPACE_NAME_PREFIX = 'promote-as-template-query'; export class DataSpaceTemplateQueryPromotionReviewerStore { applicationStore; sdlcServerClient; depotServerClient; initState = ActionState.create(); promoteState = ActionState.create(); loadQueryState = ActionState.create(); loadWorkspacesState = ActionState.create(); editorStore; graphManagerExtension; currentQuery; currentQueryInfo; currentQueryProject; currentProject; currentProjectConfiguration; currentProjectConfigurationStatus; currentProjectEntities = []; dependencyEntities = []; groupWorkspaces = []; workspaceName = ''; dataSpacePath; dataSpaceEntity; templateQueryId = 'template_id'; templateQueryTitle = 'template_title'; templateQueryDescription = ''; constructor(applicationStore, sdlcServerClient, depotServerClient) { makeObservable(this, { editorStore: observable, graphManagerExtension: observable, currentQuery: observable, currentQueryInfo: observable, currentQueryProject: observable, currentProject: observable, currentProjectConfiguration: observable, currentProjectConfigurationStatus: observable, currentProjectEntities: observable, dataSpaceEntity: observable, groupWorkspaces: observable, workspaceName: observable, templateQueryId: observable, templateQueryTitle: observable, templateQueryDescription: observable, isWorkspaceNameValid: computed, isTemplateQueryIdValid: computed, setWorkspaceName: action, setTemplateQueryId: action, setTemplateQueryTitle: action, setTemplateQueryDescription: action, initialize: flow, loadQuery: flow, loadProject: flow, promoteAsTemplateQuery: flow, }); this.applicationStore = applicationStore; this.sdlcServerClient = sdlcServerClient; this.depotServerClient = depotServerClient; this.editorStore = new EditorStore(applicationStore, sdlcServerClient, depotServerClient); this.graphManagerExtension = DSL_DataSpace_getGraphManagerExtension(this.editorStore.graphManagerState.graphManager); } setWorkspaceName(val) { this.workspaceName = val; } setTemplateQueryId(val) { this.templateQueryId = val; } setTemplateQueryTitle(val) { this.templateQueryTitle = val; } setTemplateQueryDescription(val) { this.templateQueryDescription = val; } get isWorkspaceNameValid() { return !this.groupWorkspaces.some((ws) => ws.workspaceId === this.workspaceName); } get isTemplateQueryIdValid() { if (this.dataSpaceEntity) { return this.graphManagerExtension.IsTemplateQueryIdValid(this.dataSpaceEntity, this.templateQueryId); } return false; } *initialize(queryId, dataSpacePath) { if (!this.initState.isInInitialState) { return; } try { this.initState.inProgress(); yield this.graphManagerExtension.graphManager.initialize({ env: this.applicationStore.config.env, tabSize: DEFAULT_TAB_SIZE, clientConfig: { baseUrl: this.applicationStore.config.engineServerUrl, queryBaseUrl: this.applicationStore.config.engineQueryServerUrl, enableCompression: true, }, }, { tracerService: this.applicationStore.tracerService, }); this.dataSpacePath = dataSpacePath; if (queryId) { let query; try { query = (yield this.graphManagerExtension.graphManager.getLightQuery(queryId)); } catch { query = undefined; } if (query) { yield flowResult(this.loadQuery(query)); } else { this.applicationStore.notificationService.notifyError(`Unable to find query with ID: ${queryId}`); } } if (this.currentQuery) { this.currentQueryProject = StoreProjectData.serialization.fromJson((yield this.depotServerClient.getProject(this.currentQuery.groupId, this.currentQuery.artifactId))); const projectData = (yield Promise.all([ this.depotServerClient.getVersionEntities(this.currentQuery.groupId, this.currentQuery.artifactId, MASTER_SNAPSHOT_ALIAS), this.sdlcServerClient.getConfiguration(this.currentQueryProject.projectId, undefined), ])); const [currentProjectEntities, currentProjectConfiguration] = [ projectData[0], ProjectConfiguration.serialization.fromJson(projectData[1]), ]; this.currentProjectConfiguration = currentProjectConfiguration; const dependencyEntities = (yield this.depotServerClient.collectDependencyEntities([ ...currentProjectConfiguration.projectDependencies, ] .map(projectDependencyToProjectCoordinates) .map((p) => ProjectDependencyCoordinates.serialization.toJson(p)), true, true)) .map((p) => ProjectVersionEntities.serialization.fromJson(p)) .flatMap((info) => info.entities); this.dependencyEntities = dependencyEntities; this.currentProjectEntities = currentProjectEntities; this.dataSpaceEntity = guaranteeNonNullable(currentProjectEntities.filter((entity) => entity.path === dataSpacePath && entity.classifierPath === DATA_SPACE_ELEMENT_CLASSIFIER_PATH)[0], `Can't find data product entity with path ${this.dataSpaceEntity}`); this.initState.pass(); } } catch (error) { assertErrorThrown(error); this.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.GENERIC_FAILURE), error); this.applicationStore.alertService.setBlockingAlert({ message: `Can't initialize template query promotion reviewer store`, }); this.initState.fail(); } } *loadQuery(query) { this.currentQuery = query; this.templateQueryTitle = query.name; try { this.loadQueryState.inProgress(); this.currentQueryInfo = (yield this.graphManagerExtension.graphManager.getQueryInfo(query.id)); this.currentQueryProject = StoreProjectData.serialization.fromJson((yield this.depotServerClient.getProject(this.currentQuery.groupId, this.currentQuery.artifactId))); const updatedQueryName = query.name.replace(/[^a-zA-Z0-9]/g, ''); this.setWorkspaceName(`${DEFAULT_WORKSPACE_NAME_PREFIX}-${updatedQueryName}`); this.applicationStore.navigationService.navigator.updateCurrentLocation(generateDataSpaceTemplateQueryPromotionRoute(this.currentQuery.groupId, this.currentQuery.artifactId, this.currentQuery.versionId, this.dataSpacePath, query.id)); const currentProject = Project.serialization.fromJson((yield this.sdlcServerClient.getProject(this.currentQueryProject.projectId))); yield flowResult(this.loadProject(currentProject)); } catch (error) { assertErrorThrown(error); this.applicationStore.notificationService.notifyError(error); } finally { this.loadQueryState.complete(); } } *loadProject(project) { this.currentProject = project; this.currentProjectConfigurationStatus = undefined; this.loadWorkspacesState.inProgress(); try { this.currentProjectConfigurationStatus = (yield fetchProjectConfigurationStatus(project.projectId, undefined, this.applicationStore, this.sdlcServerClient)); this.groupWorkspaces = (yield this.sdlcServerClient.getGroupWorkspaces(project.projectId)) .map((v) => Workspace.serialization.fromJson(v)) .filter((workspace) => workspace.workspaceType === WorkspaceType.GROUP); this.loadWorkspacesState.pass(); } catch (error) { assertErrorThrown(error); this.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error); this.applicationStore.notificationService.notifyError(error); this.loadWorkspacesState.fail(); } } *promoteAsTemplateQuery() { const query = this.currentQuery; const project = this.currentProject; if (this.promoteState.isInProgress || !query || !this.currentQueryInfo || !this.currentProjectConfiguration || !project || !this.workspaceName || !this.templateQueryTitle || !this.dataSpaceEntity || !this.isWorkspaceNameValid || !this.isTemplateQueryIdValid) { return; } try { this.promoteState.inProgress(); // 1. prepare project entities this.applicationStore.alertService.setBlockingAlert({ message: `Fetching and updating project...`, prompt: 'Please do not close the application', showLoading: true, }); // update datasapce entity const updatedDataSpaceEntity = (yield this.graphManagerExtension.addNewExecutableToDataSpaceEntity(this.dataSpaceEntity, this.currentQueryInfo, { id: this.templateQueryId, title: this.templateQueryTitle, description: this.templateQueryDescription, })); guaranteeNonNullable(this.currentProjectEntities.filter((entity) => entity.path === this.dataSpacePath && entity.classifierPath === DATA_SPACE_ELEMENT_CLASSIFIER_PATH)[0]).content = updatedDataSpaceEntity.content; // 2. check if the graph compiles properly this.applicationStore.alertService.setBlockingAlert({ message: `Checking workspace compilation status...`, prompt: 'Please do not close the application', showLoading: true, }); let compilationFailed = false; try { yield this.graphManagerExtension.graphManager.compileEntities([ ...this.dependencyEntities, ...this.currentProjectEntities, ]); } catch { compilationFailed = true; } // 3. proceed to setup the workspace const setupWorkspace = async () => { let workspace; try { this.applicationStore.alertService.setBlockingAlert({ message: `Creating workspace...`, prompt: 'Please do not close the application', showLoading: true, }); // i. create workspace workspace = Workspace.serialization.fromJson(await this.sdlcServerClient.createWorkspace(project.projectId, undefined, this.workspaceName, WorkspaceType.GROUP)); // ii. update data product this.applicationStore.alertService.setBlockingAlert({ message: `Generating code commit...`, prompt: 'Please do not close the application', showLoading: true, }); await this.sdlcServerClient.performEntityChanges(project.projectId, workspace, { message: 'promote-as-template-query: promote query as a template query to data product', entityChanges: [ { classifierPath: updatedDataSpaceEntity.classifierPath, entityPath: updatedDataSpaceEntity.path, content: updatedDataSpaceEntity.content, type: EntityChangeType.MODIFY, }, ], }); // iii create review this.applicationStore.alertService.setBlockingAlert({ message: `Generating code review...`, prompt: 'Please do not close the application', showLoading: true, }); await flowResult(this.editorStore.initialize(project.projectId, undefined, workspace.workspaceId, workspace.workspaceType, undefined)); const workspaceReviewState = this.editorStore.workspaceReviewState; const workspaceContainsSnapshotDependencies = this.editorStore.projectConfigurationEditorState .containsSnapshotDependencies; const isCreateReviewDisabled = Boolean(workspaceReviewState.workspaceReview) || workspaceContainsSnapshotDependencies || !workspaceReviewState.canCreateReview || workspaceReviewState.sdlcState.isActiveProjectSandbox; workspaceReviewState.reviewTitle = 'code review - promote query as a template query to data product'; if (!isCreateReviewDisabled) { await flowResult(workspaceReviewState.createWorkspaceReview(workspaceReviewState.reviewTitle)); } else { this.applicationStore.notificationService.notifyError(`Can't create code review`); } // iv. complete, redirect user to the service query editor screen this.applicationStore.alertService.setBlockingAlert(undefined); this.promoteState.pass(); this.applicationStore.alertService.setActionAlertInfo({ message: `Successfully promoted query into data product '${this.dataSpacePath}'. Now your template query can be found in workspace '${this.workspaceName}' of project '${project.name}' (${project.projectId})`, prompt: compilationFailed ? `The workspace might not compile at the moment, please make sure to fix the issue and submit a review to make the data product part of the project to complete template query promotion` : `Please make sure to get the generated code-review reviewed and approved`, type: ActionAlertType.STANDARD, actions: compilationFailed ? [ { label: 'Open Workspace', type: ActionAlertActionType.PROCEED, handler: () => { this.applicationStore.navigationService.navigator.goToLocation(generateEditorRoute(project.projectId, undefined, this.workspaceName, WorkspaceType.GROUP)); }, default: true, }, ] : [ { label: 'Open Code Review', type: ActionAlertActionType.PROCEED, handler: () => { if (workspaceReviewState.workspaceReview) { this.applicationStore.navigationService.navigator.visitAddress(this.applicationStore.navigationService.navigator.generateAddress(generateReviewRoute(workspaceReviewState.workspaceReview.projectId, workspaceReviewState.workspaceReview.id))); } }, default: true, }, { label: 'Open Workspace', type: ActionAlertActionType.PROCEED, handler: () => { this.applicationStore.navigationService.navigator.goToLocation(generateEditorRoute(project.projectId, undefined, this.workspaceName, WorkspaceType.GROUP)); }, }, ], }); } catch (error) { assertErrorThrown(error); this.applicationStore.alertService.setBlockingAlert(undefined); this.applicationStore.notificationService.notifyError(`Can't set up workspace: ${error.message}`); if (workspace) { await this.sdlcServerClient.deleteWorkspace(project.projectId, workspace); } this.promoteState.fail(); } }; this.applicationStore.alertService.setBlockingAlert(undefined); if (compilationFailed) { this.applicationStore.alertService.setActionAlertInfo({ message: `We have found compilation issues with the workspace. Your query can still be promoted, but you would need to fix compilation issues afterwards`, prompt: `Do you still want to proceed to promote the query as a template query?`, type: ActionAlertType.STANDARD, actions: [ { label: `Proceed`, type: ActionAlertActionType.PROCEED_WITH_CAUTION, handler: () => { setupWorkspace().catch(this.applicationStore.alertUnhandledError); }, }, { label: 'Abort', type: ActionAlertActionType.PROCEED, handler: () => { this.promoteState.fail(); }, default: true, }, ], }); } else { yield setupWorkspace(); } this.promoteState.pass(); } catch (error) { assertErrorThrown(error); this.applicationStore.alertService.setBlockingAlert(undefined); this.applicationStore.notificationService.notifyError(error); this.promoteState.fail(); } } } //# sourceMappingURL=DataSpaceTemplateQueryPromotionReviewerStore.js.map