@finos/legend-application-studio
Version:
Legend Studio application core
325 lines • 14.8 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 { assertErrorThrown, LogEvent, guaranteeNonNullable, ActionState, assertNonEmptyString, isNonNullable, } from '@finos/legend-shared';
import { makeObservable, action, observable, flow, computed, flowResult, } from 'mobx';
import { ACTIVITY_MODE } from '../editor/EditorConfig.js';
import { EntityDiff, Comparison, reprocessEntityDiffs, Project, Review, ReviewApproval, } from '@finos/legend-server-sdlc';
import { LEGEND_STUDIO_APP_EVENT } from '../../__lib__/LegendStudioEvent.js';
import { DEFAULT_TAB_SIZE } from '@finos/legend-application';
import { EntityDiffViewState } from '../editor/editor-state/entity-diff-editor-state/EntityDiffViewState.js';
import { SPECIAL_REVISION_ALIAS } from '../editor/editor-state/entity-diff-editor-state/EntityDiffEditorState.js';
export class ProjectReviewReport {
diffs;
fromEntities = [];
toEntities = [];
fromConfig;
toConfig;
fromToProjectConfig;
constructor(diffs) {
this.diffs = diffs;
makeObservable(this, {
fromEntities: observable,
toEntities: observable,
fromToProjectConfig: observable,
addFromConfig: action,
addToConfig: action,
});
}
addFromConfig(config) {
this.fromConfig = config;
this.createFromToConfigIfPossible();
}
addToConfig(config) {
this.toConfig = config;
this.createFromToConfigIfPossible();
}
createFromToConfigIfPossible() {
if (this.fromConfig && this.toConfig) {
this.fromToProjectConfig = [this.fromConfig, this.toConfig];
}
}
findFromEntity(entityPath) {
return this.fromEntities.find((e) => e.path === entityPath);
}
addFromEntity(entity) {
this.fromEntities.push(entity);
return this;
}
addToEntity(entity) {
this.toEntities.push(entity);
return this;
}
findToEntity(entityPath) {
return this.toEntities.find((e) => e.path === entityPath);
}
}
export class ProjectReviewerStore {
editorStore;
currentProjectId;
currentProject;
currentPatch;
currentReviewId;
currentReview;
// comparison
buildReviewReportState = ActionState.create();
reviewReport;
fetchCurrentReviewState = ActionState.create();
approveState = ActionState.create();
reviewApproval;
closeState = ActionState.create();
commitState = ActionState.create();
reOpenState = ActionState.create();
constructor(editorStore) {
makeObservable(this, {
currentProjectId: observable,
currentProject: observable,
currentPatch: observable,
currentReviewId: observable,
currentReview: observable,
fetchCurrentReviewState: observable,
buildReviewReportState: observable,
approveState: observable,
closeState: observable,
commitState: observable,
reOpenState: observable,
reviewReport: observable,
reviewApproval: observable,
projectId: computed,
patchReleaseVersionId: computed,
reviewId: computed,
review: computed,
setProjectIdAndReviewId: action,
refresh: action,
initializeEngine: flow,
fetchReviewComparison: flow,
fetchProject: flow,
fetchReviewApprovals: flow,
fetchReview: flow,
approveReview: flow,
commitReview: flow,
reOpenReview: flow,
closeReview: flow,
});
this.editorStore = editorStore;
this.editorStore.activeActivity = ACTIVITY_MODE.REVIEW;
}
get projectId() {
return guaranteeNonNullable(this.currentProjectId, 'Project ID must exist');
}
get patchReleaseVersionId() {
return this.currentPatch?.patchReleaseVersionId.id;
}
get reviewId() {
return guaranteeNonNullable(this.currentReviewId, 'Review ID must exist');
}
get review() {
return guaranteeNonNullable(this.currentReview, 'Review must exist');
}
get approvalString() {
const approvals = this.reviewApproval?.approvedBy;
if (approvals?.length) {
return `Approved by ${approvals.map((e) => e.name).join(',')}.`;
}
return undefined;
}
setProjectIdAndReviewId(projectId, reviewId) {
this.currentProjectId = projectId;
this.currentReviewId = reviewId;
}
initialize() {
flowResult(this.initializeEngine()).catch(this.editorStore.applicationStore.alertUnhandledError);
flowResult(this.fetchReview()).catch(this.editorStore.applicationStore.alertUnhandledError);
flowResult(this.fetchProject()).catch(this.editorStore.applicationStore.alertUnhandledError);
flowResult(this.fetchReviewApprovals()).catch(this.editorStore.applicationStore.alertUnhandledError);
flowResult(this.fetchReviewComparison()).catch(this.editorStore.applicationStore.alertUnhandledError);
}
*initializeEngine() {
try {
// setup engine used for to/from grammar transfomation
yield this.editorStore.graphManagerState.graphManager.initialize({
env: this.editorStore.applicationStore.config.env,
tabSize: DEFAULT_TAB_SIZE,
clientConfig: {
baseUrl: this.editorStore.applicationStore.config.engineServerUrl,
queryBaseUrl: this.editorStore.applicationStore.config.engineQueryServerUrl,
enableCompression: true,
},
}, {
tracerService: this.editorStore.applicationStore.tracerService,
});
}
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);
}
}
openReviewChange(diff) {
const fromEntityGetter = (entityPath) => entityPath ? this.reviewReport?.findFromEntity(entityPath) : undefined;
const toEntityGetter = (entityPath) => entityPath ? this.reviewReport?.findToEntity(entityPath) : undefined;
const fromEntity = EntityDiff.shouldOldEntityExist(diff)
? guaranteeNonNullable(fromEntityGetter(diff.getValidatedOldPath()), `Can't find entity with path '${diff.oldPath}'`)
: undefined;
const toEntity = EntityDiff.shouldNewEntityExist(diff)
? guaranteeNonNullable(toEntityGetter(diff.getValidatedNewPath()), `Can't find entity with path '${diff.newPath}'`)
: undefined;
const diffState = new EntityDiffViewState(this.editorStore, SPECIAL_REVISION_ALIAS.WORKSPACE_BASE, SPECIAL_REVISION_ALIAS.WORKSPACE_HEAD, diff.oldPath, diff.newPath, fromEntity, toEntity, fromEntityGetter, toEntityGetter);
this.editorStore.tabManagerState.openTab(this.editorStore.tabManagerState.tabs.find((t) => t.match(diffState)) ??
diffState);
}
refresh() {
this.editorStore.tabManagerState.closeAllTabs();
this.reviewReport = undefined;
flowResult(this.fetchReviewComparison()).catch(this.editorStore.applicationStore.alertUnhandledError);
}
/**
* To save load time, this function will levergae the reviewId coming from the URl and doesn't
* assume the review has completed been fetched
*/
*fetchReviewComparison() {
this.buildReviewReportState.inProgress();
try {
const comparison = Comparison.serialization.fromJson((yield this.editorStore.sdlcServerClient.getReviewComparision(this.projectId, this.patchReleaseVersionId, this.reviewId)));
const report = new ProjectReviewReport(reprocessEntityDiffs(comparison.entityDiffs));
this.reviewReport = report;
const fromToRequests = Promise.all([
...report.diffs
.map((diff) => diff.oldPath)
.filter(isNonNullable)
.map((fromDiff) => this.editorStore.sdlcServerClient
.getReviewFromEntity(this.projectId, this.patchReleaseVersionId, this.reviewId, fromDiff)
.then((fromEntity) => report.addFromEntity(fromEntity))),
...report.diffs
.map((diff) => diff.newPath)
.filter(isNonNullable)
.map((toDiff) => this.editorStore.sdlcServerClient
.getReviewToEntity(this.projectId, this.patchReleaseVersionId, this.reviewId, toDiff)
.then((toEntity) => report.addToEntity(toEntity))),
]);
if (comparison.projectConfigurationUpdated) {
yield Promise.all([
this.editorStore.sdlcServerClient
.getReviewFromConfiguration(this.projectId, this.patchReleaseVersionId, this.reviewId)
.then((config) => report.addFromConfig(config)),
this.editorStore.sdlcServerClient
.getReviewToConfiguration(this.projectId, this.patchReleaseVersionId, this.reviewId)
.then((config) => report.addToConfig(config)),
]);
}
yield fromToRequests;
}
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.buildReviewReportState.complete();
}
}
*fetchProject() {
try {
this.currentProject = Project.serialization.fromJson((yield this.editorStore.sdlcServerClient.getProject(this.projectId)));
}
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);
}
}
*fetchReviewApprovals() {
try {
this.reviewApproval = ReviewApproval.serialization.fromJson((yield this.editorStore.sdlcServerClient.getReviewApprovals(this.projectId, this.patchReleaseVersionId, this.reviewId)));
}
catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error);
}
}
*fetchReview() {
try {
// TODO: can we assume review also an integer ?
assertNonEmptyString(this.currentReviewId, 'Review ID provided must be a valid non empty string');
this.fetchCurrentReviewState.inProgress();
this.currentReview = Review.serialization.fromJson((yield this.editorStore.sdlcServerClient.getReview(this.projectId, this.patchReleaseVersionId, this.reviewId)));
}
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.fetchCurrentReviewState.complete();
}
}
*approveReview() {
this.approveState.inProgress();
try {
this.currentReview = Review.serialization.fromJson((yield this.editorStore.sdlcServerClient.approveReview(this.projectId, this.patchReleaseVersionId, this.review.id)));
}
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.approveState.complete();
}
}
*commitReview() {
this.commitState.inProgress();
try {
this.currentReview = Review.serialization.fromJson((yield this.editorStore.sdlcServerClient.commitReview(this.projectId, this.patchReleaseVersionId, this.review.id, { message: `${this.review.title} [review]` })));
}
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.commitState.complete();
}
}
*reOpenReview() {
this.reOpenState.inProgress();
try {
this.currentReview = Review.serialization.fromJson((yield this.editorStore.sdlcServerClient.reopenReview(this.projectId, this.patchReleaseVersionId, this.review.id)));
}
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.reOpenState.complete();
}
}
*closeReview() {
this.closeState.inProgress();
try {
this.currentReview = Review.serialization.fromJson((yield this.editorStore.sdlcServerClient.closeReview(this.projectId, this.patchReleaseVersionId, this.review.id)));
}
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.closeState.complete();
}
}
}
//# sourceMappingURL=ProjectReviewerStore.js.map