@finos/legend-graph
Version:
Legend graph and graph manager
176 lines • 13.9 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 { 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