@sap/cds 5.6.3 → 5.7.3
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 +135 -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 +3 -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 +0 -34
- package/lib/deploy.js +25 -17
- package/lib/env/defaults.js +3 -1
- package/lib/env/index.js +8 -3
- 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 -69
- 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 +29 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +7 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +9 -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/validator/ConditionalRequestValidator.js +0 -8
- 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 +22 -38
- 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 +63 -33
- 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/i18n/messages.properties +2 -0
- 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 +298 -121
- 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 +38 -52
- package/libx/_runtime/db/expand/index.js +3 -1
- package/libx/_runtime/db/generic/arrayed.js +3 -1
- 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/read.js +12 -8
- 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 +1 -0
- package/libx/_runtime/fiori/generic/patch.js +1 -1
- package/libx/_runtime/fiori/generic/read.js +128 -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-utils/options-messaging.js +1 -0
- 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 +15 -0
- 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/gql/resolvers/parse/ast2cqn/columns.js +1 -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} +222 -130
- package/libx/odata/index.js +23 -14
- 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 +4 -5
- 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
|
@@ -9,7 +9,6 @@ const onlyKeysRemain = require('../utils/onlyKeysRemain')
|
|
|
9
9
|
|
|
10
10
|
const _targetEntityDoesNotExist = async req => {
|
|
11
11
|
const { query } = req
|
|
12
|
-
|
|
13
12
|
const cqn = SELECT.from(query.UPDATE.entity, [1])
|
|
14
13
|
|
|
15
14
|
if (query.UPDATE.entity.as) {
|
|
@@ -22,7 +21,6 @@ const _targetEntityDoesNotExist = async req => {
|
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
const exists = await cds.tx(req).run(cqn)
|
|
25
|
-
|
|
26
24
|
return exists.length === 0
|
|
27
25
|
}
|
|
28
26
|
|
|
@@ -54,6 +52,7 @@ const _pick = element => {
|
|
|
54
52
|
|
|
55
53
|
const _updateReqData = (req, that) => {
|
|
56
54
|
const template = getTemplate('app-output', that, req.target, { pick: _pick })
|
|
55
|
+
|
|
57
56
|
if (template.elements.size > 0) {
|
|
58
57
|
const arrayData = Array.isArray(req.data) ? req.data : [req.data]
|
|
59
58
|
for (const row of arrayData) {
|
|
@@ -73,26 +72,19 @@ module.exports = cds.service.impl(function () {
|
|
|
73
72
|
req.reject(501, 'PERSISTENCE_SKIP_NO_GENERIC_CRUD', [req.target.name])
|
|
74
73
|
}
|
|
75
74
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
req.reject(501, 'NO_DATABASE_CONNECTION')
|
|
79
|
-
}
|
|
75
|
+
// REVISIT: error message
|
|
76
|
+
if (!cds.db) req.reject(501, 'NO_DATABASE_CONNECTION')
|
|
80
77
|
|
|
81
78
|
let result
|
|
82
79
|
|
|
83
80
|
// no changes, no op (otherwise, @cds.on.update gets new values), but we need to check existence
|
|
84
81
|
if (req.event === 'UPDATE' && onlyKeysRemain(req)) {
|
|
85
|
-
if (await _targetEntityDoesNotExist(req))
|
|
86
|
-
req.reject(404)
|
|
87
|
-
}
|
|
88
|
-
|
|
82
|
+
if (await _targetEntityDoesNotExist(req)) req.reject(404)
|
|
89
83
|
result = req.data
|
|
90
84
|
}
|
|
91
85
|
|
|
92
86
|
if (req.event === 'DELETE' && req.target._isSingleton) {
|
|
93
|
-
if (!req.target['@odata.singleton.nullable'])
|
|
94
|
-
req.reject(400, 'SINGLETON_NOT_NULLABLE')
|
|
95
|
-
}
|
|
87
|
+
if (!req.target['@odata.singleton.nullable']) req.reject(400, 'SINGLETON_NOT_NULLABLE')
|
|
96
88
|
|
|
97
89
|
const singleton = await cds.tx(req).run(SELECT.one(req.target))
|
|
98
90
|
if (!singleton) req.reject(404)
|
|
@@ -103,21 +95,17 @@ module.exports = cds.service.impl(function () {
|
|
|
103
95
|
result = await cds.tx(req).run(req.query, req.data)
|
|
104
96
|
}
|
|
105
97
|
|
|
106
|
-
if (req.event === 'READ')
|
|
107
|
-
return result
|
|
108
|
-
}
|
|
98
|
+
if (req.event === 'READ') return result
|
|
109
99
|
|
|
110
100
|
if (req.event === 'DELETE') {
|
|
111
|
-
if (result === 0)
|
|
112
|
-
req.reject(404)
|
|
113
|
-
}
|
|
114
|
-
|
|
101
|
+
if (result === 0) req.reject(404)
|
|
115
102
|
return result
|
|
116
103
|
}
|
|
117
104
|
|
|
118
|
-
// case: no authorization check and payload more than just keys but no changes
|
|
119
|
-
|
|
120
|
-
|
|
105
|
+
// case: no authorization check and payload more than just keys but no changes
|
|
106
|
+
// -> affected rows === 0 -> no change or not exists?
|
|
107
|
+
if (req.event === 'UPDATE' && result === 0 && !req._authChecked) {
|
|
108
|
+
if (await _targetEntityDoesNotExist(req)) req.reject(404)
|
|
121
109
|
}
|
|
122
110
|
|
|
123
111
|
// flag to trigger read after write in protocol adapter
|
|
@@ -59,6 +59,14 @@ const _isDraftCoreComputed = (req, element, event) =>
|
|
|
59
59
|
element['@Core.Computed'] &&
|
|
60
60
|
!((event === 'CREATE' && element['@cds.on.insert']) || element['@cds.on.update'])
|
|
61
61
|
|
|
62
|
+
const _getMediaTypeProperty = element => element['@Core.MediaType'] && element['@Core.MediaType']['=']
|
|
63
|
+
|
|
64
|
+
const _getMediaTypeValue = req =>
|
|
65
|
+
req._.req &&
|
|
66
|
+
req._.req.headers['content-type'] &&
|
|
67
|
+
!req._.req.headers['content-type'].match(/json|multipart/i) &&
|
|
68
|
+
req._.req.headers['content-type']
|
|
69
|
+
|
|
62
70
|
const _processCategory = ({ row, key, category, isRoot, event, value, req, element }) => {
|
|
63
71
|
category = getSimpleCategory(category)
|
|
64
72
|
|
|
@@ -91,6 +99,11 @@ const _processCategory = ({ row, key, category, isRoot, event, value, req, eleme
|
|
|
91
99
|
// REVISIT: remove delay_assert_deep_assoc with cds^6
|
|
92
100
|
if (!cds.env.features.delay_assert_deep_assoc) checkIfAssocDeep(element, value.val, req)
|
|
93
101
|
}
|
|
102
|
+
|
|
103
|
+
// set media type from content-type header if streaming
|
|
104
|
+
const mtProperty = _getMediaTypeProperty(element)
|
|
105
|
+
const mtValue = _getMediaTypeValue(req)
|
|
106
|
+
if (category === 'stream' && row[key] && mtProperty && mtValue) row[mtProperty] = mtValue
|
|
94
107
|
}
|
|
95
108
|
|
|
96
109
|
const processorFn = (errors, req) => {
|
|
@@ -144,6 +157,8 @@ const _pick = element => {
|
|
|
144
157
|
categories.push('uuid')
|
|
145
158
|
}
|
|
146
159
|
|
|
160
|
+
if (element['@Core.MediaType']) categories.push('stream')
|
|
161
|
+
|
|
147
162
|
if (categories.length) return { categories }
|
|
148
163
|
}
|
|
149
164
|
|
|
@@ -289,6 +304,7 @@ function _actionFunctionHandler(req) {
|
|
|
289
304
|
for (const row of arrayData) {
|
|
290
305
|
_processActionFunction(row, eventParams, errors, req.event, this)
|
|
291
306
|
}
|
|
307
|
+
|
|
292
308
|
_callError(req, errors)
|
|
293
309
|
}
|
|
294
310
|
|
|
@@ -302,18 +318,22 @@ module.exports = cds.service.impl(function () {
|
|
|
302
318
|
for (const operation of this.operations) {
|
|
303
319
|
operationNames.push(operation.name.substring(this.name.length + 1))
|
|
304
320
|
}
|
|
321
|
+
|
|
305
322
|
if (operationNames.length > 0) {
|
|
306
323
|
this.before(operationNames, _actionFunctionHandler)
|
|
307
324
|
}
|
|
308
325
|
|
|
309
326
|
for (const entity of this.entities) {
|
|
310
327
|
const boundOps = []
|
|
328
|
+
|
|
311
329
|
if (entity.actions) {
|
|
312
330
|
boundOps.push(...Object.keys(entity.actions))
|
|
313
331
|
}
|
|
332
|
+
|
|
314
333
|
if (entity.functions) {
|
|
315
334
|
boundOps.push(...Object.keys(entity.functions))
|
|
316
335
|
}
|
|
336
|
+
|
|
317
337
|
if (boundOps.length > 0) {
|
|
318
338
|
this.before(boundOps, entity.name, _actionFunctionHandler)
|
|
319
339
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
|
-
const { getDefaultPageSize } = require('../utils/page')
|
|
2
|
+
const { getDefaultPageSize, getMaxPageSize } = require('../utils/page')
|
|
3
3
|
|
|
4
4
|
const _handler = function (req) {
|
|
5
5
|
// only if http request
|
|
@@ -11,7 +11,7 @@ const _handler = function (req) {
|
|
|
11
11
|
let { rows, offset } = req.query.SELECT.limit || {}
|
|
12
12
|
rows = rows && 'val' in rows ? rows.val : getDefaultPageSize(req.target)
|
|
13
13
|
offset = offset && 'val' in offset ? offset.val : 0
|
|
14
|
-
req.query.limit(...[rows, offset])
|
|
14
|
+
req.query.limit(...[Math.min(rows, getMaxPageSize(req.target)), offset])
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
/**
|
|
@@ -40,24 +40,18 @@ const processorFn = req => {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
/* istanbul ignore else */
|
|
44
43
|
if (category === 'default') {
|
|
45
44
|
row[key] = args.val
|
|
46
|
-
} else if (category === 'null') {
|
|
47
|
-
|
|
45
|
+
} else if (category === 'null' && !element._isStructured) {
|
|
46
|
+
row[key] = null
|
|
48
47
|
}
|
|
49
48
|
}
|
|
50
49
|
}
|
|
51
50
|
|
|
52
|
-
// params: element, target, parent, templateElements
|
|
53
51
|
const _pick = element => {
|
|
54
52
|
if (!element.isAssociation && !element.key && !element._isReadOnly) {
|
|
55
|
-
|
|
56
|
-
if (element.
|
|
57
|
-
return { category: 'default', args: element.default }
|
|
58
|
-
} else if (!element.notNull) {
|
|
59
|
-
return { category: 'null' }
|
|
60
|
-
}
|
|
53
|
+
if (element.default) return { category: 'default', args: element.default }
|
|
54
|
+
if (!element.notNull) return { category: 'null' }
|
|
61
55
|
}
|
|
62
56
|
}
|
|
63
57
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
2
|
const LOG = cds.log('app')
|
|
3
|
+
const { getAllKeys } = require('../../cds-services/adapter/odata-v4/odata-to-cqn/utils')
|
|
3
4
|
|
|
4
5
|
const DRAFT_COLUMNS = ['IsActiveEntity', 'HasDraftEntity', 'HasActiveEntity']
|
|
5
6
|
|
|
@@ -17,14 +18,11 @@ const _getStaticOrders = req => {
|
|
|
17
18
|
|
|
18
19
|
// implicit sorting?
|
|
19
20
|
if (cds.env.features.implicit_sorting !== false && (req.target._isSingleton || query.SELECT.limit)) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
!defaultOrders.some(o => o.by['='] === keyName)
|
|
26
|
-
)
|
|
27
|
-
ordersFromKeys.push({ by: { '=': keyName } })
|
|
21
|
+
const keys = getAllKeys(entity, true)
|
|
22
|
+
for (const key of keys) {
|
|
23
|
+
if (!DRAFT_COLUMNS.includes(key) && !defaultOrders.some(o => o.by['='] === key)) {
|
|
24
|
+
ordersFromKeys.push({ by: { '=': key } })
|
|
25
|
+
}
|
|
28
26
|
}
|
|
29
27
|
}
|
|
30
28
|
|
|
@@ -61,28 +59,12 @@ const _handler = function (req) {
|
|
|
61
59
|
if (select.groupBy && select.groupBy.length > 0) {
|
|
62
60
|
staticOrders = staticOrders.filter(d => select.groupBy.find(e => e.ref[0] === d.by['=']))
|
|
63
61
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const managedKey = orderBy.ref && orderBy.ref.length > 1 && orderBy.ref.join('_')
|
|
71
|
-
const element = managedKey && req.target.elements[managedKey]
|
|
72
|
-
const isManagedKey = element && element.key && !element.is2one
|
|
73
|
-
|
|
74
|
-
// don't add duplicates
|
|
75
|
-
return (
|
|
76
|
-
(orderBy.ref && orderBy.ref.length === 1 && orderBy.ref[0] === defaultOrder.by['=']) ||
|
|
77
|
-
(isManagedKey && managedKey === defaultOrder.by['='])
|
|
78
|
-
)
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
if (!some) {
|
|
82
|
-
const orderByItem = { ref: [defaultOrder.by['=']], sort: defaultOrder.desc ? 'desc' : 'asc' }
|
|
83
|
-
select.orderBy.push(orderByItem)
|
|
84
|
-
}
|
|
85
|
-
}
|
|
62
|
+
if (!staticOrders.length) return
|
|
63
|
+
;(select.orderBy || (select.orderBy = [])).push(
|
|
64
|
+
...staticOrders
|
|
65
|
+
.filter(d => !select.orderBy.find(o => o.ref && o.ref.join('_') === d.by['=']))
|
|
66
|
+
.map(d => ({ ref: [d.by['=']], sort: d.desc ? 'desc' : 'asc' }))
|
|
67
|
+
)
|
|
86
68
|
}
|
|
87
69
|
|
|
88
70
|
/**
|
|
@@ -73,6 +73,8 @@ ENTITY_IS_NOT_CRUD_VIA_NAVIGATION=Entity "{0}" is not {1} via association "{2}"
|
|
|
73
73
|
ENTITY_IS_AUTOEXPOSED=Entity "{0}" is not explicitely exposed as part of the service
|
|
74
74
|
EXPAND_IS_RESTRICTED=Navigation property "{0}" is not allowed for expand operation
|
|
75
75
|
|
|
76
|
+
EXPAND_COUNT_UNSUPPORTED="$count" is not supported for expand operation
|
|
77
|
+
|
|
76
78
|
# rest protocol adapter
|
|
77
79
|
INVALID_RESOURCE="{0}" is not a valid resource
|
|
78
80
|
INVALID_PARAMETER="{0}" is not a valid parameter
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const { performance } = require('perf_hooks')
|
|
2
|
+
|
|
3
|
+
const _statisticsRequested = req =>
|
|
4
|
+
(req.query && req.query['sap-statistics'] === 'true') ||
|
|
5
|
+
(req.headers && req.headers['sap-statistics'] === 'true' && (!req.query || !req.query['sap-statistics']))
|
|
6
|
+
|
|
7
|
+
module.exports = app => {
|
|
8
|
+
if (app._perf_measured) return
|
|
9
|
+
else app._perf_measured = true
|
|
10
|
+
|
|
11
|
+
app.use((req, res, next) => {
|
|
12
|
+
if (!_statisticsRequested(req)) return next()
|
|
13
|
+
|
|
14
|
+
const t0 = performance.now()
|
|
15
|
+
const { writeHead } = res
|
|
16
|
+
res.writeHead = function (...args) {
|
|
17
|
+
const total = Number((performance.now() - t0) / 1000).toFixed(2)
|
|
18
|
+
if (res.statusCode < 400) res.setHeader('sap-statistics', `total=${total}`)
|
|
19
|
+
writeHead.call(this, ...args)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
next()
|
|
23
|
+
})
|
|
24
|
+
}
|
|
@@ -16,6 +16,15 @@ const getEntityNameFromUpdateCQN = cqn => {
|
|
|
16
16
|
return (cqn.UPDATE.entity.ref && cqn.UPDATE.entity.ref[0]) || cqn.UPDATE.entity.name || cqn.UPDATE.entity
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
const addToWhere = (cqn, where) => {
|
|
20
|
+
const partial = cqn.SELECT || cqn.UPDATE || cqn.DELETE
|
|
21
|
+
if (!partial.where) partial.where = where
|
|
22
|
+
else {
|
|
23
|
+
partial.where.unshift('(')
|
|
24
|
+
partial.where.push(')', 'and', '(', ...where, ')')
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
19
28
|
// scope: simple wheres à la "[{ ref: ['foo'] }, '=', { val: 'bar' }, 'and', ... ]"
|
|
20
29
|
function where2obj(where, target = null) {
|
|
21
30
|
const data = {}
|
|
@@ -34,8 +43,56 @@ function where2obj(where, target = null) {
|
|
|
34
43
|
return data
|
|
35
44
|
}
|
|
36
45
|
|
|
46
|
+
function targetFromPath(path, model) {
|
|
47
|
+
const { definitions } = model
|
|
48
|
+
let current
|
|
49
|
+
for (const r of path) {
|
|
50
|
+
if (r.id) {
|
|
51
|
+
current = current
|
|
52
|
+
? definitions[current.elements[r.id].target]
|
|
53
|
+
: definitions[r.id] || definitions[r.id.replace(/_drafts$/, '')]
|
|
54
|
+
} else {
|
|
55
|
+
const next = current.elements[r]
|
|
56
|
+
if (next.isAssociation) {
|
|
57
|
+
current = definitions[next.target]
|
|
58
|
+
} else {
|
|
59
|
+
current = next
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return current
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function isPathToDraft(path, model) {
|
|
67
|
+
const { definitions } = model
|
|
68
|
+
let draft = false
|
|
69
|
+
let current
|
|
70
|
+
for (const r of path) {
|
|
71
|
+
if (r.id) {
|
|
72
|
+
const isaIndex = r.where.findIndex(ele => ele.ref && ele.ref[0] === 'IsActiveEntity')
|
|
73
|
+
if (isaIndex) draft = !r.where[isaIndex + 2].val
|
|
74
|
+
current = current ? definitions[current.elements[r.id].target] : definitions[r.id]
|
|
75
|
+
} else {
|
|
76
|
+
if (r === 'SiblingEntity') draft = !!draft
|
|
77
|
+
else {
|
|
78
|
+
const next = current.elements[r]
|
|
79
|
+
if (next.isAssociation) {
|
|
80
|
+
if (next._isAssociationStrict) draft = false
|
|
81
|
+
current = definitions[next.target]
|
|
82
|
+
} else {
|
|
83
|
+
current = next
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return draft
|
|
89
|
+
}
|
|
90
|
+
|
|
37
91
|
module.exports = {
|
|
92
|
+
addToWhere,
|
|
38
93
|
getEntityNameFromDeleteCQN,
|
|
39
94
|
getEntityNameFromUpdateCQN,
|
|
40
|
-
where2obj
|
|
95
|
+
where2obj,
|
|
96
|
+
targetFromPath,
|
|
97
|
+
isPathToDraft
|
|
41
98
|
}
|