@sap/cds-compiler 2.11.4 → 2.13.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +159 -1
- package/bin/cds_update_identifiers.js +7 -7
- package/bin/cdsc.js +22 -23
- package/bin/cdsse.js +2 -2
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +25 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +30 -63
- package/lib/api/options.js +5 -5
- package/lib/api/validate.js +0 -5
- package/lib/backends.js +15 -23
- package/lib/base/dictionaries.js +0 -8
- package/lib/base/error.js +26 -0
- package/lib/base/keywords.js +7 -17
- package/lib/base/location.js +9 -4
- package/lib/base/message-registry.js +52 -2
- package/lib/base/messages.js +16 -26
- package/lib/base/model.js +2 -62
- package/lib/base/optionProcessorHelper.js +246 -183
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +94 -0
- package/lib/checks/types.js +1 -1
- package/lib/checks/validator.js +12 -7
- package/lib/compiler/assert-consistency.js +10 -6
- package/lib/compiler/base.js +0 -1
- package/lib/compiler/builtins.js +8 -6
- package/lib/compiler/checks.js +46 -12
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1103 -0
- package/lib/compiler/extend.js +983 -0
- package/lib/compiler/finalize-parse-cdl.js +231 -0
- package/lib/compiler/index.js +33 -14
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1226 -0
- package/lib/compiler/propagator.js +113 -47
- package/lib/compiler/resolve.js +1433 -0
- package/lib/compiler/shared.js +76 -38
- package/lib/compiler/tweak-assocs.js +529 -0
- package/lib/compiler/utils.js +204 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +38 -25
- package/lib/edm/annotations/preprocessAnnotations.js +3 -3
- package/lib/edm/csn2edm.js +10 -9
- package/lib/edm/edm.js +19 -20
- package/lib/edm/edmPreprocessor.js +166 -95
- package/lib/edm/edmUtils.js +127 -34
- package/lib/gen/Dictionary.json +92 -43
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +11 -1
- package/lib/gen/language.tokens +86 -82
- package/lib/gen/languageLexer.interp +18 -1
- package/lib/gen/languageLexer.js +925 -847
- package/lib/gen/languageLexer.tokens +78 -74
- package/lib/gen/languageParser.js +5434 -4298
- package/lib/json/from-csn.js +59 -17
- package/lib/json/to-csn.js +143 -71
- package/lib/language/antlrParser.js +3 -3
- package/lib/language/docCommentParser.js +3 -3
- package/lib/language/genericAntlrParser.js +144 -54
- package/lib/language/language.g4 +424 -203
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +472 -61
- package/lib/main.js +38 -11
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +321 -204
- package/lib/model/csnUtils.js +224 -263
- package/lib/model/enrichCsn.js +97 -40
- package/lib/model/revealInternalProperties.js +27 -6
- package/lib/model/sortViews.js +2 -1
- package/lib/modelCompare/compare.js +17 -12
- package/lib/optionProcessor.js +7 -6
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +36 -33
- package/lib/render/toCdl.js +174 -275
- package/lib/render/toHdbcds.js +201 -115
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +149 -75
- package/lib/render/utils/common.js +22 -8
- package/lib/render/utils/sql.js +10 -7
- package/lib/render/utils/stringEscapes.js +111 -0
- package/lib/sql-identifier.js +1 -1
- package/lib/transform/.eslintrc.json +5 -0
- package/lib/transform/braceExpression.js +4 -2
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/db/applyTransformations.js +35 -12
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +187 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +61 -56
- package/lib/transform/db/expansion.js +50 -29
- package/lib/transform/db/flattening.js +552 -105
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +94 -28
- package/lib/transform/db/views.js +5 -4
- package/lib/transform/draft/.eslintrc.json +38 -0
- package/lib/transform/{db/draft.js → draft/db.js} +9 -7
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +94 -801
- package/lib/transform/forOdataNew.js +22 -175
- package/lib/transform/localized.js +36 -32
- package/lib/transform/odata/generateForeignKeyElements.js +3 -3
- package/lib/transform/odata/referenceFlattener.js +95 -89
- package/lib/transform/odata/structureFlattener.js +1 -1
- package/lib/transform/odata/toFinalBaseType.js +86 -12
- package/lib/transform/odata/typesExposure.js +5 -5
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +47 -33
- package/lib/transform/translateAssocsToJoins.js +10 -27
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +170 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/file.js +2 -1
- package/lib/utils/objectUtils.js +30 -0
- package/lib/utils/timetrace.js +8 -2
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/lib/compiler/definer.js +0 -2340
- package/lib/compiler/resolver.js +0 -2988
- package/lib/transform/universalCsnEnricher.js +0 -67
|
@@ -7,7 +7,7 @@ const { getUtils,
|
|
|
7
7
|
cloneCsn,
|
|
8
8
|
forEachDefinition,
|
|
9
9
|
forEachMemberRecursively,
|
|
10
|
-
|
|
10
|
+
applyTransformationsOnNonDictionary,
|
|
11
11
|
getArtifactDatabaseNameOf,
|
|
12
12
|
getElementDatabaseNameOf,
|
|
13
13
|
isAspect,
|
|
@@ -16,7 +16,7 @@ const { getUtils,
|
|
|
16
16
|
} = require('../model/csnUtils');
|
|
17
17
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
18
18
|
const validate = require('../checks/validator');
|
|
19
|
-
const { isArtifactInSomeService,
|
|
19
|
+
const { isArtifactInSomeService, isLocalizedArtifactInService } = require('./odata/utils');
|
|
20
20
|
const ReferenceFlattener = require('./odata/referenceFlattener');
|
|
21
21
|
const { flattenCSN } = require('./odata/structureFlattener');
|
|
22
22
|
const generateForeignKeys = require('./odata/generateForeignKeyElements');
|
|
@@ -24,7 +24,8 @@ const expandStructKeysInAssociations = require('./odata/expandStructKeysInAssoci
|
|
|
24
24
|
const expandToFinalBaseType = require('./odata/toFinalBaseType');
|
|
25
25
|
const { timetrace } = require('../utils/timetrace');
|
|
26
26
|
const { attachPath } = require('./odata/attachPath');
|
|
27
|
-
const enrichUniversalCsn = require('./universalCsnEnricher');
|
|
27
|
+
const enrichUniversalCsn = require('./universalCsn/universalCsnEnricher');
|
|
28
|
+
const generateDrafts = require('./draft/odata');
|
|
28
29
|
|
|
29
30
|
const { addLocalizationViews } = require('./localized');
|
|
30
31
|
|
|
@@ -75,7 +76,7 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
75
76
|
// copy the model as we don't want to change the input model
|
|
76
77
|
let csn = cloneCsn(inputModel, options);
|
|
77
78
|
|
|
78
|
-
const { error, warning, info, throwWithError } = makeMessageFunction(csn, options, 'for.odata');
|
|
79
|
+
const { message, error, warning, info, throwWithError } = makeMessageFunction(csn, options, 'for.odata');
|
|
79
80
|
throwWithError();
|
|
80
81
|
|
|
81
82
|
// the new transformer works only with new CSN
|
|
@@ -84,22 +85,16 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
84
85
|
const transformers = transformUtils.getTransformers(csn, options, '_');
|
|
85
86
|
const {
|
|
86
87
|
addDefaultTypeFacets,
|
|
87
|
-
createForeignKeyElement,
|
|
88
|
-
createAndAddDraftAdminDataProjection, createScalarElement,
|
|
89
|
-
createAssociationElement, createAssociationPathComparison,
|
|
90
|
-
addElement, createAction, assignAction,
|
|
91
88
|
extractValidFromToKeyElement,
|
|
92
89
|
checkAssignment, checkMultipleAssignments,
|
|
93
|
-
recurseElements, setAnnotation,
|
|
90
|
+
recurseElements, setAnnotation, renameAnnotation,
|
|
94
91
|
expandStructsInExpression
|
|
95
92
|
} = transformers;
|
|
96
93
|
|
|
97
94
|
const csnUtils = getUtils(csn);
|
|
98
95
|
const {
|
|
99
96
|
getCsnDef,
|
|
100
|
-
getFinalType,
|
|
101
97
|
getServiceName,
|
|
102
|
-
hasAnnotationValue,
|
|
103
98
|
isAssocOrComposition,
|
|
104
99
|
isAssociation,
|
|
105
100
|
isStructured,
|
|
@@ -133,7 +128,7 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
133
128
|
addLocalizationViews(csn, options, acceptLocalizedView);
|
|
134
129
|
|
|
135
130
|
validate.forOdata(csn, {
|
|
136
|
-
error, warning, info, inspectRef, effectiveType, artifactRef, csn, options, csnUtils, services, getFinalBaseType, isAspect, isExternalServiceMember
|
|
131
|
+
message, error, warning, info, inspectRef, effectiveType, artifactRef, csn, options, csnUtils, services, getFinalBaseType, isAspect, isExternalServiceMember
|
|
137
132
|
});
|
|
138
133
|
|
|
139
134
|
|
|
@@ -191,10 +186,10 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
191
186
|
generateForeignKeys(csn, options, referenceFlattener, csnUtils, error, isExternalServiceMember);
|
|
192
187
|
|
|
193
188
|
// Apply default type facets as set by options
|
|
194
|
-
// Flatten on-conditions in unmanaged associations
|
|
189
|
+
// Flatten on-conditions in unmanaged associations
|
|
195
190
|
/* FIXME (HJB): Is this comment still correct? processOnCond only strips $self
|
|
196
191
|
We should not remove $self prefixes in structured OData to not
|
|
197
|
-
interfer with path resolution
|
|
192
|
+
interfer with path resolution
|
|
198
193
|
*/
|
|
199
194
|
// This must be done before all the draft logic as all
|
|
200
195
|
// composition targets are annotated with @odata.draft.enabled in this step
|
|
@@ -209,19 +204,7 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
209
204
|
// - structured types must not contain associations for OData V2
|
|
210
205
|
// - Element must not be an 'array of' for OData V2 TODO: move to the validator
|
|
211
206
|
// - Perform checks for exposed non-abstract entities and views - check media type and key-ness
|
|
212
|
-
|
|
213
|
-
forEachDefinition(csn, (def, defName) => {
|
|
214
|
-
if (def.kind === 'entity' || def.kind === 'view') {
|
|
215
|
-
// Generate artificial draft fields if requested
|
|
216
|
-
if (def['@odata.draft.enabled']) {
|
|
217
|
-
// Ignore if not part of a service
|
|
218
|
-
if (isArtifactInSomeService(defName, services)) {
|
|
219
|
-
generateDraftForOdata(def, defName, def, visitedArtifacts);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
visitedArtifacts[defName] = true;
|
|
224
|
-
}, { skipArtifact: isExternalServiceMember });
|
|
207
|
+
generateDrafts(csn, options, services)
|
|
225
208
|
|
|
226
209
|
// Deal with all kind of annotations manipulations here
|
|
227
210
|
forEachDefinition(csn, (def, defName) => {
|
|
@@ -303,8 +286,9 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
303
286
|
// If @Core.Computed is explicitly set, don't overwrite it!
|
|
304
287
|
if (node['@Core.Computed'] !== undefined) return;
|
|
305
288
|
|
|
306
|
-
// For @odata.on.insert/update,
|
|
307
|
-
|
|
289
|
+
// For @odata.on.insert/update, also add @Core.Computed
|
|
290
|
+
// @odata.on is deprecated, use @cds.on {update|insert} instead
|
|
291
|
+
if(['@odata.on.insert', '@odata.on.update', '@cds.on.insert', '@cds.on.update'].some(a => node[a]))
|
|
308
292
|
node['@Core.Computed'] = true;
|
|
309
293
|
}
|
|
310
294
|
|
|
@@ -341,7 +325,7 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
341
325
|
// but '@Core.Immutable' for everything else.
|
|
342
326
|
if (!(node['@readonly'] && node['@insertonly'])) {
|
|
343
327
|
if (name === '@readonly' && node[name] !== null) {
|
|
344
|
-
if (node.kind === 'entity'
|
|
328
|
+
if (node.kind === 'entity') {
|
|
345
329
|
setAnnotation(node, '@Capabilities.DeleteRestrictions.Deletable', false);
|
|
346
330
|
setAnnotation(node, '@Capabilities.InsertRestrictions.Insertable', false);
|
|
347
331
|
setAnnotation(node, '@Capabilities.UpdateRestrictions.Updatable', false);
|
|
@@ -351,7 +335,7 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
351
335
|
}
|
|
352
336
|
// @insertonly is effective on entities/queries only
|
|
353
337
|
else if (name === '@insertonly' && node[name] !== null) {
|
|
354
|
-
if (node.kind === 'entity'
|
|
338
|
+
if (node.kind === 'entity') {
|
|
355
339
|
setAnnotation(node, '@Capabilities.DeleteRestrictions.Deletable', false);
|
|
356
340
|
setAnnotation(node, '@Capabilities.ReadRestrictions.Readable', false);
|
|
357
341
|
setAnnotation(node, '@Capabilities.UpdateRestrictions.Updatable', false);
|
|
@@ -422,151 +406,14 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
422
406
|
// removes leading $self in on-conditions's references
|
|
423
407
|
function removeLeadingDollarSelfInOnCondition(assoc) {
|
|
424
408
|
if (!assoc.on) return; // nothing to do
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// Generate all that is required in ODATA for draft enablement of 'artifact' into the artifact,
|
|
434
|
-
// into its transitively reachable composition targets, and into the model.
|
|
435
|
-
// 'rootArtifact' is the root artifact where composition traversal started.
|
|
436
|
-
|
|
437
|
-
// Constraints
|
|
438
|
-
// Draft Root: Exactly one PK of type UUID
|
|
439
|
-
// Draft Node: One PK of type UUID + 0..1 PK of another type
|
|
440
|
-
// Draft Node: Must not be reachable from multiple draft roots
|
|
441
|
-
function generateDraftForOdata(artifact, artifactName, rootArtifact, visitedArtifacts) {
|
|
442
|
-
// Sanity check
|
|
443
|
-
// @ts-ignore
|
|
444
|
-
if (!isArtifactInSomeService(artifactName, services)) {
|
|
445
|
-
throw new Error('Expecting artifact to be part of a service: ' + JSON.stringify(artifact));
|
|
446
|
-
}
|
|
447
|
-
// Nothing to do if already draft-enabled (composition traversal may have circles)
|
|
448
|
-
if ((artifact['@Common.DraftRoot.PreparationAction'] || artifact['@Common.DraftNode.PreparationAction'])
|
|
449
|
-
&& artifact.actions && artifact.actions.draftPrepare) {
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
// Generate the DraftAdministrativeData projection into the service, unless there is already one
|
|
454
|
-
// @ts-ignore
|
|
455
|
-
let draftAdminDataProjectionName = `${getServiceOfArtifact(artifactName, services)}.DraftAdministrativeData`;
|
|
456
|
-
let draftAdminDataProjection = csn.definitions[draftAdminDataProjectionName];
|
|
457
|
-
if (!draftAdminDataProjection) {
|
|
458
|
-
// @ts-ignore
|
|
459
|
-
draftAdminDataProjection = createAndAddDraftAdminDataProjection(getServiceOfArtifact(artifactName, services));
|
|
460
|
-
}
|
|
461
|
-
// Report an error if it is not an entity or not what we expect
|
|
462
|
-
if (draftAdminDataProjection.kind !== 'entity' || !draftAdminDataProjection.elements['DraftUUID']) {
|
|
463
|
-
error(null, ['definitions', draftAdminDataProjectionName], { name: draftAdminDataProjectionName },
|
|
464
|
-
`Generated entity $(NAME) conflicts with existing artifact`);
|
|
465
|
-
}
|
|
466
|
-
// Generate the annotations describing the draft actions (only draft roots can be activated/edited)
|
|
467
|
-
if (artifact == rootArtifact) {
|
|
468
|
-
resetAnnotation(artifact, '@Common.DraftRoot.ActivationAction', 'draftActivate', info, ['definitions', draftAdminDataProjectionName]);
|
|
469
|
-
resetAnnotation(artifact, '@Common.DraftRoot.EditAction', 'draftEdit', info, ['definitions', draftAdminDataProjectionName]);
|
|
470
|
-
resetAnnotation(artifact, '@Common.DraftRoot.PreparationAction', 'draftPrepare', info, ['definitions', draftAdminDataProjectionName]);
|
|
471
|
-
} else {
|
|
472
|
-
resetAnnotation(artifact, '@Common.DraftNode.PreparationAction', 'draftPrepare', info, ['definitions', draftAdminDataProjectionName]);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
artifact.elements && Object.values(artifact.elements).forEach( elem => {
|
|
476
|
-
// Make all non-key elements nullable
|
|
477
|
-
if (elem.notNull && elem.key !== true) {
|
|
478
|
-
delete elem.notNull;
|
|
479
|
-
}
|
|
480
|
-
});
|
|
481
|
-
// Generate the additional elements into the draft-enabled artifact
|
|
482
|
-
|
|
483
|
-
// key IsActiveEntity : Boolean default true
|
|
484
|
-
let isActiveEntity = createScalarElement('IsActiveEntity', 'cds.Boolean', true, true, false);
|
|
485
|
-
isActiveEntity.IsActiveEntity['@UI.Hidden'] = true;
|
|
486
|
-
addElement(isActiveEntity, artifact, artifactName);
|
|
487
|
-
|
|
488
|
-
// HasActiveEntity : Boolean default false
|
|
489
|
-
let hasActiveEntity = createScalarElement('HasActiveEntity', 'cds.Boolean', false, false, true);
|
|
490
|
-
hasActiveEntity.HasActiveEntity['@UI.Hidden'] = true;
|
|
491
|
-
addElement(hasActiveEntity, artifact, artifactName);
|
|
492
|
-
|
|
493
|
-
// HasDraftEntity : Boolean default false;
|
|
494
|
-
let hasDraftEntity = createScalarElement('HasDraftEntity', 'cds.Boolean', false, false, true);
|
|
495
|
-
hasDraftEntity.HasDraftEntity['@UI.Hidden'] = true;
|
|
496
|
-
addElement(hasDraftEntity, artifact, artifactName);
|
|
497
|
-
|
|
498
|
-
// @odata.contained: true
|
|
499
|
-
// DraftAdministrativeData : Association to one DraftAdministrativeData;
|
|
500
|
-
let draftAdministrativeData = createAssociationElement('DraftAdministrativeData', draftAdminDataProjectionName, true);
|
|
501
|
-
draftAdministrativeData.DraftAdministrativeData.cardinality = { max: 1, };
|
|
502
|
-
draftAdministrativeData.DraftAdministrativeData['@odata.contained'] = true;
|
|
503
|
-
draftAdministrativeData.DraftAdministrativeData['@UI.Hidden'] = true;
|
|
504
|
-
addElement(draftAdministrativeData, artifact, artifactName);
|
|
505
|
-
|
|
506
|
-
// Note that we need to do the ODATA transformation steps for managed associations
|
|
507
|
-
// (foreign key field generation, generatedFieldName) by hand, because the corresponding
|
|
508
|
-
// transformation steps have already been done on all artifacts when we come here)
|
|
509
|
-
let uuidDraftKey = draftAdministrativeData.DraftAdministrativeData.keys.filter(key => key.ref && key.ref.length === 1 && key.ref[0] === 'DraftUUID');
|
|
510
|
-
if (uuidDraftKey && uuidDraftKey[0]) {
|
|
511
|
-
uuidDraftKey = uuidDraftKey[0]; // filter returns an array, but it has only one element
|
|
512
|
-
let path = ['definitions', artifactName, 'elements', 'DraftAdministrativeData', 'keys', 0];
|
|
513
|
-
createForeignKeyElement(draftAdministrativeData.DraftAdministrativeData, 'DraftAdministrativeData', uuidDraftKey, artifact, artifactName, path);
|
|
514
|
-
}
|
|
515
|
-
// SiblingEntity : Association to one <artifact> on (... IsActiveEntity unequal, all other key fields equal ...)
|
|
516
|
-
let siblingEntity = createAssociationElement('SiblingEntity', artifactName, false);
|
|
517
|
-
siblingEntity.SiblingEntity.cardinality = { max: 1 };
|
|
518
|
-
addElement(siblingEntity, artifact, artifactName);
|
|
519
|
-
// ... on SiblingEntity.IsActiveEntity != IsActiveEntity ...
|
|
520
|
-
siblingEntity.SiblingEntity.on = createAssociationPathComparison('SiblingEntity', 'IsActiveEntity', '!=', 'IsActiveEntity');
|
|
521
|
-
|
|
522
|
-
// Iterate elements
|
|
523
|
-
artifact.elements && Object.entries(artifact.elements).forEach( ([elemName, elem]) => {
|
|
524
|
-
if (elemName !== 'IsActiveEntity' && elem.key) {
|
|
525
|
-
// Amend the ON-condition above:
|
|
526
|
-
// ... and SiblingEntity.<keyfield> = <keyfield> ... (for all key fields except 'IsActiveEntity')
|
|
527
|
-
let cond = createAssociationPathComparison('SiblingEntity', elemName, '=', elemName);
|
|
528
|
-
cond.push('and');
|
|
529
|
-
cond.push(...siblingEntity.SiblingEntity.on);
|
|
530
|
-
siblingEntity.SiblingEntity.on = cond;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// Draft-enable the targets of composition elements (draft nodes), too
|
|
534
|
-
// TODO rewrite
|
|
535
|
-
if (elem.target && elem.type && getFinalType(elem.type) === 'cds.Composition') {
|
|
536
|
-
let draftNode = csn.definitions[elem.target];
|
|
537
|
-
|
|
538
|
-
// Ignore if that is our own draft root
|
|
539
|
-
if (draftNode != rootArtifact) {
|
|
540
|
-
// Report error when the draft node has @odata.draft.enabled itself
|
|
541
|
-
if (hasAnnotationValue(draftNode, '@odata.draft.enabled', true)) {
|
|
542
|
-
error(null, ['definitions', artifactName, 'elements', elemName], 'Composition in draft-enabled entity can\'t lead to another entity with “@odata.draft.enabled”');
|
|
543
|
-
}
|
|
544
|
-
// Ignore composition if not part of a service or explicitly draft disabled
|
|
545
|
-
else if (!getServiceName(elem.target) || hasAnnotationValue(draftNode, '@odata.draft.enabled', false)) {
|
|
546
|
-
return;
|
|
547
|
-
}
|
|
548
|
-
else {
|
|
549
|
-
// Generate draft stuff into the target
|
|
550
|
-
generateDraftForOdata(draftNode, elem.target, rootArtifact, visitedArtifacts);
|
|
551
|
-
}
|
|
409
|
+
// TODO: Shouldn't this only run on the on-condition and not the whole assoc-node?
|
|
410
|
+
applyTransformationsOnNonDictionary({ assoc }, 'assoc', {
|
|
411
|
+
ref: (node, prop, ref) => {
|
|
412
|
+
// remove leading $self when at the begining of a ref
|
|
413
|
+
if (ref.length > 1 && ref[0] === '$self')
|
|
414
|
+
node.ref.splice(0, 1);
|
|
552
415
|
}
|
|
553
|
-
}
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
// Generate the actions into the draft-enabled artifact (only draft roots can be activated/edited)
|
|
557
|
-
|
|
558
|
-
// action draftPrepare (SideEffectsQualifier: String) return <artifact>;
|
|
559
|
-
let draftPrepare = createAction('draftPrepare', artifactName, 'SideEffectsQualifier', 'cds.String');
|
|
560
|
-
assignAction(draftPrepare, artifact);
|
|
561
|
-
|
|
562
|
-
if (artifact == rootArtifact) {
|
|
563
|
-
// action draftActivate() return <artifact>;
|
|
564
|
-
let draftActivate = createAction('draftActivate', artifactName);
|
|
565
|
-
assignAction(draftActivate, artifact);
|
|
566
|
-
|
|
567
|
-
// action draftEdit (PreserveChanges: Boolean) return <artifact>;
|
|
568
|
-
let draftEdit = createAction('draftEdit', artifactName, 'PreserveChanges', 'cds.Boolean');
|
|
569
|
-
assignAction(draftEdit, artifact);
|
|
416
|
+
});
|
|
570
417
|
}
|
|
571
418
|
}
|
|
572
419
|
|
|
@@ -5,14 +5,12 @@ const { setProp } = require('../base/model');
|
|
|
5
5
|
const { hasErrors } = require('../base/messages');
|
|
6
6
|
const { cloneCsnDictionary } = require('../model/csnUtils');
|
|
7
7
|
const { cleanSymbols } = require('../base/cleanSymbols.js');
|
|
8
|
-
const { rejectManagedAssociationsAndStructuresForHdbcsNames } = require('../checks/selectItems');
|
|
9
8
|
const {
|
|
10
9
|
cloneCsn,
|
|
11
10
|
forEachDefinition,
|
|
12
11
|
forEachGeneric,
|
|
13
12
|
forAllQueries,
|
|
14
13
|
sortCsnDefinitionsForTests,
|
|
15
|
-
getUtils,
|
|
16
14
|
} = require('../model/csnUtils');
|
|
17
15
|
|
|
18
16
|
/**
|
|
@@ -50,7 +48,7 @@ const _targetFor = Symbol('_targetFor');
|
|
|
50
48
|
* We have three kinds of localized convenience views:
|
|
51
49
|
*
|
|
52
50
|
* 1. "direct ones" using coalesce() for the table entities with localized
|
|
53
|
-
* elements: as projection on the original (created in
|
|
51
|
+
* elements: as projection on the original (created in extend.js)
|
|
54
52
|
* 2. for table entities with associations to entities which have a localized
|
|
55
53
|
* convenience views or redirections thereon: as projection on the original
|
|
56
54
|
* 3. for view entities with associations to entities which have a localized
|
|
@@ -76,27 +74,17 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
|
|
|
76
74
|
if (hasErrors(options.messages))
|
|
77
75
|
return csn;
|
|
78
76
|
|
|
79
|
-
|
|
77
|
+
const messageFunctions = makeMessageFunction(csn, options);
|
|
78
|
+
if (hasExistingLocalizationViews(csn, options, messageFunctions))
|
|
80
79
|
return csn;
|
|
81
80
|
|
|
82
|
-
const { info, error } = makeMessageFunction(csn, options);
|
|
83
|
-
|
|
84
81
|
const noCoalesce = (options.localizedLanguageFallback === 'none' ||
|
|
85
82
|
options.localizedWithoutCoalesce);
|
|
86
83
|
|
|
87
84
|
createDirectConvenienceViews(); // 1
|
|
88
85
|
createTransitiveConvenienceViews(); // 2 + 3
|
|
89
86
|
|
|
90
|
-
forEachDefinition(csn, (definition,
|
|
91
|
-
cleanSymbols(definition, _hasLocalizedView, _isViewForEntity, _isViewForView, _targetFor)
|
|
92
|
-
if(definition.query) {
|
|
93
|
-
// reject managed association and structure publishing for to-hdbcds.hdbcds
|
|
94
|
-
const that = { csnUtils: getUtils(csn), options, error };
|
|
95
|
-
rejectManagedAssociationsAndStructuresForHdbcsNames.call(that, definition, path)
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
|
|
87
|
+
forEachDefinition(csn, definition => cleanSymbols(definition, _hasLocalizedView, _isViewForEntity, _isViewForView, _targetFor));
|
|
100
88
|
|
|
101
89
|
sortCsnDefinitionsForTests(csn, options);
|
|
102
90
|
return csn;
|
|
@@ -113,7 +101,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
|
|
|
113
101
|
return;
|
|
114
102
|
|
|
115
103
|
if (isInLocalizedNamespace(artName))
|
|
116
|
-
// We already issued a warning for it in
|
|
104
|
+
// We already issued a warning for it in hasExistingLocalizationViews()
|
|
117
105
|
return;
|
|
118
106
|
|
|
119
107
|
const localized = getLocalizedTextElements( artName );
|
|
@@ -138,13 +126,13 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
|
|
|
138
126
|
|
|
139
127
|
if (csn.definitions[viewName]) {
|
|
140
128
|
// Already exists, skip creation.
|
|
141
|
-
info( null, artPath, null, 'Convenience view can\'t be created due to conflicting names' );
|
|
129
|
+
messageFunctions.info( null, artPath, null, 'Convenience view can\'t be created due to conflicting names' );
|
|
142
130
|
return;
|
|
143
131
|
}
|
|
144
132
|
|
|
145
133
|
art[_hasLocalizedView] = viewName;
|
|
146
134
|
|
|
147
|
-
if(acceptLocalizedView && !acceptLocalizedView(viewName, artName))
|
|
135
|
+
if (acceptLocalizedView && !acceptLocalizedView(viewName, artName))
|
|
148
136
|
return;
|
|
149
137
|
|
|
150
138
|
let view;
|
|
@@ -350,7 +338,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
|
|
|
350
338
|
if (elem.key || elem.$key || elem.localized)
|
|
351
339
|
textElements.push( elemName );
|
|
352
340
|
|
|
353
|
-
// TODO: Already warned about in
|
|
341
|
+
// TODO: Already warned about in extend.js
|
|
354
342
|
// if (elem.key && isLocalized)
|
|
355
343
|
// warning( 'localized-key', path, {}, 'Keyword "localized" is ignored for primary keys' );
|
|
356
344
|
}, artPath);
|
|
@@ -360,7 +348,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
|
|
|
360
348
|
return null;
|
|
361
349
|
|
|
362
350
|
if (!isEntityPreprocessed( art )) {
|
|
363
|
-
info( null, artPath, { name: artName },
|
|
351
|
+
messageFunctions.info( null, artPath, { name: artName },
|
|
364
352
|
'Skipped creation of convenience view for $(NAME) because the artifact is missing localization elements' );
|
|
365
353
|
return null;
|
|
366
354
|
}
|
|
@@ -369,13 +357,13 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
|
|
|
369
357
|
const textsEntity = csn.definitions[textsName];
|
|
370
358
|
|
|
371
359
|
if (!textsEntity) {
|
|
372
|
-
info( null, artPath, { name: artName },
|
|
360
|
+
messageFunctions.info( null, artPath, { name: artName },
|
|
373
361
|
'Skipped creation of convenience view for $(NAME) because its texts entity could not be found' );
|
|
374
362
|
return null;
|
|
375
363
|
}
|
|
376
364
|
|
|
377
365
|
if (!isValidTextsEntity( textsEntity )) {
|
|
378
|
-
info( null, [ 'definitions', textsName ], { name: artName },
|
|
366
|
+
messageFunctions.info( null, [ 'definitions', textsName ], { name: artName },
|
|
379
367
|
'Skipped creation of convenience view for $(NAME) because its texts entity does not appear to be valid' );
|
|
380
368
|
return null;
|
|
381
369
|
}
|
|
@@ -696,18 +684,32 @@ function copyPersistenceAnnotations(target, source) {
|
|
|
696
684
|
*
|
|
697
685
|
* @param {CSN.Model} csn
|
|
698
686
|
* @param {CSN.Options} options
|
|
687
|
+
* @param {object} messageFunctions
|
|
699
688
|
*/
|
|
700
|
-
function hasExistingLocalizationViews(csn, options) {
|
|
689
|
+
function hasExistingLocalizationViews(csn, options, messageFunctions) {
|
|
701
690
|
if (!csn || !csn.definitions)
|
|
702
691
|
return false;
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
692
|
+
|
|
693
|
+
let hasExistingViews = false;
|
|
694
|
+
let hasNonViews = false;
|
|
695
|
+
|
|
696
|
+
for (const name in csn.definitions) {
|
|
697
|
+
const art = csn.definitions[name];
|
|
698
|
+
if (isInLocalizedNamespace(name) || name === 'localized') {
|
|
699
|
+
if (!art.query && !art.projection) {
|
|
700
|
+
if (!name.endsWith('.texts')) {
|
|
701
|
+
hasNonViews = true;
|
|
702
|
+
messageFunctions.error('reserved-namespace-localized', ['definitions', name], {},
|
|
703
|
+
'The namespace "localized" is reserved for localization views');
|
|
704
|
+
}
|
|
705
|
+
} else if (!hasExistingViews) {
|
|
706
|
+
hasExistingViews = true;
|
|
707
|
+
messageFunctions.info( null, [ 'definitions', name ], {},
|
|
708
|
+
'Input CSN already contains localization views, no further ones will be created' );
|
|
709
|
+
}
|
|
710
|
+
}
|
|
709
711
|
}
|
|
710
|
-
return
|
|
712
|
+
return hasExistingViews || hasNonViews;
|
|
711
713
|
}
|
|
712
714
|
|
|
713
715
|
/**
|
|
@@ -739,9 +741,10 @@ function isEntityPreprocessed(entity) {
|
|
|
739
741
|
|
|
740
742
|
/**
|
|
741
743
|
* @param {string} name
|
|
744
|
+
* @returns {boolean}
|
|
742
745
|
*/
|
|
743
746
|
function isInLocalizedNamespace(name) {
|
|
744
|
-
return name.startsWith('localized.');
|
|
747
|
+
return name === 'localized' || name.startsWith('localized.');
|
|
745
748
|
}
|
|
746
749
|
|
|
747
750
|
/**
|
|
@@ -749,6 +752,7 @@ function isInLocalizedNamespace(name) {
|
|
|
749
752
|
*
|
|
750
753
|
* @param {CSN.Model} csn
|
|
751
754
|
* @param {string} artifactName
|
|
755
|
+
* @returns {boolean}
|
|
752
756
|
*/
|
|
753
757
|
function hasLocalizedConvenienceView(csn, artifactName) {
|
|
754
758
|
return !isInLocalizedNamespace(artifactName) && !!csn.definitions[`localized.${ artifactName }`];
|
|
@@ -37,7 +37,7 @@ module.exports = function (csn, options, referenceFlattener, csnUtils, error, is
|
|
|
37
37
|
sortedAssociations.forEach(item => {
|
|
38
38
|
const { definitionName, structuralNodeName, elementName, element, parent, path } = item;
|
|
39
39
|
|
|
40
|
-
if (csnUtils.
|
|
40
|
+
if (csnUtils.isManagedAssociation(element) && element.keys) {
|
|
41
41
|
if (flatKeys) // tackling the ref value in assoc.keys
|
|
42
42
|
takeoverForeignKeysOfTargetAssociations(element, path, generatedForeignKeyNamesForPath);
|
|
43
43
|
// TODO: move in separate function
|
|
@@ -201,13 +201,13 @@ module.exports = function (csn, options, referenceFlattener, csnUtils, error, is
|
|
|
201
201
|
// Transfer selected type properties from target key element
|
|
202
202
|
// FIXME: There is currently no other way but to treat the annotation '@odata.Type' as a type property.
|
|
203
203
|
for (let prop of ['type', 'length', 'scale', 'precision', 'srid', 'default', '@odata.Type']) {
|
|
204
|
-
if (fkArtifact[prop]
|
|
204
|
+
if (fkArtifact[prop] !== undefined) {
|
|
205
205
|
foreignKeyElement[prop] = fkArtifact[prop];
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
// If the association is non-fkArtifact resp. key, so should be the foreign key field
|
|
209
209
|
for (let prop of ['notNull', 'key']) {
|
|
210
|
-
if (assoc[prop]
|
|
210
|
+
if (assoc[prop] !== undefined) {
|
|
211
211
|
foreignKeyElement[prop] = assoc[prop];
|
|
212
212
|
}
|
|
213
213
|
}
|