@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.
Files changed (187) hide show
  1. package/CHANGELOG.md +135 -0
  2. package/_i18n/i18n_fr.properties +4 -4
  3. package/apis/cds.d.ts +7 -10
  4. package/apis/connect.d.ts +3 -3
  5. package/apis/core.d.ts +2 -4
  6. package/apis/models.d.ts +2 -3
  7. package/apis/ql.d.ts +0 -1
  8. package/apis/services.d.ts +3 -3
  9. package/bin/build/buildTaskFactory.js +16 -10
  10. package/bin/build/buildTaskProviderFactory.js +3 -3
  11. package/bin/build/constants.js +2 -1
  12. package/bin/build/provider/buildTaskProviderInternal.js +14 -14
  13. package/bin/build/provider/hana/2migration.js +2 -3
  14. package/bin/build/provider/hana/index.js +34 -0
  15. package/bin/build/provider/hana/migrationtable.js +90 -22
  16. package/bin/build/provider/hana/template/undeploy.json +5 -0
  17. package/bin/build/provider/node-cf/index.js +9 -2
  18. package/bin/serve.js +16 -18
  19. package/lib/compile/cdsc.js +15 -5
  20. package/lib/compile/etc/_localized.js +4 -4
  21. package/lib/compile/extend.js +8 -0
  22. package/lib/compile/index.js +3 -1
  23. package/lib/compile/minify.js +61 -0
  24. package/lib/compile/resolve.js +4 -1
  25. package/lib/compile/to/gql.js +9 -0
  26. package/lib/compile/to/sql.js +26 -30
  27. package/lib/connect/index.js +1 -1
  28. package/lib/core/entities.js +0 -3
  29. package/lib/core/infer.js +1 -0
  30. package/lib/core/reflect.js +0 -34
  31. package/lib/deploy.js +25 -17
  32. package/lib/env/defaults.js +3 -1
  33. package/lib/env/index.js +8 -3
  34. package/lib/env/presets.js +38 -0
  35. package/lib/env/requires.js +16 -11
  36. package/lib/index.js +13 -11
  37. package/lib/log/format/kibana.js +4 -2
  38. package/lib/log/index.js +2 -2
  39. package/lib/ql/Whereable.js +1 -0
  40. package/lib/req/cds-context.js +79 -0
  41. package/lib/req/context.js +5 -77
  42. package/lib/req/request.js +1 -1
  43. package/lib/serve/Service-api.js +8 -4
  44. package/lib/serve/Service-dispatch.js +0 -7
  45. package/lib/serve/Service-methods.js +6 -8
  46. package/lib/serve/Transaction.js +35 -30
  47. package/lib/serve/adapters.js +1 -4
  48. package/lib/utils/axios.js +1 -1
  49. package/libx/_runtime/audit/Service.js +44 -20
  50. package/libx/_runtime/audit/generic/personal/access.js +16 -11
  51. package/libx/_runtime/audit/generic/personal/modification.js +5 -5
  52. package/libx/_runtime/audit/generic/personal/utils.js +46 -37
  53. package/libx/_runtime/{common/auth → auth}/index.js +21 -7
  54. package/libx/_runtime/{common/auth → auth}/strategies/JWT.js +2 -2
  55. package/libx/_runtime/{common/auth → auth}/strategies/basic.js +2 -2
  56. package/libx/_runtime/{common/auth → auth}/strategies/dummy.js +1 -1
  57. package/libx/_runtime/{common/auth → auth}/strategies/mock.js +2 -2
  58. package/libx/_runtime/{common/auth → auth}/strategies/utils/uaa.js +1 -1
  59. package/libx/_runtime/{common/auth → auth}/strategies/utils/xssec.js +0 -0
  60. package/libx/_runtime/{common/auth → auth}/strategies/xsuaa.js +2 -2
  61. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +7 -2
  62. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +0 -7
  63. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +0 -8
  64. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +3 -4
  65. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +6 -7
  66. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +3 -4
  67. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +2 -11
  68. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +16 -6
  69. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +26 -69
  70. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +0 -7
  71. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +3 -66
  72. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +29 -1
  73. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +7 -1
  74. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +9 -5
  75. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +2 -2
  76. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +13 -10
  77. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/ConditionalRequestControlCommand.js +0 -7
  78. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/ConditionalRequestValidator.js +0 -8
  79. package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +54 -76
  80. package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +0 -7
  81. package/libx/_runtime/cds-services/adapter/rest/handlers/create.js +3 -6
  82. package/libx/_runtime/cds-services/adapter/rest/handlers/delete.js +3 -6
  83. package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +3 -6
  84. package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +3 -6
  85. package/libx/_runtime/cds-services/adapter/rest/handlers/update.js +3 -6
  86. package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +2 -2
  87. package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +8 -4
  88. package/libx/_runtime/cds-services/services/Service.js +0 -6
  89. package/libx/_runtime/cds-services/services/utils/columns.js +10 -3
  90. package/libx/_runtime/cds-services/services/utils/compareJson.js +4 -7
  91. package/libx/_runtime/cds-services/services/utils/differ.js +4 -1
  92. package/libx/_runtime/cds-services/services/utils/handlerUtils.js +1 -41
  93. package/libx/_runtime/cds-services/util/assert.js +1 -262
  94. package/libx/_runtime/cds.js +6 -9
  95. package/libx/_runtime/common/aspects/entity.js +1 -1
  96. package/libx/_runtime/common/composition/delete.js +4 -2
  97. package/libx/_runtime/common/composition/update.js +22 -38
  98. package/libx/_runtime/common/composition/utils.js +3 -7
  99. package/libx/_runtime/common/error/standardError.js +11 -0
  100. package/libx/_runtime/common/generic/auth.js +63 -33
  101. package/libx/_runtime/common/generic/crud.js +11 -23
  102. package/libx/_runtime/common/generic/input.js +20 -0
  103. package/libx/_runtime/common/generic/paging.js +2 -2
  104. package/libx/_runtime/common/generic/put.js +4 -10
  105. package/libx/_runtime/common/generic/sorting.js +12 -30
  106. package/libx/_runtime/common/i18n/messages.properties +2 -0
  107. package/libx/_runtime/common/perf/index.js +24 -0
  108. package/libx/_runtime/common/utils/cqn.js +58 -1
  109. package/libx/_runtime/common/utils/cqn2cqn4sql.js +298 -121
  110. package/libx/_runtime/common/utils/csn.js +38 -56
  111. package/libx/_runtime/common/utils/entityFromCqn.js +6 -6
  112. package/libx/_runtime/common/utils/resolveView.js +4 -5
  113. package/libx/_runtime/common/utils/rewriteAsterisks.js +46 -5
  114. package/libx/_runtime/common/utils/search2cqn4sql.js +21 -9
  115. package/libx/_runtime/common/utils/structured.js +35 -25
  116. package/libx/_runtime/db/Service.js +0 -6
  117. package/libx/_runtime/db/expand/expand-v2.js +130 -0
  118. package/libx/_runtime/db/expand/expandCQNToJoin.js +38 -52
  119. package/libx/_runtime/db/expand/index.js +3 -1
  120. package/libx/_runtime/db/generic/arrayed.js +3 -1
  121. package/libx/_runtime/db/generic/input.js +52 -10
  122. package/libx/_runtime/db/generic/integrity.js +367 -26
  123. package/libx/_runtime/db/generic/virtual.js +51 -13
  124. package/libx/_runtime/db/query/read.js +12 -8
  125. package/libx/_runtime/db/query/update.js +9 -3
  126. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +8 -9
  127. package/libx/_runtime/{common → db}/utils/propagateForeignKeys.js +11 -14
  128. package/libx/_runtime/fiori/generic/activate.js +1 -0
  129. package/libx/_runtime/fiori/generic/before.js +2 -1
  130. package/libx/_runtime/fiori/generic/edit.js +1 -0
  131. package/libx/_runtime/fiori/generic/patch.js +1 -1
  132. package/libx/_runtime/fiori/generic/read.js +128 -57
  133. package/libx/_runtime/fiori/uiflex/handler/transformRESULT.js +0 -4
  134. package/libx/_runtime/fiori/uiflex/index.js +1 -1
  135. package/libx/_runtime/fiori/uiflex/{extensibility/index.js → service.js} +6 -4
  136. package/libx/_runtime/fiori/utils/delete.js +7 -1
  137. package/libx/_runtime/hana/Service.js +1 -8
  138. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +5 -14
  139. package/libx/_runtime/hana/execute.js +10 -4
  140. package/libx/_runtime/hana/pool.js +55 -45
  141. package/libx/_runtime/hana/search.js +7 -6
  142. package/libx/_runtime/hana/search2cqn4sql.js +8 -5
  143. package/libx/_runtime/hana/searchToContains.js +3 -1
  144. package/libx/_runtime/index.js +5 -5
  145. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +3 -3
  146. package/libx/_runtime/messaging/Outbox.js +53 -0
  147. package/libx/_runtime/messaging/common-utils/AMQPClient.js +17 -10
  148. package/libx/_runtime/messaging/common-utils/connections.js +14 -9
  149. package/libx/_runtime/messaging/common-utils/waitingTime.js +2 -0
  150. package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -3
  151. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -2
  152. package/libx/_runtime/messaging/enterprise-messaging.js +21 -15
  153. package/libx/_runtime/messaging/file-based.js +5 -5
  154. package/libx/_runtime/messaging/message-queuing-utils/options-messaging.js +1 -0
  155. package/libx/_runtime/messaging/message-queuing.js +2 -3
  156. package/libx/_runtime/messaging/outbox/OutboxRunner.js +75 -0
  157. package/libx/_runtime/messaging/outbox/utils.js +192 -0
  158. package/libx/_runtime/messaging/service.js +16 -30
  159. package/libx/_runtime/remote/Service.js +15 -0
  160. package/libx/_runtime/remote/utils/client.js +15 -3
  161. package/libx/_runtime/remote/utils/{dataConversion.js → data.js} +12 -2
  162. package/libx/_runtime/sqlite/Service.js +7 -10
  163. package/libx/_runtime/sqlite/customBuilder/CustomExpressionBuilder.js +19 -0
  164. package/libx/_runtime/sqlite/execute.js +18 -12
  165. package/libx/_runtime/types/api.js +2 -1
  166. package/libx/gql/resolvers/parse/ast2cqn/columns.js +1 -1
  167. package/libx/odata/{odata2cqn/afterburner.js → afterburner.js} +28 -16
  168. package/libx/odata/{cqn2odata/index.js → cqn2odata.js} +1 -1
  169. package/libx/odata/{odata2cqn/grammar.pegjs → grammar.pegjs} +222 -130
  170. package/libx/odata/index.js +23 -14
  171. package/libx/odata/parser.js +1 -0
  172. package/libx/odata/utils.js +57 -0
  173. package/libx/rest/RestAdapter.js +2 -6
  174. package/libx/rest/utils/data.js +1 -6
  175. package/package.json +4 -3
  176. package/server.js +4 -5
  177. package/srv/audit-log.cds +87 -0
  178. package/{libx/_runtime/fiori/uiflex/extensibility/index.cds → srv/flex.cds} +0 -0
  179. package/srv/flex.js +1 -0
  180. package/srv/outbox.cds +11 -0
  181. package/srv/outbox.js +0 -0
  182. package/libx/_runtime/cds-services/adapter/perf/performance.js +0 -104
  183. package/libx/_runtime/cds-services/adapter/perf/performanceMeasurement.js +0 -33
  184. package/libx/odata/odata2cqn/index.js +0 -3
  185. package/libx/odata/odata2cqn/parser.js +0 -1
  186. package/libx/odata/readme.md +0 -1
  187. 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
- if (!cds.db) {
77
- // REVISIT: error message
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 -> affected rows === 0 -> no change or not exists?
119
- if (!req._authChecked && req.event === 'UPDATE' && result === 0 && (await _targetEntityDoesNotExist(req))) {
120
- req.reject(404)
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
- if (!element._isStructured) row[key] = null
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
- /* istanbul ignore else */
56
- if (element.default) {
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
- for (const keyName in entity.elements) {
21
- if (
22
- entity.elements[keyName].key &&
23
- !entity.elements[keyName].is2one &&
24
- !DRAFT_COLUMNS.includes(keyName) &&
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
- if (!select.orderBy && staticOrders.length === 0) return
66
- select.orderBy = select.orderBy || []
67
-
68
- for (const defaultOrder of staticOrders) {
69
- const some = select.orderBy.some(orderBy => {
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
  }