@finos/legend-application-studio
Version:
Legend Studio application core
427 lines • 20.2 kB
JavaScript
/**
* 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, flowResult, makeObservable, flow } from 'mobx';
import { LEGEND_STUDIO_APP_EVENT } from '../../__lib__/LegendStudioEvent.js';
import { assertErrorThrown, LogEvent, ActionState, IllegalStateError, UnsupportedOperationError, guaranteeNonNullable, guaranteeType, exactSearch, } from '@finos/legend-shared';
import { generateSetupRoute } from '../../__lib__/LegendStudioNavigation.js';
import { SANDBOX_SDLC_TAG, WorkspaceType, ImportReport, Project, Review, Workspace, Patch, isProjectSandbox, } from '@finos/legend-server-sdlc';
import { DEFAULT_TAB_SIZE, DEFAULT_TYPEAHEAD_SEARCH_LIMIT, DEFAULT_TYPEAHEAD_SEARCH_MINIMUM_SEARCH_LENGTH, } from '@finos/legend-application';
import { fetchProjectConfigurationStatus, ProjectConfigurationStatus, } from './ProjectConfigurationStatus.js';
import { GraphManagerState } from '@finos/legend-graph';
export class WorkspaceSetupStore {
applicationStore;
sdlcServerClient;
initState = ActionState.create();
projects = [];
currentProject;
currentProjectConfigurationStatus;
loadProjectsState = ActionState.create();
createOrImportProjectState = ActionState.create();
importProjectSuccessReport;
showCreateProjectModal = false;
engineInitializeState = ActionState.create();
enginePromise;
createSandboxProjectState = ActionState.create();
sandboxProject = false;
hasSandboxAccess;
sandboxModal = false;
loadSandboxState = ActionState.create();
patches = [];
loadPatchesState = ActionState.create();
workspaces = [];
currentWorkspace;
loadWorkspacesState = ActionState.create();
createWorkspaceState = ActionState.create();
showCreateWorkspaceModal = false;
showAdvancedWorkspaceFilterOptions = false;
graphManagerState;
constructor(applicationStore, sdlcServerClient) {
makeObservable(this, {
projects: observable,
currentProject: observable,
currentProjectConfigurationStatus: observable,
importProjectSuccessReport: observable,
showCreateProjectModal: observable,
workspaces: observable,
currentWorkspace: observable,
showAdvancedWorkspaceFilterOptions: observable,
loadSandboxState: observable,
showCreateWorkspaceModal: observable,
sandboxProject: observable,
createSandboxProjectState: observable,
engineInitializeState: observable,
enginePromise: observable,
sandboxModal: observable,
hasSandboxAccess: observable,
setShowCreateProjectModal: action,
setShowCreateWorkspaceModal: action,
setShowAdvancedWorkspaceFilterOptions: action,
setImportProjectSuccessReport: action,
setSandboxModal: action,
changeWorkspace: action,
resetProject: action,
resetWorkspace: action,
initialize: flow,
loadProjects: flow,
loadSandboxProject: flow,
changeProject: flow,
createProject: flow,
importProject: flow,
createSandboxProject: flow,
createWorkspace: flow,
initializeEngine: flow,
});
this.applicationStore = applicationStore;
this.sdlcServerClient = sdlcServerClient;
this.graphManagerState = new GraphManagerState(applicationStore.pluginManager, applicationStore.logService);
if (this.supportsCreatingSandboxProject) {
flowResult(this.initializeEngine()).catch(applicationStore.alertUnhandledError);
}
}
get supportsCreatingSandboxProject() {
return this.applicationStore.config.options
.TEMPORARY__enableCreationOfSandboxProjects;
}
setShowCreateProjectModal(val) {
this.showCreateProjectModal = val;
}
setShowCreateWorkspaceModal(val) {
this.showCreateWorkspaceModal = val;
}
setShowAdvancedWorkspaceFilterOptions(val) {
this.showAdvancedWorkspaceFilterOptions = val;
}
setImportProjectSuccessReport(importProjectSuccessReport) {
this.importProjectSuccessReport = importProjectSuccessReport;
}
resetProject() {
this.currentProject = undefined;
this.patches = [];
this.workspaces = [];
this.currentWorkspace = undefined;
this.applicationStore.navigationService.navigator.updateCurrentLocation(generateSetupRoute(undefined, undefined, undefined, undefined));
this.currentProjectConfigurationStatus = undefined;
}
resetWorkspace() {
this.currentWorkspace = undefined;
if (this.currentProject) {
this.applicationStore.navigationService.navigator.updateCurrentLocation(generateSetupRoute(this.currentProject.projectId, undefined, undefined, undefined));
}
}
setSandboxModal(val) {
this.sandboxModal = val;
}
*createSandboxProject() {
try {
if (!this.hasSandboxAccess) {
this.setSandboxModal(true);
return;
}
// create sandbox project and pilot workspace
this.applicationStore.alertService.setBlockingAlert({
message: 'Creating sandbox project...',
showLoading: true,
});
const sandboxProject = (yield this.graphManagerState.graphManager.createSandboxProject());
this.applicationStore.alertService.setBlockingAlert({
message: `Sandbox project ${sandboxProject.projectId} created. Creating default workspace...`,
showLoading: true,
});
yield flowResult(this.loadSandboxProject());
const sandbox = guaranteeType(this.sandboxProject, Project, 'Error retrieving sandbox project');
const initialWorkspace = Workspace.serialization.fromJson((yield this.sdlcServerClient.createWorkspace(sandbox.projectId, undefined, 'myWorkspace', WorkspaceType.GROUP)));
yield flowResult(this.changeProject(sandbox, {
workspaceId: initialWorkspace.workspaceId,
workspaceType: WorkspaceType.GROUP,
}));
this.applicationStore.alertService.setBlockingAlert(undefined);
this.applicationStore.notificationService.notifySuccess(`Sandbox project with workspace created`);
}
catch (error) {
assertErrorThrown(error);
this.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.ENGINE_MANAGER_FAILURE), error);
this.applicationStore.notificationService.notifyError(error);
}
finally {
this.applicationStore.alertService.setBlockingAlert(undefined);
}
}
*initialize(projectId, workspaceId, groupWorkspaceId) {
if (!this.initState.isInInitialState) {
return;
}
this.initState.inProgress();
// TODO: when we genericize the way to initialize an application page
this.applicationStore.assistantService.setIsHidden(false);
try {
if (projectId) {
let project;
try {
project = Project.serialization.fromJson((yield this.sdlcServerClient.getProject(projectId)));
}
catch {
this.applicationStore.navigationService.navigator.updateCurrentLocation(generateSetupRoute(undefined, undefined));
this.initState.pass();
return;
}
yield flowResult(this.changeProject(project, workspaceId
? { workspaceId: workspaceId, workspaceType: WorkspaceType.USER }
: groupWorkspaceId
? {
workspaceId: groupWorkspaceId,
workspaceType: WorkspaceType.GROUP,
}
: undefined));
}
this.initState.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.initState.fail();
}
}
*initializeEngine() {
this.engineInitializeState.inProgress();
try {
const initPromise = this.graphManagerState.graphManager.initialize({
env: this.applicationStore.config.env,
tabSize: DEFAULT_TAB_SIZE,
clientConfig: {
baseUrl: this.applicationStore.config.engineServerUrl,
},
}, {
tracerService: this.applicationStore.tracerService,
disableGraphConfiguration: true,
});
this.enginePromise = initPromise;
yield initPromise;
this.enginePromise = undefined;
this.engineInitializeState.complete();
}
catch (error) {
assertErrorThrown(error);
this.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.ENGINE_MANAGER_FAILURE), error);
this.engineInitializeState.complete();
}
}
*loadProjects(searchText) {
const isValidSearchString = searchText.length >= DEFAULT_TYPEAHEAD_SEARCH_MINIMUM_SEARCH_LENGTH;
this.loadProjectsState.inProgress();
try {
this.projects = (yield this.sdlcServerClient.getProjects(undefined,
// We apply an exact search on the input text because we show exact searches with
// custom selector. This avoids losing some results with the additional filtering.
isValidSearchString ? exactSearch(searchText) : undefined, undefined, DEFAULT_TYPEAHEAD_SEARCH_LIMIT)).map((v) => Project.serialization.fromJson(v));
this.loadProjectsState.pass();
}
catch (error) {
assertErrorThrown(error);
this.applicationStore.notificationService.notifyError(error);
this.loadProjectsState.fail();
}
}
*loadSandboxProject() {
if (!this.supportsCreatingSandboxProject) {
return;
}
this.sandboxProject = false;
try {
this.loadSandboxState.inProgress();
if (this.enginePromise) {
yield this.enginePromise;
}
const sandboxProject = (yield this.sdlcServerClient.getProjects(undefined, this.sdlcServerClient.currentUser?.userId, [SANDBOX_SDLC_TAG], 1)).map((v) => Project.serialization.fromJson(v));
if (this.hasSandboxAccess === undefined) {
this.hasSandboxAccess =
(yield this.graphManagerState.graphManager.userHasPrototypeProjectAccess(this.sdlcServerClient.currentUser?.userId ?? ''));
}
this.sandboxProject = true;
if (sandboxProject.length > 1) {
throw new UnsupportedOperationError('Only one sandbox project is supported per user.');
}
else if (sandboxProject.length === 1) {
this.sandboxProject = guaranteeNonNullable(sandboxProject[0]);
}
this.loadSandboxState.pass();
}
catch (error) {
this.sandboxProject = true;
assertErrorThrown(error);
this.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.WORKSPACE_SETUP_FAILURE), error);
this.loadSandboxState.fail();
}
finally {
this.loadSandboxState.complete();
}
}
*changeProject(project, workspaceInfo) {
this.currentProject = project;
this.currentProjectConfigurationStatus = undefined;
this.loadPatchesState.inProgress();
try {
if (isProjectSandbox(project)) {
this.patches = [];
}
else {
this.patches = (yield this.sdlcServerClient.getPatches(project.projectId)).map((v) => Patch.serialization.fromJson(v));
}
this.loadPatchesState.pass();
}
catch (error) {
assertErrorThrown(error);
this.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.DEPOT_MANAGER_FAILURE), error);
this.applicationStore.notificationService.notifyError(error);
this.loadPatchesState.fail();
}
this.loadWorkspacesState.inProgress();
try {
if (isProjectSandbox(project)) {
const result = new ProjectConfigurationStatus();
result.projectId = project.projectId;
result.isConfigured = true;
this.currentProjectConfigurationStatus = result;
}
else {
this.currentProjectConfigurationStatus =
(yield fetchProjectConfigurationStatus(project.projectId, undefined, this.applicationStore, this.sdlcServerClient));
}
const workspacesInConflictResolutionIds = isProjectSandbox(project)
? []
: (yield this.sdlcServerClient.getWorkspacesInConflictResolutionMode(project.projectId, undefined)).map((workspace) => workspace.workspaceId);
this.workspaces = (yield this.sdlcServerClient.getWorkspaces(project.projectId))
.map((v) => Workspace.serialization.fromJson(v))
.filter(
// NOTE we don't handle workspaces that only have conflict resolution but no standard workspace
// since that indicates bad state of the SDLC server
(workspace) => !workspacesInConflictResolutionIds.includes(workspace.workspaceId));
for (const patch of this.patches) {
this.workspaces = this.workspaces.concat((yield this.sdlcServerClient.getWorkspaces(project.projectId, patch.patchReleaseVersionId.id))
.map((v) => {
const w = Workspace.serialization.fromJson(v);
w.source = patch.patchReleaseVersionId.id;
return w;
})
.filter(
// NOTE we don't handle workspaces that only have conflict resolution but no standard workspace
// since that indicates bad state of the SDLC server
(workspace) => !workspacesInConflictResolutionIds.includes(workspace.workspaceId)));
}
if (workspaceInfo) {
const matchingWorkspace = this.workspaces.find((workspace) => workspace.workspaceType === workspaceInfo.workspaceType &&
workspace.workspaceId === workspaceInfo.workspaceId);
if (matchingWorkspace) {
this.changeWorkspace(matchingWorkspace);
}
else {
this.applicationStore.navigationService.navigator.updateCurrentLocation(generateSetupRoute(project.projectId, undefined));
}
}
else {
this.currentWorkspace = undefined;
this.applicationStore.navigationService.navigator.updateCurrentLocation(generateSetupRoute(project.projectId, undefined));
}
this.loadWorkspacesState.pass();
}
catch (error) {
assertErrorThrown(error);
this.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.DEPOT_MANAGER_FAILURE), error);
this.applicationStore.notificationService.notifyError(error);
this.loadWorkspacesState.fail();
}
}
changeWorkspace(workspace) {
if (!this.currentProject) {
throw new IllegalStateError(`Can't change workspace: project is not specified`);
}
this.currentWorkspace = workspace;
this.applicationStore.navigationService.navigator.updateCurrentLocation(generateSetupRoute(this.currentProject.projectId, workspace.source, workspace.workspaceId, workspace.workspaceType));
}
*createProject(name, description, groupId, artifactId, tags = []) {
this.createOrImportProjectState.inProgress();
try {
const createdProject = Project.serialization.fromJson((yield this.sdlcServerClient.createProject({
name,
description,
groupId,
artifactId,
tags,
})));
this.applicationStore.notificationService.notifySuccess(`Project '${name}' is succesfully created`);
yield flowResult(this.changeProject(createdProject));
this.setShowCreateProjectModal(false);
}
catch (error) {
assertErrorThrown(error);
this.applicationStore.notificationService.notifyError(error);
}
finally {
this.createOrImportProjectState.reset();
}
}
*importProject(id, groupId, artifactId) {
this.createOrImportProjectState.inProgress();
try {
const report = ImportReport.serialization.fromJson((yield this.sdlcServerClient.importProject({
id,
groupId,
artifactId,
})));
const importReview = Review.serialization.fromJson((yield this.sdlcServerClient.getReview(report.project.projectId, undefined, report.reviewId)));
this.setImportProjectSuccessReport({
projectName: report.project.name,
projectId: report.project.projectId,
reviewUrl: importReview.webURL,
});
yield flowResult(this.changeProject(report.project));
}
catch (error) {
assertErrorThrown(error);
this.applicationStore.notificationService.notifyError(error);
}
finally {
this.createOrImportProjectState.reset();
}
}
*createWorkspace(projectId, patchReleaseVersionId, workspaceId, workspaceType) {
this.createWorkspaceState.inProgress();
try {
const newWorkspace = Workspace.serialization.fromJson((yield this.sdlcServerClient.createWorkspace(projectId, patchReleaseVersionId, workspaceId, workspaceType)));
newWorkspace.source = patchReleaseVersionId;
this.applicationStore.notificationService.notifySuccess(`Workspace '${newWorkspace.workspaceId}' is succesfully created`);
const matchingWorkspace = this.workspaces.find((workspace) => workspace.workspaceId === newWorkspace.workspaceId &&
workspace.workspaceType === newWorkspace.workspaceType);
const newWorkspaceToSelect = matchingWorkspace ?? newWorkspace;
this.changeWorkspace(newWorkspaceToSelect);
this.setShowCreateWorkspaceModal(false);
// NOTE: do this after closing the modal to not interfere
// with validation of existing workspaces in create workspace modal
if (!matchingWorkspace) {
this.workspaces.push(newWorkspace);
}
}
catch (error) {
assertErrorThrown(error);
this.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.WORKSPACE_SETUP_FAILURE), error);
this.applicationStore.notificationService.notifyError(error);
}
finally {
this.createWorkspaceState.reset();
}
}
}
//# sourceMappingURL=WorkspaceSetupStore.js.map