@sap/cds 5.6.1 → 5.7.1
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 +136 -0
- package/_i18n/i18n_fr.properties +4 -4
- package/apis/cds.d.ts +7 -10
- package/apis/connect.d.ts +3 -3
- package/apis/core.d.ts +2 -4
- package/apis/models.d.ts +2 -3
- package/apis/ql.d.ts +0 -1
- package/apis/services.d.ts +7 -3
- package/bin/build/buildTaskFactory.js +16 -10
- package/bin/build/buildTaskProviderFactory.js +3 -3
- package/bin/build/constants.js +2 -1
- package/bin/build/provider/buildTaskProviderInternal.js +14 -14
- package/bin/build/provider/hana/2migration.js +2 -3
- package/bin/build/provider/hana/index.js +34 -0
- package/bin/build/provider/hana/migrationtable.js +90 -22
- package/bin/build/provider/hana/template/undeploy.json +5 -0
- package/bin/build/provider/node-cf/index.js +9 -2
- package/bin/serve.js +16 -18
- package/lib/compile/cdsc.js +15 -5
- package/lib/compile/etc/_localized.js +4 -4
- package/lib/compile/extend.js +8 -0
- package/lib/compile/index.js +3 -1
- package/lib/compile/minify.js +61 -0
- package/lib/compile/resolve.js +4 -1
- package/lib/compile/to/gql.js +9 -0
- package/lib/compile/to/sql.js +26 -30
- package/lib/connect/index.js +1 -1
- package/lib/core/entities.js +0 -3
- package/lib/core/infer.js +1 -0
- package/lib/core/reflect.js +1 -34
- package/lib/deploy.js +25 -17
- package/lib/env/defaults.js +3 -1
- package/lib/env/index.js +13 -4
- package/lib/env/presets.js +38 -0
- package/lib/env/requires.js +16 -11
- package/lib/index.js +13 -11
- package/lib/log/format/kibana.js +4 -2
- package/lib/log/index.js +2 -2
- package/lib/ql/Whereable.js +1 -0
- package/lib/req/cds-context.js +79 -0
- package/lib/req/context.js +5 -77
- package/lib/req/request.js +1 -1
- package/lib/serve/Service-api.js +8 -4
- package/lib/serve/Service-dispatch.js +0 -7
- package/lib/serve/Service-methods.js +6 -8
- package/lib/serve/Transaction.js +35 -30
- package/lib/serve/adapters.js +1 -4
- package/lib/utils/axios.js +1 -1
- package/libx/_runtime/audit/Service.js +44 -20
- package/libx/_runtime/audit/generic/personal/access.js +16 -11
- package/libx/_runtime/audit/generic/personal/modification.js +5 -5
- package/libx/_runtime/audit/generic/personal/utils.js +46 -37
- package/libx/_runtime/{common/auth → auth}/index.js +21 -7
- package/libx/_runtime/{common/auth → auth}/strategies/JWT.js +2 -2
- package/libx/_runtime/{common/auth → auth}/strategies/basic.js +2 -2
- package/libx/_runtime/{common/auth → auth}/strategies/dummy.js +1 -1
- package/libx/_runtime/{common/auth → auth}/strategies/mock.js +2 -2
- package/libx/_runtime/{common/auth → auth}/strategies/utils/uaa.js +1 -1
- package/libx/_runtime/{common/auth → auth}/strategies/utils/xssec.js +0 -0
- package/libx/_runtime/{common/auth → auth}/strategies/xsuaa.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +7 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +0 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +0 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +3 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +6 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +3 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +2 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +16 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +26 -65
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +0 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +3 -66
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +26 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +5 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +13 -10
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/ConditionalRequestControlCommand.js +0 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/SetResponseHeadersCommand.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/ConditionalRequestValidator.js +0 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +18 -15
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +54 -76
- package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +0 -7
- package/libx/_runtime/cds-services/adapter/rest/handlers/create.js +3 -6
- package/libx/_runtime/cds-services/adapter/rest/handlers/delete.js +3 -6
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +3 -6
- package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +3 -6
- package/libx/_runtime/cds-services/adapter/rest/handlers/update.js +3 -6
- package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +2 -2
- package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +8 -4
- package/libx/_runtime/cds-services/services/Service.js +0 -6
- package/libx/_runtime/cds-services/services/utils/columns.js +10 -3
- package/libx/_runtime/cds-services/services/utils/compareJson.js +4 -7
- package/libx/_runtime/cds-services/services/utils/differ.js +4 -1
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +1 -41
- package/libx/_runtime/cds-services/util/assert.js +1 -262
- package/libx/_runtime/cds.js +6 -9
- package/libx/_runtime/common/aspects/entity.js +1 -1
- package/libx/_runtime/common/composition/delete.js +4 -2
- package/libx/_runtime/common/composition/update.js +27 -35
- package/libx/_runtime/common/composition/utils.js +3 -7
- package/libx/_runtime/common/error/standardError.js +11 -0
- package/libx/_runtime/common/generic/auth.js +61 -30
- package/libx/_runtime/common/generic/crud.js +11 -23
- package/libx/_runtime/common/generic/input.js +20 -0
- package/libx/_runtime/common/generic/paging.js +2 -2
- package/libx/_runtime/common/generic/put.js +4 -10
- package/libx/_runtime/common/generic/sorting.js +12 -30
- package/libx/_runtime/common/perf/index.js +24 -0
- package/libx/_runtime/common/utils/cqn.js +58 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +289 -114
- package/libx/_runtime/common/utils/csn.js +38 -56
- package/libx/_runtime/common/utils/entityFromCqn.js +6 -6
- package/libx/_runtime/common/utils/resolveView.js +4 -5
- package/libx/_runtime/common/utils/rewriteAsterisks.js +46 -5
- package/libx/_runtime/common/utils/search2cqn4sql.js +21 -9
- package/libx/_runtime/common/utils/structured.js +35 -25
- package/libx/_runtime/db/Service.js +0 -6
- package/libx/_runtime/db/expand/expand-v2.js +130 -0
- package/libx/_runtime/db/expand/expandCQNToJoin.js +82 -61
- package/libx/_runtime/db/expand/index.js +3 -1
- package/libx/_runtime/db/generic/arrayed.js +14 -27
- package/libx/_runtime/db/generic/input.js +52 -10
- package/libx/_runtime/db/generic/integrity.js +367 -26
- package/libx/_runtime/db/generic/virtual.js +51 -13
- package/libx/_runtime/db/query/update.js +9 -3
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +8 -9
- package/libx/_runtime/{common → db}/utils/propagateForeignKeys.js +11 -14
- package/libx/_runtime/fiori/generic/activate.js +1 -0
- package/libx/_runtime/fiori/generic/before.js +2 -1
- package/libx/_runtime/fiori/generic/edit.js +2 -1
- package/libx/_runtime/fiori/generic/patch.js +1 -1
- package/libx/_runtime/fiori/generic/read.js +151 -57
- package/libx/_runtime/fiori/uiflex/handler/transformRESULT.js +0 -4
- package/libx/_runtime/fiori/uiflex/index.js +1 -1
- package/libx/_runtime/fiori/uiflex/{extensibility/index.js → service.js} +6 -4
- package/libx/_runtime/fiori/utils/delete.js +7 -1
- package/libx/_runtime/hana/Service.js +1 -8
- package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +5 -14
- package/libx/_runtime/hana/execute.js +10 -4
- package/libx/_runtime/hana/pool.js +55 -45
- package/libx/_runtime/hana/search.js +7 -6
- package/libx/_runtime/hana/search2cqn4sql.js +8 -5
- package/libx/_runtime/hana/searchToContains.js +3 -1
- package/libx/_runtime/index.js +5 -5
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +3 -3
- package/libx/_runtime/messaging/Outbox.js +53 -0
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +17 -10
- package/libx/_runtime/messaging/common-utils/connections.js +14 -9
- package/libx/_runtime/messaging/common-utils/waitingTime.js +2 -0
- package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -3
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -2
- package/libx/_runtime/messaging/enterprise-messaging.js +21 -15
- package/libx/_runtime/messaging/file-based.js +5 -5
- package/libx/_runtime/messaging/message-queuing.js +2 -3
- package/libx/_runtime/messaging/outbox/OutboxRunner.js +75 -0
- package/libx/_runtime/messaging/outbox/utils.js +192 -0
- package/libx/_runtime/messaging/service.js +16 -30
- package/libx/_runtime/remote/Service.js +21 -2
- package/libx/_runtime/remote/utils/client.js +15 -3
- package/libx/_runtime/remote/utils/{dataConversion.js → data.js} +12 -2
- package/libx/_runtime/sqlite/Service.js +7 -10
- package/libx/_runtime/sqlite/customBuilder/CustomExpressionBuilder.js +19 -0
- package/libx/_runtime/sqlite/execute.js +18 -12
- package/libx/_runtime/types/api.js +2 -1
- package/libx/odata/{odata2cqn/afterburner.js → afterburner.js} +28 -16
- package/libx/odata/{cqn2odata/index.js → cqn2odata.js} +1 -1
- package/libx/odata/{odata2cqn/grammar.pegjs → grammar.pegjs} +182 -118
- package/libx/odata/index.js +18 -15
- package/libx/odata/parser.js +1 -0
- package/libx/odata/utils.js +57 -0
- package/libx/rest/RestAdapter.js +2 -6
- package/libx/rest/utils/data.js +1 -6
- package/package.json +4 -3
- package/server.js +13 -10
- package/srv/audit-log.cds +87 -0
- package/{libx/_runtime/fiori/uiflex/extensibility/index.cds → srv/flex.cds} +0 -0
- package/srv/flex.js +1 -0
- package/srv/outbox.cds +11 -0
- package/srv/outbox.js +0 -0
- package/libx/_runtime/cds-services/adapter/perf/performance.js +0 -104
- package/libx/_runtime/cds-services/adapter/perf/performanceMeasurement.js +0 -33
- package/libx/odata/odata2cqn/index.js +0 -3
- package/libx/odata/odata2cqn/parser.js +0 -1
- package/libx/odata/readme.md +0 -1
- package/libx/odata/utils/index.js +0 -64
|
@@ -11,15 +11,27 @@ const { isAsteriskColumn } = require('../../common/utils/rewriteAsterisks')
|
|
|
11
11
|
const GET_KEY_VALUE = Symbol.for('sap.cds.getKeyValue')
|
|
12
12
|
const TO_MANY = Symbol.for('sap.cds.toMany')
|
|
13
13
|
const TO_ACTIVE = Symbol.for('sap.cds.toActive')
|
|
14
|
-
|
|
15
14
|
const SKIP_MAPPING = Symbol.for('sap.cds.skipMapping')
|
|
16
15
|
const IDENTIFIER = Symbol.for('sap.cds.identifier')
|
|
17
16
|
const IS_ACTIVE = Symbol.for('sap.cds.isActive')
|
|
18
17
|
const IS_UNION_DRAFT = Symbol.for('sap.cds.isUnionDraft')
|
|
18
|
+
|
|
19
19
|
const { DRAFT_COLUMNS } = require('../../common/constants/draft')
|
|
20
20
|
|
|
21
21
|
const { getCQNUnionFrom } = require('../../common/utils/union')
|
|
22
22
|
|
|
23
|
+
function getCqnCopy(readToOneCQN) {
|
|
24
|
+
const readToOneCQNCopy = JSON.parse(JSON.stringify(readToOneCQN))
|
|
25
|
+
if (readToOneCQN[GET_KEY_VALUE] !== undefined) readToOneCQNCopy[GET_KEY_VALUE] = readToOneCQN[GET_KEY_VALUE]
|
|
26
|
+
if (readToOneCQN[TO_MANY] !== undefined) readToOneCQNCopy[TO_MANY] = readToOneCQN[TO_MANY]
|
|
27
|
+
if (readToOneCQN[TO_ACTIVE] !== undefined) readToOneCQNCopy[TO_ACTIVE] = readToOneCQN[TO_ACTIVE]
|
|
28
|
+
if (readToOneCQN[SKIP_MAPPING] !== undefined) readToOneCQNCopy[SKIP_MAPPING] = readToOneCQN[SKIP_MAPPING]
|
|
29
|
+
if (readToOneCQN[IDENTIFIER] !== undefined) readToOneCQNCopy[IDENTIFIER] = readToOneCQN[IDENTIFIER]
|
|
30
|
+
if (readToOneCQN[IS_ACTIVE] !== undefined) readToOneCQNCopy[IS_ACTIVE] = readToOneCQN[IS_ACTIVE]
|
|
31
|
+
if (readToOneCQN[IS_UNION_DRAFT] !== undefined) readToOneCQNCopy[IS_UNION_DRAFT] = readToOneCQN[IS_UNION_DRAFT]
|
|
32
|
+
return readToOneCQNCopy
|
|
33
|
+
}
|
|
34
|
+
|
|
23
35
|
class JoinCQNFromExpanded {
|
|
24
36
|
constructor(cqn, csn, locale) {
|
|
25
37
|
this._SELECT = Object.assign({}, cqn.SELECT)
|
|
@@ -39,12 +51,8 @@ class JoinCQNFromExpanded {
|
|
|
39
51
|
* @returns {this}
|
|
40
52
|
*/
|
|
41
53
|
buildJoinQueries() {
|
|
42
|
-
const unionTableRef = this._getUnionTable(this._SELECT)
|
|
43
54
|
// side effect: this_aliases is set
|
|
44
|
-
const aliases = this._getTableAlias(this._SELECT, []
|
|
45
|
-
|
|
46
|
-
// Add table aliases to all refs in where part obtained from annotations
|
|
47
|
-
this._adaptAliasForWhere(this._SELECT.where)
|
|
55
|
+
const aliases = this._getTableAlias(this._SELECT, [])
|
|
48
56
|
|
|
49
57
|
// Update elements at WHERE, so there are no issues with ambiguity
|
|
50
58
|
this._adaptWhereOrderBy(this._SELECT, aliases)
|
|
@@ -91,16 +99,16 @@ class JoinCQNFromExpanded {
|
|
|
91
99
|
* @private
|
|
92
100
|
*/
|
|
93
101
|
_createJoinCQNFromExpanded(SELECT, toManyTree, defaultLanguage) {
|
|
94
|
-
const
|
|
95
|
-
const
|
|
102
|
+
const joinArgs = SELECT.from.args
|
|
103
|
+
const isJoinOfTwoSelects = joinArgs && joinArgs.every(a => a.SELECT)
|
|
96
104
|
|
|
97
105
|
const unionTableRef = this._getUnionTable(SELECT)
|
|
98
106
|
const unionTable = unionTableRef && unionTableRef.table
|
|
99
|
-
const tableAlias = this._getTableAlias(SELECT, toManyTree
|
|
107
|
+
const tableAlias = this._getTableAlias(SELECT, toManyTree)
|
|
100
108
|
|
|
101
|
-
const readToOneCQN = this._getReadToOneCQN(SELECT,
|
|
109
|
+
const readToOneCQN = this._getReadToOneCQN(SELECT, isJoinOfTwoSelects ? 'filterExpand' : tableAlias)
|
|
102
110
|
|
|
103
|
-
if (
|
|
111
|
+
if (isJoinOfTwoSelects) {
|
|
104
112
|
// mappings
|
|
105
113
|
const mappings = this._getMappingObject(toManyTree)
|
|
106
114
|
const prefix = `${tableAlias}_`
|
|
@@ -110,7 +118,7 @@ class JoinCQNFromExpanded {
|
|
|
110
118
|
mappings[c.as.replace(prefix, '')] = c.as
|
|
111
119
|
})
|
|
112
120
|
// expand to one
|
|
113
|
-
const entity = this._csn.definitions[
|
|
121
|
+
const entity = this._csn.definitions[joinArgs[0].SELECT.from.SET.args[1].SELECT.from.ref[0]]
|
|
114
122
|
const givenColumns = readToOneCQN.columns
|
|
115
123
|
readToOneCQN.columns = []
|
|
116
124
|
this._expandedToFlat({ entity, givenColumns, readToOneCQN, tableAlias, toManyTree, defaultLanguage })
|
|
@@ -128,6 +136,9 @@ class JoinCQNFromExpanded {
|
|
|
128
136
|
this._expandedToFlat({ entity, givenColumns, readToOneCQN, tableAlias, toManyTree, defaultLanguage })
|
|
129
137
|
}
|
|
130
138
|
|
|
139
|
+
// brute force hack
|
|
140
|
+
readToOneCQN.columns = readToOneCQN.columns.filter(c => c.as !== 'filterExpand_IsActiveEntity')
|
|
141
|
+
|
|
131
142
|
// Add at start, so that the deepest level is post processed first
|
|
132
143
|
this.queries.push({
|
|
133
144
|
SELECT: readToOneCQN,
|
|
@@ -146,11 +157,14 @@ class JoinCQNFromExpanded {
|
|
|
146
157
|
* @returns {string}
|
|
147
158
|
* @private
|
|
148
159
|
*/
|
|
149
|
-
_getTableAlias(SELECT, toManyTree
|
|
150
|
-
return this._createAlias(toManyTree.length === 0 ?
|
|
160
|
+
_getTableAlias(SELECT, toManyTree) {
|
|
161
|
+
return this._createAlias(toManyTree.length === 0 ? this._getRef(SELECT).table : toManyTree.join(':'))
|
|
151
162
|
}
|
|
152
163
|
|
|
153
164
|
_getRef(SELECT) {
|
|
165
|
+
const unionTableRef = this._getUnionTable(SELECT)
|
|
166
|
+
if (unionTableRef) return unionTableRef
|
|
167
|
+
|
|
154
168
|
const table = Object.prototype.hasOwnProperty.call(SELECT.from, 'join')
|
|
155
169
|
? this._getRefFromJoin(SELECT.from.args)
|
|
156
170
|
: SELECT.from
|
|
@@ -241,10 +255,18 @@ class JoinCQNFromExpanded {
|
|
|
241
255
|
: column
|
|
242
256
|
}
|
|
243
257
|
|
|
244
|
-
_adaptJoin(tableAlias, cqn, from) {
|
|
258
|
+
_adaptJoin(tableAlias, cqn, from, mapping = {}) {
|
|
245
259
|
from.args = from.args.slice(0)
|
|
246
260
|
if (Object.prototype.hasOwnProperty.call(from.args[0], 'join')) {
|
|
247
|
-
this._adaptJoin(tableAlias, cqn, from.args[0])
|
|
261
|
+
this._adaptJoin(tableAlias, cqn, from.args[0], mapping)
|
|
262
|
+
if (from.on) {
|
|
263
|
+
// we come here for SiblingEntity with expand
|
|
264
|
+
for (const originalIdentifier in mapping) {
|
|
265
|
+
from.on = from.on.map(column =>
|
|
266
|
+
this._adaptTableNameInColumn(column, originalIdentifier, mapping[originalIdentifier])
|
|
267
|
+
)
|
|
268
|
+
}
|
|
269
|
+
}
|
|
248
270
|
} else {
|
|
249
271
|
const index = from.args[0].ref ? 0 : from.args.length - 1
|
|
250
272
|
const target = Object.assign({}, from.args[index], { as: tableAlias })
|
|
@@ -253,6 +275,7 @@ class JoinCQNFromExpanded {
|
|
|
253
275
|
from.args[index] = target
|
|
254
276
|
from.on = from.on.map(column => this._adaptTableNameInColumn(column, originalIdentifier, tableAlias))
|
|
255
277
|
cqn.columns = cqn.columns.map(column => this._adaptTableNameInColumn(column, originalIdentifier, tableAlias))
|
|
278
|
+
mapping[originalIdentifier] = tableAlias
|
|
256
279
|
}
|
|
257
280
|
}
|
|
258
281
|
|
|
@@ -316,32 +339,6 @@ class JoinCQNFromExpanded {
|
|
|
316
339
|
whereElement.ref && whereElement.ref.splice(0, 1, Object.values(this._aliases)[0])
|
|
317
340
|
}
|
|
318
341
|
|
|
319
|
-
_adaptAliasForFrom(from) {
|
|
320
|
-
if (from.args) {
|
|
321
|
-
from.args.forEach(arg => {
|
|
322
|
-
this._adaptAliasForFrom(arg)
|
|
323
|
-
})
|
|
324
|
-
} else if (from.SELECT) {
|
|
325
|
-
this._adaptAliasForFrom(from.SELECT.from)
|
|
326
|
-
if (from.SELECT.where) {
|
|
327
|
-
this._adaptAliasForWhere(from.SELECT.where)
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
_adaptAliasForWhere(where) {
|
|
333
|
-
if (where) {
|
|
334
|
-
for (const whereElement of where) {
|
|
335
|
-
if (whereElement.SELECT) {
|
|
336
|
-
if (whereElement.SELECT.where) {
|
|
337
|
-
this._adaptAliasForWhere(whereElement.SELECT.where)
|
|
338
|
-
}
|
|
339
|
-
this._adaptAliasForFrom(whereElement.SELECT.from)
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
342
|
_navigationNeedsAlias(element, { table } = {}) {
|
|
346
343
|
const entity = this._csn.definitions[table]
|
|
347
344
|
if (entity) {
|
|
@@ -362,9 +359,9 @@ class JoinCQNFromExpanded {
|
|
|
362
359
|
|
|
363
360
|
if (element.ref.length === 1) {
|
|
364
361
|
element.ref.unshift(tableAlias)
|
|
365
|
-
} else if (this._elementAliasNeedsReplacement(element, this.
|
|
362
|
+
} else if (this._elementAliasNeedsReplacement(element, this._getRef(cqn))) {
|
|
366
363
|
element.ref[0] = tableAlias
|
|
367
|
-
} else if (this._navigationNeedsAlias(element, this.
|
|
364
|
+
} else if (this._navigationNeedsAlias(element, this._getRef(cqn))) {
|
|
368
365
|
element.ref.unshift(tableAlias)
|
|
369
366
|
}
|
|
370
367
|
|
|
@@ -377,7 +374,7 @@ class JoinCQNFromExpanded {
|
|
|
377
374
|
} else if (element.SELECT && element.SELECT.where) {
|
|
378
375
|
element = {
|
|
379
376
|
SELECT: Object.assign({}, element.SELECT, {
|
|
380
|
-
where: this._adaptWhereSELECT(this.
|
|
377
|
+
where: this._adaptWhereSELECT(this._getRef(cqn), element.SELECT.where, tableAlias)
|
|
381
378
|
})
|
|
382
379
|
}
|
|
383
380
|
}
|
|
@@ -480,6 +477,8 @@ class JoinCQNFromExpanded {
|
|
|
480
477
|
const toManyColumns = []
|
|
481
478
|
const mappings = this._getMappingObject(toManyTree)
|
|
482
479
|
|
|
480
|
+
const readToOneCQNCopy = getCqnCopy(readToOneCQN)
|
|
481
|
+
|
|
483
482
|
for (const column of givenColumns) {
|
|
484
483
|
let navigation
|
|
485
484
|
if (column.expand) {
|
|
@@ -523,7 +522,16 @@ class JoinCQNFromExpanded {
|
|
|
523
522
|
}
|
|
524
523
|
|
|
525
524
|
// only as second step handle expand to many, or else keys might still be unknown
|
|
526
|
-
this._toMany({
|
|
525
|
+
this._toMany({
|
|
526
|
+
entity,
|
|
527
|
+
readToOneCQN,
|
|
528
|
+
tableAlias,
|
|
529
|
+
toManyColumns,
|
|
530
|
+
toManyTree,
|
|
531
|
+
mappings,
|
|
532
|
+
defaultLanguage,
|
|
533
|
+
readToOneCQNCopy
|
|
534
|
+
})
|
|
527
535
|
}
|
|
528
536
|
|
|
529
537
|
adjustOrderBy(orderBy, mappings, column, tableAlias) {
|
|
@@ -562,13 +570,15 @@ class JoinCQNFromExpanded {
|
|
|
562
570
|
const on = []
|
|
563
571
|
for (const key in entity._target.keys) {
|
|
564
572
|
if (key !== 'IsActiveEntity') {
|
|
573
|
+
if (on.length) on.push('AND')
|
|
565
574
|
on.push({ ref: [`${tableAlias}_drafts`, key] }, '=', { ref: [tableAlias, key] })
|
|
566
575
|
}
|
|
567
576
|
}
|
|
577
|
+
|
|
568
578
|
return {
|
|
569
579
|
args: [cqn, { ref: [draftTable], as: `${tableAlias}_drafts` }],
|
|
570
580
|
join: 'left',
|
|
571
|
-
on: on
|
|
581
|
+
on: ['(', ...on, ')']
|
|
572
582
|
}
|
|
573
583
|
}
|
|
574
584
|
|
|
@@ -587,8 +597,8 @@ class JoinCQNFromExpanded {
|
|
|
587
597
|
)
|
|
588
598
|
}
|
|
589
599
|
|
|
590
|
-
_getTarget(entity, column) {
|
|
591
|
-
const navigation = getNavigationIfStruct(entity, column.ref)
|
|
600
|
+
_getTarget(entity, column, parentAlias) {
|
|
601
|
+
const navigation = getNavigationIfStruct(entity, column.ref[0] === parentAlias ? column.ref.slice(1) : column.ref)
|
|
592
602
|
return (navigation && navigation.target) || column.ref[0]
|
|
593
603
|
}
|
|
594
604
|
|
|
@@ -606,17 +616,19 @@ class JoinCQNFromExpanded {
|
|
|
606
616
|
*/
|
|
607
617
|
// eslint-disable-next-line complexity
|
|
608
618
|
_addJoinAndElements({ column, entity, readToOneCQN, toManyTree, parentAlias, defaultLanguage }) {
|
|
609
|
-
const extendedToManyTree = toManyTree.concat(column.ref)
|
|
619
|
+
const extendedToManyTree = toManyTree.concat(column.ref[0] === parentAlias ? column.ref.slice(1) : column.ref)
|
|
610
620
|
const tableAlias = this._createAlias(extendedToManyTree.join(':'))
|
|
611
|
-
const target = this._getTarget(entity, column)
|
|
621
|
+
const target = this._getTarget(entity, column, parentAlias)
|
|
622
|
+
|
|
623
|
+
const name = column.ref[column.ref.length - 1]
|
|
624
|
+
const element = name && entity.elements[name]
|
|
612
625
|
|
|
613
626
|
// if union always only expand with active, otherwise evaluate flag
|
|
614
627
|
// if flag shows false, we check entity for associations to non draft
|
|
615
628
|
const activeTableRequired =
|
|
616
629
|
readToOneCQN[IS_UNION_DRAFT] ||
|
|
617
630
|
readToOneCQN[IS_ACTIVE] ||
|
|
618
|
-
(
|
|
619
|
-
!entity.elements[column.ref[0]]['@odata.draft.enclosed']) ||
|
|
631
|
+
(element && element.type === 'cds.Association' && !element['@odata.draft.enclosed']) ||
|
|
620
632
|
!this._csn.definitions[target]._isDraftEnabled
|
|
621
633
|
|
|
622
634
|
const colTarget = target && ensureUnlocalized(target)
|
|
@@ -626,9 +638,7 @@ class JoinCQNFromExpanded {
|
|
|
626
638
|
(colTarget && this._csn.definitions[colTarget] && this._csn.definitions[colTarget]['@cds.localized'] === false)
|
|
627
639
|
|
|
628
640
|
const join =
|
|
629
|
-
column.ref[0] === 'DraftAdministrativeData' || !
|
|
630
|
-
? 'left'
|
|
631
|
-
: 'inner'
|
|
641
|
+
column.ref[0] === 'DraftAdministrativeData' || !(element && element.notNull) || this._isDraft ? 'left' : 'inner'
|
|
632
642
|
|
|
633
643
|
const args = [
|
|
634
644
|
readToOneCQN.from.SET ? this._unionToSubQuery(readToOneCQN) : readToOneCQN.from,
|
|
@@ -669,11 +679,11 @@ class JoinCQNFromExpanded {
|
|
|
669
679
|
}
|
|
670
680
|
|
|
671
681
|
// special case of navigation to one requires additional LEFT JOIN and CASE for HasDraftEntity
|
|
672
|
-
const compToOne = this._isNavigationToOne(readToOneCQN[IS_ACTIVE],
|
|
682
|
+
const compToOne = this._isNavigationToOne(readToOneCQN[IS_ACTIVE], element)
|
|
673
683
|
const index = column.expand.findIndex(col => col.ref && col.ref[col.ref.length - 1] === 'HasDraftEntity')
|
|
674
684
|
|
|
675
685
|
if (compToOne && index !== -1) {
|
|
676
|
-
readToOneCQN.from = this._addJoinCompToOne(readToOneCQN.from,
|
|
686
|
+
readToOneCQN.from = this._addJoinCompToOne(readToOneCQN.from, element, tableAlias)
|
|
677
687
|
if (activeTableRequired) {
|
|
678
688
|
column.expand[index] = {
|
|
679
689
|
xpr: [
|
|
@@ -823,7 +833,9 @@ class JoinCQNFromExpanded {
|
|
|
823
833
|
const subSelectColumns = this._getSubSelectColumns(readToOneCQN)
|
|
824
834
|
|
|
825
835
|
if (subSelectColumns.length === 0) {
|
|
826
|
-
return entity._relations[
|
|
836
|
+
return entity._relations[
|
|
837
|
+
tableAlias === columns[0] || parentAlias === columns[0] ? columns.slice(1) : columns
|
|
838
|
+
].join(tableAlias, parentAlias)
|
|
827
839
|
}
|
|
828
840
|
|
|
829
841
|
const aliases = this._getAliases(subSelectColumns)
|
|
@@ -957,7 +969,16 @@ class JoinCQNFromExpanded {
|
|
|
957
969
|
return DRAFT_COLUMNS.includes(ref[0])
|
|
958
970
|
}
|
|
959
971
|
|
|
960
|
-
_toMany({
|
|
972
|
+
_toMany({
|
|
973
|
+
entity,
|
|
974
|
+
readToOneCQN,
|
|
975
|
+
tableAlias,
|
|
976
|
+
toManyColumns,
|
|
977
|
+
toManyTree,
|
|
978
|
+
mappings,
|
|
979
|
+
defaultLanguage,
|
|
980
|
+
readToOneCQNCopy
|
|
981
|
+
}) {
|
|
961
982
|
if (toManyColumns.length === 0) {
|
|
962
983
|
return
|
|
963
984
|
}
|
|
@@ -968,7 +989,7 @@ class JoinCQNFromExpanded {
|
|
|
968
989
|
const select = this._buildExpandedCQN({
|
|
969
990
|
column,
|
|
970
991
|
entity,
|
|
971
|
-
readToOneCQN,
|
|
992
|
+
readToOneCQN: readToOneCQNCopy,
|
|
972
993
|
toManyTree,
|
|
973
994
|
mappings,
|
|
974
995
|
parentAlias,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
const { hasExpand, createJoinCQNFromExpanded } = require('./expandCQNToJoin')
|
|
2
2
|
const rawToExpanded = require('./rawToExpanded')
|
|
3
|
+
const expandV2 = require('./expand-v2')
|
|
3
4
|
|
|
4
5
|
module.exports = {
|
|
5
6
|
hasExpand,
|
|
6
7
|
createJoinCQNFromExpanded,
|
|
7
|
-
rawToExpanded
|
|
8
|
+
rawToExpanded,
|
|
9
|
+
expandV2
|
|
8
10
|
}
|
|
@@ -1,27 +1,14 @@
|
|
|
1
|
-
// REVISIT: use templating mechanism (resp. results.metadata, once available) to make more efficient
|
|
2
|
-
|
|
3
1
|
const { getEntityFromCQN } = require('../../common/utils/entityFromCqn')
|
|
2
|
+
const getTemplate = require('../../common/utils/template')
|
|
3
|
+
const templateProcessor = require('../../common/utils/templateProcessor')
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
// and 100% of all currently existing stakeholder projects don't.
|
|
9
|
-
// but that's not that easy to fix -> see comment about results.metadata below
|
|
10
|
-
for (const row of result) {
|
|
11
|
-
for (const column in row) {
|
|
12
|
-
if (elements[column] === undefined || row[column] === undefined) continue
|
|
5
|
+
const _pick = element => {
|
|
6
|
+
if (element.kind === 'element' && element.items) return 'arrayed'
|
|
7
|
+
}
|
|
13
8
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
} else if (elements[column].is2many) {
|
|
18
|
-
_toArray(row[column], elements[column]._target.elements)
|
|
19
|
-
} else if (elements[column].is2one) {
|
|
20
|
-
_toArray([row[column]], elements[column]._target.elements)
|
|
21
|
-
} else if (elements[column].elements) {
|
|
22
|
-
_toArray([row[column]], elements[column].elements)
|
|
23
|
-
}
|
|
24
|
-
}
|
|
9
|
+
const _processFn = ({ row, key, plain }) => {
|
|
10
|
+
if (plain === 'arrayed' && row && row[key] && (typeof row[key] === 'string' || Buffer.isBuffer(row[key]))) {
|
|
11
|
+
row[key] = JSON.parse(row[key])
|
|
25
12
|
}
|
|
26
13
|
}
|
|
27
14
|
|
|
@@ -35,12 +22,12 @@ const _toArray = (result, elements) => {
|
|
|
35
22
|
module.exports = function (result, req) {
|
|
36
23
|
if (!this.model) return
|
|
37
24
|
|
|
38
|
-
if (!Array.isArray(result)) result = [result]
|
|
39
|
-
|
|
40
|
-
// REVISIT: We need results.metadata to make that more efficient
|
|
41
|
-
// results.metadata ~= cds.infer(req.query).metadata
|
|
42
|
-
// REVISIT: No entity for sets/unions outside of common draft scenarios
|
|
43
25
|
const entity = getEntityFromCQN(req, this)
|
|
44
26
|
if (!entity) return
|
|
45
|
-
|
|
27
|
+
|
|
28
|
+
const template = getTemplate('db-arrayed', this, entity, { pick: _pick })
|
|
29
|
+
if (template.elements.size === 0) return
|
|
30
|
+
|
|
31
|
+
for (const row of Array.isArray(result) ? result : [result])
|
|
32
|
+
templateProcessor({ processFn: _processFn, row, template })
|
|
46
33
|
}
|
|
@@ -14,7 +14,7 @@ const cds = require('../../cds')
|
|
|
14
14
|
const normalizeTimeData = require('../utils/normalizeTimeData')
|
|
15
15
|
|
|
16
16
|
const { enrichDataWithKeysFromWhere } = require('../../common/utils/keys')
|
|
17
|
-
const
|
|
17
|
+
const propagateForeignKeys = require('../utils/propagateForeignKeys')
|
|
18
18
|
const getTemplate = require('../../common/utils/template')
|
|
19
19
|
const templateProcessor = require('../../common/utils/templateProcessor')
|
|
20
20
|
|
|
@@ -25,12 +25,14 @@ const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
|
|
|
25
25
|
const _isManaged = (category, event) =>
|
|
26
26
|
(category === '@cds.on.insert' && event === 'CREATE') || (category === '@cds.on.update' && event === 'UPDATE')
|
|
27
27
|
|
|
28
|
+
const _shouldGenerateUUID = element => element.key && !DRAFT_COLUMNS_MAP[element.name] && element.type === 'cds.UUID'
|
|
29
|
+
|
|
28
30
|
const _processComplexCategory = ({ row, key, val, category, req, element }) => {
|
|
29
31
|
const categoryArgs = category.args
|
|
30
32
|
category = category.category
|
|
31
33
|
|
|
32
34
|
// propagate keys
|
|
33
|
-
if (category === 'propagateForeignKeys') {
|
|
35
|
+
if (category === 'propagateForeignKeys' && row[key]) {
|
|
34
36
|
propagateForeignKeys(key, row, element._foreignKeys, element._isCompositionEffective)
|
|
35
37
|
return
|
|
36
38
|
}
|
|
@@ -73,6 +75,20 @@ const _processCategory = ({ category, row, key, element, val, req }) => {
|
|
|
73
75
|
return
|
|
74
76
|
}
|
|
75
77
|
|
|
78
|
+
if (category === 'comp2one') {
|
|
79
|
+
if (!val) return
|
|
80
|
+
|
|
81
|
+
for (const keyName in element._target.keys) {
|
|
82
|
+
const k = element._target.keys[keyName]
|
|
83
|
+
if (k.type === 'cds.Association' || k.name === 'IsActiveEntity') continue
|
|
84
|
+
|
|
85
|
+
if (k.type !== 'cds.UUID' && !(k.name in val)) {
|
|
86
|
+
req.error(400, 'ASSERT_NOT_NULL', key + '[' + k + ']', [key + '[' + k + ']'])
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
76
92
|
// not null without default (for better error message)
|
|
77
93
|
if (category === '!default' && val == null && req.event === 'CREATE') {
|
|
78
94
|
req.error(400, 'ASSERT_NOT_NULL', key, [key])
|
|
@@ -101,12 +117,24 @@ const processorFn =
|
|
|
101
117
|
}
|
|
102
118
|
}
|
|
103
119
|
|
|
120
|
+
const _isVirtualOrCalculated = element => {
|
|
121
|
+
if (element.virtual) return true
|
|
122
|
+
if (
|
|
123
|
+
element.parent &&
|
|
124
|
+
element.parent.projection &&
|
|
125
|
+
element.parent.projection.columns &&
|
|
126
|
+
element.parent.projection.columns.find(c => c.as === element.name && (c.xpr || c.val || c.func))
|
|
127
|
+
) {
|
|
128
|
+
return true
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
104
132
|
// params: element, target, parent, templateElements
|
|
105
|
-
const
|
|
133
|
+
const _pickCRUD = element => {
|
|
106
134
|
// collect actions to apply
|
|
107
135
|
const categories = []
|
|
108
136
|
|
|
109
|
-
if (element
|
|
137
|
+
if (_isVirtualOrCalculated(element)) {
|
|
110
138
|
categories.push('virtual')
|
|
111
139
|
return { categories } // > no need to continue
|
|
112
140
|
}
|
|
@@ -136,22 +164,37 @@ const _pick = element => {
|
|
|
136
164
|
categories.push('associationEffective')
|
|
137
165
|
}
|
|
138
166
|
|
|
167
|
+
// REVISIT: element._foreignKeys.length seems to be a very broad check
|
|
139
168
|
if (element.isAssociation && element._foreignKeys.length) {
|
|
140
169
|
categories.push({ category: 'propagateForeignKeys' })
|
|
141
170
|
}
|
|
142
171
|
|
|
143
|
-
|
|
144
|
-
if (element.key && !DRAFT_COLUMNS_MAP[element.name] && element.type === 'cds.UUID') {
|
|
172
|
+
if (_shouldGenerateUUID(element)) {
|
|
145
173
|
categories.push('uuid')
|
|
146
174
|
}
|
|
147
175
|
|
|
176
|
+
if (element.isComposition && element.is2one) {
|
|
177
|
+
categories.push('comp2one')
|
|
178
|
+
}
|
|
179
|
+
|
|
148
180
|
if (categories.length) return { categories }
|
|
149
181
|
}
|
|
150
182
|
|
|
151
|
-
const
|
|
183
|
+
const _pickDraft = element => {
|
|
152
184
|
// collect actions to apply
|
|
153
185
|
const categories = []
|
|
186
|
+
|
|
154
187
|
if (element.virtual) categories.push('virtual')
|
|
188
|
+
|
|
189
|
+
// REVISIT: element._foreignKeys.length seems to be a very broad check
|
|
190
|
+
if (element.isAssociation && element._foreignKeys.length) {
|
|
191
|
+
categories.push({ category: 'propagateForeignKeys' })
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (_shouldGenerateUUID(element)) {
|
|
195
|
+
categories.push('uuid')
|
|
196
|
+
}
|
|
197
|
+
|
|
155
198
|
if (categories.length) return { categories }
|
|
156
199
|
}
|
|
157
200
|
|
|
@@ -174,10 +217,9 @@ function _handler(req) {
|
|
|
174
217
|
|
|
175
218
|
let template
|
|
176
219
|
if (draft) {
|
|
177
|
-
|
|
178
|
-
template = getTemplate('db-virtual', this, target, { pick: _pickVirtual })
|
|
220
|
+
template = getTemplate('db-input-draft', this, target, { pick: _pickDraft })
|
|
179
221
|
} else {
|
|
180
|
-
template = getTemplate('db-input', this, target, { pick:
|
|
222
|
+
template = getTemplate('db-input-crud', this, target, { pick: _pickCRUD })
|
|
181
223
|
}
|
|
182
224
|
|
|
183
225
|
if (template.elements.size === 0) return
|