@sap/cds 5.7.5 → 5.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 +97 -0
- package/app/fiori/routes.js +1 -1
- package/bin/deploy/to-hana/cfUtil.js +251 -138
- package/bin/deploy/to-hana/gitUtil.js +55 -0
- package/bin/deploy/to-hana/hana.js +92 -93
- package/bin/deploy/to-hana/hdiDeployUtil.js +42 -27
- package/bin/deploy/to-hana/index.js +14 -13
- package/bin/mtx/in-cds.js +1 -0
- package/bin/serve.js +1 -1
- package/bin/version.js +1 -0
- package/lib/compile/cdsc.js +0 -6
- package/lib/compile/resolve.js +1 -1
- package/lib/compile/to/srvinfo.js +1 -1
- package/lib/core/classes.js +21 -1
- package/lib/env/index.js +3 -2
- package/lib/env/requires.js +4 -0
- package/lib/i18n/localize.js +5 -8
- package/lib/index.js +1 -0
- package/lib/log/errors.js +1 -1
- package/lib/log/format/kibana.js +3 -3
- package/lib/ql/SELECT.js +2 -2
- package/lib/req/cds-context.js +1 -1
- package/lib/req/context.js +1 -1
- package/lib/serve/Transaction.js +9 -5
- package/lib/serve/index.js +13 -21
- package/lib/utils/tests.js +90 -66
- package/libx/_runtime/audit/generic/personal/modification.js +0 -8
- package/libx/_runtime/auth/index.js +7 -6
- package/libx/_runtime/auth/strategies/dwc.js +43 -0
- package/libx/_runtime/auth/utils.js +24 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +11 -32
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +12 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +7 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +24 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +43 -38
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +11 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/boundToCQN.js +1 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/deleteToCQN.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/orderByToCQN.js +9 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +17 -30
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +12 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/AbstractEdmStructuredType.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/ResourcePathParser.js +23 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriHelper.js +7 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriTokenizer.js +2 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/PrimitiveValueDecoder.js +19 -47
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/ValueConverter.js +4 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +7 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/CommandExecutor.js +0 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/ConditionalRequestControlCommand.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ContextURLFactory.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ErrorJsonSerializer.js +2 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/TrustedResourceJsonSerializer.js +2 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/ConditionalRequestValidator.js +6 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +41 -17
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +1 -17
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +60 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +47 -10
- package/libx/_runtime/cds-services/adapter/rest/Rest.js +22 -1
- package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +8 -3
- package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +3 -0
- package/libx/_runtime/cds-services/services/utils/columns.js +5 -1
- package/libx/_runtime/cds-services/services/utils/compareJson.js +15 -16
- package/libx/_runtime/cds-services/services/utils/differ.js +2 -8
- package/libx/_runtime/common/aspects/Association.js +16 -0
- package/libx/_runtime/common/composition/data.js +28 -37
- package/libx/_runtime/common/composition/delete.js +107 -58
- package/libx/_runtime/common/composition/index.js +3 -3
- package/libx/_runtime/common/composition/insert.js +14 -27
- package/libx/_runtime/common/composition/tree.js +1 -1
- package/libx/_runtime/common/composition/update.js +39 -34
- package/libx/_runtime/common/error/frontend.js +19 -5
- package/libx/_runtime/common/generic/auth.js +20 -85
- package/libx/_runtime/common/generic/crud.js +22 -1
- package/libx/_runtime/common/i18n/messages.properties +2 -1
- package/libx/_runtime/common/utils/cqn.js +2 -6
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +95 -122
- package/libx/_runtime/common/utils/csn.js +29 -6
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +21 -1
- package/libx/_runtime/common/utils/keys.js +2 -1
- package/libx/_runtime/common/utils/path.js +1 -1
- package/libx/_runtime/common/utils/resolveView.js +12 -4
- package/libx/_runtime/common/utils/rewriteAsterisks.js +27 -13
- package/libx/_runtime/common/utils/search2cqn4sql.js +11 -6
- package/libx/_runtime/common/utils/structured.js +10 -4
- package/libx/_runtime/common/utils/vcap.js +27 -10
- package/libx/_runtime/db/data-conversion/post-processing.js +20 -13
- package/libx/_runtime/db/expand/expand-v2.js +21 -12
- package/libx/_runtime/db/expand/expandCQNToJoin.js +67 -26
- package/libx/_runtime/db/expand/index.js +3 -0
- package/libx/_runtime/db/generic/create.js +0 -10
- package/libx/_runtime/db/generic/index.js +3 -0
- package/libx/_runtime/db/generic/read.js +2 -24
- package/libx/_runtime/db/generic/rewrite.js +1 -3
- package/libx/_runtime/db/generic/update.js +1 -1
- package/libx/_runtime/db/query/delete.js +10 -4
- package/libx/_runtime/db/query/insert.js +3 -4
- package/libx/_runtime/db/query/read.js +4 -1
- package/libx/_runtime/db/query/update.js +5 -5
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +9 -2
- package/libx/_runtime/db/sql-builder/FunctionBuilder.js +3 -0
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +7 -3
- package/libx/_runtime/db/sql-builder/index.js +3 -0
- package/libx/_runtime/db/utils/columns.js +5 -2
- package/libx/_runtime/db/utils/deep.js +16 -14
- package/libx/_runtime/db/utils/generateAliases.js +56 -6
- package/libx/_runtime/fiori/generic/before.js +73 -49
- package/libx/_runtime/fiori/generic/edit.js +14 -18
- package/libx/_runtime/fiori/generic/patch.js +8 -11
- package/libx/_runtime/fiori/generic/read.js +20 -19
- package/libx/_runtime/fiori/generic/readOverDraft.js +1 -4
- package/libx/_runtime/fiori/utils/handler.js +1 -11
- package/libx/_runtime/hana/Service.js +1 -1
- package/libx/_runtime/hana/conversion.js +12 -1
- package/libx/_runtime/hana/dynatrace.js +11 -5
- package/libx/_runtime/hana/execute.js +132 -19
- package/libx/_runtime/hana/search.js +3 -3
- package/libx/_runtime/hana/search2cqn4sql.js +23 -25
- package/libx/_runtime/hana/searchToContains.js +1 -1
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +0 -1
- package/libx/_runtime/messaging/enterprise-messaging.js +1 -0
- package/libx/_runtime/messaging/file-based.js +3 -1
- package/libx/_runtime/messaging/service.js +4 -1
- package/libx/_runtime/remote/utils/client.js +41 -24
- package/libx/_runtime/remote/utils/data.js +54 -12
- package/libx/_runtime/sqlite/Service.js +1 -1
- package/libx/_runtime/sqlite/conversion.js +10 -0
- package/libx/_runtime/types/api.js +2 -2
- package/libx/gql/resolvers/crud/update.js +8 -5
- package/libx/gql/resolvers/parse/ast/enrich.js +1 -0
- package/libx/odata/afterburner.js +29 -6
- package/libx/odata/cqn2odata.js +9 -0
- package/libx/odata/grammar.pegjs +49 -21
- package/libx/odata/index.js +2 -2
- package/libx/odata/parser.js +1 -1
- package/libx/odata/utils.js +2 -2
- package/libx/rest/RestAdapter.js +29 -1
- package/libx/rest/middleware/auth.js +1 -3
- package/libx/rest/middleware/parse.js +1 -0
- package/package.json +1 -1
- package/server.js +1 -1
- package/bin/deploy/to-hana/logger.js +0 -27
- package/bin/deploy/to-hana/runCommand.js +0 -113
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +0 -37
- package/libx/_runtime/common/utils/auth.js +0 -16
|
@@ -152,9 +152,15 @@ const _newColumns = (columns = [], transition, service, withAlias = false) => {
|
|
|
152
152
|
const newColumns = []
|
|
153
153
|
|
|
154
154
|
columns.forEach(column => {
|
|
155
|
+
let newColumn
|
|
156
|
+
if (column.func) {
|
|
157
|
+
newColumn = { ...column }
|
|
158
|
+
newColumn.args = _newColumns(column.args, transition, service, withAlias)
|
|
159
|
+
newColumns.push(newColumn)
|
|
160
|
+
return newColumns
|
|
161
|
+
}
|
|
155
162
|
const mapped = column.ref && transition.mapping.get(column.ref[0])
|
|
156
163
|
|
|
157
|
-
let newColumn
|
|
158
164
|
if (mapped && mapped.ref) {
|
|
159
165
|
newColumn = { ...column }
|
|
160
166
|
|
|
@@ -238,7 +244,7 @@ const _newEntries = (entries = [], transition, service) =>
|
|
|
238
244
|
const _newWhere = (where = [], transition, tableName, alias, isSubselect = false) => {
|
|
239
245
|
const newWhere = where.map(whereElement => {
|
|
240
246
|
const newWhereElement = { ...whereElement }
|
|
241
|
-
if (!whereElement.ref && !whereElement.SELECT) return whereElement
|
|
247
|
+
if (!whereElement.ref && !whereElement.SELECT && !whereElement.func) return whereElement
|
|
242
248
|
if (whereElement.SELECT && whereElement.SELECT.where) {
|
|
243
249
|
newWhereElement.SELECT.where = _newWhere(whereElement.SELECT.where, transition, tableName, alias, true)
|
|
244
250
|
return newWhereElement
|
|
@@ -246,6 +252,9 @@ const _newWhere = (where = [], transition, tableName, alias, isSubselect = false
|
|
|
246
252
|
if (newWhereElement.ref) {
|
|
247
253
|
_newWhereRef(newWhereElement, transition, alias, tableName, isSubselect)
|
|
248
254
|
return newWhereElement
|
|
255
|
+
} else if (newWhereElement.func) {
|
|
256
|
+
newWhereElement.args = _newWhere(newWhereElement.args, transition, tableName, alias)
|
|
257
|
+
return newWhereElement
|
|
249
258
|
} else {
|
|
250
259
|
return whereElement
|
|
251
260
|
}
|
|
@@ -337,8 +346,7 @@ const _newSelect = (query, transitions, service) => {
|
|
|
337
346
|
}
|
|
338
347
|
if (!newSelect.columns && targetTransition.mapping.size) newSelect.columns = _initialColumns(targetTransition)
|
|
339
348
|
if (newSelect.columns) {
|
|
340
|
-
|
|
341
|
-
rewriteAsterisks({ SELECT: query.SELECT }, service.model, isDB)
|
|
349
|
+
rewriteAsterisks({ SELECT: query.SELECT }, service.model, { _4db: service instanceof cds.DatabaseService })
|
|
342
350
|
newSelect.columns = _newColumns(newSelect.columns, targetTransition, service, service.kind !== 'app-service')
|
|
343
351
|
}
|
|
344
352
|
if (newSelect.having) newSelect.having = _newColumns(newSelect.having, targetTransition)
|
|
@@ -30,10 +30,11 @@ const _cqlDraftColumns = target => {
|
|
|
30
30
|
]
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const _expandColumn = (column, target,
|
|
33
|
+
const _expandColumn = (column, target, _4db) => {
|
|
34
34
|
if (!(column.ref && column.expand)) return
|
|
35
35
|
const nextTarget = getNavigationIfStruct(target, column.ref)
|
|
36
|
-
if (nextTarget && nextTarget._target && nextTarget._target.elements)
|
|
36
|
+
if (nextTarget && nextTarget._target && nextTarget._target.elements)
|
|
37
|
+
_rewriteAsterisks(column, nextTarget._target, _4db)
|
|
37
38
|
return column
|
|
38
39
|
}
|
|
39
40
|
|
|
@@ -46,32 +47,33 @@ const rewriteExpandAsterisk = (columns, target) => {
|
|
|
46
47
|
const { expand } = columns.splice(expandAllColIdx, 1)[0]
|
|
47
48
|
for (const elName in target.elements) {
|
|
48
49
|
if (target.elements[elName]._target && !columns.find(col => col.expand && col.ref && col.ref[0] === elName)) {
|
|
50
|
+
if (elName === 'SiblingEntity') continue
|
|
49
51
|
columns.push({ ref: [elName], expand: [...expand] })
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
const _rewriteAsterisk = (columns, target,
|
|
57
|
+
const _rewriteAsterisk = (columns, target, _4db, isRoot) => {
|
|
56
58
|
const asteriskColumnIndex = columns.findIndex(col => isAsteriskColumn(col))
|
|
57
59
|
if (asteriskColumnIndex > -1) {
|
|
58
60
|
columns.splice(
|
|
59
61
|
asteriskColumnIndex,
|
|
60
62
|
1,
|
|
61
|
-
...getColumns(target, {
|
|
63
|
+
...getColumns(target, { _4db })
|
|
62
64
|
.map(c => ({ ref: [c.name] }))
|
|
63
65
|
.filter(c => !columns.find(_isDuplicate(c)) && (isRoot || c.ref[0] !== 'DraftAdministrativeData_DraftUUID'))
|
|
64
66
|
)
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
const _rewriteAsterisks = (cqn, target,
|
|
70
|
+
const _rewriteAsterisks = (cqn, target, _4db, isRoot) => {
|
|
69
71
|
if (cqn.expand === '*') cqn.expand = ['*']
|
|
70
72
|
const columns = cqn.expand || cqn.columns
|
|
71
|
-
_rewriteAsterisk(columns, target,
|
|
73
|
+
_rewriteAsterisk(columns, target, _4db, isRoot)
|
|
72
74
|
rewriteExpandAsterisk(columns, target)
|
|
73
75
|
for (const column of columns) {
|
|
74
|
-
_expandColumn(column, target,
|
|
76
|
+
_expandColumn(column, target, _4db)
|
|
75
77
|
}
|
|
76
78
|
return columns
|
|
77
79
|
}
|
|
@@ -83,9 +85,17 @@ const _targetOfQueryIfNotDraft = (query, model) => {
|
|
|
83
85
|
return target
|
|
84
86
|
}
|
|
85
87
|
|
|
86
|
-
const rewriteAsterisks = (query, model,
|
|
88
|
+
const rewriteAsterisks = (query, model, options) => {
|
|
89
|
+
/*
|
|
90
|
+
* REVISIT:
|
|
91
|
+
* - _4db: called on db level
|
|
92
|
+
* - _4fiori: cqn2cqn4sql called in a fiori handler
|
|
93
|
+
* this is extremely obfuscated!
|
|
94
|
+
*/
|
|
95
|
+
const { _4db, _4fiori } = options
|
|
96
|
+
|
|
87
97
|
if (!query.SELECT.columns || !query.SELECT.columns.length) {
|
|
88
|
-
if (
|
|
98
|
+
if (_4db || _4fiori) {
|
|
89
99
|
if (
|
|
90
100
|
query.SELECT.from.SET &&
|
|
91
101
|
query.SELECT.from.SET.args[0] &&
|
|
@@ -101,7 +111,7 @@ const rewriteAsterisks = (query, model, db = false, isDraft = false, onlyKeys =
|
|
|
101
111
|
for (const arg of query.SELECT.from.args) {
|
|
102
112
|
const _targetName = arg.ref[0].id || arg.ref[0]
|
|
103
113
|
const _target = model.definitions[ensureNoDraftsSuffix(_targetName)]
|
|
104
|
-
const columns = getColumns(_target, {
|
|
114
|
+
const columns = getColumns(_target, { _4db })
|
|
105
115
|
.filter(
|
|
106
116
|
c =>
|
|
107
117
|
!query.SELECT.columns.some(
|
|
@@ -116,16 +126,20 @@ const rewriteAsterisks = (query, model, db = false, isDraft = false, onlyKeys =
|
|
|
116
126
|
} else {
|
|
117
127
|
const target = _targetOfQueryIfNotDraft(query, model)
|
|
118
128
|
if (!target) return
|
|
119
|
-
|
|
120
|
-
|
|
129
|
+
|
|
130
|
+
query.SELECT.columns = getColumns(target, { _4db }).map(col => ({ ref: [col.name] }))
|
|
131
|
+
if (_4db && target._isDraftEnabled) query.SELECT.columns.push(..._cqlDraftColumns(target))
|
|
121
132
|
}
|
|
122
133
|
}
|
|
134
|
+
|
|
123
135
|
return
|
|
124
136
|
}
|
|
137
|
+
|
|
125
138
|
const target = _targetOfQueryIfNotDraft(query, model)
|
|
126
139
|
if (!target) return
|
|
140
|
+
|
|
127
141
|
// REVISIT: Also support JOINs/SETs here
|
|
128
|
-
query.SELECT.columns = _rewriteAsterisks(query.SELECT, target,
|
|
142
|
+
query.SELECT.columns = _rewriteAsterisks(query.SELECT, target, _4db, true)
|
|
129
143
|
}
|
|
130
144
|
|
|
131
145
|
module.exports = {
|
|
@@ -18,8 +18,8 @@ const _search2cqn4sql = (query, model, options = {}) => {
|
|
|
18
18
|
|
|
19
19
|
// Call custom (optimized search to cqn for sql implementation) that tries
|
|
20
20
|
// to optimize the search behavior for a specific database service.
|
|
21
|
-
//
|
|
22
|
-
if (typeof search2cqn4sql === 'function' && !query.SELECT.
|
|
21
|
+
// REVISIT: $search query option combined with $count is not currently optimized
|
|
22
|
+
if (typeof search2cqn4sql === 'function' && !query.SELECT.count) {
|
|
23
23
|
const search2cqnOptions = { columns, locale: options.locale }
|
|
24
24
|
return search2cqn4sql(query, entity, search2cqnOptions)
|
|
25
25
|
}
|
|
@@ -30,9 +30,14 @@ const _search2cqn4sql = (query, model, options = {}) => {
|
|
|
30
30
|
query._aggregated || /* if new parser */ query.SELECT.groupBy ? query.having(expression) : query.where(expression)
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
const search2cqn4sql = (query, model, options) => {
|
|
34
|
+
if (query.SELECT.from.SET) {
|
|
35
|
+
return query.SELECT.from.SET.args.forEach(arg => _search2cqn4sql(arg, model, options))
|
|
36
|
+
}
|
|
37
|
+
|
|
37
38
|
return _search2cqn4sql(query, model, options)
|
|
38
39
|
}
|
|
40
|
+
|
|
41
|
+
// convert $search system query option to WHERE/HAVING clause using
|
|
42
|
+
// the operator LIKE or CONTAINS
|
|
43
|
+
module.exports = search2cqn4sql
|
|
@@ -99,6 +99,7 @@ const _getVal = (data, name) => {
|
|
|
99
99
|
|
|
100
100
|
const _filterForStructProperty = (structElement, structData, op, prefix = '', nav = []) => {
|
|
101
101
|
const filterArray = []
|
|
102
|
+
const andOr = op === '!=' ? 'or' : 'and'
|
|
102
103
|
|
|
103
104
|
for (const elementName in structElement.elements) {
|
|
104
105
|
const element = structElement.elements[elementName]
|
|
@@ -123,8 +124,8 @@ const _filterForStructProperty = (structElement, structData, op, prefix = '', na
|
|
|
123
124
|
for (const key in assoc._target.keys) {
|
|
124
125
|
if (element.name === `${assocName}_${key}`) {
|
|
125
126
|
const ref = [`${prefix}_${assocName}_${key}`]
|
|
126
|
-
const val = _getVal(structData[assocName], key)
|
|
127
|
-
filterArray.push({ ref }, op, { val },
|
|
127
|
+
const val = _getVal(structData && structData[assocName], key)
|
|
128
|
+
filterArray.push({ ref }, op, { val }, andOr)
|
|
128
129
|
}
|
|
129
130
|
}
|
|
130
131
|
}
|
|
@@ -134,7 +135,7 @@ const _filterForStructProperty = (structElement, structData, op, prefix = '', na
|
|
|
134
135
|
{ ref: [...nav, `${prefix}_${element.name}`] },
|
|
135
136
|
op,
|
|
136
137
|
{ val: _getVal(structData, element.name) },
|
|
137
|
-
|
|
138
|
+
andOr
|
|
138
139
|
)
|
|
139
140
|
}
|
|
140
141
|
}
|
|
@@ -182,7 +183,12 @@ const _transformStructToFlatWhereHaving = ([first, op, second], resArray, struct
|
|
|
182
183
|
} else {
|
|
183
184
|
// transform complex structured to multiple single structured
|
|
184
185
|
const { nestedElement, prefix } = _nestedStructElement(structProperties, structElement)
|
|
185
|
-
|
|
186
|
+
const filterForStructProperty = _filterForStructProperty(nestedElement, structData, op, prefix, nav)
|
|
187
|
+
if (filterForStructProperty.length) {
|
|
188
|
+
filterForStructProperty.pop() // last and/or
|
|
189
|
+
if (op === '!=') resArray.push('(', ...filterForStructProperty, ')')
|
|
190
|
+
else resArray.push(...filterForStructProperty)
|
|
191
|
+
}
|
|
186
192
|
}
|
|
187
193
|
|
|
188
194
|
if (resArray[resArray.length - 1] === 'and') {
|
|
@@ -1,11 +1,28 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
const cds = require('../../../../libx/_runtime/cds')
|
|
2
|
+
|
|
3
|
+
const getAppMetadata = () => {
|
|
4
|
+
const appMetadata = cds.env.app
|
|
5
|
+
|
|
6
|
+
if (appMetadata) {
|
|
7
|
+
return {
|
|
8
|
+
appID: appMetadata.id,
|
|
9
|
+
appName: appMetadata.name,
|
|
10
|
+
appURL: appMetadata.url
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// fallback: if the app metadata is undefined, then extract the metadata from the underlying environment (CF/Kyma/...)
|
|
15
|
+
const vcapApplication = process.env.VCAP_APPLICATION && JSON.parse(process.env.VCAP_APPLICATION)
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
appID: vcapApplication && vcapApplication.application_id,
|
|
19
|
+
appName: vcapApplication && vcapApplication.application_name,
|
|
20
|
+
appURL:
|
|
21
|
+
vcapApplication &&
|
|
22
|
+
vcapApplication.application_uris &&
|
|
23
|
+
vcapApplication.application_uris[0] &&
|
|
24
|
+
`https://${vcapApplication.application_uris[0].replace(/^https?:\/\//, '')}`
|
|
25
|
+
}
|
|
11
26
|
}
|
|
27
|
+
|
|
28
|
+
module.exports = getAppMetadata()
|
|
@@ -90,7 +90,8 @@ const _extractRefs = (from, as) => {
|
|
|
90
90
|
return [ref]
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
// REVISIT: check why we need includeAlias for some draft cases (check AFC tests)
|
|
94
|
+
const _addMapperFunction = (elements, toService, key, type, alias, includeAlias) => {
|
|
94
95
|
if (!toService.has(type)) {
|
|
95
96
|
return
|
|
96
97
|
}
|
|
@@ -99,9 +100,8 @@ const _addMapperFunction = (elements, toService, key, type, from, includeAlias)
|
|
|
99
100
|
|
|
100
101
|
// ambiguous cases will lead to SQL syntax errors anyway, so no need for a check
|
|
101
102
|
elements.set(key, convertFunction)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
elements.set(`${from.as}_${key}`, convertFunction)
|
|
103
|
+
if (includeAlias && alias) {
|
|
104
|
+
elements.set(`${alias}_${key}`, convertFunction)
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
@@ -109,13 +109,13 @@ const _filterUnique = (value, index, arr) => {
|
|
|
109
109
|
return arr.indexOf(value) === index
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
const _addMapperFunction4struct = (elements, toService, parent, struct,
|
|
112
|
+
const _addMapperFunction4struct = (elements, toService, parent, struct, alias, includeAlias) => {
|
|
113
113
|
for (const k in struct.elements) {
|
|
114
114
|
const current = struct.elements[k]
|
|
115
115
|
if (current._isStructured) {
|
|
116
|
-
_addMapperFunction4struct(elements, toService, `${parent}_${k}`, current,
|
|
116
|
+
_addMapperFunction4struct(elements, toService, `${parent}_${k}`, current, alias, includeAlias)
|
|
117
117
|
} else {
|
|
118
|
-
_addMapperFunction(elements, toService, `${parent}_${k}`, current.type,
|
|
118
|
+
_addMapperFunction(elements, toService, `${parent}_${k}`, current.type, alias, includeAlias)
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
}
|
|
@@ -126,7 +126,6 @@ const _addMapperFunction4struct = (elements, toService, parent, struct, from, in
|
|
|
126
126
|
* @param {Map} toService - Mapping instructions for data conversions based on CDS data types
|
|
127
127
|
* @param {object} csn - Reflected CSN
|
|
128
128
|
* @param {object} cqn - CQN that is used to query the DB.
|
|
129
|
-
* @param {boolean} [includeAlias] - Include mapping for aliases. Defaults to false.
|
|
130
129
|
* @returns {Map<any, any>}
|
|
131
130
|
* @private
|
|
132
131
|
*/
|
|
@@ -142,13 +141,16 @@ const _getElementCombinations = (toService, csn, cqn, includeAlias = false) => {
|
|
|
142
141
|
}
|
|
143
142
|
|
|
144
143
|
const entity = csn.definitions[ensureUnlocalized(entityName)]
|
|
145
|
-
|
|
146
144
|
for (const key in entity.elements) {
|
|
147
145
|
const element = entity.elements[key]
|
|
148
146
|
if (element._isStructured) {
|
|
149
|
-
_addMapperFunction4struct(elements, toService, key, element, from, includeAlias)
|
|
147
|
+
_addMapperFunction4struct(elements, toService, key, element, from.as, includeAlias)
|
|
150
148
|
} else {
|
|
151
|
-
|
|
149
|
+
if ('SET' in cqn.SELECT.from) {
|
|
150
|
+
_addMapperFunction(elements, toService, key, element.type, cqn.SELECT.from.as, includeAlias)
|
|
151
|
+
} else {
|
|
152
|
+
_addMapperFunction(elements, toService, key, element.type, from.as, includeAlias)
|
|
153
|
+
}
|
|
152
154
|
}
|
|
153
155
|
}
|
|
154
156
|
}
|
|
@@ -182,15 +184,17 @@ const _getMapperForListedElements = (toService, csn, cqn) => {
|
|
|
182
184
|
|
|
183
185
|
for (const element of cqn.SELECT.columns) {
|
|
184
186
|
if (element.ref) {
|
|
185
|
-
const identifier = element.ref
|
|
187
|
+
const identifier = element.ref.length === 1 ? element.ref[0] : undefined
|
|
186
188
|
const name = element.as ? element.as : identifier
|
|
187
189
|
|
|
188
190
|
if (element.cast) {
|
|
189
191
|
mapper.set(name, _getCastFunction(element.cast))
|
|
190
192
|
} else if (elements.has(name)) {
|
|
191
193
|
mapper.set(name, elements.get(name))
|
|
192
|
-
} else if (elements.has(identifier)
|
|
194
|
+
} else if (elements.has(identifier)) {
|
|
193
195
|
mapper.set(name, elements.get(identifier))
|
|
196
|
+
} else if (elements.has(element.ref.join('_'))) {
|
|
197
|
+
mapper.set(name || element.ref[element.ref.length - 1], elements.get(element.ref.join('_')))
|
|
194
198
|
}
|
|
195
199
|
} else if (element.as && element.cast) {
|
|
196
200
|
mapper.set(element.as, _getCastFunction(element.cast))
|
|
@@ -514,6 +518,9 @@ const getPropertyMapper = (csn, cqn) => {
|
|
|
514
518
|
return new Map()
|
|
515
519
|
}
|
|
516
520
|
|
|
521
|
+
/*
|
|
522
|
+
* this module is required by cds-pg. -> in case of incompatible changes, we should let them know.
|
|
523
|
+
*/
|
|
517
524
|
module.exports = {
|
|
518
525
|
getPropertyMapper,
|
|
519
526
|
getPostProcessMapper,
|
|
@@ -11,9 +11,10 @@ const _removeParentKeysFromRow = (row, prefix, keys) => {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
const _autoExpandNavsAndAttachToResult = async (entity, previousResult, depth) => {
|
|
14
|
+
const _autoExpandNavsAndAttachToResult = async (entity, previousResult, depth, options) => {
|
|
15
15
|
for (const nav in entity._associations) {
|
|
16
16
|
const navigation = entity._associations[nav]
|
|
17
|
+
if (options.onlyCompositions && navigation._isAssociationEffective) continue
|
|
17
18
|
|
|
18
19
|
// do not expand backlinks
|
|
19
20
|
if (navigation._isBacklink) continue
|
|
@@ -26,7 +27,9 @@ const _autoExpandNavsAndAttachToResult = async (entity, previousResult, depth) =
|
|
|
26
27
|
.on(entity._relations[navigation.name].join(childAlias, parentAlias))
|
|
27
28
|
|
|
28
29
|
// set alias for expanded columns already
|
|
29
|
-
const childColumns = getColumns(navigation._target
|
|
30
|
+
const childColumns = getColumns(navigation._target, { _4db: true, onlyKeys: options.onlyKeys }).map(c => ({
|
|
31
|
+
ref: [childAlias, c.name]
|
|
32
|
+
}))
|
|
30
33
|
const parentKeys = Object.keys(entity.keys).filter(k => !entity.keys[k].isAssociation)
|
|
31
34
|
// mark parent key with prefix in alias
|
|
32
35
|
const parentKeysWithAlias = parentKeys.map(pk => ({ ref: [parentAlias, pk], as: `$$pk_${pk}` }))
|
|
@@ -69,16 +72,17 @@ const _autoExpandNavsAndAttachToResult = async (entity, previousResult, depth) =
|
|
|
69
72
|
|
|
70
73
|
// expand next level if needed
|
|
71
74
|
if (depth - 1 !== 0 && result.length && navigation._target._associations) {
|
|
72
|
-
await _autoExpandNavsAndAttachToResult(navigation._target, result, depth - 1)
|
|
75
|
+
await _autoExpandNavsAndAttachToResult(navigation._target, result, depth - 1, options)
|
|
73
76
|
}
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
return previousResult
|
|
77
80
|
}
|
|
78
81
|
|
|
79
|
-
const _foreignKeysOfTopLevelNavs = entity => {
|
|
82
|
+
const _foreignKeysOfTopLevelNavs = (entity, options) => {
|
|
80
83
|
const requiredFks = new Set()
|
|
81
84
|
for (const nav in entity._associations) {
|
|
85
|
+
if (options.onlyCompositions && entity._associations[nav]._isAssociationEffective) continue
|
|
82
86
|
const onCond = entity._relations[nav].join('child', 'parent')
|
|
83
87
|
for (const ele of onCond) {
|
|
84
88
|
if (ele.ref && ele.ref[0] === 'parent') {
|
|
@@ -89,6 +93,15 @@ const _foreignKeysOfTopLevelNavs = entity => {
|
|
|
89
93
|
return [...requiredFks]
|
|
90
94
|
}
|
|
91
95
|
|
|
96
|
+
const _addForeignKeys = (columns, entity, options) => {
|
|
97
|
+
const fks = _foreignKeysOfTopLevelNavs(entity, options)
|
|
98
|
+
fks.forEach(fk => {
|
|
99
|
+
if (!columns.some(c => c.ref[0] === fk)) {
|
|
100
|
+
columns.push({ ref: [fk] })
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
92
105
|
/**
|
|
93
106
|
* 1. Creates flattened SQL statements for each expand layer
|
|
94
107
|
* 2. Mixes in foreign keys if needed
|
|
@@ -98,18 +111,15 @@ const _foreignKeysOfTopLevelNavs = entity => {
|
|
|
98
111
|
* @returns object
|
|
99
112
|
*/
|
|
100
113
|
const expandV2 = async (model, dbc, query, user, locale, txTimestamp, executeSelectCQN) => {
|
|
114
|
+
const expandColumn = query.SELECT.columns.find(c => c.expand && typeof c.expand === 'string')
|
|
115
|
+
const options = Object.assign({ onlyKeys: false, onlyCompositions: false }, expandColumn._options)
|
|
101
116
|
// remove expand columns from query without modifying
|
|
102
117
|
const topLevelSelect = query.clone().columns(query.SELECT.columns.filter(c => !c.expand))
|
|
103
118
|
|
|
104
119
|
const entity = model.definitions[topLevelSelect.SELECT.from.ref[0]]
|
|
105
120
|
|
|
106
121
|
// ensure foreign keys are selected if needed
|
|
107
|
-
|
|
108
|
-
fks.forEach(fk => {
|
|
109
|
-
if (!topLevelSelect.SELECT.columns.some(c => c.ref[0] === fk)) {
|
|
110
|
-
topLevelSelect.SELECT.columns.push({ ref: [fk] })
|
|
111
|
-
}
|
|
112
|
-
})
|
|
122
|
+
_addForeignKeys(topLevelSelect.SELECT.columns, entity, options)
|
|
113
123
|
|
|
114
124
|
const result = await executeSelectCQN(model, dbc, topLevelSelect, user, locale, txTimestamp)
|
|
115
125
|
|
|
@@ -119,9 +129,8 @@ const expandV2 = async (model, dbc, query, user, locale, txTimestamp, executeSel
|
|
|
119
129
|
|
|
120
130
|
// _associations contains compositions and associations
|
|
121
131
|
if (entity._associations) {
|
|
122
|
-
const expandColumn = query.SELECT.columns.find(c => c.expand && typeof c.expand === 'string')
|
|
123
132
|
const depth = expandColumn.expand === '**' ? -1 : Number(expandColumn.expand.replace('*', ''))
|
|
124
|
-
await _autoExpandNavsAndAttachToResult(entity, Array.isArray(result) ? result : [result], depth)
|
|
133
|
+
await _autoExpandNavsAndAttachToResult(entity, Array.isArray(result) ? result : [result], depth, options)
|
|
125
134
|
}
|
|
126
135
|
|
|
127
136
|
return result
|
|
@@ -2,10 +2,15 @@ const cds = require('../../cds')
|
|
|
2
2
|
|
|
3
3
|
const { getAllKeys } = require('../../cds-services/adapter/odata-v4/odata-to-cqn/utils')
|
|
4
4
|
|
|
5
|
+
const { deepCopyObject } = require('../../common/utils/copy')
|
|
5
6
|
const { getNavigationIfStruct } = require('../../common/utils/structured')
|
|
6
7
|
const { ensureNoDraftsSuffix, ensureDraftsSuffix, ensureUnlocalized } = require('../../common/utils/draft')
|
|
7
|
-
const { filterKeys } = require('../../fiori/utils/handler')
|
|
8
8
|
const { isAsteriskColumn } = require('../../common/utils/rewriteAsterisks')
|
|
9
|
+
const { getCQNUnionFrom } = require('../../common/utils/union')
|
|
10
|
+
|
|
11
|
+
const { DRAFT_COLUMNS } = require('../../common/constants/draft')
|
|
12
|
+
|
|
13
|
+
const { filterKeys } = require('../../fiori/utils/handler')
|
|
9
14
|
|
|
10
15
|
// Symbols are used to add extra information in response structure
|
|
11
16
|
const GET_KEY_VALUE = Symbol.for('sap.cds.getKeyValue')
|
|
@@ -16,12 +21,9 @@ const IDENTIFIER = Symbol.for('sap.cds.identifier')
|
|
|
16
21
|
const IS_ACTIVE = Symbol.for('sap.cds.isActive')
|
|
17
22
|
const IS_UNION_DRAFT = Symbol.for('sap.cds.isUnionDraft')
|
|
18
23
|
|
|
19
|
-
const { DRAFT_COLUMNS } = require('../../common/constants/draft')
|
|
20
|
-
|
|
21
|
-
const { getCQNUnionFrom } = require('../../common/utils/union')
|
|
22
|
-
|
|
23
24
|
function getCqnCopy(readToOneCQN) {
|
|
24
|
-
|
|
25
|
+
// REVISIT: Use query.clone() instead
|
|
26
|
+
const readToOneCQNCopy = deepCopyObject(readToOneCQN)
|
|
25
27
|
if (readToOneCQN[GET_KEY_VALUE] !== undefined) readToOneCQNCopy[GET_KEY_VALUE] = readToOneCQN[GET_KEY_VALUE]
|
|
26
28
|
if (readToOneCQN[TO_MANY] !== undefined) readToOneCQNCopy[TO_MANY] = readToOneCQN[TO_MANY]
|
|
27
29
|
if (readToOneCQN[TO_ACTIVE] !== undefined) readToOneCQNCopy[TO_ACTIVE] = readToOneCQN[TO_ACTIVE]
|
|
@@ -518,6 +520,17 @@ class JoinCQNFromExpanded {
|
|
|
518
520
|
|
|
519
521
|
// REVISIT required for other cqn properties as well?
|
|
520
522
|
this.adjustOrderBy(readToOneCQN.orderBy, mappings, column, tableAlias)
|
|
523
|
+
|
|
524
|
+
// In case active parent entity has orderBy with draft specific columns we need to add them to parent CQN
|
|
525
|
+
if (
|
|
526
|
+
readToOneCQN[IS_ACTIVE] &&
|
|
527
|
+
readToOneCQN.orderBy &&
|
|
528
|
+
column.as &&
|
|
529
|
+
(column.as === 'IsActiveEntity' || column.as === 'HasActiveEntity' || column.as === 'HasDraftEntity')
|
|
530
|
+
) {
|
|
531
|
+
readToOneCQNCopy.orderBy = readToOneCQN.orderBy
|
|
532
|
+
this._addColumnsInCaseOrderByHasDraft(readToOneCQNCopy, readToOneCQN.columns[readToOneCQN.columns.length - 1])
|
|
533
|
+
}
|
|
521
534
|
}
|
|
522
535
|
}
|
|
523
536
|
|
|
@@ -549,6 +562,14 @@ class JoinCQNFromExpanded {
|
|
|
549
562
|
}
|
|
550
563
|
}
|
|
551
564
|
|
|
565
|
+
_addColumnsInCaseOrderByHasDraft(readToOneCQNCopy, column) {
|
|
566
|
+
readToOneCQNCopy.orderBy.forEach(order => {
|
|
567
|
+
if (order.as === column.as) {
|
|
568
|
+
readToOneCQNCopy.columns.push(column)
|
|
569
|
+
}
|
|
570
|
+
})
|
|
571
|
+
}
|
|
572
|
+
|
|
552
573
|
/**
|
|
553
574
|
* Follow the tree to get to the relevant config object.
|
|
554
575
|
*
|
|
@@ -1130,28 +1151,37 @@ class JoinCQNFromExpanded {
|
|
|
1130
1151
|
}
|
|
1131
1152
|
}
|
|
1132
1153
|
}
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
each.
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1154
|
+
|
|
1155
|
+
if (!cqn[IS_ACTIVE]) {
|
|
1156
|
+
const ks = Object.keys(expandedEntity.keys).filter(
|
|
1157
|
+
c => !expandedEntity.keys[c].isAssociation && !DRAFT_COLUMNS.includes(c)
|
|
1158
|
+
)
|
|
1159
|
+
const user = (cds.context && cds.context.user && cds.context.user.id) || 'anonymous'
|
|
1160
|
+
const unionFrom = getCQNUnionFrom(cols, ref.replace(/_drafts$/, ''), ref, ks, user)
|
|
1161
|
+
for (const each of cqn.columns) {
|
|
1162
|
+
if (!each.as) continue
|
|
1163
|
+
// replace val with ref
|
|
1164
|
+
if (each.as === 'IsActiveEntity' || each.as === 'HasActiveEntity') {
|
|
1165
|
+
delete each.val
|
|
1166
|
+
each.ref = [tableAlias, each.as]
|
|
1167
|
+
each.as = tableAlias + '_' + each.as
|
|
1168
|
+
}
|
|
1169
|
+
// ensure the cast
|
|
1170
|
+
if (
|
|
1171
|
+
each.as.match(/IsActiveEntity$/) ||
|
|
1172
|
+
each.as.match(/HasActiveEntity$/) ||
|
|
1173
|
+
each.as.match(/HasDraftEntity$/)
|
|
1174
|
+
) {
|
|
1175
|
+
each.cast = { type: 'cds.Boolean' }
|
|
1176
|
+
}
|
|
1149
1177
|
}
|
|
1178
|
+
const cs = cqn.columns
|
|
1179
|
+
.filter(c => !c.expand && c.ref && c.ref[0] === tableAlias)
|
|
1180
|
+
.map(c => ({ ref: [c.ref[1]] }))
|
|
1181
|
+
const unionArgs = cqn.from.args
|
|
1182
|
+
unionArgs[0].SELECT = { columns: cs, from: unionFrom, distinct: true }
|
|
1183
|
+
delete unionArgs[0].ref
|
|
1150
1184
|
}
|
|
1151
|
-
const cs = cqn.columns.filter(c => !c.expand && c.ref && c.ref[0] === tableAlias).map(c => ({ ref: [c.ref[1]] }))
|
|
1152
|
-
const unionArgs = cqn.from.args
|
|
1153
|
-
unionArgs[0].SELECT = { columns: cs, from: unionFrom, distinct: true }
|
|
1154
|
-
delete unionArgs[0].ref
|
|
1155
1185
|
}
|
|
1156
1186
|
|
|
1157
1187
|
return cqn
|
|
@@ -1324,6 +1354,17 @@ class JoinCQNFromExpanded {
|
|
|
1324
1354
|
}
|
|
1325
1355
|
}
|
|
1326
1356
|
|
|
1357
|
+
if (readToOneCQN[IS_ACTIVE] && readToOneCQN.columns.length > 0) {
|
|
1358
|
+
readToOneCQN.columns.forEach(column => {
|
|
1359
|
+
if (
|
|
1360
|
+
column.as === `${parentAlias}_IsActiveEntity` ||
|
|
1361
|
+
column.as === `${parentAlias}_HasActiveEntity` ||
|
|
1362
|
+
column.as === `${parentAlias}_HasDraftEntity`
|
|
1363
|
+
)
|
|
1364
|
+
columns.push(column)
|
|
1365
|
+
})
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1327
1368
|
const subSelect = Object.assign({}, readToOneCQN, { columns })
|
|
1328
1369
|
|
|
1329
1370
|
const SELECT = { from: { SELECT: subSelect }, columns: outerColumns, distinct: true }
|
|
@@ -2,6 +2,9 @@ const { hasExpand, createJoinCQNFromExpanded } = require('./expandCQNToJoin')
|
|
|
2
2
|
const rawToExpanded = require('./rawToExpanded')
|
|
3
3
|
const expandV2 = require('./expand-v2')
|
|
4
4
|
|
|
5
|
+
/*
|
|
6
|
+
* this module is required by cds-pg. -> in case of incompatible changes, we should let them know.
|
|
7
|
+
*/
|
|
5
8
|
module.exports = {
|
|
6
9
|
hasExpand,
|
|
7
10
|
createJoinCQNFromExpanded,
|
|
@@ -15,16 +15,6 @@ module.exports = async function (req) {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
try {
|
|
18
|
-
// REVISIT: should be handled in protocol adapter
|
|
19
|
-
// execute validation query first to fail early
|
|
20
|
-
if (req.query._validationQuery) {
|
|
21
|
-
const validationResult = await this._read(this.model, this.dbc, req.query._validationQuery, req)
|
|
22
|
-
|
|
23
|
-
if (validationResult.length === 0) {
|
|
24
|
-
// > validation target (e.g., root of navigation) doesn't exist
|
|
25
|
-
req.reject(404)
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
18
|
const results = await this._insert(this.model, this.dbc, req.query, req)
|
|
29
19
|
return new InsertResult(req, results)
|
|
30
20
|
} catch (err) {
|
|
@@ -12,6 +12,9 @@ const DELETE = require('./delete')
|
|
|
12
12
|
const structured = require('./structured')
|
|
13
13
|
const arrayed = require('./arrayed')
|
|
14
14
|
|
|
15
|
+
/*
|
|
16
|
+
* this module is required by cds-pg. -> in case of incompatible changes, we should let them know.
|
|
17
|
+
*/
|
|
15
18
|
module.exports = {
|
|
16
19
|
rewrite,
|
|
17
20
|
virtual,
|
|
@@ -7,32 +7,10 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @param req - cds.Request
|
|
9
9
|
*/
|
|
10
|
-
module.exports =
|
|
10
|
+
module.exports = function (req) {
|
|
11
11
|
if (typeof req.query === 'string') {
|
|
12
12
|
return this._execute.sql(this.dbc, req.query, req.data)
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
// execute validation query first to fail early
|
|
17
|
-
if (req.query._validationQuery) {
|
|
18
|
-
const validationResult = await this._read(this.model, this.dbc, req.query._validationQuery, req)
|
|
19
|
-
|
|
20
|
-
if (validationResult.length === 0) {
|
|
21
|
-
// > validation target (e.g., root of navigation) doesn't exist
|
|
22
|
-
req.reject(404)
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const result = await this._read(this.model, this.dbc, req.query, req)
|
|
27
|
-
|
|
28
|
-
if (
|
|
29
|
-
req.query._validationQuery &&
|
|
30
|
-
req.query._validationQuery.__navToManyWithKeys &&
|
|
31
|
-
(!result || result.length === 0)
|
|
32
|
-
) {
|
|
33
|
-
// > navigation to collection with key specified without result -> 404
|
|
34
|
-
req.reject(404)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return result
|
|
15
|
+
return this._read(this.model, this.dbc, req.query, req)
|
|
38
16
|
}
|