@sap/cds 6.7.2 → 6.8.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 +53 -1
- package/README.md +1 -0
- package/_i18n/i18n.properties +9 -6
- package/_i18n/i18n_ar.properties +6 -6
- package/_i18n/i18n_cs.properties +6 -6
- package/_i18n/i18n_da.properties +6 -6
- package/_i18n/i18n_de.properties +6 -6
- package/_i18n/i18n_en.properties +6 -6
- package/_i18n/i18n_es.properties +6 -6
- package/_i18n/i18n_fi.properties +6 -6
- package/_i18n/i18n_fr.properties +6 -6
- package/_i18n/i18n_hu.properties +6 -6
- package/_i18n/i18n_it.properties +6 -6
- package/_i18n/i18n_ja.properties +6 -6
- package/_i18n/i18n_ko.properties +6 -6
- package/_i18n/i18n_ms.properties +6 -6
- package/_i18n/i18n_nl.properties +6 -6
- package/_i18n/i18n_no.properties +6 -6
- package/_i18n/i18n_pl.properties +6 -6
- package/_i18n/i18n_pt.properties +6 -6
- package/_i18n/i18n_ro.properties +6 -6
- package/_i18n/i18n_ru.properties +6 -6
- package/_i18n/i18n_sv.properties +6 -6
- package/_i18n/i18n_th.properties +6 -6
- package/_i18n/i18n_tr.properties +8 -8
- package/_i18n/i18n_zh_CN.properties +3 -3
- package/_i18n/i18n_zh_TW.properties +6 -6
- package/apis/core.d.ts +30 -31
- package/apis/csn.d.ts +1 -1
- package/apis/ql.d.ts +69 -39
- package/apis/serve.d.ts +4 -3
- package/apis/services.d.ts +20 -7
- package/bin/build/buildTaskEngine.js +1 -1
- package/bin/build/index.js +1 -1
- package/bin/build/provider/buildTaskProviderInternal.js +9 -6
- package/bin/build/provider/hana/index.js +11 -4
- package/bin/build/provider/mtx-extension/index.js +13 -1
- package/bin/build/provider/mtx-sidecar/index.js +3 -3
- package/bin/build/provider/nodejs/index.js +23 -0
- package/bin/plugins.js +2 -1
- package/bin/version.js +3 -2
- package/common.cds +3 -2
- package/lib/auth/index.js +3 -0
- package/lib/auth/mocked-users.js +13 -0
- package/lib/compile/etc/_localized.js +3 -0
- package/lib/compile/for/lean_drafts.js +0 -1
- package/lib/core/entities.js +7 -3
- package/lib/dbs/cds-deploy.js +36 -12
- package/lib/env/cds-env.js +47 -14
- package/lib/env/cds-requires.js +16 -7
- package/lib/env/defaults.js +2 -2
- package/lib/env/schemas/cds-rc.json +1 -8
- package/lib/index.js +1 -1
- package/lib/ql/STREAM.js +89 -0
- package/lib/ql/cds-ql.js +2 -1
- package/lib/req/request.js +6 -2
- package/lib/req/user.js +1 -1
- package/lib/srv/middlewares/index.js +9 -7
- package/lib/srv/middlewares/trace.js +6 -5
- package/lib/srv/srv-api.js +1 -0
- package/lib/utils/cds-utils.js +1 -1
- package/lib/utils/tar.js +30 -31
- package/libx/_runtime/audit/Service.js +96 -37
- package/libx/_runtime/audit/generic/personal/utils.js +26 -13
- package/libx/_runtime/audit/utils/v2.js +21 -22
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +2 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +2 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/batch/BatchProcessor.js +2 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +10 -3
- package/libx/_runtime/cds-services/services/Service.js +2 -7
- package/libx/_runtime/cds-services/services/utils/differ.js +1 -1
- package/libx/_runtime/cds-services/util/assert.js +28 -5
- package/libx/_runtime/common/aspects/any.js +4 -1
- package/libx/_runtime/common/generic/auth/utils.js +30 -41
- package/libx/_runtime/common/generic/crud.js +1 -1
- package/libx/_runtime/common/i18n/messages.properties +1 -1
- package/libx/_runtime/common/utils/generateOnCond.js +18 -22
- package/libx/_runtime/db/expand/expandCQNToJoin.js +49 -41
- package/libx/_runtime/db/expand/rawToExpanded.js +3 -5
- package/libx/_runtime/db/generic/rewrite.js +3 -0
- package/libx/_runtime/db/utils/generateAliases.js +1 -1
- package/libx/_runtime/fiori/generic/activate.js +1 -1
- package/libx/_runtime/fiori/generic/before.js +18 -19
- package/libx/_runtime/fiori/generic/prepare.js +1 -1
- package/libx/_runtime/fiori/generic/read.js +1 -1
- package/libx/_runtime/fiori/lean-draft.js +87 -53
- package/libx/_runtime/fiori/utils/handler.js +0 -6
- package/libx/_runtime/hana/customBuilder/CustomFunctionBuilder.js +1 -1
- package/libx/_runtime/hana/customBuilder/CustomReferenceBuilder.js +2 -1
- package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +0 -5
- package/libx/_runtime/hana/execute.js +18 -11
- package/libx/_runtime/hana/pool.js +26 -18
- package/libx/_runtime/hana/search2Contains.js +1 -1
- package/libx/_runtime/hana/search2cqn4sql.js +26 -18
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +23 -16
- package/libx/_runtime/messaging/outbox/utils.js +6 -1
- package/libx/_runtime/remote/Service.js +83 -48
- package/libx/_runtime/remote/utils/client.js +17 -19
- package/libx/_runtime/sqlite/execute.js +2 -0
- package/libx/rest/middleware/read.js +2 -1
- package/libx/rest/middleware/update.js +1 -1
- package/package.json +1 -1
|
@@ -66,7 +66,6 @@ class JoinCQNFromExpanded {
|
|
|
66
66
|
|
|
67
67
|
// Get first level of expanding regarding to many and all to one if not part of a nested to many expand.
|
|
68
68
|
this._createJoinCQNFromExpanded(this._SELECT, [])
|
|
69
|
-
|
|
70
69
|
return this
|
|
71
70
|
}
|
|
72
71
|
|
|
@@ -117,12 +116,10 @@ class JoinCQNFromExpanded {
|
|
|
117
116
|
*/
|
|
118
117
|
_createJoinCQNFromExpanded(SELECT, toManyTree, defaultLanguage) {
|
|
119
118
|
const joinArgs = SELECT.from.args
|
|
120
|
-
const isJoinOfTwoSelects = joinArgs
|
|
121
|
-
|
|
119
|
+
const isJoinOfTwoSelects = joinArgs?.every(a => a.SELECT)
|
|
122
120
|
const unionTableRef = this._getUnionTable(SELECT)
|
|
123
|
-
const unionTable = unionTableRef
|
|
121
|
+
const unionTable = unionTableRef?.table
|
|
124
122
|
const tableAlias = this._getTableAlias(SELECT, toManyTree)
|
|
125
|
-
|
|
126
123
|
const readToOneCQN = this._getReadToOneCQN(SELECT, isJoinOfTwoSelects ? 'filterExpand' : tableAlias)
|
|
127
124
|
|
|
128
125
|
if (isJoinOfTwoSelects) {
|
|
@@ -134,6 +131,7 @@ class JoinCQNFromExpanded {
|
|
|
134
131
|
.forEach(c => {
|
|
135
132
|
mappings[c.as.replace(prefix, '')] = c.as
|
|
136
133
|
})
|
|
134
|
+
|
|
137
135
|
// expand to one
|
|
138
136
|
const entity = this._csn.definitions[joinArgs[0].SELECT.from.SET.args[1].SELECT.from.ref[0]]
|
|
139
137
|
this._addImplicitOrderBy(readToOneCQN, entity, tableAlias)
|
|
@@ -147,9 +145,7 @@ class JoinCQNFromExpanded {
|
|
|
147
145
|
const entity = this._getEntityForTable(table)
|
|
148
146
|
this._addImplicitOrderBy(readToOneCQN, entity, tableAlias)
|
|
149
147
|
if (unionTable) readToOneCQN[IS_UNION_DRAFT] = true
|
|
150
|
-
|
|
151
148
|
readToOneCQN[IS_ACTIVE] = isDraftTree ? this._isDraftTargetActive(table) : true
|
|
152
|
-
|
|
153
149
|
const givenColumns = readToOneCQN.columns
|
|
154
150
|
readToOneCQN.columns = []
|
|
155
151
|
if (entity['@cds.localized'] === false) defaultLanguage = true
|
|
@@ -178,7 +174,8 @@ class JoinCQNFromExpanded {
|
|
|
178
174
|
* @private
|
|
179
175
|
*/
|
|
180
176
|
_getTableAlias(SELECT, toManyTree) {
|
|
181
|
-
|
|
177
|
+
const ref = this._getRef(SELECT)
|
|
178
|
+
return this._createAlias(toManyTree.length === 0 ? ref.table : toManyTree.join(':'), ref.as)
|
|
182
179
|
}
|
|
183
180
|
|
|
184
181
|
_getRef(SELECT) {
|
|
@@ -212,15 +209,21 @@ class JoinCQNFromExpanded {
|
|
|
212
209
|
* Create an alias from value.
|
|
213
210
|
*
|
|
214
211
|
* @param {string} value
|
|
212
|
+
* @param {string} [alias]
|
|
215
213
|
* @returns {string}
|
|
216
214
|
* @private
|
|
217
215
|
*/
|
|
218
|
-
_createAlias(value) {
|
|
216
|
+
_createAlias(value, alias) {
|
|
219
217
|
if (!this._aliases) {
|
|
220
218
|
this._aliases = {}
|
|
221
219
|
}
|
|
222
220
|
|
|
223
221
|
if (!this._aliases[value]) {
|
|
222
|
+
if (alias) {
|
|
223
|
+
this._aliases[value] = alias
|
|
224
|
+
return alias
|
|
225
|
+
}
|
|
226
|
+
|
|
224
227
|
const aliasNum = Object.keys(this._aliases).length
|
|
225
228
|
|
|
226
229
|
if (aliasNum < 26) {
|
|
@@ -319,6 +322,7 @@ class JoinCQNFromExpanded {
|
|
|
319
322
|
list: element.list.map(element => this._checkOrderByWhereElementRecursive(cqn, element, tableAlias))
|
|
320
323
|
})
|
|
321
324
|
}
|
|
325
|
+
|
|
322
326
|
return this._checkOrderByWhereElementRecursive(cqn, element, tableAlias)
|
|
323
327
|
}
|
|
324
328
|
|
|
@@ -333,9 +337,7 @@ class JoinCQNFromExpanded {
|
|
|
333
337
|
*/
|
|
334
338
|
_adaptWhereOrderBy(cqn, tableAlias) {
|
|
335
339
|
if (cqn.where) {
|
|
336
|
-
cqn.where = cqn.where.map(element =>
|
|
337
|
-
return this._adaptWhereElement(element, cqn, tableAlias)
|
|
338
|
-
})
|
|
340
|
+
cqn.where = cqn.where.map(element => this._adaptWhereElement(element, cqn, tableAlias))
|
|
339
341
|
}
|
|
340
342
|
|
|
341
343
|
if (cqn.having) {
|
|
@@ -343,15 +345,11 @@ class JoinCQNFromExpanded {
|
|
|
343
345
|
}
|
|
344
346
|
|
|
345
347
|
if (cqn.orderBy) {
|
|
346
|
-
cqn.orderBy = cqn.orderBy.map(element =>
|
|
347
|
-
return this._checkOrderByWhereElementRecursive(cqn, element, tableAlias)
|
|
348
|
-
})
|
|
348
|
+
cqn.orderBy = cqn.orderBy.map(element => this._checkOrderByWhereElementRecursive(cqn, element, tableAlias))
|
|
349
349
|
}
|
|
350
350
|
|
|
351
351
|
if (cqn.groupBy) {
|
|
352
|
-
cqn.groupBy = cqn.groupBy.map(element =>
|
|
353
|
-
return this._checkOrderByWhereElementRecursive(cqn, element, tableAlias)
|
|
354
|
-
})
|
|
352
|
+
cqn.groupBy = cqn.groupBy.map(element => this._checkOrderByWhereElementRecursive(cqn, element, tableAlias))
|
|
355
353
|
}
|
|
356
354
|
|
|
357
355
|
return cqn
|
|
@@ -393,7 +391,7 @@ class JoinCQNFromExpanded {
|
|
|
393
391
|
element.xpr = element.xpr.map(nestedElement => {
|
|
394
392
|
return this._checkOrderByWhereElementRecursive(cqn, nestedElement, tableAlias)
|
|
395
393
|
})
|
|
396
|
-
} else if (element.SELECT
|
|
394
|
+
} else if (element.SELECT?.where) {
|
|
397
395
|
element = {
|
|
398
396
|
SELECT: Object.assign({}, element.SELECT, {
|
|
399
397
|
where: this._adaptWhereSELECT(this._getRef(cqn), element.SELECT.where, tableAlias)
|
|
@@ -416,6 +414,7 @@ class JoinCQNFromExpanded {
|
|
|
416
414
|
if (element.xpr) {
|
|
417
415
|
return { xpr: this._adaptWhereSELECT(aliasedTable, element.xpr, tableAlias) }
|
|
418
416
|
}
|
|
417
|
+
|
|
419
418
|
return this._elementAliasNeedsReplacement(element, aliasedTable)
|
|
420
419
|
? Object.assign({}, element, { ref: [tableAlias, element.ref[1]] })
|
|
421
420
|
: element
|
|
@@ -498,11 +497,11 @@ class JoinCQNFromExpanded {
|
|
|
498
497
|
_expandedToFlat({ entity, givenColumns, readToOneCQN, tableAlias, toManyTree, defaultLanguage }) {
|
|
499
498
|
const toManyColumns = []
|
|
500
499
|
const mappings = this._getMappingObject(toManyTree)
|
|
501
|
-
|
|
502
500
|
const readToOneCQNCopy = getCqnCopy(readToOneCQN)
|
|
503
501
|
|
|
504
502
|
for (const column of givenColumns) {
|
|
505
503
|
let navigation
|
|
504
|
+
|
|
506
505
|
if (column.expand) {
|
|
507
506
|
navigation = getNavigationIfStruct(entity, tableAlias === column.ref[0] ? column.ref.slice(1) : column.ref)
|
|
508
507
|
if (this._skip(navigation && navigation._target)) continue
|
|
@@ -518,6 +517,7 @@ class JoinCQNFromExpanded {
|
|
|
518
517
|
// Expands with to one target can be processed directly
|
|
519
518
|
const navProp = column.ref[column.ref.length - 1]
|
|
520
519
|
const navTarget = entity.elements[navProp]
|
|
520
|
+
|
|
521
521
|
if (
|
|
522
522
|
entity._isDraftEnabled &&
|
|
523
523
|
navTarget._isAssociationStrict &&
|
|
@@ -527,6 +527,7 @@ class JoinCQNFromExpanded {
|
|
|
527
527
|
) {
|
|
528
528
|
mappings[navProp] = { [TO_ACTIVE]: true }
|
|
529
529
|
}
|
|
530
|
+
|
|
530
531
|
this._addJoinAndElements({
|
|
531
532
|
column,
|
|
532
533
|
entity,
|
|
@@ -537,7 +538,8 @@ class JoinCQNFromExpanded {
|
|
|
537
538
|
})
|
|
538
539
|
} else {
|
|
539
540
|
// No expand, directly add the column and its mapping.
|
|
540
|
-
|
|
541
|
+
const columnAliased = this._addAliasToColumn(column, entity, tableAlias, mappings)
|
|
542
|
+
readToOneCQN.columns.push(columnAliased)
|
|
541
543
|
|
|
542
544
|
// REVISIT required for other cqn properties as well?
|
|
543
545
|
this.adjustOrderBy(readToOneCQN.orderBy, mappings, column, tableAlias)
|
|
@@ -554,6 +556,7 @@ class JoinCQNFromExpanded {
|
|
|
554
556
|
}
|
|
555
557
|
}
|
|
556
558
|
}
|
|
559
|
+
|
|
557
560
|
// only as second step handle expand to many, or else keys might still be unknown
|
|
558
561
|
this._toMany({
|
|
559
562
|
entity,
|
|
@@ -656,7 +659,6 @@ class JoinCQNFromExpanded {
|
|
|
656
659
|
const extendedToManyTree = toManyTree.concat(column.ref[0] === parentAlias ? column.ref.slice(1) : column.ref)
|
|
657
660
|
const tableAlias = this._createAlias(extendedToManyTree.join(':'))
|
|
658
661
|
const target = this._getTarget(entity, column, parentAlias)
|
|
659
|
-
|
|
660
662
|
const name = column.ref[column.ref.length - 1]
|
|
661
663
|
const element = name && entity.elements[name]
|
|
662
664
|
|
|
@@ -763,8 +765,7 @@ class JoinCQNFromExpanded {
|
|
|
763
765
|
const givenColumns = column.expand.map(col => {
|
|
764
766
|
if (
|
|
765
767
|
activeTableRequired &&
|
|
766
|
-
col.ref &&
|
|
767
|
-
col.ref.length &&
|
|
768
|
+
col.ref?.length &&
|
|
768
769
|
(col.ref[0] === 'IsActiveEntity' || col.ref[0] === 'HasActiveEntity')
|
|
769
770
|
) {
|
|
770
771
|
return {
|
|
@@ -847,7 +848,7 @@ class JoinCQNFromExpanded {
|
|
|
847
848
|
continue
|
|
848
849
|
}
|
|
849
850
|
|
|
850
|
-
if (arg.SELECT
|
|
851
|
+
if (arg.SELECT?.columns.some(column => column[IDENTIFIER])) {
|
|
851
852
|
return arg.SELECT.columns
|
|
852
853
|
}
|
|
853
854
|
|
|
@@ -972,9 +973,7 @@ class JoinCQNFromExpanded {
|
|
|
972
973
|
*/
|
|
973
974
|
_addAliasToColumn(column, entity, tableAlias, mappings) {
|
|
974
975
|
// No identifier for this row entry or technical column
|
|
975
|
-
if (this._isAliasNotNeeded(column))
|
|
976
|
-
return column
|
|
977
|
-
}
|
|
976
|
+
if (this._isAliasNotNeeded(column)) return column
|
|
978
977
|
|
|
979
978
|
if (Array.isArray(column.xpr)) {
|
|
980
979
|
return this._buildNewAliasColumn(
|
|
@@ -993,6 +992,7 @@ class JoinCQNFromExpanded {
|
|
|
993
992
|
mappings
|
|
994
993
|
)
|
|
995
994
|
}
|
|
995
|
+
|
|
996
996
|
return this._buildNewAliasColumn(column, entity, tableAlias, mappings)
|
|
997
997
|
}
|
|
998
998
|
|
|
@@ -1073,10 +1073,7 @@ class JoinCQNFromExpanded {
|
|
|
1073
1073
|
defaultLanguage,
|
|
1074
1074
|
readToOneCQNCopy
|
|
1075
1075
|
}) {
|
|
1076
|
-
if (toManyColumns.length === 0)
|
|
1077
|
-
return
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1076
|
+
if (toManyColumns.length === 0) return
|
|
1080
1077
|
this._addKeysIfNeeded({ entity, readToOneCQN, tableAlias })
|
|
1081
1078
|
|
|
1082
1079
|
for (const { column, parentAlias } of toManyColumns) {
|
|
@@ -1089,6 +1086,7 @@ class JoinCQNFromExpanded {
|
|
|
1089
1086
|
parentAlias,
|
|
1090
1087
|
defaultLanguage
|
|
1091
1088
|
})
|
|
1089
|
+
|
|
1092
1090
|
this._createJoinCQNFromExpanded(select, toManyTree.concat([column.ref[column.ref.length - 1]]), defaultLanguage)
|
|
1093
1091
|
}
|
|
1094
1092
|
}
|
|
@@ -1160,7 +1158,6 @@ class JoinCQNFromExpanded {
|
|
|
1160
1158
|
// eslint-disable-next-line complexity
|
|
1161
1159
|
_buildExpandedCQN({ column, entity, readToOneCQN, toManyTree, mappings, parentAlias, defaultLanguage }) {
|
|
1162
1160
|
const isUnion = !!readToOneCQN.from.SET
|
|
1163
|
-
|
|
1164
1161
|
const colRef = parentAlias === column.ref[0] ? column.ref.slice(1) : column.ref.slice(0)
|
|
1165
1162
|
const element = entity.elements[colRef[0]]
|
|
1166
1163
|
const colTarget = ensureUnlocalized(element.target)
|
|
@@ -1168,24 +1165,21 @@ class JoinCQNFromExpanded {
|
|
|
1168
1165
|
defaultLanguage ||
|
|
1169
1166
|
entity['@cds.localized'] === false ||
|
|
1170
1167
|
this._csn.definitions[colTarget]['@cds.localized'] === false
|
|
1171
|
-
|
|
1172
1168
|
const expandActive =
|
|
1173
1169
|
readToOneCQN[IS_ACTIVE] ||
|
|
1174
1170
|
(element._isAssociationStrict && !element['@odata.draft.enclosed']) ||
|
|
1175
1171
|
!this._csn.definitions[colTarget]._isDraftEnabled
|
|
1176
|
-
|
|
1177
1172
|
const ref = this._refFromRefByExpand(column.ref[0], colTarget, defaultLanguageThis, expandActive)
|
|
1178
1173
|
const tableAlias = this._createAlias(toManyTree.concat(colRef).join(':'))
|
|
1179
1174
|
const on = entity._relations[colRef[0]].join(tableAlias, 'filterExpand')
|
|
1180
1175
|
const filterExpand = this._getFilterExpandCQN(readToOneCQN, on, parentAlias, entity.keys)
|
|
1181
1176
|
const expandedEntity = this._csn.definitions[colTarget]
|
|
1182
1177
|
const joinColumns = this._getJoinColumnsFromOnAddToMapping(mappings[colRef[0]], parentAlias, on, entity)
|
|
1183
|
-
|
|
1184
1178
|
let cqn = {
|
|
1185
1179
|
from: {
|
|
1186
1180
|
join: 'inner',
|
|
1187
1181
|
args: [{ ref: [ref], as: tableAlias }, filterExpand],
|
|
1188
|
-
on
|
|
1182
|
+
on
|
|
1189
1183
|
}
|
|
1190
1184
|
}
|
|
1191
1185
|
|
|
@@ -1211,16 +1205,17 @@ class JoinCQNFromExpanded {
|
|
|
1211
1205
|
}
|
|
1212
1206
|
|
|
1213
1207
|
if (column.limit) throw getError(501, 'Pagination is not supported in expand')
|
|
1214
|
-
|
|
1215
1208
|
cqn = this._adaptWhereOrderBy(cqn, tableAlias)
|
|
1216
1209
|
|
|
1217
1210
|
if (isUnion) {
|
|
1218
1211
|
const cols = column.expand.filter(c => !c.expand && !(c.ref[0] in DRAFT_COLUMNS_MAP)).map(c => c.ref[0])
|
|
1212
|
+
|
|
1219
1213
|
// ensure the join columns are selected
|
|
1220
1214
|
for (const each of joinColumns) {
|
|
1221
1215
|
const col = each.ref[each.ref.length - 1]
|
|
1222
1216
|
if (!cols.includes(col)) cols.push(col)
|
|
1223
1217
|
}
|
|
1218
|
+
|
|
1224
1219
|
// ensure the foreign keys are selected in case of expand to one
|
|
1225
1220
|
for (const each of cqn.columns) {
|
|
1226
1221
|
if (each.expand) {
|
|
@@ -1238,14 +1233,17 @@ class JoinCQNFromExpanded {
|
|
|
1238
1233
|
)
|
|
1239
1234
|
const user = (cds.context && cds.context.user && cds.context.user.id) || 'anonymous'
|
|
1240
1235
|
const unionFrom = getCQNUnionFrom(cols, ref.replace(/_drafts$/, ''), ref, ks, user)
|
|
1236
|
+
|
|
1241
1237
|
for (const each of cqn.columns) {
|
|
1242
1238
|
if (!each.as) continue
|
|
1239
|
+
|
|
1243
1240
|
// replace val with ref
|
|
1244
1241
|
if (each.as === 'IsActiveEntity' || each.as === 'HasActiveEntity') {
|
|
1245
1242
|
delete each.val
|
|
1246
1243
|
each.ref = [tableAlias, each.as]
|
|
1247
1244
|
each.as = tableAlias + '_' + each.as
|
|
1248
1245
|
}
|
|
1246
|
+
|
|
1249
1247
|
// ensure the cast
|
|
1250
1248
|
if (
|
|
1251
1249
|
each.as.match(/IsActiveEntity$/) ||
|
|
@@ -1255,6 +1253,7 @@ class JoinCQNFromExpanded {
|
|
|
1255
1253
|
each.cast = { type: 'cds.Boolean' }
|
|
1256
1254
|
}
|
|
1257
1255
|
}
|
|
1256
|
+
|
|
1258
1257
|
const cs = cqn.columns
|
|
1259
1258
|
.filter(c => !c.expand && c.ref && c.ref[0] === tableAlias)
|
|
1260
1259
|
.map(c => ({ ref: [c.ref[1]] }))
|
|
@@ -1278,6 +1277,7 @@ class JoinCQNFromExpanded {
|
|
|
1278
1277
|
const sort = element.sort
|
|
1279
1278
|
if (element.args)
|
|
1280
1279
|
return { func: element.func, args: this._copyOrderBy(element.args, alias, expandedEntity), sort }
|
|
1280
|
+
|
|
1281
1281
|
const ref =
|
|
1282
1282
|
element.ref[0] === alias
|
|
1283
1283
|
? [...element.ref]
|
|
@@ -1286,6 +1286,7 @@ class JoinCQNFromExpanded {
|
|
|
1286
1286
|
: this._isPathExpressionToOne(element.ref, expandedEntity)
|
|
1287
1287
|
? [alias, ...element.ref]
|
|
1288
1288
|
: [alias, element.ref[1]]
|
|
1289
|
+
|
|
1289
1290
|
return (sort && { ref, sort }) || { ref }
|
|
1290
1291
|
})
|
|
1291
1292
|
}
|
|
@@ -1304,6 +1305,7 @@ class JoinCQNFromExpanded {
|
|
|
1304
1305
|
where: where
|
|
1305
1306
|
}
|
|
1306
1307
|
}
|
|
1308
|
+
|
|
1307
1309
|
return {
|
|
1308
1310
|
xpr: ['case', 'when', hasDraftQuery, 'IS NOT NULL', 'then', 'true', 'else', 'false', 'end'],
|
|
1309
1311
|
as: 'HasDraftEntity',
|
|
@@ -1368,6 +1370,7 @@ class JoinCQNFromExpanded {
|
|
|
1368
1370
|
outerColumns.push(...outerCols)
|
|
1369
1371
|
continue
|
|
1370
1372
|
}
|
|
1373
|
+
|
|
1371
1374
|
if (typeof entry === 'object' && entry.ref && entry.ref[0] === 'filterExpand') {
|
|
1372
1375
|
columns.push(this._getColumnObjectForFilterExpand(readToOneCQN, parentAlias, entry.ref[1]))
|
|
1373
1376
|
outerColumns.push({ ref: [entry.ref[1]] })
|
|
@@ -1434,7 +1437,7 @@ class JoinCQNFromExpanded {
|
|
|
1434
1437
|
}
|
|
1435
1438
|
|
|
1436
1439
|
_getValueFromEntry(entry, parentAlias, key, struct) {
|
|
1437
|
-
let value = entry[key]
|
|
1440
|
+
let value = entry[key] ?? entry[key.toUpperCase()]
|
|
1438
1441
|
if (value === undefined) {
|
|
1439
1442
|
value = entry[`${parentAlias}_${key}`] || entry[`${parentAlias}_${key}`.toUpperCase()]
|
|
1440
1443
|
}
|
|
@@ -1464,6 +1467,7 @@ class JoinCQNFromExpanded {
|
|
|
1464
1467
|
}
|
|
1465
1468
|
struct = current.elements[key.replace(parentAlias + '_', '')]
|
|
1466
1469
|
}
|
|
1470
|
+
|
|
1467
1471
|
// build value for spreading (cf. mapping[GET_KEY_VALUE])
|
|
1468
1472
|
value = []
|
|
1469
1473
|
for (const k in struct.elements) {
|
|
@@ -1480,7 +1484,8 @@ class JoinCQNFromExpanded {
|
|
|
1480
1484
|
|
|
1481
1485
|
_addColumNames(entity, parentAlias, columnNames) {
|
|
1482
1486
|
for (const keyName in entity.keys) {
|
|
1483
|
-
|
|
1487
|
+
const key = entity.keys[keyName]
|
|
1488
|
+
if (key.is2one || key.is2many) continue
|
|
1484
1489
|
const columnNameAlt = keyName === 'IsActiveEntity' ? 'IsActiveEntity' : `${parentAlias}_${keyName}`
|
|
1485
1490
|
if (!columnNames.includes(columnNameAlt)) {
|
|
1486
1491
|
columnNames.push(columnNameAlt)
|
|
@@ -1509,6 +1514,7 @@ class JoinCQNFromExpanded {
|
|
|
1509
1514
|
columns.push(...this._getJoinColumnsFromOnAddToMapping(mapping, parentAlias, entry.xpr, entity))
|
|
1510
1515
|
continue
|
|
1511
1516
|
}
|
|
1517
|
+
|
|
1512
1518
|
if (typeof entry === 'object' && entry.ref && entry.ref[0] !== 'filterExpand') {
|
|
1513
1519
|
const as = entry.ref.join('_')
|
|
1514
1520
|
columns.push({
|
|
@@ -1528,6 +1534,7 @@ class JoinCQNFromExpanded {
|
|
|
1528
1534
|
|
|
1529
1535
|
for (const key of keyList) {
|
|
1530
1536
|
const parts = key.split('_')
|
|
1537
|
+
|
|
1531
1538
|
// For draft-enabled entities, associations may not take over 'IsActiveEntity', e.g.
|
|
1532
1539
|
// when a draft points to an active entity
|
|
1533
1540
|
if (parts[parts.length - 1] !== 'IsActiveEntity') {
|
|
@@ -1575,7 +1582,6 @@ class JoinCQNFromExpanded {
|
|
|
1575
1582
|
this._addMissingJoinElements(columns, joinColumns)
|
|
1576
1583
|
this._addMissingKeyColumns(columns, tableAlias, keys, isActive, entity)
|
|
1577
1584
|
this._addMissingParentKeyColumns(columns, 'filterExpand', parentKeys, isActive)
|
|
1578
|
-
|
|
1579
1585
|
return columns
|
|
1580
1586
|
}
|
|
1581
1587
|
|
|
@@ -1604,11 +1610,13 @@ class JoinCQNFromExpanded {
|
|
|
1604
1610
|
columns.push(this._createCalculatedBooleanColumn('IsActiveEntity', isActive))
|
|
1605
1611
|
return
|
|
1606
1612
|
}
|
|
1613
|
+
|
|
1607
1614
|
if (isActive) {
|
|
1608
1615
|
if (columnName === 'HasActiveEntity') {
|
|
1609
1616
|
columns.push(this._createCalculatedBooleanColumn('HasActiveEntity', false))
|
|
1610
1617
|
return
|
|
1611
1618
|
}
|
|
1619
|
+
|
|
1612
1620
|
if (columnName === 'HasDraftEntity') {
|
|
1613
1621
|
columns.push(this._getHasDraftEntityXpr(entity, tableAlias))
|
|
1614
1622
|
return
|
|
@@ -158,9 +158,7 @@ class RawToExpanded {
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
// No property holds any value. A to null must have failed.
|
|
161
|
-
if (isEntityNull)
|
|
162
|
-
return
|
|
163
|
-
}
|
|
161
|
+
if (isEntityNull) return
|
|
164
162
|
|
|
165
163
|
return row
|
|
166
164
|
}
|
|
@@ -175,10 +173,10 @@ class RawToExpanded {
|
|
|
175
173
|
*/
|
|
176
174
|
_isNull(isEntityNull, value, key) {
|
|
177
175
|
if (isEntityNull === undefined) {
|
|
178
|
-
return value
|
|
176
|
+
return value == null || key === 'IsActiveEntity'
|
|
179
177
|
}
|
|
180
178
|
|
|
181
|
-
return isEntityNull === true && (value
|
|
179
|
+
return isEntityNull === true && (value == null || key === 'IsActiveEntity')
|
|
182
180
|
}
|
|
183
181
|
|
|
184
182
|
/**
|
|
@@ -14,6 +14,9 @@ const _isLinked = req => {
|
|
|
14
14
|
function handler(req) {
|
|
15
15
|
if (typeof req.query === 'string') return
|
|
16
16
|
|
|
17
|
+
// invoke req.subject before it gets modified
|
|
18
|
+
req.subject
|
|
19
|
+
|
|
17
20
|
if (!this.model) {
|
|
18
21
|
// best-effort rewrite of path in from
|
|
19
22
|
req.query = cqn2cqn4sql(req.query, { definitions: {} }, { service: this })
|
|
@@ -122,7 +122,7 @@ const fioriGenericActivate = async function (req) {
|
|
|
122
122
|
if (!draftData) req.reject(404)
|
|
123
123
|
if (adminData.InProcessByUser !== req.user.id) {
|
|
124
124
|
// REVISIT: security log?
|
|
125
|
-
req.reject(403, 'DRAFT_LOCKED_BY_ANOTHER_USER')
|
|
125
|
+
req.reject(403, 'DRAFT_LOCKED_BY_ANOTHER_USER', [adminData.InProcessByUser])
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
/*
|
|
@@ -4,7 +4,7 @@ const { SELECT } = cds.ql
|
|
|
4
4
|
|
|
5
5
|
const { isNavigationToMany } = require('../utils/req')
|
|
6
6
|
const { getKeysCondition, removeIsActiveEntityRecursively } = require('../utils/where')
|
|
7
|
-
const {
|
|
7
|
+
const { ensureNoDraftsSuffix, ensureDraftsSuffix, draftIsLocked } = require('../utils/handler')
|
|
8
8
|
|
|
9
9
|
const { DRAFT_COLUMNS_ADMIN_MAP } = require('../../common/constants/draft')
|
|
10
10
|
const { deepCopyArray } = require('../../common/utils/copy')
|
|
@@ -32,7 +32,7 @@ const _validateDraft = (req, draftResult, isBoundAction) => {
|
|
|
32
32
|
// user than the one who locked the entity and the configured drafts cancellation
|
|
33
33
|
// timeout timer has expired
|
|
34
34
|
if (draftIsLocked(draftAdminData.LastChangeDateTime)) {
|
|
35
|
-
req.reject(403, 'DRAFT_LOCKED_BY_ANOTHER_USER')
|
|
35
|
+
req.reject(403, 'DRAFT_LOCKED_BY_ANOTHER_USER', [draftAdminData.CreatedByUser])
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
// At this point, the request user ID isn't the owner of the draft.
|
|
@@ -60,16 +60,19 @@ const _getSelectDraftDataCqn = (entityName, where) => {
|
|
|
60
60
|
|
|
61
61
|
const _getRoot = req => {
|
|
62
62
|
if (!req.query) return
|
|
63
|
+
|
|
63
64
|
const refObj = req.query.SELECT?.from || req.query.UPDATE?.entity || req.query.INSERT?.into || req.query.DELETE?.from
|
|
65
|
+
const ref0 = refObj.ref[0]
|
|
66
|
+
|
|
64
67
|
const root = {
|
|
65
|
-
entityName: ensureDraftsSuffix(
|
|
66
|
-
where: removeIsActiveEntityRecursively(deepCopyArray(
|
|
68
|
+
entityName: ensureDraftsSuffix(ref0.id),
|
|
69
|
+
where: removeIsActiveEntityRecursively(deepCopyArray(ref0.where))
|
|
67
70
|
}
|
|
68
71
|
|
|
69
|
-
for (const item of
|
|
72
|
+
for (const item of ref0.where) {
|
|
70
73
|
if (item.ref && item.ref[item.ref.length - 1] === 'IsActiveEntity') {
|
|
71
|
-
const index =
|
|
72
|
-
root.IsActiveEntity =
|
|
74
|
+
const index = ref0.where.indexOf(item)
|
|
75
|
+
root.IsActiveEntity = ref0.where[index + 2].val
|
|
73
76
|
break
|
|
74
77
|
}
|
|
75
78
|
}
|
|
@@ -115,14 +118,12 @@ const _addDraftDataFromExistingDraft = async req => {
|
|
|
115
118
|
* Generic Handler for before NEW requests.
|
|
116
119
|
*/
|
|
117
120
|
const _new = async function (req) {
|
|
118
|
-
if (isDraftActivateAction(req)) return // REVISIT: How can NEW be draftActivate???
|
|
119
|
-
|
|
120
121
|
if (isNavigationToMany(req)) {
|
|
121
|
-
// REVISIT: How can NEW be a navigation to many?
|
|
122
122
|
const result = await _addDraftDataFromExistingDraft(req)
|
|
123
123
|
|
|
124
124
|
// in order to fix corner case where active subitems are created in draft case
|
|
125
125
|
if (result.length === 0) req.reject(404)
|
|
126
|
+
|
|
126
127
|
return
|
|
127
128
|
}
|
|
128
129
|
|
|
@@ -134,19 +135,17 @@ const _new = async function (req) {
|
|
|
134
135
|
/**
|
|
135
136
|
* Generic Handler for before PATCH and UPDATE requests.
|
|
136
137
|
*/
|
|
137
|
-
const
|
|
138
|
-
if (isDraftActivateAction(req)) return
|
|
139
|
-
|
|
138
|
+
const _patch = async function (req) {
|
|
140
139
|
const result = await _addDraftDataFromExistingDraft(req)
|
|
141
140
|
|
|
142
|
-
// means that the draft does not
|
|
141
|
+
// no result means that the draft does not exist
|
|
143
142
|
if (result.length === 0) req.reject(404)
|
|
144
143
|
}
|
|
145
144
|
|
|
146
145
|
/**
|
|
147
146
|
* Generic Handler for before DELETE and CANCEL requests.
|
|
148
147
|
*/
|
|
149
|
-
const
|
|
148
|
+
const _cancel = async function (req) {
|
|
150
149
|
await _addDraftDataFromExistingDraft(req)
|
|
151
150
|
}
|
|
152
151
|
|
|
@@ -181,12 +180,12 @@ const _registerBoundActionHandlers = function (entityName, actions) {
|
|
|
181
180
|
}
|
|
182
181
|
|
|
183
182
|
_new._initial = true
|
|
184
|
-
|
|
185
|
-
|
|
183
|
+
_patch._initial = true
|
|
184
|
+
_cancel._initial = true
|
|
186
185
|
|
|
187
186
|
module.exports = cds.service.impl((srv, entity) => {
|
|
188
187
|
srv.before('NEW', entity, _new)
|
|
189
|
-
srv.before(
|
|
190
|
-
srv.before(
|
|
188
|
+
srv.before('PATCH', entity, _patch)
|
|
189
|
+
srv.before('CANCEL', entity, _cancel)
|
|
191
190
|
_registerBoundActionHandlers.call(srv, entity.name, entity.actions)
|
|
192
191
|
})
|
|
@@ -39,7 +39,7 @@ const fioriGenericPrepare = async function (req) {
|
|
|
39
39
|
if (!result) req.reject(404)
|
|
40
40
|
if (result.draftAdmin_inProcessByUser !== req.user.id) {
|
|
41
41
|
// REVISIT: security log?
|
|
42
|
-
req.reject(403, 'DRAFT_LOCKED_BY_ANOTHER_USER')
|
|
42
|
+
req.reject(403, 'DRAFT_LOCKED_BY_ANOTHER_USER', [result.draftAdmin_inProcessByUser])
|
|
43
43
|
}
|
|
44
44
|
delete result.draftAdmin_inProcessByUser
|
|
45
45
|
return result
|
|
@@ -787,7 +787,7 @@ const _getOrderByEnrichedColumns = (orderBy, columns, entity) => {
|
|
|
787
787
|
const enrichedCol = []
|
|
788
788
|
|
|
789
789
|
if (orderBy && orderBy.length > 1) {
|
|
790
|
-
const colNames = columns.map(el => el.ref[el.ref.length - 1])
|
|
790
|
+
const colNames = columns.filter(el => el.ref).map(el => el.ref[el.ref.length - 1])
|
|
791
791
|
|
|
792
792
|
// REVISIT: GET Books?$select=title&$expand=NotBooks($select=pages)&$orderby=NotBooks/title - what's then?
|
|
793
793
|
for (const el of orderBy) {
|