@sap/cds 5.8.4 → 5.9.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.
Files changed (248) hide show
  1. package/CHANGELOG.md +198 -77
  2. package/app/fiori/preview.js +16 -11
  3. package/app/fiori/routes.js +15 -8
  4. package/app/index.js +1 -1
  5. package/bin/build/buildTaskFactory.js +3 -3
  6. package/bin/build/buildTaskProviderFactory.js +1 -1
  7. package/bin/build/constants.js +1 -1
  8. package/bin/build/provider/buildTaskHandlerEdmx.js +12 -7
  9. package/bin/build/provider/buildTaskHandlerInternal.js +1 -1
  10. package/bin/build/provider/buildTaskProviderInternal.js +8 -2
  11. package/bin/build/provider/hana/2migration.js +27 -24
  12. package/bin/build/provider/hana/index.js +17 -18
  13. package/bin/build/provider/hana/migrationtable.js +9 -10
  14. package/bin/build/provider/java-cf/index.js +4 -5
  15. package/bin/build/provider/node-cf/index.js +99 -6
  16. package/bin/cds.js +17 -18
  17. package/bin/deploy/to-hana/cfUtil.js +16 -19
  18. package/bin/deploy/to-hana/hana.js +7 -24
  19. package/bin/deploy/to-hana/hdiDeployUtil.js +8 -4
  20. package/bin/mtx/in-cds.js +2 -2
  21. package/bin/serve.js +10 -3
  22. package/bin/utils/modules.js +7 -0
  23. package/bin/version.js +56 -3
  24. package/lib/compile/cdsc.js +7 -2
  25. package/lib/compile/etc/_localized.js +37 -25
  26. package/lib/compile/etc/csv.js +8 -8
  27. package/lib/compile/for/drafts.js +9 -0
  28. package/lib/compile/for/java.js +16 -0
  29. package/lib/compile/for/nodejs.js +12 -0
  30. package/lib/compile/index.js +3 -0
  31. package/lib/compile/minify.js +16 -2
  32. package/lib/compile/parse.js +2 -2
  33. package/lib/compile/resolve.js +35 -18
  34. package/lib/compile/to/json.js +3 -1
  35. package/lib/compile/to/sql.js +2 -2
  36. package/lib/compile/to/srvinfo.js +4 -2
  37. package/lib/connect/bindings.js +1 -1
  38. package/lib/connect/index.js +3 -4
  39. package/lib/core/entities.js +15 -14
  40. package/lib/core/index.js +39 -36
  41. package/lib/core/reflect.js +4 -2
  42. package/lib/deploy.js +114 -127
  43. package/lib/env/defaults.js +1 -0
  44. package/lib/env/index.js +165 -165
  45. package/lib/env/presets.js +1 -0
  46. package/lib/env/requires.js +121 -50
  47. package/lib/index.js +2 -0
  48. package/lib/log/format/kibana.js +2 -2
  49. package/lib/ql/SELECT.js +10 -0
  50. package/lib/ql/parse.js +1 -0
  51. package/lib/req/cds-context.js +4 -1
  52. package/lib/req/context.js +50 -56
  53. package/lib/req/event.js +1 -6
  54. package/lib/req/locale.js +6 -5
  55. package/lib/req/request.js +2 -0
  56. package/lib/req/user.js +7 -5
  57. package/lib/serve/Service-api.js +10 -7
  58. package/lib/serve/Service-dispatch.js +9 -11
  59. package/lib/serve/Service-methods.js +30 -41
  60. package/lib/serve/Transaction.js +10 -7
  61. package/lib/serve/adapters.js +11 -9
  62. package/lib/serve/factory.js +14 -9
  63. package/lib/serve/index.js +28 -15
  64. package/lib/utils/data.js +1 -1
  65. package/lib/utils/index.js +27 -30
  66. package/lib/utils/resources/index.js +101 -0
  67. package/lib/utils/resources/tar.js +71 -0
  68. package/lib/utils/resources/utils.js +11 -0
  69. package/libx/_runtime/audit/Service.js +36 -39
  70. package/libx/_runtime/audit/generic/personal/access.js +3 -4
  71. package/libx/_runtime/audit/generic/personal/modification.js +3 -4
  72. package/libx/_runtime/audit/utils/v2.js +1 -2
  73. package/libx/_runtime/auth/index.js +126 -84
  74. package/libx/_runtime/auth/strategies/JWT.js +12 -19
  75. package/libx/_runtime/auth/strategies/dummy.js +1 -5
  76. package/libx/_runtime/auth/strategies/dwc.js +11 -9
  77. package/libx/_runtime/auth/strategies/mock.js +0 -4
  78. package/libx/_runtime/auth/strategies/{utils/xssec.js → xssecUtils.js} +7 -4
  79. package/libx/_runtime/auth/strategies/xsuaa.js +12 -19
  80. package/libx/_runtime/auth/utils.js +22 -1
  81. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +104 -98
  82. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +8 -3
  83. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
  84. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -1
  85. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/language.js +2 -8
  86. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +4 -29
  87. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +2 -1
  88. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +3 -2
  89. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +2 -2
  90. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +4 -6
  91. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +24 -21
  92. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +8 -2
  93. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +2 -0
  94. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/DispatcherCommand.js +2 -6
  95. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -12
  96. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +33 -9
  97. package/libx/_runtime/cds-services/adapter/odata-v4/utils/dispatcherUtils.js +56 -0
  98. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +2 -2
  99. package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +10 -3
  100. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +9 -11
  101. package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +6 -3
  102. package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +4 -2
  103. package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/utils.js +1 -1
  104. package/libx/_runtime/cds-services/adapter/rest/utils/binary.js +1 -1
  105. package/libx/_runtime/cds-services/adapter/rest/utils/key-value-utils.js +2 -3
  106. package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +6 -4
  107. package/libx/_runtime/cds-services/adapter/rest/utils/result.js +1 -0
  108. package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +8 -5
  109. package/libx/_runtime/cds-services/services/Service.js +40 -0
  110. package/libx/_runtime/cds-services/services/utils/columns.js +4 -3
  111. package/libx/_runtime/cds-services/services/utils/compareJson.js +4 -4
  112. package/libx/_runtime/cds-services/services/utils/differ.js +3 -3
  113. package/libx/_runtime/cds-services/services/utils/handlerUtils.js +4 -4
  114. package/libx/_runtime/cds-services/util/assert.js +20 -14
  115. package/libx/_runtime/cds.js +9 -1
  116. package/libx/_runtime/common/aspects/any.js +5 -0
  117. package/libx/_runtime/common/aspects/entity.js +25 -7
  118. package/libx/_runtime/common/aspects/utils.js +2 -2
  119. package/libx/_runtime/common/composition/data.js +6 -0
  120. package/libx/_runtime/common/composition/insert.js +3 -2
  121. package/libx/_runtime/common/composition/tree.js +4 -10
  122. package/libx/_runtime/common/composition/update.js +4 -4
  123. package/libx/_runtime/common/constants/draft.js +29 -26
  124. package/libx/_runtime/common/error/constants.js +2 -2
  125. package/libx/_runtime/common/error/frontend.js +7 -15
  126. package/libx/_runtime/common/generic/auth/capabilities.js +59 -0
  127. package/libx/_runtime/common/generic/auth/constants.js +20 -0
  128. package/libx/_runtime/common/generic/auth/expand.js +54 -0
  129. package/libx/_runtime/common/generic/auth/index.js +32 -0
  130. package/libx/_runtime/common/generic/auth/insertOnly.js +15 -0
  131. package/libx/_runtime/common/generic/auth/readOnly.js +26 -0
  132. package/libx/_runtime/common/generic/auth/requires.js +34 -0
  133. package/libx/_runtime/common/generic/auth/restrict.js +298 -0
  134. package/libx/_runtime/common/generic/auth/restrictions.js +85 -0
  135. package/libx/_runtime/common/generic/auth/utils.js +213 -0
  136. package/libx/_runtime/common/generic/crud.js +8 -6
  137. package/libx/_runtime/common/generic/etag.js +1 -1
  138. package/libx/_runtime/common/generic/input.js +35 -35
  139. package/libx/_runtime/common/generic/sorting.js +2 -3
  140. package/libx/_runtime/common/generic/temporal.js +2 -2
  141. package/libx/_runtime/common/i18n/messages.properties +1 -1
  142. package/libx/_runtime/common/toggles/handler.js +21 -0
  143. package/libx/_runtime/common/utils/copy.js +10 -1
  144. package/libx/_runtime/common/utils/cqn2cqn4sql.js +111 -35
  145. package/libx/_runtime/common/utils/csn.js +63 -1
  146. package/libx/_runtime/common/utils/dollar.js +10 -1
  147. package/libx/_runtime/common/utils/draft.js +46 -7
  148. package/libx/_runtime/common/utils/entityFromCqn.js +13 -9
  149. package/libx/_runtime/common/utils/extensibilityUtils.js +18 -0
  150. package/libx/_runtime/common/utils/foreignKeyPropagations.js +88 -104
  151. package/libx/_runtime/common/utils/generateOnCond.js +4 -1
  152. package/libx/_runtime/common/utils/quotingStyles.js +2 -0
  153. package/libx/_runtime/common/utils/resolveStructured.js +25 -9
  154. package/libx/_runtime/common/utils/resolveView.js +4 -1
  155. package/libx/_runtime/common/utils/rewriteAsterisks.js +3 -16
  156. package/libx/_runtime/common/utils/structured.js +33 -37
  157. package/libx/_runtime/common/utils/template.js +17 -8
  158. package/libx/_runtime/common/utils/templateProcessor.js +28 -28
  159. package/libx/_runtime/db/data-conversion/post-processing.js +118 -412
  160. package/libx/_runtime/db/expand/expandCQNToJoin.js +45 -41
  161. package/libx/_runtime/db/expand/rawToExpanded.js +29 -8
  162. package/libx/_runtime/db/generic/index.js +1 -3
  163. package/libx/_runtime/db/generic/input.js +5 -10
  164. package/libx/_runtime/db/generic/rewrite.js +5 -2
  165. package/libx/_runtime/db/generic/structured.js +2 -2
  166. package/libx/_runtime/db/query/delete.js +2 -2
  167. package/libx/_runtime/db/query/insert.js +1 -1
  168. package/libx/_runtime/db/query/update.js +9 -14
  169. package/libx/_runtime/db/sql-builder/CreateBuilder.js +4 -3
  170. package/libx/_runtime/db/sql-builder/FunctionBuilder.js +8 -8
  171. package/libx/_runtime/db/sql-builder/InsertBuilder.js +14 -1
  172. package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -2
  173. package/libx/_runtime/db/sql-builder/dataTypes.js +3 -3
  174. package/libx/_runtime/db/utils/columns.js +3 -3
  175. package/libx/_runtime/db/utils/normalizeTimeData.js +2 -2
  176. package/libx/_runtime/db/utils/propagateForeignKeys.js +6 -2
  177. package/libx/_runtime/extensibility/mps/index.js +5 -0
  178. package/libx/_runtime/extensibility/mps/service.js +111 -0
  179. package/libx/_runtime/extensibility/mps/tar.js +42 -0
  180. package/libx/_runtime/extensibility/mps/utils.js +11 -0
  181. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformREAD.js +0 -0
  182. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformRESULT.js +17 -5
  183. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformWRITE.js +1 -0
  184. package/libx/_runtime/extensibility/uiflex/index.js +54 -0
  185. package/libx/_runtime/extensibility/uiflex/service.js +276 -0
  186. package/libx/_runtime/{fiori → extensibility}/uiflex/utils.js +22 -7
  187. package/libx/_runtime/fiori/generic/activate.js +2 -2
  188. package/libx/_runtime/fiori/generic/before.js +4 -4
  189. package/libx/_runtime/fiori/generic/new.js +3 -3
  190. package/libx/_runtime/fiori/generic/patch.js +1 -1
  191. package/libx/_runtime/fiori/generic/read.js +58 -66
  192. package/libx/_runtime/fiori/generic/readOverDraft.js +74 -16
  193. package/libx/_runtime/fiori/utils/handler.js +6 -13
  194. package/libx/_runtime/fiori/utils/where.js +6 -5
  195. package/libx/_runtime/hana/Service.js +4 -10
  196. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +1 -1
  197. package/libx/_runtime/hana/driver.js +2 -2
  198. package/libx/_runtime/hana/execute.js +45 -75
  199. package/libx/_runtime/hana/pool.js +1 -1
  200. package/libx/_runtime/hana/streaming.js +2 -1
  201. package/libx/_runtime/index.js +6 -6
  202. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +5 -21
  203. package/libx/_runtime/messaging/Outbox.js +2 -2
  204. package/libx/_runtime/messaging/common-utils/AMQPClient.js +4 -14
  205. package/libx/_runtime/messaging/common-utils/connections.js +5 -7
  206. package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +30 -0
  207. package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -1
  208. package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +36 -30
  209. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -12
  210. package/libx/_runtime/messaging/enterprise-messaging.js +8 -8
  211. package/libx/_runtime/messaging/file-based.js +5 -5
  212. package/libx/_runtime/messaging/message-queuing.js +14 -12
  213. package/libx/_runtime/messaging/outbox/utils.js +18 -19
  214. package/libx/_runtime/messaging/redis-messaging.js +91 -0
  215. package/libx/_runtime/messaging/service.js +8 -6
  216. package/libx/_runtime/remote/Service.js +44 -8
  217. package/libx/_runtime/remote/utils/client.js +24 -19
  218. package/libx/_runtime/remote/utils/data.js +11 -11
  219. package/libx/_runtime/sqlite/Service.js +6 -9
  220. package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +5 -2
  221. package/libx/_runtime/types/api.js +10 -2
  222. package/libx/common/utils/ucsn.js +109 -0
  223. package/libx/gql/resolvers/crud/update.js +5 -0
  224. package/libx/gql/resolvers/parse/ast2cqn/columns.js +3 -1
  225. package/libx/gql/schema/typeDefMap.js +2 -2
  226. package/libx/odata/afterburner.js +110 -16
  227. package/libx/odata/cqn2odata.js +24 -27
  228. package/libx/odata/grammar.pegjs +9 -1
  229. package/libx/odata/parseToCqn.js +39 -0
  230. package/libx/odata/parser.js +1 -1
  231. package/libx/rest/RestAdapter.js +9 -1
  232. package/libx/rest/middleware/input.js +54 -0
  233. package/libx/rest/middleware/operation.js +14 -1
  234. package/libx/rest/middleware/parse.js +11 -7
  235. package/package.json +2 -2
  236. package/server.js +34 -19
  237. package/srv/audit-log.cds +2 -2
  238. package/srv/flex.cds +8 -2
  239. package/srv/flex.js +1 -1
  240. package/srv/mps.cds +23 -0
  241. package/srv/mps.js +1 -0
  242. package/libx/_runtime/auth/strategies/utils/uaa.js +0 -21
  243. package/libx/_runtime/common/generic/auth.js +0 -874
  244. package/libx/_runtime/common/toggles/alpha.js +0 -43
  245. package/libx/_runtime/db/generic/arrayed.js +0 -33
  246. package/libx/_runtime/fiori/uiflex/index.js +0 -35
  247. package/libx/_runtime/fiori/uiflex/service.js +0 -150
  248. package/libx/rest/utils/data.js +0 -60
