@finos/legend-application-pure-ide
Version:
Legend Pure IDE application core
261 lines • 11.5 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 { deserialize } from 'serializr';
import { TreeState } from './TreeState.js';
import { ElementConceptAttribute, PropertyConceptAttribute, ConceptNode, ConceptType, PackageConceptAttribute, } from '../server/models/ConceptTree.js';
import { action, flow, flowResult, makeObservable, observable } from 'mobx';
import { FileCoordinate } from '../server/models/File.js';
import { ActionState, assertErrorThrown, assertTrue, assertType, guaranteeNonNullable, } from '@finos/legend-shared';
import { FIND_USAGE_FUNCTION_PATH, PackageableElementUsage, } from '../server/models/Usage.js';
import { ELEMENT_PATH_DELIMITER, extractPackagePathFromPath, } from '@finos/legend-graph';
import { ACTIVITY_MODE } from './PureIDEConfig.js';
const getParentPath = (path) => {
const trimmedPath = path.trim();
const idx = trimmedPath.lastIndexOf(ELEMENT_PATH_DELIMITER);
if (idx <= 0) {
return undefined;
}
return trimmedPath.substring(0, idx);
};
export class ConceptTreeState extends TreeState {
loadConceptActivity = ActionState.create();
statusText;
nodeForRenameConcept;
nodeForMoveElement;
constructor(ideStore) {
super(ideStore);
makeObservable(this, {
statusText: observable,
nodeForRenameConcept: observable,
nodeForMoveElement: observable,
setStatusText: action,
setNodeForRenameConcept: action,
setNodeForMoveElement: action,
pullConceptsActivity: action,
pollConceptsActivity: flow,
revealConcept: flow,
});
}
setStatusText(value) {
this.statusText = value;
}
setNodeForRenameConcept(value) {
this.nodeForRenameConcept = value;
}
setNodeForMoveElement(value) {
this.nodeForMoveElement = value;
}
async getRootNodes() {
await flowResult(this.pollConceptsActivity());
return (await this.ideStore.client.getConceptChildren()).map((node) => deserialize(ConceptNode, node));
}
buildTreeData(rootNodes) {
const rootIds = [];
const nodes = new Map();
rootNodes.forEach((node) => {
const id = node.li_attr.id;
rootIds.push(id);
nodes.set(id, {
data: node,
id,
label: node.text,
isLoading: false,
});
});
return { rootIds, nodes };
}
async getChildNodes(node) {
return (await this.ideStore.client.getConceptChildren(node.data.li_attr.pureId)).map((child) => deserialize(ConceptNode, child));
}
processChildNodes(node, childNodes) {
const treeData = this.getTreeData();
const childrenIds = [];
childNodes.forEach((childNode) => {
const id = childNode.li_attr.id;
childrenIds.push(id);
treeData.nodes.set(id, {
data: childNode,
id,
label: childNode.text,
isLoading: false,
parent: node,
});
});
node.childrenIds = childrenIds;
}
*openNode(node) {
if (node.data.li_attr instanceof PropertyConceptAttribute ||
node.data.li_attr instanceof ElementConceptAttribute) {
if (node.data.li_attr.pureType === 'Diagram') {
yield flowResult(this.ideStore.loadDiagram(node.data.li_attr.file, node.data.li_attr.pureId, Number.parseInt(node.data.li_attr.line, 10), Number.parseInt(node.data.li_attr.column, 10)));
}
else {
yield flowResult(this.ideStore.loadFile(node.data.li_attr.file, new FileCoordinate(node.data.li_attr.file, Number.parseInt(node.data.li_attr.line, 10), Number.parseInt(node.data.li_attr.column, 10))));
}
}
}
*pollConceptsActivity() {
if (!this.loadConceptActivity.isInInitialState) {
return;
}
this.loadConceptActivity.inProgress();
this.setStatusText('Loading concepts activity...');
try {
yield this.pullConceptsActivity();
}
finally {
this.setStatusText(undefined);
this.loadConceptActivity.reset();
}
}
async pullConceptsActivity() {
const result = (await this.ideStore.client.getConceptActivity());
if (result.text) {
this.setStatusText(`Preparing - ${result.text}`);
}
if (result.initializing) {
return new Promise((resolve, reject) => setTimeout(() => {
try {
resolve(this.pullConceptsActivity());
}
catch (error) {
reject(error);
}
}, 1000));
}
return Promise.resolve();
}
*revealConcept(path, options) {
if (options?.forceOpenExplorerPanel) {
this.ideStore.setActiveActivity(ACTIVITY_MODE.CONCEPT_EXPLORER, {
keepShowingIfMatchedCurrent: true,
});
}
const paths = [];
let currentPath = path;
while (currentPath) {
paths.unshift(currentPath);
currentPath = getParentPath(currentPath);
}
for (const _path of paths) {
const node = guaranteeNonNullable(this.getTreeData().nodes.get(_path), `Can't find node with path '${_path}'`);
if (node.data.li_attr instanceof PackageConceptAttribute) {
yield flowResult(this.expandNode(node));
}
else {
if (options?.packageOnly) {
throw new Error(`Can't reveal non-package path`);
}
// TODO: this is not needed so we haven't implemented this yet
}
}
this.setSelectedNode(guaranteeNonNullable(this.getTreeData().nodes.get(path), `Can't find node with path '${path}'`));
}
async movePackageableElements(elementNodeAttributes, destinationPackage) {
let elementsUsage = [];
try {
elementsUsage = (await this.ideStore.client.getPackageableElementsUsage(elementNodeAttributes.map((attr) => attr.pureId))).map((usage) => deserialize(PackageableElementUsage, usage));
}
catch {
this.ideStore.applicationStore.notificationService.notifyError(`Can't find usage for child packageable elements`);
return;
}
finally {
this.ideStore.applicationStore.alertService.setBlockingAlert(undefined);
}
const inputs = [];
assertTrue(elementsUsage.length === elementNodeAttributes.length, `Can't find matching usages for packageable elements`);
for (let i = 0; i < elementsUsage.length; ++i) {
const elementInfo = guaranteeNonNullable(elementNodeAttributes[i]);
inputs.push({
pureName: elementInfo.pureName,
pureType: elementInfo.pureType,
sourcePackage: guaranteeNonNullable(extractPackagePathFromPath(elementInfo.pureId)),
destinationPackage,
usages: guaranteeNonNullable(elementsUsage[i]).second,
});
}
await flowResult(this.ideStore.movePackageableElements(inputs));
}
async renameConcept(node, newName) {
const attr = node.data.li_attr;
const oldName = attr.pureName ?? attr.pureId;
let usages = [];
try {
this.ideStore.applicationStore.alertService.setBlockingAlert({
message: 'Finding concept usages...',
showLoading: true,
});
switch (attr.pureType) {
case ConceptType.PROPERTY:
case ConceptType.QUALIFIED_PROPERTY: {
assertType(attr, PropertyConceptAttribute);
usages = await this.ideStore.findConceptUsages(FIND_USAGE_FUNCTION_PATH.PROPERTY, [`'${attr.classPath}'`, `'${attr.pureId}'`]);
break;
}
case ConceptType.PACKAGE: {
let elementsUsage = [];
let childElementsInfo = [];
try {
childElementsInfo =
await this.ideStore.client.getChildPackageableElements(oldName);
elementsUsage = (await this.ideStore.client.getPackageableElementsUsage(childElementsInfo.map((info) => info.pureId))).map((usage) => deserialize(PackageableElementUsage, usage));
}
catch {
this.ideStore.applicationStore.notificationService.notifyError(`Can't find usage for child packageable elements`);
return;
}
finally {
this.ideStore.applicationStore.alertService.setBlockingAlert(undefined);
}
const inputs = [];
assertTrue(elementsUsage.length === childElementsInfo.length, `Can't find matching usages for child packageable elements`);
const newPackage = extractPackagePathFromPath(oldName)
?.concat(ELEMENT_PATH_DELIMITER)
.concat(newName) ?? newName;
for (let i = 0; i < elementsUsage.length; ++i) {
const elementInfo = guaranteeNonNullable(childElementsInfo[i]);
const sourcePackage = guaranteeNonNullable(extractPackagePathFromPath(elementInfo.pureId));
const destinationPackage = newPackage.concat(sourcePackage.substring(sourcePackage.indexOf(oldName) + oldName.length));
inputs.push({
pureName: elementInfo.pureName,
pureType: elementInfo.pureType,
sourcePackage,
destinationPackage,
usages: guaranteeNonNullable(elementsUsage[i]).second,
});
}
await flowResult(this.ideStore.movePackageableElements(inputs));
return;
}
default: {
usages = await this.ideStore.findConceptUsages(FIND_USAGE_FUNCTION_PATH.ELEMENT, [`'${attr.pureId}'`]);
break;
}
}
}
catch (error) {
assertErrorThrown(error);
this.ideStore.applicationStore.notificationService.notifyError(error);
return;
}
finally {
this.ideStore.applicationStore.alertService.setBlockingAlert(undefined);
}
await flowResult(this.ideStore.renameConcept(oldName, newName, attr.pureType, usages));
}
}
//# sourceMappingURL=ConceptTreeState.js.map