UNPKG

@sap/cds-compiler

Version:

CDS (Core Data Services) compiler and backends

135 lines (106 loc) 5.58 kB
'use strict'; const { getUtils, mergeTransformers, applyTransformations, } = require('../../model/csnUtils'); const transformUtils = require('../transformUtils'); const effectiveFlattening = require('./flattening'); const flattening = require('../db/flattening'); const types = require('./types'); // const { addLocalizationViews } = require('../../transform/localized'); const validate = require('../../checks/validator'); const expansion = require('../db/expansion'); const queries = require('./queries'); const associations = require('./associations'); const handleExists = require('../db/assocsToQueries/transformExists'); const misc = require('./misc'); const annotations = require('./annotations'); const { rewriteCalculatedElementsInViews, processCalculatedElementsInEntities } = require('../db/rewriteCalculatedElements'); const { cloneFullCsn } = require('../../model/cloneCsn'); const { featureFlags } = require('../featureFlags'); const getServiceFilterFunction = require('./service'); /** * This is just a PoC for now! * * Transform the given CSN into a so called effective CSN, by * - dissolving structured types * - turning managed into unmanaged associations * @private * @param {CSN.Model} model Input CSN - will not be transformed * @param {CSN.Options} options * @param {object} messageFunctions * @returns {CSN.Model} */ function effectiveCsn( model, options, messageFunctions ) { const csn = cloneFullCsn(model, options); delete csn.namespace; // must not be set for effective CSN delete csn.vocabularies; // must not be set for effective CSN messageFunctions.setModel(csn); const transformerUtils = transformUtils.getTransformers(csn, options, messageFunctions, '_'); const redoProjections = queries.projectionToSELECTAndAddColumns(csn); // re-use from transformers; otherwise we have two caches and tuple expansion // would use a different one than handleExists, leading to unnecessary re-initialization let { csnUtils } = transformerUtils; csnUtils.initAllDefinitions(); // Run validations on CSN - each validator function has access to the message functions and the inspect ref via this const cleanup = validate.forRelationalDB(csn, { ...messageFunctions, csnUtils, ...csnUtils, csn, options, }); if (csn.meta?.[featureFlags]?.$calculatedElements) rewriteCalculatedElementsInViews(csn, options, csnUtils, '_', messageFunctions); // Needs to happen before tuple expansion, so the newly generated WHERE-conditions have it applied handleExists(csn, options, messageFunctions, csnUtils); // Check if structured elements and managed associations are compared in an expression // and expand these structured elements. This tuple expansion allows all other // subsequent procession steps to see plain paths in expressions. // If errors are detected, throwWithAnyError() will return from further processing transformerUtils.expandStructsInExpression({ drillRef: true }); messageFunctions.throwWithAnyError(); // Expand a structured thing in: keys, columns, order by, group by expansion.expandStructureReferences(csn, options, '_', messageFunctions, csnUtils); const resolveTypesInActionsAfterFlattening = types.resolve(csn, csnUtils, transformerUtils, options); // Remove properties attached by validator - they do not "grow" as the model grows. cleanup(); effectiveFlattening.flattenRefs(csn, options, csnUtils, messageFunctions); flattening.flattenElements(csn, options, messageFunctions, '_', { skipDict: { actions: true } }); // ensure getElement works on flattened struct_assoc columns and getFinalTypeInfo refreshes the cache csnUtils = getUtils(csn, 'init-all'); resolveTypesInActionsAfterFlattening(csnUtils); processCalculatedElementsInEntities(csn, options); associations.managedToUnmanaged(csn, options, csnUtils, messageFunctions); associations.transformBacklinks(csn, options, csnUtils, messageFunctions); const transformers = mergeTransformers([ options.remapOdataAnnotations ? annotations.remapODataAnnotations(csn) : {}, misc.removeDefinitionsAndProperties(csn, options), options.deriveAnalyticalAnnotations ? annotations.sealAnnoMagic(csn) : {}, ], null); if (options.addCdsPersistenceName) { applyTransformations(csn, misc.attachPersistenceName(csn, options, csnUtils), [], { skipIgnore: false, skipArtifact: artifact => artifact.kind !== 'entity', skipDict: { actions: true }, skipStandard: { items: true }, }); } const artifactTransformers = []; const collector = { service: null, containedArtifacts: Object.create(null), }; if (options.effectiveServiceName) artifactTransformers.push(getServiceFilterFunction(options.effectiveServiceName, collector)); applyTransformations(csn, transformers, artifactTransformers, { skipIgnore: false, processAnnotations: true }); if (!options.resolveProjections) redoProjections.forEach(fn => fn()); // Remove unapplied extensions/annotations delete csn.extensions; if (options.effectiveServiceName) { if (collector.service) { csn.definitions = collector.containedArtifacts; csn.definitions[options.effectiveServiceName] = collector.service; } else { messageFunctions.warning(null, null, { name: options.effectiveServiceName, option: 'effectiveServiceName' }, 'Could not find a service matching requested effective service $(NAME) (option $(OPTION))'); csn.definitions = Object.create(null); } } messageFunctions.throwWithError(); return csn; } module.exports = { effectiveCsn };