@@ -4,12 +4,7 @@ const LOG = cds.log('hana|db|sql')
4
4
  const { HANA_TYPE_CONVERSION_MAP } = require('./conversion')
5
5
  const CustomBuilder = require('./customBuilder')
6
6
  const { sqlFactory } = require('../db/sql-builder/')
7
- const {
8
- getPostProcessMapper,
9
- getPropertyMapper,
10
- getStructMapper,
11
- postProcess
12
- } = require('../db/data-conversion/post-processing')
7
+ const { getPostProcessMapper, postProcess } = require('../db/data-conversion/post-processing')
13
8
  const { createJoinCQNFromExpanded, hasExpand, rawToExpanded, expandV2 } = require('../db/expand')
14
9
  const {
15
10
  hasStreamInsert,
@@ -35,19 +30,6 @@ function _cqnToSQL(model, query, user, locale, txTimestamp) {
35
30
 
36
31
  const SANITIZE_VALUES = process.env.NODE_ENV === 'production' && cds.env.log.sanitize_values !== false
37
32
 
38
- function _getOutputParameters(stmt) {
39
- const result = {}
40
- const info = stmt.getParameterInfo()
41
- for (let i = 0; i < info.length; i++) {
42
- const param = info[i]
43
- if (param.direction === 2) {
44
- result[param.name] = stmt.getParameterValue(i)
45
- }
46
- }
47
-
48
- return Object.keys(result).length > 0 ? result : undefined
49
- }
50
-
51
33
  const BINARY_TYPES = {
52
34
  12: 'BINARY',
53
35
  13: 'VARBINARY',
@@ -148,47 +130,20 @@ function _executeAsPreparedStatement(dbc, sql, values, reject, resolve) {
148
130
  }
149
131
  }
150
132
 
151
- if (cds.env.features.new_call_prodecure) {
152
- // procedure call metadata
153
- let outParameters
154
- const isProcedureCall = _isProcedureCall(sql)
155
- if (isProcedureCall) {
156
- try {
157
- const procedureName = _getProcedureName(sql)
158
- outParameters = await _getProcedureMetadata(procedureName, dbc)
159
- } catch (e) {
160
- LOG._warn && LOG.warn('Unable to fetch procedure metadata due to error:', e)
161
- }
133
+ // procedure call metadata
134
+ let outParameters
135
+ const isProcedureCall = _isProcedureCall(sql)
136
+ if (isProcedureCall) {
137
+ try {
138
+ const procedureName = _getProcedureName(sql)
139
+ outParameters = await _getProcedureMetadata(procedureName, dbc)
140
+ } catch (e) {
141
+ LOG._warn && LOG.warn('Unable to fetch procedure metadata due to error:', e)
162
142
  }
163
-
164
- // on @sap/hana-client, we need to use execQuery in case of calling procedures
165
- stmt[isProcedureCall && dbc.name !== 'hdb' ? 'execQuery' : 'exec'](values, function (err, rows, ...args) {
166
- if (err) {
167
- stmt.drop(() => {})
168
- err.query = sql
169
- if (values) err.values = SANITIZE_VALUES ? ['***'] : values
170
- return reject(err)
171
- }
172
-
173
- let result
174
- if (isProcedureCall) {
175
- result =
176
- dbc.name === 'hdb'
177
- ? _hdbGetResultForProcedure(rows, args, outParameters)
178
- : _hcGetResultForProcedure(stmt, rows, outParameters)
179
- } else {
180
- result = rows
181
- }
182
-
183
- stmt.drop(() => {})
184
-
185
- resolve(result)
186
- })
187
-
188
- return
189
143
  }
190
144
 
191
- stmt.exec(values, function (err, rows, procedureReturn) {
145
+ // on @sap/hana-client, we need to use execQuery in case of calling procedures
146
+ stmt[isProcedureCall && dbc.name !== 'hdb' ? 'execQuery' : 'exec'](values, function (err, rows, ...args) {
192
147
  if (err) {
193
148
  stmt.drop(() => {})
194
149
  err.query = sql
@@ -196,13 +151,19 @@ function _executeAsPreparedStatement(dbc, sql, values, reject, resolve) {
196
151
  return reject(err)
197
152
  }
198
153
 
199
- let result = rows
200
- if (dbc.name !== 'hdb') {
201
- result = _getOutputParameters(stmt) || rows
154
+ let result
155
+ if (isProcedureCall) {
156
+ result =
157
+ dbc.name === 'hdb'
158
+ ? _hdbGetResultForProcedure(rows, args, outParameters)
159
+ : _hcGetResultForProcedure(stmt, rows, outParameters)
160
+ } else {
161
+ result = rows
202
162
  }
203
163
 
204
164
  stmt.drop(() => {})
205
- resolve(procedureReturn || result)
165
+
166
+ resolve(result)
206
167
  })
207
168
  })
208
169
  }
@@ -236,13 +197,13 @@ function _executeSimpleSQL(dbc, sql, values) {
236
197
  })
237
198
  }
238
199
 
239
- function _executeSelectSQL(dbc, sql, values, isOne, postMapper, propertyMapper, objStructMapper) {
200
+ function _executeSelectSQL(dbc, sql, values, isOne, postMapper) {
240
201
  return _executeSimpleSQL(dbc, sql, values).then(result => {
241
202
  if (isOne) {
242
203
  result = result.length > 0 ? result[0] : null
243
204
  }
244
205
 
245
- return postProcess(result, postMapper, propertyMapper, objStructMapper)
206
+ return postProcess(result, postMapper)
246
207
  })
247
208
  }
248
209
 
@@ -273,17 +234,9 @@ function executeSelectCQN(model, dbc, query, user, locale, txTimestamp) {
273
234
  }
274
235
 
275
236
  const { sql, values = [] } = _cqnToSQL(model, query, user, locale, txTimestamp)
276
- const propertyMapper = getPropertyMapper(model, query, true)
277
-
278
- return _executeSelectSQL(
279
- dbc,
280
- sql,
281
- values,
282
- query.SELECT.one,
283
- getPostProcessMapper(HANA_TYPE_CONVERSION_MAP, model, query),
284
- propertyMapper,
285
- getStructMapper(model, query, propertyMapper)
286
- )
237
+ const postProcessMapper = getPostProcessMapper(HANA_TYPE_CONVERSION_MAP, model, query)
238
+
239
+ return _executeSelectSQL(dbc, sql, values, query.SELECT.one, postProcessMapper)
287
240
  }
288
241
 
289
242
  function _getValuesProxy(values) {
@@ -327,11 +280,28 @@ function executeInsertCQN(model, dbc, query, user, locale, txTimestamp) {
327
280
  }
328
281
 
329
282
  return _executeSimpleSQL(dbc, sql, values).then(affectedRows => {
283
+ const entriesOrRows = query.INSERT.entries || query.INSERT.rows
284
+ const affectedRowsCount = Array.isArray(affectedRows)
285
+ ? affectedRows.reduce((sum, rows) => sum + rows, 0)
286
+ : affectedRows
287
+ if (entriesOrRows && entriesOrRows.length !== affectedRowsCount) {
288
+ LOG._warn &&
289
+ LOG.warn(
290
+ `INSERT input deviates from affected rows (input: ${entriesOrRows.length}, affectedRows: ${affectedRowsCount})`,
291
+ {
292
+ sql,
293
+ args: values && values.length,
294
+ values,
295
+ query
296
+ }
297
+ )
298
+ throw new Error('Possible data loss by INSERT into HANA db. Please, update a corresponding HANA driver.')
299
+ }
330
300
  // InsertResult needs an object per row with its values
331
301
  // query.INSERT.values -> one row
332
302
  if (query.INSERT.values) return [{ affectedRows: 1, values: [values] }]
333
303
  // query.INSERT.entries or .rows -> multiple rows
334
- if (query.INSERT.entries || query.INSERT.rows) return values.map(v => ({ affectedRows: 1, values: v }))
304
+ if (entriesOrRows) return values.map(v => ({ affectedRows: 1, values: v }))
335
305
  // INSERT into SELECT
336
306
  return [{ affectedRows }]
337
307
  })
@@ -97,7 +97,7 @@ function factory4(creds, tenant) {
97
97
  /*
98
98
  * default generic-pool config
99
99
  */
100
- const defaultConfig = { min: 0, max: 100, testOnBorrow: true }
100
+ const defaultConfig = { min: 0, max: 100, testOnBorrow: true, fifo: false }
101
101
 
102
102
  const _getPoolConfig = function () {
103
103
  const { pool: poolConfig } = cds.env.requires.db
@@ -18,7 +18,8 @@ const streamExtension = _loadStreamExtensionIfNeeded()
18
18
 
19
19
  function hasStreamInsert(insert, model) {
20
20
  if (!model) return true
21
- const into = model.definitions[ensureNoDraftsSuffix(insert.into)]
21
+ const name = insert.into.ref ? insert.into.ref[0] : insert.into
22
+ const into = model.definitions[ensureNoDraftsSuffix(name)]
22
23
  if (!into) return false
23
24
 
24
25
  if (insert.entries && insert.entries.length > 0) {
@@ -5,12 +5,12 @@ module.exports = {
5
5
  return this._odatav4 || (this._odatav4 = require('./cds-services/adapter/odata-v4/to'))
6
6
  },
7
7
 
8
- get rest() {
9
- if (!this._rest) {
10
- if (global.cds.env.features.rest_new_adapter) this._rest = require('../rest')
11
- else this._rest = require('./cds-services/adapter/rest/to')
12
- }
13
- return this._rest
8
+ get old_rest() {
9
+ return this._old_rest || (this._old_rest = require('./cds-services/adapter/rest/to'))
10
+ },
11
+
12
+ get new_rest() {
13
+ return this._new_rest || (this._new_rest = require('../rest'))
14
14
  }
15
15
  },
16
16
 
@@ -1,8 +1,8 @@
1
1
  const cds = require('../cds')
2
- const LOG = cds.log('messaging')
3
2
  const MessagingService = require('./service.js')
4
3
  const { queueName } = require('./common-utils/naming-conventions')
5
4
  const optionsApp = require('../common/utils/vcap.js')
5
+ const normalizeIncomingMessage = require('./common-utils/normalizeIncomingMessage')
6
6
 
7
7
  class AMQPWebhookMessaging extends MessagingService {
8
8
  async init() {
@@ -33,25 +33,9 @@ class AMQPWebhookMessaging extends MessagingService {
33
33
  const management = this.getManagement()
34
34
  if (!opt.doNotDeploy) this.queued(management.createQueueAndSubscriptions.bind(management))()
35
35
  this.queued(this.listenToClient.bind(this))(async (_topic, _payload, _other, { done, failed }) => {
36
- const event = _topic
37
- // Some messaging systems don't adhere to the standard that the payload has a `data` property.
38
- // For these cases, we interpret the whole payload as `data`.
39
- let data, headers
40
- if (typeof _payload === 'object' && 'data' in _payload) {
41
- data = _payload.data
42
- headers = { ..._payload }
43
- delete headers.data
44
- } else {
45
- data = _payload
46
- headers = {}
47
- }
48
- const msg = {
49
- event,
50
- data,
51
- headers,
52
- inbound: true,
53
- ...(_other || {})
54
- }
36
+ const msg = Object.assign(normalizeIncomingMessage(_payload), _other || {})
37
+ msg.event = _topic
38
+
55
39
  if (!msg._) msg._ = {}
56
40
  msg._.topic = _topic
57
41
  try {
@@ -61,7 +45,7 @@ class AMQPWebhookMessaging extends MessagingService {
61
45
  // In case of AMQP and Solace, the `failed` callback must be called
62
46
  // with an error, otherwise there are problems with the redelivery count.
63
47
  failed(new Error('processing failed'))
64
- LOG.error('ERROR occured in asynchronous event processing:', e)
48
+ this.LOG.error('ERROR occured in asynchronous event processing:', e)
65
49
  }
66
50
  })
67
51
  }
@@ -19,7 +19,7 @@ class OutboxService extends cds.Service {
19
19
  this.emit = async function (...args) {
20
20
  const msg = typeof args[0] === 'object' ? args[0] : { event: args[0], data: args[1], headers: args[2] }
21
21
  const context = this.context || cds.context
22
- if (this.options.outbox && context && typeof context.on === 'function') {
22
+ if (this.options.outbox && context) {
23
23
  const outboxOpts = Object.assign(
24
24
  {},
25
25
  (typeof cds.requires.outbox === 'object' && cds.requires.outbox) || {},
@@ -38,7 +38,7 @@ class OutboxService extends cds.Service {
38
38
  try {
39
39
  await this._emitImmediate(msg)
40
40
  } catch (e) {
41
- LOG._error && LOG.error('Emit failed', { event: msg.event, cause: e })
41
+ LOG.error('Emit failed', { event: msg.event, cause: e })
42
42
  // opts.crashOnError is not official!!!
43
43
  if (isUnrecoverable(this, e) && outboxOpts.crashOnError !== false) process.exit(1)
44
44
  }
@@ -1,17 +1,8 @@
1
1
  const cds = require('../../cds.js')
2
- const LOG = cds.log('messaging')
3
2
  const ClientAmqp = require('@sap/xb-msg-amqp-v100').Client
4
3
  const { connect, disconnect } = require('./connections')
5
4
  const { hasPersistentOutbox } = require('../outbox/utils')
6
5
 
7
- const _JSONorString = string => {
8
- try {
9
- return JSON.parse(string)
10
- } catch (e) {
11
- return string
12
- }
13
- }
14
-
15
6
  const addDataListener = (client, queue, prefix, cb) =>
16
7
  new Promise((resolve, reject) => {
17
8
  const source = `${prefix}${queue}`
@@ -19,8 +10,7 @@ const addDataListener = (client, queue, prefix, cb) =>
19
10
  .receiver(queue)
20
11
  .attach(source)
21
12
  .on('data', async raw => {
22
- const buffer = raw.payload && Buffer.concat(raw.payload.chunks)
23
- const payload = buffer && _JSONorString(buffer.toString())
13
+ const payload = raw.payload && Buffer.concat(raw.payload.chunks).toString()
24
14
  const topic =
25
15
  raw.source &&
26
16
  raw.source.properties &&
@@ -36,7 +26,7 @@ const addDataListener = (client, queue, prefix, cb) =>
36
26
 
37
27
  const sender = (client, optionsApp) => client.sender(`${optionsApp.appName}-${optionsApp.appID}`)
38
28
 
39
- const emit = ({ data, event: topic, headers = {} }, stream, prefix) =>
29
+ const emit = ({ data, event: topic, headers = {} }, stream, prefix, LOG) =>
40
30
  new Promise((resolve, reject) => {
41
31
  LOG._info && LOG.info('Emit', { topic })
42
32
  const message = { ...headers, data }
@@ -69,7 +59,7 @@ class AMQPClient {
69
59
  this.client = new ClientAmqp(this.optionsAMQP)
70
60
  this.sender = sender(this.client, this.service.optionsApp)
71
61
  this.stream = this.sender.attach('')
72
- return connect(this.client, this.keepAlive)
62
+ return connect(this.client, this.service.LOG, this.keepAlive)
73
63
  }
74
64
 
75
65
  async disconnect() {
@@ -84,7 +74,7 @@ class AMQPClient {
84
74
  // REVISIT: Is this a robust way to find out if the connection is working?
85
75
  if (hasPersistentOutbox(this.service, cds.context && cds.context.tenant) && !this.sender.opened())
86
76
  throw new Error('AMQP: Sender is not open')
87
- await emit(msg, this.stream, this.prefix.topic)
77
+ await emit(msg, this.stream, this.prefix.topic, this.service.LOG)
88
78
  if (!this.keepAlive) return this.disconnect()
89
79
  }
90
80
 
@@ -1,11 +1,9 @@
1
- const cds = require('../../cds')
2
- const LOG = cds.log('messaging')
3
1
  const waitingTime = require('./waitingTime')
4
2
 
5
- const _connectUntilConnected = (client, x) => {
3
+ const _connectUntilConnected = (client, LOG, x) => {
6
4
  const _waitingTime = waitingTime(x)
7
5
  setTimeout(() => {
8
- connect(client, true)
6
+ connect(client, LOG, true)
9
7
  .then(() => {
10
8
  LOG._warn && LOG.warn('Reconnected to Enterprise Messaging Client')
11
9
  })
@@ -14,12 +12,12 @@ const _connectUntilConnected = (client, x) => {
14
12
  LOG.warn(
15
13
  `Connection to Enterprise Messaging Client lost: Reconnecting in ${Math.round(_waitingTime / 1000)} s`
16
14
  )
17
- _connectUntilConnected(client, x + 1)
15
+ _connectUntilConnected(client, LOG, x + 1)
18
16
  })
19
17
  }, _waitingTime)
20
18
  }
21
19
 
22
- const connect = (client, keepAlive) => {
20
+ const connect = (client, LOG, keepAlive) => {
23
21
  return new Promise((resolve, reject) => {
24
22
  client
25
23
  .once('connected', function () {
@@ -37,7 +35,7 @@ const connect = (client, keepAlive) => {
37
35
  client.once('disconnected', () => {
38
36
  client.removeAllListeners('error')
39
37
  client.removeAllListeners('connected')
40
- _connectUntilConnected(client, 0)
38
+ _connectUntilConnected(client, LOG, 0)
41
39
  })
42
40
  }
43
41
 
@@ -0,0 +1,30 @@
1
+ const _JSONorString = string => {
2
+ try {
3
+ return JSON.parse(string)
4
+ } catch (e) {
5
+ return string
6
+ }
7
+ }
8
+
9
+ // Some messaging systems don't adhere to the standard that the payload has a `data` property.
10
+ // For these cases, we interpret the whole payload as `data`.
11
+ const normalizeIncomingMessage = message => {
12
+ const _payload = typeof message === 'object' ? message : _JSONorString(message)
13
+ let data, headers
14
+ if (typeof _payload === 'object' && 'data' in _payload) {
15
+ data = _payload.data
16
+ headers = { ..._payload }
17
+ delete headers.data
18
+ } else {
19
+ data = _payload
20
+ headers = {}
21
+ }
22
+
23
+ return {
24
+ data,
25
+ headers,
26
+ inbound: true
27
+ }
28
+ }
29
+
30
+ module.exports = normalizeIncomingMessage
@@ -34,7 +34,8 @@ class EnterpriseMessagingShared extends AMQPWebhookMessaging {
34
34
  queueName,
35
35
  subscribedTopics: this.subscribedTopics,
36
36
  alternativeTopics: this.alternativeTopics,
37
- namespace: this.options.credentials && this.options.credentials.namespace
37
+ namespace: this.options.credentials && this.options.credentials.namespace,
38
+ LOG: this.LOG
38
39
  })
39
40
  return this.management
40
41
  }
@@ -1,6 +1,4 @@
1
1
  const authorizedRequest = require('../common-utils/authorizedRequest')
2
- const cds = require('../../cds.js')
3
- const LOG = cds.log('messaging')
4
2
  const sleep = require('util').promisify(setTimeout)
5
3
 
6
4
  const _getWebhookName = queueName => queueName
@@ -19,7 +17,8 @@ class EMManagement {
19
17
  subscribedTopics,
20
18
  maxRetries,
21
19
  subdomain,
22
- namespace
20
+ namespace,
21
+ LOG
23
22
  }) {
24
23
  this.subdomain = subdomain
25
24
  this.options = optionsManagement
@@ -33,6 +32,7 @@ class EMManagement {
33
32
  this.maxRetries = maxRetries === undefined ? 10 : maxRetries
34
33
  this.subdomainInfo = this.subdomain ? `(subdomain: ${this.subdomain})` : ''
35
34
  this.namespace = namespace
35
+ this.LOG = LOG
36
36
  }
37
37
 
38
38
  async getQueue(queueName = this.queueName) {
@@ -42,8 +42,11 @@ class EMManagement {
42
42
  path: `/hub/rest/api/v1/management/messaging/queues/${encodeURIComponent(queueName)}`,
43
43
  oa2: this.options.oa2,
44
44
  attemptInfo: () =>
45
- LOG._info &&
46
- LOG.info('Get queue', this.subdomain ? { queue: queueName, subdomain: this.subdomain } : { queue: queueName }),
45
+ this.LOG._info &&
46
+ this.LOG.info(
47
+ 'Get queue',
48
+ this.subdomain ? { queue: queueName, subdomain: this.subdomain } : { queue: queueName }
49
+ ),
47
50
  errMsg: `Queue "${queueName}" could not be retrieved ${this.subdomainInfo}`,
48
51
  target: { kind: 'QUEUE', queue: queueName },
49
52
  tokenStore: this
@@ -57,7 +60,8 @@ class EMManagement {
57
60
  uri: this.options.uri,
58
61
  path: `/hub/rest/api/v1/management/messaging/queues`,
59
62
  oa2: this.options.oa2,
60
- attemptInfo: () => LOG._info && LOG.info('Get queues', this.subdomain ? { subdomain: this.subdomain } : {}),
63
+ attemptInfo: () =>
64
+ this.LOG._info && this.LOG.info('Get queues', this.subdomain ? { subdomain: this.subdomain } : {}),
61
65
  errMsg: `Queues could not be retrieved ${this.subdomainInfo}`,
62
66
  target: { kind: 'QUEUE' },
63
67
  tokenStore: this
@@ -73,8 +77,8 @@ class EMManagement {
73
77
  oa2: this.options.oa2,
74
78
  dataObj: this.queueConfig,
75
79
  attemptInfo: () =>
76
- LOG._info &&
77
- LOG.info(
80
+ this.LOG._info &&
81
+ this.LOG.info(
78
82
  'Create queue',
79
83
  this.subdomain ? { queue: queueName, subdomain: this.subdomain } : { queue: queueName }
80
84
  ),
@@ -91,8 +95,8 @@ class EMManagement {
91
95
  path: `/hub/rest/api/v1/management/messaging/queues/${encodeURIComponent(queueName)}`,
92
96
  oa2: this.options.oa2,
93
97
  attemptInfo: () =>
94
- LOG._info &&
95
- LOG.info(
98
+ this.LOG._info &&
99
+ this.LOG.info(
96
100
  'Delete queue',
97
101
  this.subdomain ? { queue: queueName, subdomain: this.subdomain } : { queue: queueName }
98
102
  ),
@@ -109,8 +113,8 @@ class EMManagement {
109
113
  path: `/hub/rest/api/v1/management/messaging/queues/${encodeURIComponent(queueName)}/subscriptions`,
110
114
  oa2: this.options.oa2,
111
115
  attemptInfo: () =>
112
- LOG._info &&
113
- LOG.info(
116
+ this.LOG._info &&
117
+ this.LOG.info(
114
118
  'Get subscriptions',
115
119
  this.subdomain ? { queue: queueName, subdomain: this.subdomain } : { queue: queueName }
116
120
  ),
@@ -130,8 +134,8 @@ class EMManagement {
130
134
  )}/subscriptions/${encodeURIComponent(topicPattern)}`,
131
135
  oa2: this.options.oa2,
132
136
  attemptInfo: () =>
133
- LOG._info &&
134
- LOG.info(
137
+ this.LOG._info &&
138
+ this.LOG.info(
135
139
  'Create subscription',
136
140
  this.subdomain
137
141
  ? { topic: topicPattern, queue: queueName, subdomain: this.subdomain }
@@ -152,8 +156,8 @@ class EMManagement {
152
156
  )}/subscriptions/${encodeURIComponent(topicPattern)}`,
153
157
  oa2: this.options.oa2,
154
158
  attemptInfo: () =>
155
- LOG._info &&
156
- LOG.info(
159
+ this.LOG._info &&
160
+ this.LOG.info(
157
161
  'Delete subscription',
158
162
  this.subdomain
159
163
  ? { topic: topicPattern, queue: queueName, subdomain: this.subdomain }
@@ -173,8 +177,8 @@ class EMManagement {
173
177
  path: `/messagingrest/v1/subscriptions/${encodeURIComponent(webhookName)}`,
174
178
  oa2: this.optionsMessagingREST.oa2,
175
179
  attemptInfo: () =>
176
- LOG._info &&
177
- LOG.info(
180
+ this.LOG._info &&
181
+ this.LOG.info(
178
182
  'Get webhook',
179
183
  this.subdomain
180
184
  ? { webhook: webhookName, queue: queueName, subdomain: this.subdomain }
@@ -195,8 +199,8 @@ class EMManagement {
195
199
  path: `/messagingrest/v1/subscriptions/${encodeURIComponent(webhookName)}`,
196
200
  oa2: this.optionsMessagingREST.oa2,
197
201
  attemptInfo: () =>
198
- LOG._info &&
199
- LOG.info(
202
+ this.LOG._info &&
203
+ this.LOG.info(
200
204
  'Delete webhook',
201
205
  this.subdomain
202
206
  ? { webhook: webhookName, queue: queueName, subdomain: this.subdomain }
@@ -245,8 +249,8 @@ class EMManagement {
245
249
  oa2: this.optionsMessagingREST.oa2,
246
250
  dataObj,
247
251
  attemptInfo: () =>
248
- LOG._info &&
249
- LOG.info(
252
+ this.LOG._info &&
253
+ this.LOG.info(
250
254
  'Create webhook',
251
255
  this.subdomain
252
256
  ? { webhook: webhookName, queue: queueName, subdomain: this.subdomain }
@@ -266,8 +270,8 @@ class EMManagement {
266
270
  path: `/messagingrest/v1/subscriptions/${encodeURIComponent(webhookName)}`,
267
271
  oa2: this.optionsMessagingREST.oa2,
268
272
  attemptInfo: () =>
269
- LOG._info &&
270
- LOG.info(
273
+ this.LOG._info &&
274
+ this.LOG.info(
271
275
  'Delete webhook',
272
276
  this.subdomain
273
277
  ? { webhook: webhookName, queue: queueName, subdomain: this.subdomain }
@@ -280,7 +284,7 @@ class EMManagement {
280
284
  }
281
285
 
282
286
  async createQueueAndSubscriptions() {
283
- LOG._info && LOG.info(`Create messaging artifacts ${this.subdomainInfo}`)
287
+ this.LOG._info && this.LOG.info(`Create messaging artifacts ${this.subdomainInfo}`)
284
288
 
285
289
  const created = await this.createQueue()
286
290
  if (created && created.statusCode === 200) {
@@ -297,7 +301,7 @@ class EMManagement {
297
301
  for (const [s, _] of this.subscribedTopics) {
298
302
  if (existingSubscriptions.some(e => s === e)) unchangedSubs.push(s)
299
303
  }
300
- LOG._info && LOG.info('Unchanged subscriptions', unchangedSubs, ' ', this.subdomainInfo)
304
+ this.LOG._info && this.LOG.info('Unchanged subscriptions', unchangedSubs, ' ', this.subdomainInfo)
301
305
  await Promise.all([
302
306
  ...obsoleteSubs.map(s => this.deleteSubscription(s)),
303
307
  ...additionalSubs.map(async s => this._createSubscription(s))
@@ -331,7 +335,7 @@ class EMManagement {
331
335
  }
332
336
 
333
337
  async undeploy() {
334
- LOG._info && LOG.info(`Delete messaging artifacts ${this.subdomainInfo}`)
338
+ this.LOG._info && this.LOG.info(`Delete messaging artifacts ${this.subdomainInfo}`)
335
339
  await this.deleteQueue()
336
340
  if (this.optionsMessagingREST) await this.deleteWebhook()
337
341
  }
@@ -342,7 +346,7 @@ class EMManagement {
342
346
  uri: this.options.uri,
343
347
  path: `/hub/rest/api/v1/management/messaging/readinessCheck`,
344
348
  oa2: this.options.oa2,
345
- attemptInfo: () => LOG._info && LOG.info(`Readiness Check ${this.subdomainInfo}`),
349
+ attemptInfo: () => this.LOG._info && this.LOG.info(`Readiness Check ${this.subdomainInfo}`),
346
350
  errMsg: `Readiness Check failed ${this.subdomainInfo}`,
347
351
  target: { kind: 'READINESSCHECK' },
348
352
  tokenStore: this
@@ -365,8 +369,10 @@ class EMManagement {
365
369
  }
366
370
  const retryAfter = e.response && e.response.headers && e.response.headers['retry-after']
367
371
  const _waitingPeriod = waitingPeriod || (retryAfter && Number(retryAfter) * 1000) || 120 * 1000
368
- LOG._info &&
369
- LOG.info(`Readiness Check failed ${this.subdomainInfo}, retrying in ${_waitingPeriod / 1000} seconds...`)
372
+ this.LOG._info &&
373
+ this.LOG.info(
374
+ `Readiness Check failed ${this.subdomainInfo}, retrying in ${_waitingPeriod / 1000} seconds...`
375
+ )
370
376
  await sleep(_waitingPeriod)
371
377
  await check()
372
378
  } else {