@sap/cds-compiler 2.4.4 → 2.10.2
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 +241 -1
- package/bin/.eslintrc.json +17 -0
- package/bin/cds_update_identifiers.js +8 -7
- package/bin/cdsc.js +180 -132
- package/bin/cdshi.js +18 -11
- package/bin/cdsse.js +38 -32
- package/bin/cdsv2m.js +8 -7
- package/doc/CHANGELOG_BETA.md +36 -1
- package/lib/api/main.js +81 -100
- package/lib/api/options.js +17 -11
- package/lib/api/validate.js +12 -8
- package/lib/backends.js +0 -81
- package/lib/base/keywords.js +32 -2
- package/lib/base/location.js +2 -2
- package/lib/base/message-registry.js +66 -4
- package/lib/base/messages.js +84 -27
- package/lib/base/model.js +2 -61
- package/lib/checks/arrayOfs.js +0 -1
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- package/lib/checks/enricher.js +8 -2
- package/lib/checks/foreignKeys.js +0 -6
- package/lib/checks/managedWithoutKeys.js +17 -0
- package/lib/checks/nonexpandableStructured.js +38 -0
- package/lib/checks/onConditions.js +9 -45
- package/lib/checks/queryNoDbArtifacts.js +27 -9
- package/lib/checks/selectItems.js +25 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +38 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +66 -13
- package/lib/compiler/assert-consistency.js +24 -12
- package/lib/compiler/builtins.js +2 -0
- package/lib/compiler/checks.js +6 -4
- package/lib/compiler/definer.js +101 -39
- package/lib/compiler/index.js +88 -59
- package/lib/compiler/resolver.js +455 -209
- package/lib/compiler/shared.js +57 -33
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +128 -99
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +361 -127
- package/lib/edm/edmUtils.js +103 -33
- package/lib/gen/Dictionary.json +74 -28
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +18 -4
- package/lib/gen/language.tokens +124 -118
- package/lib/gen/languageLexer.interp +13 -1
- package/lib/gen/languageLexer.js +870 -839
- package/lib/gen/languageLexer.tokens +116 -111
- package/lib/gen/languageParser.js +5894 -5614
- package/lib/json/from-csn.js +152 -67
- package/lib/json/to-csn.js +334 -135
- package/lib/language/antlrParser.js +4 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +24 -14
- package/lib/language/language.g4 +188 -128
- package/lib/main.d.ts +435 -0
- package/lib/main.js +31 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +463 -187
- package/lib/model/csnUtils.js +280 -136
- package/lib/model/enrichCsn.js +75 -4
- package/lib/model/revealInternalProperties.js +2 -1
- package/lib/modelCompare/compare.js +70 -25
- package/lib/optionProcessor.js +13 -10
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +8 -5
- package/lib/render/toCdl.js +123 -40
- package/lib/render/toHdbcds.js +156 -65
- package/lib/render/toSql.js +87 -11
- package/lib/render/utils/common.js +55 -9
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/{sql → db}/.eslintrc.json +0 -0
- package/lib/transform/{sql → db}/assertUnique.js +7 -8
- package/lib/transform/{sql → db}/constraints.js +35 -20
- package/lib/transform/db/draft.js +353 -0
- package/lib/transform/db/expansion.js +582 -0
- package/lib/transform/db/flattening.js +325 -0
- package/lib/transform/{sql → db}/groupByOrderBy.js +8 -16
- package/lib/transform/{sql → db}/helpers.js +0 -0
- package/lib/transform/{sql → db}/transformExists.js +256 -60
- package/lib/transform/forHanaNew.js +216 -765
- package/lib/transform/forOdataNew.js +60 -56
- package/lib/transform/localized.js +48 -26
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/expandStructKeysInAssociations.js +2 -2
- package/lib/transform/odata/generateForeignKeyElements.js +13 -12
- package/lib/transform/odata/referenceFlattener.js +60 -36
- package/lib/transform/odata/sortByAssociationDependency.js +4 -4
- package/lib/transform/odata/structuralPath.js +76 -0
- package/lib/transform/odata/structureFlattener.js +21 -22
- package/lib/transform/odata/toFinalBaseType.js +5 -5
- package/lib/transform/odata/typesExposure.js +27 -17
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +141 -77
- package/lib/transform/translateAssocsToJoins.js +17 -14
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +0 -11
- package/lib/utils/moduleResolve.js +6 -8
- package/lib/utils/timetrace.js +6 -1
- package/package.json +2 -1
- package/lib/base/deepCopy.js +0 -66
- package/lib/json/walker.js +0 -26
- package/lib/utils/string.js +0 -17
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
hasAnnotationValue, getUtils, getServiceNames, forEachDefinition,
|
|
5
|
+
getResultingName, forEachMemberRecursively,
|
|
6
|
+
} = require('../../model/csnUtils');
|
|
7
|
+
const { setProp, isDeprecatedEnabled } = require('../../base/model');
|
|
8
|
+
const { getTransformers } = require('../transformUtilsNew');
|
|
9
|
+
const draftAnnotation = '@odata.draft.enabled';
|
|
10
|
+
const booleanBuiltin = 'cds.Boolean';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Generate all the different entities/views/fields required for DRAFT.
|
|
14
|
+
*
|
|
15
|
+
* @param {CSN.Model} csn
|
|
16
|
+
* @param {CSN.Options} options
|
|
17
|
+
* @param {string} pathDelimiter
|
|
18
|
+
* @param {object} messageFunctions
|
|
19
|
+
*/
|
|
20
|
+
function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
|
|
21
|
+
const draftSuffix = isDeprecatedEnabled(options, 'generatedEntityNameWithUnderscore') ? '_drafts' : '.drafts';
|
|
22
|
+
// All services of the model - needed for drafts
|
|
23
|
+
const allServices = getServiceNames(csn);
|
|
24
|
+
const {
|
|
25
|
+
createForeignKeyElement, createAndAddDraftAdminDataProjection, createScalarElement, createAssociationElement,
|
|
26
|
+
addElement, copyAndAddElement, createAssociationPathComparison,
|
|
27
|
+
} = getTransformers(csn, options, pathDelimiter);
|
|
28
|
+
const { getCsnDef, isComposition } = getUtils(csn);
|
|
29
|
+
const { error, warning } = messageFunctions;
|
|
30
|
+
|
|
31
|
+
forEachDefinition(csn, generateDraft);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Generate the draft stuff for a given artifact
|
|
35
|
+
*
|
|
36
|
+
* @param {CSN.Artifact} artifact
|
|
37
|
+
* @param {string} artifactName
|
|
38
|
+
*/
|
|
39
|
+
function generateDraft(artifact, artifactName) {
|
|
40
|
+
if ((artifact.kind === 'entity' || artifact.kind === 'view') &&
|
|
41
|
+
hasAnnotationValue(artifact, draftAnnotation) &&
|
|
42
|
+
isPartOfService(artifactName)) {
|
|
43
|
+
// Determine the set of target draft nodes belonging to this draft root (the draft root
|
|
44
|
+
// itself plus all its transitively composition-reachable targets)
|
|
45
|
+
const draftNodes = Object.create(null);
|
|
46
|
+
collectDraftNodesInto(artifact, artifactName, artifact, draftNodes);
|
|
47
|
+
// Draft-enable all of them
|
|
48
|
+
for (const name in draftNodes)
|
|
49
|
+
generateDraftForHana(draftNodes[name], name, artifactName);
|
|
50
|
+
|
|
51
|
+
// Redirect associations/compositions between draft shadow nodes
|
|
52
|
+
for (const name in draftNodes) {
|
|
53
|
+
const shadowNode = csn.definitions[`${name}${draftSuffix}`];
|
|
54
|
+
// Might not exist because of previous errors
|
|
55
|
+
if (shadowNode)
|
|
56
|
+
redirectDraftTargets(csn.definitions[`${name}${draftSuffix}`], draftNodes);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Collect all artifacts that are transitively reachable via compositions from 'artifact' into 'draftNodes'.
|
|
63
|
+
* Check that no artifact other than the root node has '@odata.draft.enabled'
|
|
64
|
+
*
|
|
65
|
+
* @param {CSN.Artifact} artifact
|
|
66
|
+
* @param {string} artifactName
|
|
67
|
+
* @param {CSN.Artifact} rootArtifact root artifact where composition traversal started.
|
|
68
|
+
* @param {object} draftNodes Dictionary of artifacts
|
|
69
|
+
*/
|
|
70
|
+
function collectDraftNodesInto(artifact, artifactName, rootArtifact, draftNodes) {
|
|
71
|
+
// Collect the artifact itself
|
|
72
|
+
draftNodes[artifactName] = artifact;
|
|
73
|
+
// Follow all composition targets in elements of 'artifact'
|
|
74
|
+
for (const elemName in artifact.elements) {
|
|
75
|
+
const elem = artifact.elements[elemName];
|
|
76
|
+
if (elem.target && isComposition(elem.type)) {
|
|
77
|
+
const draftNode = getCsnDef(elem.target);
|
|
78
|
+
const draftNodeName = elem.target;
|
|
79
|
+
// Sanity check
|
|
80
|
+
if (!draftNode)
|
|
81
|
+
throw new Error(`Expecting target to be resolved: ${JSON.stringify(elem, null, 2)}`);
|
|
82
|
+
|
|
83
|
+
// Ignore composition if not part of a service
|
|
84
|
+
if (!isPartOfService(draftNodeName)) {
|
|
85
|
+
warning(null, [ 'definitions', artifactName, 'elements', elemName ], { target: draftNodeName },
|
|
86
|
+
'Ignoring draft node for composition target $(TARGET) because it is not part of a service');
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
// Barf if a draft node other than the root has @odata.draft.enabled itself
|
|
90
|
+
if (draftNode !== rootArtifact && hasAnnotationValue(draftNode, draftAnnotation)) {
|
|
91
|
+
error(null, [ 'definitions', artifactName, 'elements', elemName ], 'Composition in draft-enabled entity can\'t lead to another entity with “@odata.draft.enabled”');
|
|
92
|
+
delete draftNodes[draftNodeName];
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
// Recurse unless already known
|
|
96
|
+
if (!hasAnnotationValue(draftNode, draftAnnotation, false) && !draftNodes[draftNodeName])
|
|
97
|
+
collectDraftNodesInto(draftNode, draftNodeName, rootArtifact, draftNodes);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Generate all that is required in HANA CDS for draft enablement of 'artifact'.
|
|
104
|
+
*
|
|
105
|
+
* @param {CSN.Artifact} artifact
|
|
106
|
+
* @param {string} artifactName
|
|
107
|
+
* @param {string} draftRootName
|
|
108
|
+
*/
|
|
109
|
+
function generateDraftForHana(artifact, artifactName, draftRootName) {
|
|
110
|
+
// Sanity check
|
|
111
|
+
if (!isPartOfService(artifactName))
|
|
112
|
+
throw new Error(`Expecting artifact to be part of a service: ${JSON.stringify(artifact)}`);
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
// The name of the draft shadow entity we should generate
|
|
116
|
+
const draftsArtifactName = `${artifactName}${draftSuffix}`;
|
|
117
|
+
|
|
118
|
+
// extract keys for UUID inspection
|
|
119
|
+
const keys = [];
|
|
120
|
+
forEachMemberRecursively(artifact, (elt, name, prop, path) => {
|
|
121
|
+
if (!elt.elements && !elt.type && !elt.virtual) // only check leafs
|
|
122
|
+
error(null, path, 'Expecting element to have a type when used in a draft-enabled artifact');
|
|
123
|
+
if (elt.key && elt.key === true && !elt.virtual)
|
|
124
|
+
keys.push(elt);
|
|
125
|
+
}, [ 'definitions', artifactName ], true, { elementsOnly: true });
|
|
126
|
+
|
|
127
|
+
// In contrast to EDM, the DB entity may have more than one technical keys but should have idealy exactly one key of type cds.UUID
|
|
128
|
+
if (keys.length !== 1)
|
|
129
|
+
warning(null, [ 'definitions', artifactName ], 'Entity annotated with “@odata.draft.enabled” should have exactly one key element');
|
|
130
|
+
|
|
131
|
+
const uuidCount = keys.reduce((acc, k) => ((k.type === 'cds.String' && k.$renamed === 'cds.UUID' && k.length === 36) ? acc + 1 : acc), 0);
|
|
132
|
+
if (uuidCount === 0)
|
|
133
|
+
warning(null, [ 'definitions', artifactName ], 'Entity annotated with “@odata.draft.enabled” should have one key element of type “cds.UUID”');
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
const matchingService = getMatchingService(artifactName);
|
|
137
|
+
// Generate the DraftAdministrativeData projection into the service, unless there is already one
|
|
138
|
+
const draftAdminDataProjectionName = `${matchingService}.DraftAdministrativeData`;
|
|
139
|
+
let draftAdminDataProjection = csn.definitions[draftAdminDataProjectionName];
|
|
140
|
+
if (!draftAdminDataProjection) {
|
|
141
|
+
draftAdminDataProjection = createAndAddDraftAdminDataProjection(matchingService, true);
|
|
142
|
+
|
|
143
|
+
if (!draftAdminDataProjection.projection.columns && draftAdminDataProjection.elements.DraftUUID)
|
|
144
|
+
draftAdminDataProjection.projection.columns = Object.keys(draftAdminDataProjection.elements).map(e => (e === 'DraftUUID' ? { key: true, ref: [ 'DraftAdministrativeData', e ] } : { ref: [ 'DraftAdministrativeData', e ] }));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Barf if it is not an entity or not what we expect
|
|
148
|
+
if (draftAdminDataProjection.kind !== 'entity' || !draftAdminDataProjection.elements.DraftUUID) {
|
|
149
|
+
// See draftAdminDataProjection which is defined in `csn.definitions`.
|
|
150
|
+
const path = [ 'definitions', draftAdminDataProjectionName ];
|
|
151
|
+
error(null, path, { name: draftAdminDataProjectionName },
|
|
152
|
+
'Generated entity $(NAME) conflicts with existing artifact');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const persistenceName = getResultingName(csn, options.forHana.names, draftsArtifactName);
|
|
156
|
+
// Duplicate the artifact as a draft shadow entity
|
|
157
|
+
if (csn.definitions[persistenceName]) {
|
|
158
|
+
const definingDraftRoot = csn.definitions[persistenceName].$draftRoot;
|
|
159
|
+
if (!definingDraftRoot) {
|
|
160
|
+
error(null, [ 'definitions', artifactName ], { name: persistenceName },
|
|
161
|
+
'Generated entity name $(NAME) conflicts with existing entity');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
else {
|
|
165
|
+
error(null, [ 'definitions', draftRootName ], { name: persistenceName },
|
|
166
|
+
`Entity $(NAME) already generated by draft root "${definingDraftRoot}"`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const draftsArtifact = {
|
|
172
|
+
kind: 'entity',
|
|
173
|
+
elements: Object.create(null),
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// Add draft shadow entity to the csn
|
|
177
|
+
csn.definitions[draftsArtifactName] = draftsArtifact;
|
|
178
|
+
|
|
179
|
+
setProp(draftsArtifact, '$draftRoot', draftRootName);
|
|
180
|
+
if (artifact.$location)
|
|
181
|
+
setProp(draftsArtifact, '$location', artifact.$location);
|
|
182
|
+
|
|
183
|
+
// Copy all elements
|
|
184
|
+
for (const elemName in artifact.elements) {
|
|
185
|
+
const origElem = artifact.elements[elemName];
|
|
186
|
+
let elem;
|
|
187
|
+
if ((isDeprecatedEnabled(options, 'renderVirtualElements') && origElem.virtual) || !origElem.virtual)
|
|
188
|
+
elem = copyAndAddElement(origElem, draftsArtifact, draftsArtifactName, elemName)[elemName];
|
|
189
|
+
if (elem) {
|
|
190
|
+
// Remove "virtual" - cap/issues 4956
|
|
191
|
+
if (elem.virtual)
|
|
192
|
+
delete elem.virtual;
|
|
193
|
+
|
|
194
|
+
// explicitly set nullable if not key and not unmanaged association
|
|
195
|
+
if (!elem.key && !elem.on)
|
|
196
|
+
elem.notNull = false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Generate the additional elements into the draft-enabled artifact
|
|
201
|
+
|
|
202
|
+
// key IsActiveEntity : Boolean default true
|
|
203
|
+
const isActiveEntity = createScalarElement('IsActiveEntity', booleanBuiltin, false);
|
|
204
|
+
// Use artifactName and not draftsArtifactName because otherwise we may point to the generated
|
|
205
|
+
// entity in CSN and won't get a proper location (draftsArtifact has inherited all
|
|
206
|
+
// elements from the original artifact).
|
|
207
|
+
addElement(isActiveEntity, draftsArtifact, artifactName);
|
|
208
|
+
|
|
209
|
+
// HasActiveEntity : Boolean default false
|
|
210
|
+
const hasActiveEntity = createScalarElement('HasActiveEntity', booleanBuiltin, false);
|
|
211
|
+
addElement(hasActiveEntity, draftsArtifact, artifactName);
|
|
212
|
+
|
|
213
|
+
// HasDraftEntity : Boolean default false;
|
|
214
|
+
const hasDraftEntity = createScalarElement('HasDraftEntity', booleanBuiltin, false);
|
|
215
|
+
addElement(hasDraftEntity, draftsArtifact, artifactName);
|
|
216
|
+
|
|
217
|
+
// DraftAdministrativeData : Association to one DraftAdministrativeData not null;
|
|
218
|
+
const draftAdministrativeData = createAssociationElement('DraftAdministrativeData', draftAdminDataProjectionName, true);
|
|
219
|
+
draftAdministrativeData.DraftAdministrativeData.cardinality = {
|
|
220
|
+
max: 1,
|
|
221
|
+
};
|
|
222
|
+
draftAdministrativeData.DraftAdministrativeData.notNull = true;
|
|
223
|
+
addElement(draftAdministrativeData, draftsArtifact, artifactName);
|
|
224
|
+
// Note that we may need to do the HANA transformation steps for managed associations
|
|
225
|
+
// (foreign key field generation, generatedFieldName, creating ON-condition) by hand,
|
|
226
|
+
// because the corresponding transformation steps have already been done on all artifacts
|
|
227
|
+
// when we come here). Only for to.hdbcds with hdbcds names this is not required.
|
|
228
|
+
/**
|
|
229
|
+
* The given association has a key named DraftUUID
|
|
230
|
+
*
|
|
231
|
+
* @param {CSN.Association} association Assoc to check
|
|
232
|
+
* @returns {object}
|
|
233
|
+
*/
|
|
234
|
+
function getDraftUUIDKey(association) {
|
|
235
|
+
if (association.keys) {
|
|
236
|
+
const filtered = association.keys.filter(o => (o.ref && !o.as && o.ref.length === 1 && o.ref[0] === 'DraftUUID') || (o.as && o.as === 'DraftUUID'));
|
|
237
|
+
if (filtered.length === 1)
|
|
238
|
+
return filtered[0];
|
|
239
|
+
|
|
240
|
+
else if (filtered.length > 1)
|
|
241
|
+
return filtered.filter(o => o.as && o.as === 'DraftUUID');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return undefined;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Get the resulting name for an obj - explicit or implicit alias
|
|
249
|
+
*
|
|
250
|
+
* @param {object} obj Any object with at least "ref"
|
|
251
|
+
* @returns {string}
|
|
252
|
+
*/
|
|
253
|
+
function getNameForRef(obj) {
|
|
254
|
+
if (obj.as)
|
|
255
|
+
return obj.as;
|
|
256
|
+
|
|
257
|
+
return obj.ref[obj.ref.length - 1];
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const draftUUIDKey = getDraftUUIDKey(draftAdministrativeData.DraftAdministrativeData);
|
|
261
|
+
if (!(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds') && draftUUIDKey) {
|
|
262
|
+
const path = [ 'definitions', draftsArtifactName, 'elements', 'DraftAdministrativeData', 'keys', 0 ];
|
|
263
|
+
createForeignKeyElement(draftAdministrativeData.DraftAdministrativeData, 'DraftAdministrativeData', draftUUIDKey, draftsArtifact, draftsArtifactName, path);
|
|
264
|
+
draftAdministrativeData.DraftAdministrativeData.on = createAssociationPathComparison('DraftAdministrativeData',
|
|
265
|
+
getNameForRef(draftUUIDKey),
|
|
266
|
+
'=',
|
|
267
|
+
`DraftAdministrativeData${pathDelimiter}DraftUUID`);
|
|
268
|
+
// The notNull has been transferred to the foreign key field and must be removed on the association
|
|
269
|
+
delete draftAdministrativeData.DraftAdministrativeData.notNull;
|
|
270
|
+
|
|
271
|
+
// The association is now unmanaged, i.e. actually it should no longer have foreign keys
|
|
272
|
+
// at all. But the processing of backlink associations below expects to have them, so
|
|
273
|
+
// we don't delete them (but mark them as implicit so that toCdl does not render them)
|
|
274
|
+
// draftAdministrativeData.DraftAdministrativeData.implicitForeignKeys = true;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Redirect all association/composition targets in 'artifact' that point to targets in
|
|
280
|
+
* the dictionary 'draftNodes' to their corresponding draft shadow artifacts.
|
|
281
|
+
*
|
|
282
|
+
* @param {CSN.Artifact} artifact
|
|
283
|
+
* @param {CSN.Artifact[]} draftNodes
|
|
284
|
+
*/
|
|
285
|
+
function redirectDraftTargets(artifact, draftNodes) {
|
|
286
|
+
for (const elemName in artifact.elements) {
|
|
287
|
+
const elem = artifact.elements[elemName];
|
|
288
|
+
if (elem.target) {
|
|
289
|
+
const targetArt = getCsnDef(elem.target);
|
|
290
|
+
// Nothing to do if target is not a draft node
|
|
291
|
+
if (!draftNodes[elem.target])
|
|
292
|
+
continue;
|
|
293
|
+
|
|
294
|
+
// Redirect the composition/association in this draft shadow entity to the target draft shadow entity
|
|
295
|
+
// console.error(`Redirecting target of ${elemName} in ${artifact.name.absolute} to ${target.name.absolute + '_drafts'}`);
|
|
296
|
+
const { shadowTarget, shadowTargetName } = getDraftShadowEntityFor(targetArt, elem.target);
|
|
297
|
+
// Might not exist because of previous errors
|
|
298
|
+
if (shadowTarget)
|
|
299
|
+
elem.target = shadowTargetName;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Returns the corresponding draft shadow artifact for draft node 'draftNode'.
|
|
305
|
+
*
|
|
306
|
+
* @param {CSN.Artifact} draftNode
|
|
307
|
+
* @param {string} draftNodeName
|
|
308
|
+
* @returns {object} Object with shadowTarget: definition and shadowTargetName: Name of the definition
|
|
309
|
+
*/
|
|
310
|
+
function getDraftShadowEntityFor(draftNode, draftNodeName) {
|
|
311
|
+
// Sanity check
|
|
312
|
+
if (!draftNodes[draftNodeName])
|
|
313
|
+
throw new Error(`Not a draft node: ${draftNodeName}`);
|
|
314
|
+
|
|
315
|
+
return { shadowTarget: csn.definitions[`${draftNodeName}${draftSuffix}`], shadowTargetName: `${draftNodeName}${draftSuffix}` };
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Check if the given artifact is part of a service.
|
|
321
|
+
*
|
|
322
|
+
* @param {string} artifactName Absolute name of the artifact
|
|
323
|
+
* @returns {boolean}
|
|
324
|
+
*/
|
|
325
|
+
function isPartOfService(artifactName) {
|
|
326
|
+
for (const serviceName of allServices) {
|
|
327
|
+
if (artifactName.startsWith(`${serviceName}.`))
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Get the service name containing the artifact.
|
|
336
|
+
*
|
|
337
|
+
* @param {string} artifactName Absolute name of the artifact
|
|
338
|
+
* @returns {boolean|string} Name of the service or false if no match is found.
|
|
339
|
+
*/
|
|
340
|
+
function getMatchingService(artifactName) {
|
|
341
|
+
const matches = [];
|
|
342
|
+
for (const serviceName of allServices) {
|
|
343
|
+
if (artifactName.startsWith(`${serviceName}.`))
|
|
344
|
+
matches.push(serviceName);
|
|
345
|
+
}
|
|
346
|
+
if (matches.length === 0)
|
|
347
|
+
return false;
|
|
348
|
+
return matches.sort((a, b) => a.length - b.length)[0];
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
module.exports = generateDrafts;
|