@finos/legend-graph
Version:
Legend graph and graph manager
467 lines • 21.7 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 { ROOT_PACKAGE_NAME, AUTO_IMPORTS, } from '../graph/MetaModelConst.js';
import { guaranteeNonNullable, guaranteeType, returnUndefOnError, IllegalStateError, isNonNullable, } from '@finos/legend-shared';
import { PrimitiveType } from '../graph/metamodel/pure/packageableElements/domain/PrimitiveType.js';
import { Enumeration } from '../graph/metamodel/pure/packageableElements/domain/Enumeration.js';
import { Multiplicity } from '../graph/metamodel/pure/packageableElements/domain/Multiplicity.js';
import { Package } from '../graph/metamodel/pure/packageableElements/domain/Package.js';
import { Class } from '../graph/metamodel/pure/packageableElements/domain/Class.js';
import { DependencyManager } from '../graph/DependencyManager.js';
import { ConcreteFunctionDefinition } from './metamodel/pure/packageableElements/function/ConcreteFunctionDefinition.js';
import { BasicModel } from './BasicModel.js';
import { FlatData } from '../graph/metamodel/pure/packageableElements/store/flatData/model/FlatData.js';
import { Database } from '../graph/metamodel/pure/packageableElements/store/relational/model/Database.js';
import { ModelStore } from '../graph/metamodel/pure/packageableElements/store/modelToModel/model/ModelStore.js';
import { Measure, Unit, } from '../graph/metamodel/pure/packageableElements/domain/Measure.js';
import { createPath } from '../graph/MetaModelUtils.js';
import { FunctionActivator } from './metamodel/pure/packageableElements/function/FunctionActivator.js';
/**
* CoreModel holds meta models which are constant and basic building block of the graph. Since throughout the lifetime
* of the application, we rebuild PureModel many times, we cannot have these basic building blocks as part of PureModel
* as that will throw off referential equality.
*
* Also, since project dependency uses primitive types, it might even
* cause the dependency model and system model to depend on PureModel which is bad, as it could potentially cause memory leak
* as we rebuild the graph.
*/
export class CoreModel extends BasicModel {
primitiveTypesIndex = new Map();
get primitiveTypes() {
return Array.from(this.primitiveTypesIndex.values());
}
constructor(graphPlugins) {
super(ROOT_PACKAGE_NAME.CORE, graphPlugins);
this.initializePrimitiveTypes();
// index model store singleton
this.setOwnStore(ModelStore.NAME, ModelStore.INSTANCE);
}
get allOwnElements() {
return [...super.allOwnElements, ...this.primitiveTypes];
}
/**
* NOTE: primitive types are special, they are not put in any package (i.e. they are not linked to `Root` package at all)
*/
initializePrimitiveTypes() {
[
PrimitiveType.STRING,
PrimitiveType.BOOLEAN,
PrimitiveType.BINARY,
PrimitiveType.NUMBER,
PrimitiveType.INTEGER,
PrimitiveType.FLOAT,
PrimitiveType.DECIMAL,
PrimitiveType.DATE,
PrimitiveType.STRICTDATE,
PrimitiveType.DATETIME,
PrimitiveType.STRICTTIME,
PrimitiveType.LATESTDATE,
PrimitiveType.BYTE,
].forEach((primitiveType) => {
this.primitiveTypesIndex.set(primitiveType.path, primitiveType);
this.setOwnType(primitiveType.path, primitiveType);
});
}
}
export class SystemModel extends BasicModel {
autoImports = [];
constructor(graphPlugins) {
super(ROOT_PACKAGE_NAME.SYSTEM, graphPlugins);
}
/**
* NOTE: auto imports are for special types and profiles from system model
* such as `Any` or `doc` profiles. We don't actually build the packages here
* just resolving them, so we have to make sure whatever package we have as
* auto imports, we must have built some elements with such package, e.g.
*
* `meta::pure::metamodel::type::Any` covers `meta::pure::metamodel::type`
* `meta::pure::profiles::doc` covers `meta::pure::profiles`
*/
initializeAutoImports() {
this.autoImports = AUTO_IMPORTS.map((_package) => guaranteeType(this.getOwnNullableElement(_package, true), Package, `Can't find auto-import package '${_package}'`));
}
}
export class GenerationModel extends BasicModel {
constructor(graphPlugins) {
super(ROOT_PACKAGE_NAME.MODEL_GENERATION, graphPlugins);
}
}
/**
* The model of Pure, a.k.a the Pure graph
*/
export class PureModel extends BasicModel {
coreModel;
systemModel;
generationModel;
dependencyManager; // used to manage the elements from dependency projects
constructor(coreModel, systemModel, graphPlugins) {
super(ROOT_PACKAGE_NAME.MAIN, graphPlugins);
this.coreModel = coreModel;
this.systemModel = systemModel;
this.generationModel = new GenerationModel(graphPlugins);
this.dependencyManager = new DependencyManager(graphPlugins);
}
get autoImports() {
return this.systemModel.autoImports;
}
get primitiveTypes() {
return this.coreModel.primitiveTypes;
}
get sectionIndices() {
return [
...this.coreModel.ownSectionIndices,
...this.systemModel.ownSectionIndices,
...this.dependencyManager.sectionIndices,
...this.ownSectionIndices,
...this.generationModel.ownSectionIndices,
];
}
get profiles() {
return [
...this.coreModel.ownProfiles,
...this.systemModel.ownProfiles,
...this.dependencyManager.profiles,
...this.ownProfiles,
...this.generationModel.ownProfiles,
];
}
get enumerations() {
return [
...this.coreModel.ownEnumerations,
...this.systemModel.ownEnumerations,
...this.dependencyManager.enumerations,
...this.ownEnumerations,
...this.generationModel.ownEnumerations,
];
}
get measures() {
return [
...this.coreModel.ownMeasures,
...this.systemModel.ownMeasures,
...this.dependencyManager.measures,
...this.ownMeasures,
...this.generationModel.ownMeasures,
];
}
get classes() {
return [
...this.coreModel.ownClasses,
...this.systemModel.ownClasses,
...this.dependencyManager.classes,
...this.ownClasses,
...this.generationModel.ownClasses,
];
}
get types() {
return [
...this.coreModel.ownTypes,
...this.systemModel.ownTypes,
...this.dependencyManager.types,
...this.ownTypes,
...this.generationModel.ownTypes,
];
}
get associations() {
return [
...this.coreModel.ownAssociations,
...this.systemModel.ownAssociations,
...this.dependencyManager.associations,
...this.ownAssociations,
...this.generationModel.ownAssociations,
];
}
get functions() {
return [
...this.coreModel.ownFunctions,
...this.systemModel.ownFunctions,
...this.dependencyManager.functions,
...this.ownFunctions,
...this.generationModel.ownFunctions,
];
}
get functionActivators() {
return [
...this.coreModel.ownFunctionActivators,
...this.systemModel.ownFunctionActivators,
...this.dependencyManager.functionActivators,
...this.ownFunctionActivators,
...this.generationModel.ownFunctionActivators,
];
}
get stores() {
return [
...this.coreModel.ownStores,
...this.systemModel.ownStores,
...this.dependencyManager.stores,
...this.ownStores,
...this.generationModel.ownStores,
];
}
get databases() {
return [
...this.coreModel.ownDatabases,
...this.systemModel.ownDatabases,
...this.dependencyManager.databases,
...this.ownDatabases,
...this.generationModel.ownDatabases,
];
}
get mappings() {
return [
...this.coreModel.ownMappings,
...this.systemModel.ownMappings,
...this.dependencyManager.mappings,
...this.ownMappings,
...this.generationModel.ownMappings,
];
}
get services() {
return [
...this.coreModel.ownServices,
...this.systemModel.ownServices,
...this.dependencyManager.services,
...this.ownServices,
...this.generationModel.ownServices,
];
}
get runtimes() {
return [
...this.coreModel.ownRuntimes,
...this.systemModel.ownRuntimes,
...this.dependencyManager.runtimes,
...this.ownRuntimes,
...this.generationModel.ownRuntimes,
];
}
get connections() {
return [
...this.coreModel.ownConnections,
...this.systemModel.ownConnections,
...this.dependencyManager.connections,
...this.ownConnections,
...this.generationModel.ownConnections,
];
}
get dataElements() {
return [
...this.coreModel.ownDataElements,
...this.systemModel.ownDataElements,
...this.dependencyManager.dataElements,
...this.ownDataElements,
...this.generationModel.ownDataElements,
];
}
get executionEnvironments() {
return [
...this.coreModel.ownExecutionEnvironments,
...this.systemModel.ownExecutionEnvironments,
...this.dependencyManager.executionEnvironments,
...this.ownExecutionEnvironments,
...this.generationModel.ownExecutionEnvironments,
];
}
get generationSpecifications() {
return [
...this.coreModel.ownGenerationSpecifications,
...this.systemModel.ownGenerationSpecifications,
...this.dependencyManager.generationSpecifications,
...this.ownGenerationSpecifications,
...this.generationModel.ownGenerationSpecifications,
];
}
get fileGenerations() {
return [
...this.coreModel.ownFileGenerations,
...this.systemModel.ownFileGenerations,
...this.dependencyManager.fileGenerations,
...this.ownFileGenerations,
...this.generationModel.ownFileGenerations,
];
}
get ingests() {
return [
...this.coreModel.ownIngests,
...this.systemModel.ownIngests,
...this.dependencyManager.ingests,
...this.ownIngests,
...this.generationModel.ownIngests,
];
}
get allElements() {
return [
...this.coreModel.allOwnElements,
...this.systemModel.allOwnElements,
...this.dependencyManager.allOwnElements,
...this.allOwnElements,
...this.generationModel.allOwnElements,
];
}
get testables() {
return [
...this.coreModel.ownTestables,
...this.systemModel.ownTestables,
...this.dependencyManager.testables,
...this.ownTestables,
...this.generationModel.ownTestables,
];
}
getPrimitiveType = (type) => guaranteeNonNullable(this.coreModel.primitiveTypesIndex.get(type), `Can't find primitive type '${type}'`);
getType = (path) => guaranteeNonNullable(this.getOwnNullableType(path) ??
this.generationModel.getOwnNullableType(path) ??
this.dependencyManager.getOwnNullableType(path) ??
this.systemModel.getOwnNullableType(path) ??
this.coreModel.getOwnNullableType(path), `Can't find type '${path}'`);
getProfile = (path) => guaranteeNonNullable(this.getOwnNullableProfile(path) ??
this.generationModel.getOwnNullableProfile(path) ??
this.dependencyManager.getOwnNullableProfile(path) ??
this.systemModel.getOwnNullableProfile(path), `Can't find profile '${path}'`);
getEnumeration = (path) => guaranteeType(this.getType(path), Enumeration, `Can't find enumeration '${path}'`);
getMeasure = (path) => guaranteeType(this.getType(path), Measure, `Can't find measure '${path}'`);
getUnit = (path) => guaranteeType(this.getType(path), Unit, `Can't find unit '${path}'`);
getClass = (path) => guaranteeType(this.getType(path), Class, `Can't find class '${path}'`);
getAssociation = (path) => guaranteeNonNullable(this.getOwnNullableAssociation(path) ??
this.generationModel.getOwnNullableAssociation(path) ??
this.dependencyManager.getOwnNullableAssociation(path) ??
this.systemModel.getOwnNullableAssociation(path), `Can't find association '${path}'`);
getPropertyOwner = (path) => guaranteeNonNullable(this.getOwnNullableAssociation(path) ??
this.generationModel.getOwnNullableAssociation(path) ??
this.dependencyManager.getOwnNullableAssociation(path) ??
this.systemModel.getOwnNullableAssociation(path) ??
guaranteeType(this.getType(path), Class), `Can't find property owner '${path}'`);
getFunction = (path) => guaranteeType(this.getOwnNullableFunction(path) ??
this.generationModel.getOwnNullableFunction(path) ??
this.dependencyManager.getOwnNullableFunction(path) ??
this.systemModel.getOwnNullableFunction(path), ConcreteFunctionDefinition, `Can't find function '${path}'`);
getFunctionActivator = (path) => guaranteeType(this.getOwnNullableFunctionActivator(path) ??
this.generationModel.getOwnNullableFunctionActivator(path) ??
this.dependencyManager.getOwnNullableFunctionActivator(path) ??
this.systemModel.getOwnNullableFunctionActivator(path), FunctionActivator, `Can't find function activator '${path}'`);
getStore = (path) => guaranteeNonNullable(this.getOwnNullableStore(path) ??
this.generationModel.getOwnNullableStore(path) ??
this.dependencyManager.getOwnNullableStore(path) ??
this.systemModel.getOwnNullableStore(path) ??
this.coreModel.getOwnNullableStore(path), `Can't find store '${path}'`);
getFlatDataStore = (path) => guaranteeType(this.getStore(path), FlatData, `Can't find flat-data store '${path}'`);
getDatabase = (path) => guaranteeType(this.getStore(path), Database, `Can't find database store '${path}'`);
getMapping = (path) => guaranteeNonNullable(this.getOwnNullableMapping(path) ??
this.generationModel.getOwnNullableMapping(path) ??
this.dependencyManager.getOwnNullableMapping(path) ??
this.systemModel.getOwnNullableMapping(path), `Can't find mapping '${path}'`);
getService = (path) => guaranteeNonNullable(this.getOwnNullableService(path) ??
this.generationModel.getOwnNullableService(path) ??
this.dependencyManager.getOwnNullableService(path) ??
this.systemModel.getOwnNullableService(path), `Can't find service '${path}'`);
getConnection = (path) => guaranteeNonNullable(this.getOwnNullableConnection(path) ??
this.generationModel.getOwnNullableConnection(path) ??
this.dependencyManager.getOwnNullableConnection(path) ??
this.systemModel.getOwnNullableConnection(path), `Can't find connection '${path}'`);
getRuntime = (path) => guaranteeNonNullable(this.getOwnNullableRuntime(path) ??
this.generationModel.getOwnNullableRuntime(path) ??
this.dependencyManager.getOwnNullableRuntime(path) ??
this.systemModel.getOwnNullableRuntime(path), `Can't find runtime '${path}'`);
getGenerationSpecification = (path) => guaranteeNonNullable(this.getOwnNullableGenerationSpecification(path) ??
this.generationModel.getOwnNullableGenerationSpecification(path) ??
this.dependencyManager.getOwnNullableGenerationSpecification(path) ??
this.systemModel.getOwnNullableGenerationSpecification(path), `Can't find generation specification '${path}'`);
getFileGeneration = (path) => guaranteeNonNullable(this.getOwnNullableFileGeneration(path) ??
this.generationModel.getOwnNullableFileGeneration(path) ??
this.dependencyManager.getOwnNullableFileGeneration(path) ??
this.systemModel.getOwnNullableFileGeneration(path), `Can't find file generation '${path}'`);
getDataElement = (path) => guaranteeNonNullable(this.getOwnNullableDataElement(path) ??
this.generationModel.getOwnNullableDataElement(path) ??
this.dependencyManager.getOwnNullableDataElement(path) ??
this.systemModel.getOwnNullableDataElement(path), `Can't find data element '${path}'`);
getExtensionElement(path, extensionElementClass, notFoundErrorMessage) {
// NOTE: beware that this method will favor main graph elements over those of subgraphs when resolving
return guaranteeNonNullable(this.getOwnNullableExtensionElement(path, extensionElementClass) ??
this.generationModel.getOwnNullableExtensionElement(path, extensionElementClass) ??
this.dependencyManager.getOwnNullableExtensionElement(path, extensionElementClass) ??
this.systemModel.getOwnNullableExtensionElement(path, extensionElementClass), notFoundErrorMessage ?? `Can't find element '${path}'`);
}
getElement = (path, includePackage) => guaranteeNonNullable(this.getNullableElement(path, includePackage), `Can't find element '${path}'`);
getNullableClass = (path) => returnUndefOnError(() => this.getClass(path));
getNullableMapping = (path) => returnUndefOnError(() => this.getMapping(path));
getNullableService = (path) => returnUndefOnError(() => this.getService(path));
getNullableRuntime = (path) => returnUndefOnError(() => this.getRuntime(path));
getNullableFileGeneration = (path) => returnUndefOnError(() => this.getFileGeneration(path));
getNullableElement(path, includePackage) {
// NOTE: beware that this method will favor main graph elements over those of subgraphs when resolving
const element = super.getOwnNullableElement(path) ??
this.dependencyManager.getNullableElement(path) ??
this.generationModel.getOwnNullableElement(path) ??
this.systemModel.getOwnNullableElement(path) ??
this.coreModel.getOwnNullableElement(path);
if (includePackage && !element) {
return (this.getNullablePackage(path) ??
this.dependencyManager.getNullableElement(path, true) ??
this.generationModel.getNullablePackage(path) ??
this.systemModel.getNullablePackage(path));
}
return element;
}
getPackages(path) {
return [
this.getNullablePackage(path),
...this.dependencyManager.getPackages(path),
this.generationModel.getNullablePackage(path),
this.systemModel.getNullablePackage(path),
].filter(isNonNullable);
}
getMultiplicity(lowerBound, upperBound) {
let multiplicity;
if (lowerBound === 1 && upperBound === 1) {
multiplicity = Multiplicity.ONE;
}
else if (lowerBound === 0 && upperBound === 1) {
multiplicity = Multiplicity.ZERO_ONE;
}
else if (lowerBound === 0 && upperBound === undefined) {
multiplicity = Multiplicity.ZERO_MANY;
}
else if (lowerBound === 1 && upperBound === undefined) {
multiplicity = Multiplicity.ONE_MANY;
}
else if (lowerBound === 0 && upperBound === 0) {
multiplicity = Multiplicity.ZERO;
}
return multiplicity ?? new Multiplicity(lowerBound, upperBound);
}
addElement(element, packagePath) {
const fullPath = createPath(packagePath ?? '', element.name);
// check for duplication first, but skip package
const existingElement = this.getNullableElement(fullPath, false);
if (existingElement) {
throw new IllegalStateError(`Can't create element '${fullPath}': another element with the same path already existed`);
}
super.addOwnElement(element, packagePath);
}
deleteElement(element) {
super.deleteOwnElement(element);
const deadReferencesCleaners = this.graphPlugins.flatMap((plugin) => plugin.getExtraDeadReferencesCleaners?.() ?? []);
for (const cleaner of deadReferencesCleaners) {
cleaner(this);
}
}
renameElement(element, newPath) {
// check for duplication first, but skip package
const existingElement = this.getNullableElement(newPath, false);
if (existingElement) {
throw new IllegalStateError(`Can't rename element '${element.path}' to '${newPath}': another element with the same path already existed`);
}
super.renameOwnElement(element, element.path, newPath);
}
}
//# sourceMappingURL=PureModel.js.map