UNPKG

@finos/legend-studio

Version:
375 lines (344 loc) 11.5 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 { observable, action, makeAutoObservable, flowResult } from 'mobx'; import { LEGEND_STUDIO_APP_EVENT } from '../LegendStudioAppEvent.js'; import { type GeneratorFn, type PlainObject, assertErrorThrown, LogEvent, ActionState, } from '@finos/legend-shared'; import { generateSetupRoute } from '../LegendStudioRouter.js'; import { type SDLCServerClient, WorkspaceType, ImportReport, Project, Review, Workspace, WorkspaceAccessType, } from '@finos/legend-server-sdlc'; import type { LegendStudioApplicationStore } from '../LegendStudioBaseStore.js'; interface ImportProjectSuccessReport { projectId: string; projectName: string; reviewUrl: string; } export interface ProjectOption { label: string; value: string; } const buildProjectOption = (project: Project): ProjectOption => ({ label: project.name, value: project.projectId, }); export interface WorkspaceOption { label: string; value: Workspace; __isNew__?: boolean | undefined; } const buildWorkspaceOption = (workspace: Workspace): WorkspaceOption => ({ label: workspace.workspaceId, value: workspace, }); export interface WorkspaceIdentifier { workspaceId: string; workspaceType: WorkspaceType; } export class WorkspaceSetupStore { applicationStore: LegendStudioApplicationStore; sdlcServerClient: SDLCServerClient; currentProjectId?: string | undefined; currentWorkspaceIdentifier?: WorkspaceIdentifier | undefined; projects?: Map<string, Project> | undefined; workspacesByProject = new Map<string, Map<string, Workspace>>(); loadWorkspacesState = ActionState.create(); createWorkspaceState = ActionState.create(); createOrImportProjectState = ActionState.create(); loadProjectsState = ActionState.create(); showCreateProjectModal = false; showCreateWorkspaceModal = false; importProjectSuccessReport?: ImportProjectSuccessReport | undefined; constructor( applicationStore: LegendStudioApplicationStore, sdlcServerClient: SDLCServerClient, ) { makeAutoObservable(this, { applicationStore: false, sdlcServerClient: false, setShowCreateProjectModal: action, setShowCreateWorkspaceModal: action, setCurrentProjectId: action, setCurrentWorkspaceIdentifier: action, setImportProjectSuccessReport: action, }); this.applicationStore = applicationStore; this.sdlcServerClient = sdlcServerClient; } get currentProject(): Project | undefined { return this.projects && this.currentProjectId ? this.projects.get(this.currentProjectId) : undefined; } get currentProjectWorkspaces(): Map<string, Workspace> | undefined { return this.currentProjectId ? this.workspacesByProject.get(this.currentProjectId) : undefined; } get currentWorkspace(): Workspace | undefined { return this.currentProjectWorkspaces && this.currentWorkspaceCompositeId ? this.currentProjectWorkspaces.get(this.currentWorkspaceCompositeId) : undefined; } get currentWorkspaceCompositeId(): string | undefined { return this.currentWorkspaceIdentifier ? this.buildWorkspaceCompositeId(this.currentWorkspaceIdentifier) : undefined; } init( workspaceId: string | undefined, groupWorkspaceId: string | undefined, ): void { if (workspaceId) { this.setCurrentWorkspaceIdentifier({ workspaceId, workspaceType: WorkspaceType.USER, }); } else if (groupWorkspaceId) { this.setCurrentWorkspaceIdentifier({ workspaceId: groupWorkspaceId, workspaceType: WorkspaceType.GROUP, }); } else { this.setCurrentWorkspaceIdentifier(undefined); } } setShowCreateProjectModal(val: boolean): void { this.showCreateProjectModal = val; } setShowCreateWorkspaceModal(val: boolean): void { this.showCreateWorkspaceModal = val; } setCurrentProjectId(id: string | undefined): void { this.currentProjectId = id; } setCurrentWorkspaceIdentifier(val: WorkspaceIdentifier | undefined): void { this.currentWorkspaceIdentifier = val; } setImportProjectSuccessReport( importProjectSuccessReport: ImportProjectSuccessReport | undefined, ): void { this.importProjectSuccessReport = importProjectSuccessReport; } *fetchProjects(): GeneratorFn<void> { this.loadProjectsState.inProgress(); try { const projects = ( (yield this.sdlcServerClient.getProjects( undefined, undefined, undefined, undefined, )) as PlainObject<Project>[] ).map((v) => Project.serialization.fromJson(v)); const projectIndex = observable<string, Project>(new Map()); projects.forEach((project) => projectIndex.set(project.projectId, project), ); this.projects = projectIndex; this.loadProjectsState.pass(); } catch (error) { assertErrorThrown(error); this.applicationStore.log.error( LogEvent.create(LEGEND_STUDIO_APP_EVENT.WORKSPACE_SETUP_FAILURE), error, ); this.applicationStore.notifyError(error); this.loadProjectsState.fail(); } } *createProject( name: string, description: string, groupId: string, artifactId: string, tags: string[] = [], ): GeneratorFn<void> { this.createOrImportProjectState.inProgress(); try { const createdProject = Project.serialization.fromJson( (yield this.sdlcServerClient.createProject({ name, description, groupId, artifactId, tags, })) as PlainObject<Project>, ); this.applicationStore.notifySuccess( `Project '${name}' is succesfully created`, ); yield flowResult(this.fetchProjects()); this.projects?.set(createdProject.projectId, createdProject); this.applicationStore.navigator.goTo( generateSetupRoute(createdProject.projectId), ); this.setShowCreateProjectModal(false); } catch (error) { assertErrorThrown(error); this.applicationStore.notifyError(error); } finally { this.createOrImportProjectState.reset(); } } *importProject( id: string, groupId: string, artifactId: string, ): GeneratorFn<void> { this.createOrImportProjectState.inProgress(); try { const report = ImportReport.serialization.fromJson( (yield this.sdlcServerClient.importProject({ id, groupId, artifactId, })) as PlainObject<ImportReport>, ); const importReview = Review.serialization.fromJson( (yield this.sdlcServerClient.getReview( report.project.projectId, report.reviewId, )) as PlainObject<Review>, ); this.setImportProjectSuccessReport({ projectName: report.project.name, projectId: report.project.projectId, reviewUrl: importReview.webURL, }); yield flowResult(this.fetchProjects()); this.projects?.set(report.project.projectId, report.project); this.setCurrentProjectId(report.project.projectId); } catch (error) { assertErrorThrown(error); this.applicationStore.notifyError(error); } finally { this.createOrImportProjectState.reset(); } } get projectOptions(): ProjectOption[] { return this.projects ? Array.from(this.projects.values()).map(buildProjectOption) : []; } get currentProjectWorkspaceOptions(): WorkspaceOption[] { return this.currentProjectWorkspaces ? Array.from(this.currentProjectWorkspaces.values()).map( buildWorkspaceOption, ) : []; } *fetchWorkspaces(projectId: string): GeneratorFn<void> { this.loadWorkspacesState.inProgress(); try { const workspacesInConflictResolutionIds = ( (yield this.sdlcServerClient.getWorkspacesInConflictResolutionMode( projectId, )) as Workspace[] ).map((workspace) => workspace.workspaceId); const workspaceIndex = observable<string, Workspace>(new Map()); ( (yield this.sdlcServerClient.getWorkspaces( projectId, )) as PlainObject<Workspace>[] ) .map((v) => Workspace.serialization.fromJson(v)) .forEach((workspace) => { // NOTE we don't handle workspaces that only have conflict resolution but no standard workspace // since that indicates bad state of the SDLC server if ( workspacesInConflictResolutionIds.includes(workspace.workspaceId) ) { workspace.accessType = WorkspaceAccessType.CONFLICT_RESOLUTION; } workspaceIndex.set( this.buildWorkspaceCompositeId(workspace), workspace, ); }); this.workspacesByProject.set(projectId, workspaceIndex); } catch (error) { assertErrorThrown(error); // TODO handle error when fetching workspaces for an individual project this.applicationStore.log.error( LogEvent.create(LEGEND_STUDIO_APP_EVENT.WORKSPACE_SETUP_FAILURE), error, ); } finally { this.loadWorkspacesState.reset(); } } buildWorkspaceCompositeId(workspace: WorkspaceIdentifier): string { return `${workspace.workspaceType}/${workspace.workspaceId}`; } *createWorkspace( projectId: string, workspaceId: string, workspaceType: WorkspaceType, ): GeneratorFn<void> { this.createWorkspaceState.inProgress(); try { const workspace = Workspace.serialization.fromJson( (yield this.sdlcServerClient.createWorkspace( projectId, workspaceId, workspaceType, )) as PlainObject<Workspace>, ); const existingWorkspaceForProject: Map<string, Workspace> | undefined = this.workspacesByProject.get(projectId); if (existingWorkspaceForProject) { existingWorkspaceForProject.set( this.buildWorkspaceCompositeId(workspace), workspace, ); } else { const workspaceIndex = observable<string, Workspace>(new Map()); workspaceIndex.set( this.buildWorkspaceCompositeId(workspace), workspace, ); this.workspacesByProject.set(projectId, workspaceIndex); } this.applicationStore.notifySuccess( `Workspace '${workspace.workspaceId}' is succesfully created`, ); this.setCurrentProjectId(projectId); this.setCurrentWorkspaceIdentifier(workspace); this.setShowCreateWorkspaceModal(false); this.createWorkspaceState.pass(); } catch (error) { assertErrorThrown(error); this.applicationStore.log.error( LogEvent.create(LEGEND_STUDIO_APP_EVENT.WORKSPACE_SETUP_FAILURE), error, ); this.applicationStore.notifyError(error); this.createWorkspaceState.fail(); } } }