UNPKG

@finos/legend-graph

Version:
306 lines 18.3 kB
/** * 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 { PRIMITIVE_TYPE, ELEMENT_PATH_DELIMITER, ROOT_PACKAGE_NAME, } from '../../../../../../../graph/MetaModelConst.js'; import { uniq, guaranteeNonNullable, assertNonEmptyString, guaranteeType, } from '@finos/legend-shared'; import { GenericType } from '../../../../../../../graph/metamodel/pure/packageableElements/domain/GenericType.js'; import { ImportAwareCodeSection, } from '../../../../../../../graph/metamodel/pure/packageableElements/section/Section.js'; import { StereotypeImplicitReference } from '../../../../../../../graph/metamodel/pure/packageableElements/domain/StereotypeReference.js'; import { GenericTypeImplicitReference, } from '../../../../../../../graph/metamodel/pure/packageableElements/domain/GenericTypeReference.js'; import { PackageableElementImplicitReference } from '../../../../../../../graph/metamodel/pure/packageableElements/PackageableElementReference.js'; import { TagImplicitReference } from '../../../../../../../graph/metamodel/pure/packageableElements/domain/TagReference.js'; import { PropertyImplicitReference } from '../../../../../../../graph/metamodel/pure/packageableElements/domain/PropertyReference.js'; import { JoinImplicitReference } from '../../../../../../../graph/metamodel/pure/packageableElements/store/relational/model/JoinReference.js'; import { FilterImplicitReference } from '../../../../../../../graph/metamodel/pure/packageableElements/store/relational/model/FilterReference.js'; import { RootFlatDataRecordTypeImplicitReference } from '../../../../../../../graph/metamodel/pure/packageableElements/store/flatData/model/RootFlatDataRecordTypeReference.js'; import { createImplicitRelationReference } from '../../../../../../../graph/metamodel/pure/packageableElements/store/relational/model/RelationReference.js'; import { EnumValueImplicitReference } from '../../../../../../../graph/metamodel/pure/packageableElements/domain/EnumValueReference.js'; import { V1_getRelation } from './helpers/V1_DatabaseBuilderHelper.js'; import { DataType } from '../../../../../../../graph/metamodel/pure/packageableElements/domain/DataType.js'; import { GraphBuilderError } from '../../../../../../../graph-manager/GraphManagerUtils.js'; import { getClassProperty, getEnumValue, getOwnProperty, getStereotype, getTag, newGenericType, } from '../../../../../../../graph/helpers/DomainHelper.js'; import { getFilter, getJoin, } from '../../../../../../../graph/helpers/STO_Relational_Helper.js'; import { getRootRecordType, getSection, } from '../../../../../../../graph/helpers/STO_FlatData_Helper.js'; import { V1_getGenericTypeFullPath } from '../../../helpers/V1_DomainHelper.js'; export const V1_buildFullPath = (packagePath, name) => `${guaranteeNonNullable(packagePath, 'Package path is required')}${ELEMENT_PATH_DELIMITER}${guaranteeNonNullable(name, 'Name is required')}`; export class V1_GraphBuilderContext { autoImports; sectionImports = []; logService; currentSubGraph; extensions; graph; section; options; constructor(builder) { this.logService = builder.logService; this.graph = builder.graph; this.autoImports = this.graph.autoImports; this.currentSubGraph = builder.currentSubGraph; this.extensions = builder.extensions; this.sectionImports = builder.sectionImports; this.section = builder.section; this.options = builder.options; } /** * Since we haven't fully supported section index, shortened paths * using imports in the graph might need to be fully resolved. * * To handle this need, we make use of references. References can auto * resolve full paths when the section index is deleted. But, when * building the graph, we leave the value specifications * raw/unprocessed. Hence, we cannot make use of references to do full * path resolution, as such, we make a best effort traversal in the model * of raw value specifications to resolve path automatically. * * We create this flag to control the behavior of lambda auto path-resolution. * This rewriting behavior should not be done for immutable graphs, such as * system, depdendencies, and generation. However, in overall, it would be controlled * also by the `TEMPORARY__preserveSectionIndex` flag. * * NOTE: When we fully support section index, we would certainly need to * revise the usefullness of this flag, perhaps, we don't auto-resolve anymore, * but this mechanism would still be beneficial as we can keep it as an utility * to resolve raw lambdas' paths when the user deliberately delete the section * index, for example. * * https://github.com/finos/legend-studio/issues/1067 */ get enableRawLambdaAutoPathResolution() { return (this.graph.root.name === ROOT_PACKAGE_NAME.MAIN && !this.options?.TEMPORARY__preserveSectionIndex); } resolve(path, resolverFn) { // Try the find from special types (not user-defined top level types) const SPECIAL_TYPES = Object.values(PRIMITIVE_TYPE).concat([]); if (SPECIAL_TYPES.includes(path)) { return { element: resolverFn(path), }; } // if the path is a path with package, no resolution from section imports is needed if (path.includes(ELEMENT_PATH_DELIMITER)) { return { element: resolverFn(path), isFullPath: true, }; } // NOTE: here we make the assumption that we have populated the indices properly so the same element // is not referred using 2 different paths in the same element index const results = new Map(); this.autoImports.forEach((importPackage) => { try { const fullPath = importPackage.path + ELEMENT_PATH_DELIMITER + path; const element = resolverFn(fullPath); if (element) { results.set(fullPath, { element, resolvedUsingSectionImports: false, }); } } catch { // do nothing } }); // only resolve section imports if there is a section if (this.section) { this.sectionImports.forEach((importPackage) => { try { const fullPath = importPackage.path + ELEMENT_PATH_DELIMITER + path; const element = resolverFn(fullPath); if (element) { results.set(fullPath, { element, resolvedUsingSectionImports: true, }); } } catch { // do nothing } }); } switch (results.size) { /** * NOTE: if nothing is found then we will try to find user-defined elements at root package (i.e. no package) * We place this after import resolution since we want to emphasize that this type of element has the lowest precedence * In fact, due to the restriction that Alloy imposes on element path, the only kinds of element * we could find at this level are packages, but they will not fit the type we look for * in PURE, since we resolve to CoreInstance, further validation needs to be done to make the resolution complete * here we count on the `resolver` to do the validation of the type of element instead */ case 0: return { element: resolverFn(path), isFullPath: true, }; case 1: return guaranteeNonNullable(Array.from(results.values())[0]); default: throw new GraphBuilderError(undefined, `Can't resolve element with path '${path}' - multiple matches found [${Array.from(results.keys()).join(', ')}]`); } } /** * This method and this class in general demonstrates the difference * between explicit and implicit reference. * See {@link PackageableElementImplicitReference} for more details. * * Notice that every method in the resolver ends up creating an implicit reference. * It does not matter whether the full path is specified or not (i.e. so almost * no inference was done), the resulting reference must be implicit, as we took the * input into account when creating this reference. */ createImplicitPackageableElementReference = (path, resolverFn) => { const { element, resolvedUsingSectionImports, isFullPath } = this.resolve(path, resolverFn); if (!resolvedUsingSectionImports && !isFullPath) { return PackageableElementImplicitReference.create(element, path); } return PackageableElementImplicitReference.resolveFromSection(element, path, resolvedUsingSectionImports ? this.section : undefined); }; resolveStereotype = (stereotypePtr) => { assertNonEmptyString(stereotypePtr.profile, `Steoreotype pointer 'profile' field is missing or empty`); assertNonEmptyString(stereotypePtr.value, `Steoreotype pointer 'value' field is missing or empty`); const ownerReference = this.resolveProfile(stereotypePtr.profile); const value = getStereotype(ownerReference.value, stereotypePtr.value); return StereotypeImplicitReference.create(ownerReference, value); }; resolveTag = (tagPtr) => { assertNonEmptyString(tagPtr.profile, `Tag pointer 'profile' field is missing or empty`); assertNonEmptyString(tagPtr.value, `Tag pointer 'value' field is missing or empty`); const ownerReference = this.resolveProfile(tagPtr.profile); const value = getTag(ownerReference.value, tagPtr.value); return TagImplicitReference.create(ownerReference, value); }; resolveGenericType = (path) => { const ownerReference = this.resolveType(path); const value = new GenericType(ownerReference.value); return GenericTypeImplicitReference.create(ownerReference, value); }; resolveGenericTypeFromProtocol = (genericType) => { const ownerReference = this.resolveType(V1_getGenericTypeFullPath(genericType)); const typeArguments = genericType.typeArguments.map((g) => this.resolveGenericTypeFromProtocol(g)); const value = newGenericType(ownerReference.value, typeArguments); return GenericTypeImplicitReference.create(ownerReference, value); }; resolveOwnProperty = (pointer) => { assertNonEmptyString(pointer.class, `Property pointer 'class' field is missing or empty`); assertNonEmptyString(pointer.property, `Property pointer 'property' field is missing or empty`); const ownerReference = this.resolvePropertyOwner(pointer.class); const value = getOwnProperty(ownerReference.value, pointer.property); return PropertyImplicitReference.create(ownerReference, value); }; resolveProperty = (pointer) => { assertNonEmptyString(pointer.class, `Property pointer 'class' field is missing or empty`); assertNonEmptyString(pointer.property, `Property pointer 'property' field is missing or empty`); const ownerReference = this.resolveClass(pointer.class); const value = getClassProperty(ownerReference.value, pointer.property); return PropertyImplicitReference.create(ownerReference, value); }; resolveRootFlatDataRecordType = (classMapping) => { assertNonEmptyString(classMapping.flatData, `Flat-data class mapping 'flatData' field is missing or empty`); assertNonEmptyString(classMapping.sectionName, `Flat-data class mapping 'sectionName' field is missing or empty`); const ownerReference = this.resolveFlatDataStore(classMapping.flatData); const value = getRootRecordType(getSection(ownerReference.value, classMapping.sectionName)); return RootFlatDataRecordTypeImplicitReference.create(ownerReference, value); }; resolveRelation = (tablePtr) => { assertNonEmptyString(tablePtr.database, `Table pointer 'database' field is missing or empty`); assertNonEmptyString(tablePtr.schema, `Table pointer 'schema' field is missing or empty`); assertNonEmptyString(tablePtr.table, `Table pointer 'table' field is missing or empty`); const ownerReference = this.resolveDatabase(tablePtr.database); const value = V1_getRelation(ownerReference.value, tablePtr.schema, tablePtr.table); return createImplicitRelationReference(ownerReference, value); }; resolveJoin = (joinPtr) => { assertNonEmptyString(joinPtr.db, `Join pointer 'db' field is missing or empty`); assertNonEmptyString(joinPtr.name, `Join pointer 'name' field is missing or empty`); const ownerReference = this.resolveDatabase(joinPtr.db); const value = getJoin(ownerReference.value, joinPtr.name); return JoinImplicitReference.create(ownerReference, value); }; resolveFilter = (filterPtr) => { assertNonEmptyString(filterPtr.db, `Filter pointer 'db' field is missing or empty`); assertNonEmptyString(filterPtr.name, `Filter pointer 'name' field is missing or empty`); const ownerReference = this.resolveDatabase(filterPtr.db); const value = getFilter(ownerReference.value, filterPtr.name); return FilterImplicitReference.create(ownerReference, value); }; resolveEnumValue = (enumeration, enumValue) => { const ownerReference = this.resolveEnumeration(enumeration); const value = getEnumValue(ownerReference.value, enumValue); return EnumValueImplicitReference.create(ownerReference, value); }; resolveElement = (path, includePackage) => this.createImplicitPackageableElementReference(path, (_path) => this.graph.getElement(_path, includePackage)); resolveType = (path) => this.createImplicitPackageableElementReference(path, this.graph.getType); resolveDataType = (path) => this.createImplicitPackageableElementReference(path, (_path) => guaranteeType(this.graph.getType(_path), DataType, `Can't find data type '${_path}'`)); resolveProfile = (path) => this.createImplicitPackageableElementReference(path, this.graph.getProfile); resolveClass = (path) => this.createImplicitPackageableElementReference(path, this.graph.getClass); resolveEnumeration = (path) => this.createImplicitPackageableElementReference(path, this.graph.getEnumeration); resolveMeasure = (path) => this.createImplicitPackageableElementReference(path, this.graph.getMeasure); resolveUnit = (path) => this.createImplicitPackageableElementReference(path, this.graph.getUnit); resolveAssociation = (path) => this.createImplicitPackageableElementReference(path, this.graph.getAssociation); resolvePropertyOwner = (path) => this.createImplicitPackageableElementReference(path, this.graph.getPropertyOwner); resolveFunction = (path) => this.createImplicitPackageableElementReference(path, this.graph.getFunction); resolveStore = (path) => this.createImplicitPackageableElementReference(path, this.graph.getStore); resolveFlatDataStore = (path) => this.createImplicitPackageableElementReference(path, this.graph.getFlatDataStore); resolveDatabase = (path) => this.createImplicitPackageableElementReference(path, this.graph.getDatabase); resolveMapping = (path) => this.createImplicitPackageableElementReference(path, this.graph.getMapping); resolveService = (path) => this.createImplicitPackageableElementReference(path, this.graph.getService); resolveConnection = (path) => this.createImplicitPackageableElementReference(path, this.graph.getConnection); resolveRuntime = (path) => this.createImplicitPackageableElementReference(path, this.graph.getRuntime); resolveGenerationSpecification = (path) => this.createImplicitPackageableElementReference(path, this.graph.getGenerationSpecification); resolveFileGeneration = (path) => this.createImplicitPackageableElementReference(path, this.graph.getFileGeneration); resolveDataElement = (path) => this.createImplicitPackageableElementReference(path, this.graph.getDataElement); } export class V1_GraphBuilderContextBuilder { logService; /** * The (sub) graph where the current processing is taking place. * This information is important because each sub-graph holds their * own indexes for elements they are responsible for. * * e.g. dependency graph, generation graph, system graph, etc. */ currentSubGraph; extensions; graph; sectionImports = []; section; options; constructor(graph, currentSubGraph, extensions, logService, options) { this.graph = graph; this.currentSubGraph = currentSubGraph; this.extensions = extensions; this.logService = logService; this.options = options; } withElement(element) { const section = this.graph.getOwnNullableSection(element.path); return this.withSection(section); } withSection(section) { this.section = section; if (section instanceof ImportAwareCodeSection) { this.sectionImports = this.sectionImports.concat(section.imports.map((i) => i.value)); } this.sectionImports = uniq(this.sectionImports); // remove duplicates return this; } build() { return new V1_GraphBuilderContext(this); } } //# sourceMappingURL=V1_GraphBuilderContext.js.map