UNPKG

@finos/legend-graph

Version:
176 lines 13.9 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 { LogEvent, UnsupportedOperationError, assertType, isNonNullable, guaranteeType, assertNonEmptyString, assertTrue, guaranteeNonNullable, } from '@finos/legend-shared'; import { GRAPH_MANAGER_EVENT } from '../../../../../../../__lib__/GraphManagerEvent.js'; import { OperationSetImplementation } from '../../../../../../../graph/metamodel/pure/packageableElements/mapping/OperationSetImplementation.js'; import { SetImplementationContainer } from '../../../../../../../graph/metamodel/pure/packageableElements/mapping/SetImplementationContainer.js'; import { PureInstanceSetImplementation } from '../../../../../../../graph/metamodel/pure/packageableElements/store/modelToModel/mapping/PureInstanceSetImplementation.js'; import { TableAlias } from '../../../../../../../graph/metamodel/pure/packageableElements/store/relational/model/RelationalOperationElement.js'; import { GroupByMapping } from '../../../../../../../graph/metamodel/pure/packageableElements/store/relational/mapping/GroupByMapping.js'; import { FlatDataInstanceSetImplementation } from '../../../../../../../graph/metamodel/pure/packageableElements/store/flatData/mapping/FlatDataInstanceSetImplementation.js'; import { RootRelationalInstanceSetImplementation } from '../../../../../../../graph/metamodel/pure/packageableElements/store/relational/mapping/RootRelationalInstanceSetImplementation.js'; import { SetImplementationImplicitReference } from '../../../../../../../graph/metamodel/pure/packageableElements/mapping/SetImplementationReference.js'; import { V1_buildRelationalOperationElement } from './helpers/V1_DatabaseBuilderHelper.js'; import { V1_buildRelationalClassMapping, V1_buildRelationalPrimaryKey, } from './helpers/V1_RelationalClassMappingBuilderHelper.js'; import { V1_PropertyMappingBuilder } from './V1_PropertyMappingBuilder.js'; import { V1_getInferredClassMappingId } from './helpers/V1_MappingBuilderHelper.js'; import { AggregationAwareSetImplementation } from '../../../../../../../graph/metamodel/pure/packageableElements/mapping/aggregationAware/AggregationAwareSetImplementation.js'; import { extractClassMappingsFromAggregationAwareClassMappings, getAllClassMappings, getAllEnumerationMappings, getOwnClassMappingById, } from '../../../../../../../graph/helpers/DSL_Mapping_Helper.js'; import { RelationFunctionInstanceSetImplementation } from '../../../../../../../graph/metamodel/pure/packageableElements/mapping/relationFunction/RelationFunctionInstanceSetImplementation.js'; import { generateFunctionPrettyName } from '../../../../../../../graph/helpers/PureLanguageHelper.js'; export class V1_ClassMappingSecondPassBuilder { context; parent; constructor(context, parent) { this.context = context; this.parent = parent; } visit_ClassMapping(classMapping) { const extraClassMappingBuilders = this.context.extensions.plugins.flatMap((plugin) => plugin.V1_getExtraClassMappingSecondPassBuilders?.() ?? []); for (const builder of extraClassMappingBuilders) { builder(classMapping, this.context, this.parent); } } visit_INTERNAL__UnknownClassMapping(classMapping) { return; } visit_OperationClassMapping(classMapping) { assertNonEmptyString(classMapping.class, `Operation class mapping 'class' field is missing or empty`); const id = V1_getInferredClassMappingId(this.context.resolveClass(classMapping.class).value, classMapping).value; const operationSetImplementation = getOwnClassMappingById(this.parent, id); assertType(operationSetImplementation, OperationSetImplementation, `Class mapping with ID '${id}' is not of type operation set implementation`); if (classMapping.extendsClassMappingId) { operationSetImplementation.superSetImplementationId = classMapping.extendsClassMappingId; } operationSetImplementation.parameters = classMapping.parameters .map((parameter) => { const setImplementation = getAllClassMappings(this.parent).find((cm) => cm.id.value === parameter); if (!setImplementation) { // TODO: we will get these cases sometimes since we haven't supported includedMappings this.context.logService.debug(LogEvent.create(GRAPH_MANAGER_EVENT.GRAPH_BUILDER_FAILURE), `Can't find class mapping with ID '${parameter}' in mapping '${this.parent.path}' (perhaps because we haven't supported included mappings)`); return undefined; } return new SetImplementationContainer(SetImplementationImplicitReference.create(setImplementation, undefined)); }) .filter(isNonNullable); } visit_MergeOperationClassMapping(classMapping) { this.visit_OperationClassMapping(classMapping); } visit_PureInstanceClassMapping(classMapping) { assertNonEmptyString(classMapping.class, `Pure instance class mapping 'class' field is missing or empty`); const pureInstanceSetImplementation = guaranteeType(getOwnClassMappingById(this.parent, V1_getInferredClassMappingId(this.context.resolveClass(classMapping.class).value, classMapping).value), PureInstanceSetImplementation); if (classMapping.extendsClassMappingId) { pureInstanceSetImplementation.superSetImplementationId = classMapping.extendsClassMappingId; } // NOTE: we have to process property mappings here instead of in the first pass like the backend because we actually resolve `target` and `source` // at this point instead of just passing in the IDs. This means we have to go through the first pass to create basic mapping elements first // before we can finally use/resolve them in this pass pureInstanceSetImplementation.propertyMappings = classMapping.propertyMappings.map((propertyMapping) => propertyMapping.accept_PropertyMappingVisitor(new V1_PropertyMappingBuilder(this.context, pureInstanceSetImplementation, pureInstanceSetImplementation, getAllEnumerationMappings(this.parent)))); } visit_RootFlatDataClassMapping(classMapping) { assertNonEmptyString(classMapping.class, `Flat-data class mapping 'class' field is missing or empty`); const flatDataInstanceSetImplementation = guaranteeType(getOwnClassMappingById(this.parent, V1_getInferredClassMappingId(this.context.resolveClass(classMapping.class).value, classMapping).value), FlatDataInstanceSetImplementation); if (classMapping.extendsClassMappingId) { flatDataInstanceSetImplementation.superSetImplementationId = classMapping.extendsClassMappingId; } flatDataInstanceSetImplementation.propertyMappings = classMapping.propertyMappings.map((propertyMapping) => propertyMapping.accept_PropertyMappingVisitor(new V1_PropertyMappingBuilder(this.context, flatDataInstanceSetImplementation, flatDataInstanceSetImplementation, getAllEnumerationMappings(this.parent)))); } visit_RelationalClassMapping(classMapping) { throw new UnsupportedOperationError(); } visit_RootRelationalClassMapping(classMapping) { assertNonEmptyString(classMapping.class, `Relational class mapping 'class' field is missing or empty`); const rootRelationalInstanceSetImplementation = guaranteeType(getOwnClassMappingById(this.parent, V1_getInferredClassMappingId(this.context.resolveClass(classMapping.class).value, classMapping).value), RootRelationalInstanceSetImplementation); rootRelationalInstanceSetImplementation.distinct = classMapping.distinct; if (classMapping.extendsClassMappingId) { rootRelationalInstanceSetImplementation.superSetImplementationId = classMapping.extendsClassMappingId; } let mainTableAlias; if (classMapping.mainTable) { const relation = this.context.resolveRelation(classMapping.mainTable); mainTableAlias = new TableAlias(); mainTableAlias.relation = relation; mainTableAlias.name = relation.value.name; rootRelationalInstanceSetImplementation.mainTableAlias = mainTableAlias; } // TODO MappingClass if (classMapping.groupBy.length) { const groupBy = new GroupByMapping(); groupBy.columns = classMapping.groupBy.map((op) => V1_buildRelationalOperationElement(op, this.context, new Map(), [])); rootRelationalInstanceSetImplementation.groupBy = groupBy; } const embedded = []; const tableAliasIndex = new Map(); V1_buildRelationalClassMapping(classMapping, this.context, rootRelationalInstanceSetImplementation, rootRelationalInstanceSetImplementation, this.parent, embedded, getAllEnumerationMappings(this.parent), tableAliasIndex); // TODO filterMapping // NOTE: why are we adding embedded relational property mappings to class mapping in the backend???? if (!mainTableAlias && !classMapping.extendsClassMappingId) { const tables = new Set(Array.from(tableAliasIndex.values()).map((e) => e.relation)); const dbs = new Set(Array.from(tableAliasIndex.values()).map((e) => e.relation.value.schema._OWNER)); assertTrue(tables.size === 1, `Can't find the main table for class '${rootRelationalInstanceSetImplementation.class.value.path}'. Please specify a main table using the ~mainTable directive`); assertTrue(dbs.size === 1, `Can't find the main table for class '${rootRelationalInstanceSetImplementation.class.value.path}': inconsistent database definitions for the mapping`); mainTableAlias = new TableAlias(); mainTableAlias.name = ''; mainTableAlias.relation = guaranteeNonNullable(Array.from(tables.values())[0]); mainTableAlias.database = Array.from(dbs.values())[0]; rootRelationalInstanceSetImplementation.mainTableAlias = mainTableAlias; } if (!rootRelationalInstanceSetImplementation.primaryKey.length && !classMapping.extendsClassMappingId) { V1_buildRelationalPrimaryKey(rootRelationalInstanceSetImplementation); } return rootRelationalInstanceSetImplementation; } visit_AggregationAwareClassMapping(classMapping) { assertNonEmptyString(classMapping.class, `Aggregation-aware class mapping 'class' field is missing or empty`); const aggragetionAwareInstanceSetImplementation = guaranteeType(getOwnClassMappingById(this.parent, V1_getInferredClassMappingId(this.context.resolveClass(classMapping.class).value, classMapping).value), AggregationAwareSetImplementation); if (classMapping.extendsClassMappingId) { aggragetionAwareInstanceSetImplementation.superSetImplementationId = classMapping.extendsClassMappingId; } const mapping = this.context.graph.getMapping(this.parent.path); classMapping.mainSetImplementation.accept_ClassMappingVisitor(new V1_ClassMappingSecondPassBuilder(this.context, mapping)); aggragetionAwareInstanceSetImplementation.propertyMappings = classMapping.propertyMappings.map((propertyMapping) => propertyMapping.accept_PropertyMappingVisitor(new V1_PropertyMappingBuilder(this.context, aggragetionAwareInstanceSetImplementation, aggragetionAwareInstanceSetImplementation, getAllEnumerationMappings(this.parent), new Map(), [ ...getAllClassMappings(mapping), ...extractClassMappingsFromAggregationAwareClassMappings(mapping), ], undefined, aggragetionAwareInstanceSetImplementation))); classMapping.aggregateSetImplementations.forEach((aggregateSetImpl) => aggregateSetImpl.setImplementation.accept_ClassMappingVisitor(new V1_ClassMappingSecondPassBuilder(this.context, mapping))); return aggragetionAwareInstanceSetImplementation; } visit_RelationFunctionClassMapping(classMapping) { assertNonEmptyString(classMapping.class, `Relation function class mapping 'class' field is missing or empty`); const relationFunctionInstanceSetImplementation = guaranteeType(getOwnClassMappingById(this.parent, V1_getInferredClassMappingId(this.context.resolveClass(classMapping.class).value, classMapping).value), RelationFunctionInstanceSetImplementation); relationFunctionInstanceSetImplementation.relationFunction = guaranteeNonNullable(this.context.graph.functions.find((fn) => generateFunctionPrettyName(fn, { fullPath: true, spacing: false, notIncludeParamName: true, }).replaceAll(/\s/gu, '') === classMapping.relationFunction.path.replaceAll(/\s/gu, ''))); relationFunctionInstanceSetImplementation.propertyMappings = classMapping.propertyMappings.map((propertyMapping) => propertyMapping.accept_PropertyMappingVisitor(new V1_PropertyMappingBuilder(this.context, relationFunctionInstanceSetImplementation, relationFunctionInstanceSetImplementation, getAllEnumerationMappings(this.parent)))); return relationFunctionInstanceSetImplementation; } } //# sourceMappingURL=V1_ClassMappingSecondPassBuilder.js.map