@finos/legend-application-studio
Version:
Legend Studio application core
315 lines • 12 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 { flow, observable, makeObservable, flowResult, action } from 'mobx';
import { ActionState, assertErrorThrown, LogEvent, isNonNullable, uuid, } from '@finos/legend-shared';
import { ProjectDependencyCoordinates, buildConflictsPaths, buildDependencyReport, RawProjectDependencyReport, } from '@finos/legend-server-depot';
import { LEGEND_STUDIO_APP_EVENT } from '../../../../__lib__/LegendStudioEvent.js';
export class ProjectDependencyConflictTreeNodeData {
id;
label;
childrenIds;
isSelected;
isOpen;
constructor(id) {
this.id = id;
this.label = id;
}
}
export class ConflictTreeNodeData extends ProjectDependencyConflictTreeNodeData {
conflict;
constructor(conflict) {
super(`${conflict.groupId}:${conflict.artifactId}`);
this.conflict = conflict;
}
get description() {
return this.id;
}
}
export class ConflictVersionNodeData extends ProjectDependencyConflictTreeNodeData {
versionConflict;
constructor(conflict) {
super(`${conflict.version.groupId}:${conflict.version.artifactId}.${conflict.version.versionId}`);
this.versionConflict = conflict;
this.label = this.versionConflict.version.versionId;
}
get description() {
return this.id;
}
}
export class ProjectDependencyTreeNodeData extends ProjectDependencyConflictTreeNodeData {
value;
constructor(id, value) {
super(id);
this.value = value;
this.label = value.artifactId;
}
get description() {
return `${this.value.groupId}:${this.value.artifactId}:${this.value.versionId}`;
}
}
export const buildDependencyNodeChildren = (parentNode, treeNodes) => {
if (!parentNode.childrenIds) {
const value = parentNode.value;
const childrenNodes = value.dependencies.map((projectVersion) => {
const childId = `${parentNode.id}.${projectVersion.id}`;
const childNode = new ProjectDependencyTreeNodeData(childId, projectVersion);
treeNodes.set(childId, childNode);
return childNode;
});
parentNode.childrenIds = childrenNodes.map((c) => c.id);
}
};
const findRootNode = (versionNode, treeData) => {
if (!treeData.rootIds.includes(versionNode.id)) {
return undefined;
}
return Array.from(treeData.nodes.values()).find((node) => node.id === versionNode.id && node.value === versionNode);
};
const walkNode = (node, visited, treeData) => {
if (!visited.has(node.value)) {
node.isOpen = true;
buildDependencyNodeChildren(node, treeData.nodes);
visited.add(node.value);
node.childrenIds
?.map((nodeId) => treeData.nodes.get(nodeId))
.filter(isNonNullable)
.forEach((n) => walkNode(n, visited, treeData));
}
else {
buildDependencyNodeChildren(node, treeData.nodes);
}
};
export const openAllDependencyNodesInTree = (treeData, graph) => {
const visited = new Set();
graph.rootNodes
.map((node) => findRootNode(node, treeData))
.filter(isNonNullable)
.forEach((node) => walkNode(node, visited, treeData));
};
const buildDependencyTreeData = (report) => {
const nodes = new Map();
const rootNodes = report.graph.rootNodes.map((versionNode) => {
const node = new ProjectDependencyTreeNodeData(versionNode.id, versionNode);
nodes.set(node.id, node);
buildDependencyNodeChildren(node, nodes);
return node;
});
const rootIds = rootNodes.map((node) => node.id);
return { rootIds, nodes };
};
const buildFlattenDependencyTreeData = (report) => {
const nodes = new Map();
const rootIds = [];
Array.from(report.graph.nodes.entries()).forEach(([key, value]) => {
const id = value.id;
const node = new ProjectDependencyTreeNodeData(id, value);
nodes.set(id, node);
rootIds.push(id);
buildDependencyNodeChildren(node, nodes);
});
return { rootIds, nodes };
};
export var DEPENDENCY_REPORT_TAB;
(function (DEPENDENCY_REPORT_TAB) {
DEPENDENCY_REPORT_TAB["EXPLORER"] = "EXPLORER";
DEPENDENCY_REPORT_TAB["CONFLICTS"] = "CONFLICTS";
})(DEPENDENCY_REPORT_TAB || (DEPENDENCY_REPORT_TAB = {}));
const buildTreeDataFromConflictVersion = (conflictVersionNode, nodes) => conflictVersionNode.versionConflict.pathsToVersion
.map((path, idx) => {
if (!path.length) {
return undefined;
}
const pathIterator = path.values();
let rootNode;
let parentNode;
let currentVersion;
while ((currentVersion = pathIterator.next().value)) {
const id = parentNode
? `${parentNode.id}.${currentVersion.id}`
: `path${idx}_${currentVersion.id}`;
const node = new ProjectDependencyTreeNodeData(id, currentVersion);
node.childrenIds = [];
nodes.set(id, node);
if (parentNode) {
parentNode.childrenIds = [node.id];
}
else {
rootNode = node;
}
parentNode = node;
}
return rootNode;
})
.filter(isNonNullable);
const buildTreeDataFromConflict = (conflict, paths) => {
const rootNode = new ConflictTreeNodeData(conflict);
const rootIds = [rootNode.id];
const nodes = new Map();
nodes.set(rootNode.id, rootNode);
const versionConflictNodes = paths.map((versionConflict) => {
const projectVersionNode = new ConflictVersionNodeData(versionConflict);
nodes.set(projectVersionNode.id, projectVersionNode);
const pathNodes = buildTreeDataFromConflictVersion(projectVersionNode, nodes);
projectVersionNode.childrenIds = pathNodes.map((n) => n.id);
return projectVersionNode;
});
rootNode.childrenIds = versionConflictNodes.map((n) => n.id);
return { rootIds, nodes };
};
export class ProjectDependencyConflictState {
uuid = uuid();
report;
conflict;
paths;
treeData;
constructor(report, conflict, paths) {
makeObservable(this, {
treeData: observable.ref,
setTreeData: action,
});
this.report = report;
this.conflict = conflict;
this.paths = paths;
this.treeData = buildTreeDataFromConflict(conflict, paths);
}
setTreeData(treeData) {
this.treeData = treeData;
}
get versionNodes() {
return this.paths.map((e) => e.version);
}
}
export class ProjectDependencyEditorState {
configState;
editorStore;
isReadOnly;
reportTab;
fetchingDependencyInfoState = ActionState.create();
dependencyReport;
dependencyTreeData;
flattenDependencyTreeData;
conflictStates;
expandConflictsState = ActionState.create();
buildConflictPathState = ActionState.create();
constructor(configState, editorStore) {
makeObservable(this, {
dependencyReport: observable,
fetchingDependencyInfoState: observable,
dependencyTreeData: observable.ref,
flattenDependencyTreeData: observable.ref,
conflictStates: observable,
reportTab: observable,
expandConflictsState: observable,
buildConflictPathState: observable,
setReportTab: action,
expandAllConflicts: action,
setFlattenDependencyTreeData: action,
clearTrees: action,
setTreeData: action,
setDependencyTreeData: action,
buildConflictPaths: action,
setConflictStates: action,
fetchDependencyReport: flow,
});
this.configState = configState;
this.editorStore = editorStore;
this.isReadOnly = editorStore.isInViewerMode;
}
expandAllConflicts() {
if (this.conflictStates) {
this.expandConflictsState.inProgress();
this.conflictStates.forEach((c) => {
const treeData = c.treeData;
Array.from(treeData.nodes.values()).forEach((n) => (n.isOpen = true));
});
this.conflictStates.forEach((c) => {
c.setTreeData({ ...c.treeData });
});
this.expandConflictsState.complete();
}
}
setTreeData(treeData, flattenView) {
if (flattenView) {
this.setFlattenDependencyTreeData(treeData);
}
else {
this.setDependencyTreeData(treeData);
}
}
setReportTab(tab) {
this.reportTab = tab;
}
setDependencyTreeData(tree) {
this.dependencyTreeData = tree;
}
setConflictStates(val) {
this.conflictStates = val;
}
setFlattenDependencyTreeData(tree) {
this.flattenDependencyTreeData = tree;
}
get projectConfiguration() {
return this.configState.projectConfiguration;
}
*fetchDependencyReport() {
try {
this.fetchingDependencyInfoState.inProgress();
this.dependencyReport = undefined;
this.clearTrees();
this.setConflictStates(undefined);
if (this.projectConfiguration?.projectDependencies) {
const dependencyCoordinates = (yield flowResult(this.editorStore.graphState.buildProjectDependencyCoordinates(this.projectConfiguration.projectDependencies)));
const dependencyInfoRaw = (yield this.editorStore.depotServerClient.analyzeDependencyTree(dependencyCoordinates.map((e) => ProjectDependencyCoordinates.serialization.toJson(e))));
const rawdependencyReport = RawProjectDependencyReport.serialization.fromJson(dependencyInfoRaw);
const report = buildDependencyReport(rawdependencyReport);
this.dependencyReport = report;
this.setDependencyTreeData(buildDependencyTreeData(report));
this.setFlattenDependencyTreeData(buildFlattenDependencyTreeData(report));
}
this.fetchingDependencyInfoState.complete();
}
catch (error) {
assertErrorThrown(error);
this.fetchingDependencyInfoState.fail();
this.dependencyReport = undefined;
this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.DEPOT_MANAGER_FAILURE), error);
}
}
buildConflictPaths() {
const report = this.dependencyReport;
if (report) {
this.setConflictStates(undefined);
this.buildConflictPathState.inProgress();
try {
report.conflictInfo = buildConflictsPaths(report);
const conflictStates = Array.from(report.conflictInfo.entries()).map(([conflict, paths]) => new ProjectDependencyConflictState(report, conflict, paths));
this.setConflictStates(conflictStates);
this.buildConflictPathState.complete();
}
catch (error) {
assertErrorThrown(error);
this.setConflictStates([]);
this.buildConflictPathState.fail();
this.editorStore.applicationStore.notificationService.notifyError(`Unable to build conflict paths ${error.message}`);
}
}
}
clearTrees() {
this.flattenDependencyTreeData = undefined;
this.dependencyTreeData = undefined;
}
}
//# sourceMappingURL=ProjectDependencyEditorState.js.map