UNPKG

@finos/legend-application-studio

Version:
415 lines (384 loc) 14.6 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 type { EditorStore } from '../../EditorStore.js'; import { EditorState } from '../EditorState.js'; import { action, computed, flow, observable, makeObservable, flowResult, } from 'mobx'; import { type GeneratorFn, type PlainObject, LogEvent, assertErrorThrown, guaranteeNonNullable, hashArray, ActionState, prettyCONSTName, } from '@finos/legend-shared'; import type { EditorSDLCState } from '../../EditorSDLCState.js'; import { type ProjectConfiguration, ProjectStructureVersion, UpdateProjectConfigurationCommand, UpdatePlatformConfigurationsCommand, ProjectType, } from '@finos/legend-server-sdlc'; import { LEGEND_STUDIO_APP_EVENT } from '../../../../__lib__/LegendStudioEvent.js'; import { SNAPSHOT_ALIAS, StoreProjectData } from '@finos/legend-server-depot'; import { ProjectDependencyEditorState } from './ProjectDependencyEditorState.js'; export enum CONFIGURATION_EDITOR_TAB { PROJECT_STRUCTURE = 'PROJECT_STRUCTURE', PROJECT_DEPENDENCIES = 'PROJECT_DEPENDENCIES', PLATFORM_CONFIGURATIONS = 'PLATFORM_CONFIGURATIONS', ADVANCED = 'ADVANCED', } export const projectTypeTabFilter = ( mode: ProjectType, tab: CONFIGURATION_EDITOR_TAB, ): boolean => { if ( mode === ProjectType.EMBEDDED && tab === CONFIGURATION_EDITOR_TAB.PLATFORM_CONFIGURATIONS ) { return false; } return true; }; export class ProjectConfigurationEditorState extends EditorState { readonly sdlcState: EditorSDLCState; readonly updatingConfigurationState = ActionState.create(); readonly fetchingProjectVersionsState = ActionState.create(); projectDependencyEditorState: ProjectDependencyEditorState; originalProjectConfiguration?: ProjectConfiguration | undefined; // TODO: we might want to remove this when we do change detection for project configuration projectConfiguration?: ProjectConfiguration | undefined; selectedTab: CONFIGURATION_EDITOR_TAB; isReadOnly = false; projects = new Map<string, StoreProjectData>(); versions = new Map<string, string[]>(); latestProjectStructureVersion: ProjectStructureVersion | undefined; manualOverwrite = false; associatedProjectsAndVersionsFetched = false; constructor(editorStore: EditorStore, sdlcState: EditorSDLCState) { super(editorStore); makeObservable(this, { originalProjectConfiguration: observable, updatingConfigurationState: observable, projectConfiguration: observable, selectedTab: observable, isReadOnly: observable, manualOverwrite: observable, projects: observable, versions: observable, associatedProjectsAndVersionsFetched: observable, fetchingProjectVersionsState: observable, latestProjectStructureVersion: observable, projectDependencyEditorState: observable, originalConfig: computed, isGroupIdChanged: computed, isArtifactIdChanged: computed, setOriginalProjectConfiguration: action, setProjectConfiguration: action, setSelectedTab: action, setManualOverwrite: action, fectchAssociatedProjectsAndVersions: flow, updateProjectConfiguration: flow, updateToLatestStructure: flow, updateConfigs: flow, fetchLatestProjectStructureVersion: flow, changeProjectType: flow, }); this.projectDependencyEditorState = new ProjectDependencyEditorState( this, this.editorStore, ); this.selectedTab = CONFIGURATION_EDITOR_TAB.PROJECT_STRUCTURE; this.isReadOnly = editorStore.isInViewerMode; this.sdlcState = sdlcState; } setOriginalProjectConfiguration( projectConfiguration: ProjectConfiguration, ): void { this.originalProjectConfiguration = projectConfiguration; } setProjectConfiguration(projectConfiguration: ProjectConfiguration): void { this.projectConfiguration = projectConfiguration; } setSelectedTab(tab: CONFIGURATION_EDITOR_TAB): void { this.selectedTab = tab; } setManualOverwrite(value: boolean): void { this.manualOverwrite = value; } get label(): string { return 'config'; } override match(tab: EditorState): boolean { return tab instanceof ProjectConfigurationEditorState; } get currentProjectConfiguration(): ProjectConfiguration { return guaranteeNonNullable( this.projectConfiguration, 'Project configuration must exist', ); } get originalConfig(): ProjectConfiguration { return guaranteeNonNullable( this.originalProjectConfiguration, 'Original project configuration is not set', ); } get isGroupIdChanged(): boolean { return ( this.currentProjectConfiguration.groupId !== this.originalProjectConfiguration?.groupId ); } get isArtifactIdChanged(): boolean { return ( this.currentProjectConfiguration.artifactId !== this.originalProjectConfiguration?.artifactId ); } get containsSnapshotDependencies(): boolean { return Boolean( this.originalProjectConfiguration?.projectDependencies.some( (dependency) => dependency.versionId.endsWith(SNAPSHOT_ALIAS), ), ); } get isInEmbeddedMode(): boolean { return ( this.originalProjectConfiguration?.projectType === ProjectType.EMBEDDED ); } *fectchAssociatedProjectsAndVersions(): GeneratorFn<void> { this.fetchingProjectVersionsState.inProgress(); try { ( (yield this.editorStore.depotServerClient.getProjects()) as PlainObject<StoreProjectData>[] ) .map((v) => StoreProjectData.serialization.fromJson(v)) .forEach((project) => this.projects.set(project.coordinates, project)); // fetch the versions for the dependency projects for (const dep of this.projectConfiguration?.projectDependencies ?? []) { const project = this.projects.get(dep.projectId); if (project) { const _versions = (yield this.editorStore.depotServerClient.getVersions( guaranteeNonNullable(dep.groupId), guaranteeNonNullable(dep.artifactId), true, )) as string[]; this.versions.set(project.coordinates, _versions); } } this.associatedProjectsAndVersionsFetched = true; } catch (error) { assertErrorThrown(error); this.editorStore.applicationStore.logService.error( LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error, ); this.editorStore.applicationStore.notificationService.notifyError( `Can't get project dependencies data. Error:\n${error.message}`, ); } finally { this.fetchingProjectVersionsState.complete(); } } *updateProjectConfiguration( updateConfigurationCommand: UpdateProjectConfigurationCommand, ): GeneratorFn<void> { try { this.updatingConfigurationState.inProgress(); yield this.editorStore.sdlcServerClient.updateConfiguration( this.editorStore.sdlcState.activeProject.projectId, this.editorStore.sdlcState.activeWorkspace, UpdateProjectConfigurationCommand.serialization.toJson( updateConfigurationCommand, ), ); this.editorStore.reset(); // reset editor yield flowResult( this.editorStore.sdlcState.fetchCurrentWorkspace( this.editorStore.sdlcState.activeProject.projectId, this.editorStore.sdlcState.activePatch?.patchReleaseVersionId.id, this.editorStore.sdlcState.activeWorkspace.workspaceId, this.editorStore.sdlcState.activeWorkspace.workspaceType, ), ); yield flowResult( this.sdlcState.fetchCurrentRevision( this.editorStore.sdlcState.activeProject.projectId, this.editorStore.sdlcState.activeWorkspace, ), ); yield flowResult(this.editorStore.initMode()); this.editorStore.tabManagerState.openTab( this.editorStore.projectConfigurationEditorState, ); } catch (error) { assertErrorThrown(error); this.editorStore.applicationStore.logService.error( LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error, ); this.editorStore.applicationStore.notificationService.notifyError(error); } finally { this.updatingConfigurationState.complete(); } } *updateToLatestStructure(): GeneratorFn<void> { if (this.latestProjectStructureVersion) { try { let latestStructure = this.latestProjectStructureVersion; if (this.isInEmbeddedMode) { const projectStructureVersion = new ProjectStructureVersion(); projectStructureVersion.version = this.latestProjectStructureVersion.version; // extension version does not exists in embedded mode projectStructureVersion.extensionVersion = undefined; latestStructure = projectStructureVersion; } const updateCommand = new UpdateProjectConfigurationCommand( this.currentProjectConfiguration.groupId, this.currentProjectConfiguration.artifactId, latestStructure, `update project configuration from ${this.editorStore.applicationStore.config.appName}: update to latest project structure`, ); yield flowResult(this.updateProjectConfiguration(updateCommand)); } catch (error) { assertErrorThrown(error); this.editorStore.applicationStore.logService.error( LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error, ); this.editorStore.applicationStore.notificationService.notifyError( error, ); } } } *changeProjectType(): GeneratorFn<void> { try { const newProjectType = this.isInEmbeddedMode ? ProjectType.MANAGED : ProjectType.EMBEDDED; const updateCommand = new UpdateProjectConfigurationCommand( this.currentProjectConfiguration.groupId, this.currentProjectConfiguration.artifactId, undefined, `update project configuration from ${ this.editorStore.applicationStore.config.appName }: changed project type to ${prettyCONSTName(newProjectType)}`, ); updateCommand.projectType = newProjectType; yield flowResult(this.updateProjectConfiguration(updateCommand)); } catch (error) { assertErrorThrown(error); this.editorStore.applicationStore.logService.error( LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error, ); this.editorStore.applicationStore.notificationService.notifyError(error); } } // TODO: we will probably need to remove this in the future when we have a better strategy for change detection and persistence of project config // See https://github.com/finos/legend-studio/issues/952 *updateConfigs(): GeneratorFn<void> { this.updatingConfigurationState.inProgress(); this.editorStore.applicationStore.alertService.setBlockingAlert({ message: `Updating project configuration...`, prompt: `Please do not reload the application`, showLoading: true, }); try { const updateProjectConfigurationCommand = new UpdateProjectConfigurationCommand( this.currentProjectConfiguration.groupId, this.currentProjectConfiguration.artifactId, this.currentProjectConfiguration.projectStructureVersion, `update project configuration from ${this.editorStore.applicationStore.config.appName}`, ); if ( hashArray(this.originalConfig.platformConfigurations ?? []) !== hashArray(this.currentProjectConfiguration.platformConfigurations ?? []) ) { updateProjectConfigurationCommand.platformConfigurations = new UpdatePlatformConfigurationsCommand( this.currentProjectConfiguration.platformConfigurations, ); } if ( this.originalConfig.runDependencyTests !== this.currentProjectConfiguration.runDependencyTests ) { updateProjectConfigurationCommand.runDependencyTests = this.currentProjectConfiguration.runDependencyTests; } updateProjectConfigurationCommand.projectDependenciesToAdd = this.currentProjectConfiguration.projectDependencies.filter( (dep) => !this.originalConfig.projectDependencies.find( (originalProjDep) => originalProjDep.hashCode === dep.hashCode, ), ); updateProjectConfigurationCommand.projectDependenciesToRemove = this.originalConfig.projectDependencies.filter( (originalProjDep) => !this.currentProjectConfiguration.projectDependencies.find( (dep) => dep.hashCode === originalProjDep.hashCode, ), ); yield flowResult( this.updateProjectConfiguration(updateProjectConfigurationCommand), ); } catch (error) { assertErrorThrown(error); this.editorStore.applicationStore.logService.error( LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error, ); this.editorStore.applicationStore.notificationService.notifyError(error); } finally { this.updatingConfigurationState.complete(); this.editorStore.applicationStore.alertService.setBlockingAlert( undefined, ); } } *fetchLatestProjectStructureVersion(): GeneratorFn<void> { try { this.latestProjectStructureVersion = ProjectStructureVersion.serialization.fromJson( (yield this.editorStore.sdlcServerClient.getLatestProjectStructureVersion()) as PlainObject<ProjectStructureVersion>, ); } catch (error) { assertErrorThrown(error); this.editorStore.applicationStore.logService.error( LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error, ); this.editorStore.applicationStore.notificationService.notifyError(error); } } }