@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
package/lib/index.js CHANGED
@@ -12,6 +12,7 @@ if (global.cds) Object.assign(module,{exports:global.cds}) ; else {
12
12
  /** @type {{ [path:string] : Service }} */ paths: {},
13
13
  /** @type Service[] */ providers: [],
14
14
  factory: require ('./serve/factory'),
15
+ adapters: require ('./serve/adapters'),
15
16
  bindings: require ('./connect/bindings'),
16
17
  })}
17
18
  /** @type {import './req/context'} */
@@ -81,6 +82,7 @@ if (global.cds) Object.assign(module,{exports:global.cds}) ; else {
81
82
  MessagingService: lazy => require('../libx/_runtime/messaging/service.js'),
82
83
  DatabaseService: lazy => require('../libx/_runtime/db/Service.js'),
83
84
  RemoteService: lazy => require('../libx/_runtime/rest/service.js'),
85
+ AuditLogService: lazy => require('../libx/_runtime/audit/Service.js'),
84
86
  odata: require('../libx/odata'),
85
87
 
86
88
  // Helpers
@@ -1,4 +1,4 @@
1
- const cds = require('../../')
1
+ const cds = require ('../../')
2
2
  const util = require('util')
3
3
 
4
4
  const _l2l = { 1: 'error', 2: 'warn', 3: 'info', 4: 'debug', 5: 'trace' }
@@ -8,7 +8,7 @@ const _l2l = { 1: 'error', 2: 'warn', 3: 'info', 4: 'debug', 5: 'trace' }
8
8
  */
9
9
  module.exports = (module, level, ...args) => {
10
10
  // config
11
- const { user: log_user, kibana_custom_fields } = cds.env.log
11
+ const { user: log_user , kibana_custom_fields } = cds.env.log
12
12
 
13
13
  // build the object to log
14
14
  const toLog = {
package/lib/ql/SELECT.js CHANGED
@@ -40,6 +40,16 @@ module.exports = class SELECT extends Whereable {
40
40
  if (cols === '*') return this.columns(...arguments)
41
41
  const c = _columns_or_not (cols)
42
42
  if (c) return this._add('columns',c)
43
+ if (typeof cols === 'string') {
44
+ try { parse.path(cols) }
45
+ catch(e) { //> it can't be a from
46
+ try { return this.columns(...arguments) }
47
+ catch(e) {
48
+ if (!e.message.startsWith('CDS compilation failed')) throw e
49
+ this._expected `Argument ${{cols}} to be a valid argument for SELECT.from or .columns`
50
+ }
51
+ }
52
+ }
43
53
  }
44
54
 
45
55
  // return a proxy assuming it's a from and switching to
package/lib/ql/parse.js CHANGED
@@ -7,6 +7,7 @@ module.exports = {
7
7
  CQL: (..._) => cds.parse.CQL (..._),
8
8
  CXL: (..._) => cds.parse.CXL (..._),
9
9
  cql: (..._) => cds.parse.cql (..._),
10
+ path: (..._) => cds.parse.path (..._),
10
11
  }
11
12
 
12
13
  const _simple = (x) => {
@@ -21,7 +21,10 @@ const { EventEmitter } = require('events')
21
21
  Reflect.defineProperty (cds,'context',{ enumerable:1,
22
22
  set(v) {
23
23
  const cr = current(); if (!cr) return
24
- const ctx = typeof v !== 'object' ? v : v.context || (v instanceof EventContext ? v : EventContext.for(v,false))
24
+ const ctx = (
25
+ v instanceof EventContext || typeof v !== 'object' ? v :
26
+ v.context || EventContext.for (v.req ? {_:v} : v)
27
+ )
25
28
  cr[_context] = ctx
26
29
  },
27
30
  get() {
@@ -1,5 +1,6 @@
1
1
  const cds = require ('../index'), { features } = cds.env, { uuid } = cds.utils
2
2
  const async_events = { succeeded:1, failed:1, done:1 }
3
+ const req_locale = require('./locale')
3
4
  const { EventEmitter } = require('events')
4
5
 
5
6
  /**
@@ -10,31 +11,23 @@ const { EventEmitter } = require('events')
10
11
  */
11
12
  class EventContext {
12
13
 
13
- toString() { return `${this.event} ${this.path}` }
14
-
15
- static for (data={}, base = cds.context) {
16
- if (data instanceof EventContext) return data
17
- if (base && features.cds_tx_inheritance) {
18
- let u = _inherit('user', u => typeof u === 'object' ? Object.create(u) : u)
19
- if (!u || !u.tenant) _inherit('tenant')
20
- if (!u || !u.locale) _inherit('locale')
21
- }
22
- function _inherit (p,fn) {
23
- if (p in data) return data[p]
24
- let pd = Reflect.getOwnPropertyDescriptor(base,p); if (!pd) return
25
- return data[p] = fn ? fn(pd.value) : pd.value
14
+ /** Creates a new instance that inherits from cds.context */
15
+ static for (_) {
16
+ const ctx = new this (_)
17
+ if (features.cds_tx_inheritance) {
18
+ const base = cds.context
19
+ if (base) ctx._set('_propagated', base)
26
20
  }
27
- // IMPORTANT: ensure user is first to be assigned to call setter_
28
- if (data.user) data = { user:1, ...data }
29
- return new this (data)
21
+ return ctx
30
22
  }
31
23
 
32
24
  constructor(_={}) {
33
- Object.assign (this, this._set('_',_))
25
+ Object.defineProperty (this, '_', { value:_, writable:true })
26
+ Object.assign (this, _)
34
27
  }
35
28
 
36
29
  _set (property, value) {
37
- Object.defineProperty (this, property, {value,writable:true})
30
+ Object.defineProperty (this, property, { value, writable:true })
38
31
  return value
39
32
  }
40
33
 
@@ -44,18 +37,16 @@ class EventContext {
44
37
  // Emitting and listening to succeeded / failed / done events
45
38
  //
46
39
 
47
- /** @returns {EventEmitter} */ get emitter() {
48
- return this._emitter || (this._emitter = this._propagated('emitter') || new EventEmitter)
40
+ get emitter() {
41
+ return this.context._emitter || (this.context._emitter = new EventEmitter)
49
42
  }
50
43
 
51
44
  async emit (event,...args) {
52
- if (!this._emitter) return
53
- if (event in async_events) {
54
- for (const each of this._emitter.listeners(event)) {
55
- await each.call(this,...args)
56
- }
57
- }
58
- else return this._emitter.emit (event,...args)
45
+ const emitter = this.context._emitter; if (!emitter) return
46
+ if (event in async_events)
47
+ for (const each of emitter.listeners(event))
48
+ await each.call (this, ...args)
49
+ else return emitter.emit (event,...args)
59
50
  }
60
51
 
61
52
  on (event, listener) {
@@ -75,48 +66,58 @@ class EventContext {
75
66
  // The following properties are inherited from root contexts, if exist...
76
67
  //
77
68
 
78
- set context(c) { if (c) this._set('context',c) }
69
+ set context(c) { if (c) this._set('context', this._set('_propagated', c)) }
79
70
  get context() { return this }
80
71
 
81
- set id(c) { if (c) super.id = c }
72
+ set id(id) {
73
+ if (id) super.id = id
74
+ }
82
75
  get id() {
83
- return this.id = this._propagated('id') || this.headers[ 'x-correlation-id' ] || uuid()
76
+ return super.id = this._propagated.id || this.headers[ 'x-correlation-id' ] || uuid()
84
77
  }
85
78
 
86
79
  set tenant(t) {
87
- if (t) super.tenant = this.user.tenant = t
80
+ if (t) super.tenant = t
88
81
  }
89
82
  get tenant() {
90
- return this.tenant = this.user.tenant || this._propagated('tenant')
83
+ return super.tenant = this._propagated.tenant
91
84
  }
92
85
 
93
86
  set user(u) {
94
- if (!u) return
95
- if (typeof u === 'string') u = new cds.User(u)
96
- if (this._.req) Object.defineProperty(u,'_req',{value:this._.req}) // REVISIT: The following is to support req.user.locale
97
- super.user = u
87
+ const user = super.user = u instanceof cds.User ? u : new cds.User(u)
88
+ for (let p of ['tenant','locale']) { // compatibility
89
+ if (u && u.hasOwnProperty(p)) this[p] = u[p] // eslint-disable-line no-prototype-builtins
90
+ else Object.defineProperty (user, p, { get: () => this[p] })
91
+ }
98
92
  }
99
93
  get user() {
100
- return this.user = this._propagated('user') || new cds.User
94
+ const u = this._propagated.user
95
+ return this.user = u ? {__proto__:u} : new cds.User.default
101
96
  }
102
97
 
103
98
  set locale(l) {
104
- if (l) super.locale = this.user.locale = l
99
+ if (l) super.locale = super._locale = l
105
100
  }
106
101
  get locale() {
107
- return this.locale = this.user.locale || this._propagated('locale')
102
+ return super.locale = this._propagated.locale || req_locale(this._.req)
103
+ }
104
+ get _locale() {
105
+ return super._locale = this._propagated._locale || req_locale.from_req(this._.req)
106
+ || this.hasOwnProperty('locale') && this.locale // eslint-disable-line no-prototype-builtins
108
107
  }
109
108
 
110
109
  get timestamp() {
111
- return super.timestamp = this._propagated('timestamp') || new Date
110
+ return super.timestamp = this._propagated.timestamp || new Date
112
111
  }
113
112
 
114
- set headers(h) { if (h) super.headers = h }
113
+ set headers(h) {
114
+ if (h) super.headers = h
115
+ }
115
116
  get headers() {
116
117
  if (this._ && this._.req && this._.req.headers) {
117
118
  return super.headers = this._.req.headers
118
119
  } else {
119
- const headers={}, outer = this._propagated('headers')
120
+ const headers={}, outer = this._propagated.headers
120
121
  if (outer) for (let each of EventContext.propagateHeaders) {
121
122
  if (each in outer) headers[each] = outer[each]
122
123
  }
@@ -125,27 +126,18 @@ class EventContext {
125
126
  }
126
127
 
127
128
 
128
- //
129
- // Connecting to transactions and request hierarchies
130
- //
131
-
132
- _propagated (p) {
133
- const ctx = this.context
134
- if (ctx !== this) return ctx[p]
135
- }
136
-
137
129
  set _tx(tx) {
138
130
  Object.defineProperty (this,'_tx',{value:tx}) //> allowed only once!
139
131
  const ctx = tx.context
140
132
  if (ctx && ctx !== this) {
141
- this.context = ctx
133
+ if (!this.hasOwnProperty('context')) this.context = ctx // eslint-disable-line no-prototype-builtins
134
+ ///////////////////////////////////////////////////////////////////
142
135
  // REVISIT: Eliminate req._children
143
- // only collect children if integrity checks are active
144
136
  if (features.assert_integrity !== false) {
145
- const reqs = ctx._children || (ctx._children = {})
137
+ const reqs = ctx._children || ctx._set('_children', {})
146
138
  const all = reqs[tx.name] || (reqs[tx.name] = [])
147
139
  all.push(this)
148
- }
140
+ } //////////////////////////////////////////////////////////////////
149
141
  }
150
142
  }
151
143
 
@@ -153,9 +145,11 @@ class EventContext {
153
145
  /** REVISIT: remove -> @deprecated */
154
146
  set _model(m){ super._model = m }
155
147
  get _model() {
156
- return super._model = this._tx && this._tx.model || this._propagated('_model')
148
+ return super._model = this._tx && this._tx.model || this._propagated._model
157
149
  }
158
150
  }
159
151
 
152
+
153
+ EventContext.prototype._set('_propagated', Object.seal({}))
160
154
  EventContext.propagateHeaders = [ 'x-correlation-id' ]
161
155
  module.exports = EventContext
package/lib/req/event.js CHANGED
@@ -4,12 +4,7 @@ const EventContext = require ('./context')
4
4
  * Instances of class cds.Event represent asynchronous messages as well as
5
5
  * synchronous requests. The latter are instances of subclass cds.Request.
6
6
  */
7
- class EventMessage extends EventContext {
8
- static for (eve) {
9
- if (eve instanceof this) return eve
10
- if (typeof eve === 'object') return new this(eve)
11
- }
12
- }
7
+ class EventMessage extends EventContext {}
13
8
 
14
9
  module.exports = exports = EventMessage
15
10
  exports.Context = EventContext
package/lib/req/locale.js CHANGED
@@ -7,15 +7,16 @@ const INCLUDE_LIST = i18n.preserved_locales.reduce((p,n)=>{
7
7
  en_US_x_sappsd: 'en_US_sappsd'
8
8
  })
9
9
 
10
- const from_req = req => req.query['sap-language'] || req.headers['x-sap-request-language'] || req.headers['accept-language']
10
+ const from_req = req => req && (
11
+ req.query && req.query['sap-language'] ||
12
+ req.headers && (req.headers['x-sap-request-language'] || req.headers['accept-language'])
13
+ )
11
14
 
12
15
  function req_locale (req) {
13
- if (!req) return i18n.default_language
14
- const locale = from_req(req)
15
- if (!locale) return i18n.default_language
16
+ const locale = from_req(req); if (!locale) return i18n.default_language
16
17
  const loc = locale.replace(/-/g,'_')
17
18
  return INCLUDE_LIST[loc]
18
- || /([a-z]+)/i.test(loc) && RegExp.$1.toLowerCase()
19
+ || /^([a-z]+)/i.test(loc) && RegExp.$1.toLowerCase()
19
20
  || i18n.default_language
20
21
  }
21
22
 
@@ -7,6 +7,8 @@ const { Responses, Errors } = require('./response')
7
7
  */
8
8
  class Request extends require('./event') {
9
9
 
10
+ toString() { return `${this.event} ${this.path}` }
11
+
10
12
  set method(m) { if (m) super.method = m }
11
13
  get method() {
12
14
  return this._set ('method', Crud2Http[this.event] || this.event)
package/lib/req/user.js CHANGED
@@ -1,5 +1,3 @@
1
- const req_locale = require('./locale')
2
-
3
1
  class User {
4
2
 
5
3
  constructor (_) {
@@ -9,9 +7,6 @@ class User {
9
7
  if (Array.isArray(_._roles)) this._roles = _._roles.reduce((p,n)=>{p[n]=1; return p},{})
10
8
  }
11
9
 
12
- get locale() { return super.locale = req_locale (this._req) }
13
- set locale(l) { if (l) super.locale = l }
14
-
15
10
  get attr() { return super.attr = {} }
16
11
 
17
12
  get _roles(){ return super._roles = {
@@ -24,6 +19,9 @@ class User {
24
19
 
25
20
  }
26
21
 
22
+ /**
23
+ * Subclass representing non-identified unauthenticated users.
24
+ */
27
25
  class Anonymous extends User {
28
26
  is (role) { return role === 'any' }
29
27
  get _roles() { return {} }
@@ -31,6 +29,9 @@ class Anonymous extends User {
31
29
  Anonymous.prototype._is_anonymous = true
32
30
  Anonymous.prototype.id = 'anonymous'
33
31
 
32
+ /**
33
+ * Subclass for executing code with superuser privileges.
34
+ */
34
35
  class Privileged extends User {
35
36
  constructor(_) { super(_||{}) }
36
37
  is() { return true }
@@ -38,4 +39,5 @@ class Privileged extends User {
38
39
  Privileged.prototype._is_privileged = true
39
40
  Privileged.prototype.id = 'privileged'
40
41
 
42
+ // exports -----------------
41
43
  module.exports = exports = Object.assign (User, { Anonymous, Privileged, default:Anonymous })
@@ -1,5 +1,6 @@
1
+ const cds = require('..'), { Event, Request } = cds
1
2
  const add_methods_to = require ('./Service-methods')
2
- const cds = require('..'), { unfold_csn: cds_localized } = cds.compile._localized
3
+ const { unfold_csn: cds_localized } = cds.compile._localized
3
4
 
4
5
 
5
6
  class Service extends require('./Service-handlers') {
@@ -15,7 +16,7 @@ class Service extends require('./Service-handlers') {
15
16
  */
16
17
  set model (csn) {
17
18
  if (csn) {
18
- super.model = cds_localized(cds.linked(cds.compile.for.odata(csn)))
19
+ super.model = cds.compile.for.nodejs(csn)
19
20
  add_methods_to (this)
20
21
  } else {
21
22
  super.model = undefined
@@ -27,7 +28,9 @@ class Service extends require('./Service-handlers') {
27
28
  */
28
29
  emit (event, data, headers) {
29
30
  const res = this._compat_sync (event, data, headers); if (res) return res
30
- const eve = cds.Event.for(event) || new cds.Event({ event, data, headers })
31
+ const eve = event instanceof Event ? event : new Event (
32
+ is_object(event) ? event : { event, data, headers }
33
+ )
31
34
  return this.dispatch (eve)
32
35
  }
33
36
 
@@ -35,9 +38,8 @@ class Service extends require('./Service-handlers') {
35
38
  * REST-style API to send synchronous requests...
36
39
  */
37
40
  send (method, path, data, headers) {
38
- const req = cds.Request.for(method) || (
39
- typeof path === 'object' ? new cds.Request({ method, data:path, headers:data }) :
40
- new cds.Request({ method, path, data, headers })
41
+ const req = method instanceof Request ? method : new Request (
42
+ is_object(method) ? method : is_object(path) ? { method, data:path, headers:data } : { method, path, data, headers }
41
43
  )
42
44
  return this.dispatch (req)
43
45
  }
@@ -51,7 +53,7 @@ class Service extends require('./Service-handlers') {
51
53
  * Querying API to send synchronous requests...
52
54
  */
53
55
  run (query, data) {
54
- const req = new cds.Request ({ query, data })
56
+ const req = new Request ({ query, data })
55
57
  return this.dispatch (req)
56
58
  }
57
59
  read (...args) { return is_query(args[0]) ? this.run(...args) : SELECT(...args).bind(this) }
@@ -113,3 +115,4 @@ const _reflect = (srv,filter) => !srv.model ? [] : srv.model.childrenOf (srv.nam
113
115
  const is_rest = x => x && typeof x === 'string' && x[0] === '/'
114
116
  const is_query = x => x && x.bind || is_array(x) && !x.raw
115
117
  const is_array = (x) => Array.isArray(x) && !x.raw
118
+ const is_object = (x) => typeof x === 'object'
@@ -13,12 +13,12 @@ exports.dispatch = async function dispatch (req) { //NOSONAR
13
13
 
14
14
  // Ensure we are in a proper transaction
15
15
  if (!this.context) {
16
- const txc = cds.context //> join an outer tx.context, if any, with a nested tx
17
- if (txc && !txc._done) return this.tx(txc).dispatch(req)
18
- else try { //> start a new top-level tx, which we need to commit/rollback
19
- const tx = cds.context = this.tx(req)
20
- return tx.dispatch(req) .then (tx.commit, tx.rollback)
21
- } finally { cds.context = txc }
16
+ const ctx = cds.context
17
+ if (ctx && ctx._tx && !ctx._done) { // join outer context with a nested tx -> we don't modify outer context
18
+ return this.tx(ctx).dispatch(req)
19
+ } else { // start a new root tx for subsequent continuation
20
+ return this.tx(tx => (cds.context=tx).dispatch(req))
21
+ }
22
22
  }
23
23
  // `this` is a tx from now on...
24
24
  if (!req._tx) req._tx = this
@@ -97,7 +97,7 @@ const _is_array = Array.isArray
97
97
  const _dummy = ()=>{} // REVISIT: required for some messaging tests which obviously still expect and call next()
98
98
 
99
99
  const _ensure_target = (srv,req) => {
100
- const q = req.query, p = req._.path; if (!q && !p) return
100
+ const q = req.query, p = req._.path || req._.entity; if (!q && !p) return
101
101
  if (srv.namespace) { // ensure fully-qualified names
102
102
  if (p) _ensure_fqn (req,'path',srv, p.startsWith('/') ? p.slice(1) : p)
103
103
  else if (q.SELECT) _ensure_fqn (q.SELECT,'from',srv)
@@ -105,10 +105,8 @@ const _ensure_target = (srv,req) => {
105
105
  else if (q.UPDATE) _ensure_fqn (q.UPDATE,'entity',srv)
106
106
  else if (q.DELETE) _ensure_fqn (q.DELETE,'from',srv)
107
107
  }
108
- if (typeof q === 'object') {
109
- const m = srv.model, defs = m && m.definitions || {}
110
- req.target = cds.infer(q,defs)
111
- }
108
+ const m = srv.model, defs = m && m.definitions || {}
109
+ req.target = typeof q === 'object' ? cds.infer(q,defs) : defs[req.path]
112
110
  }
113
111
 
114
112
  const _ensure_fqn = (x,p,srv, name = x[p]) => {
@@ -12,53 +12,42 @@ module.exports = (srv) => {
12
12
 
13
13
  const add_handler_for = (srv, def) => {
14
14
  const event = def.name.match(/\w*$/)[0]
15
+
16
+ // Use existing methods as handler implementations
15
17
  const method = srv[event]
16
18
  if (method) {
17
- // use existing method as handler
18
- if (method._handled || method.name in srv.__proto__) return
19
- srv.on (event, ({data}) => {
19
+ if (method._is_stub || method.name in srv.__proto__.__proto__) return
20
+ srv.on (event, function ({params,data}) {
20
21
  const args = []; if (def.parent) args.push (def.parent)
22
+ for (let p in params) args.push(params[p])
21
23
  for (let p in data) args.push(data[p])
22
- return method.apply (srv,args)
24
+ return method.apply (this,args)
23
25
  })
24
- method._handled = true
25
- } else {
26
- // add method to emit request
27
- const method = srv[event] = function (...args) {
28
- const [$] = args, target = this.entities [ $ && $.name ? $.name.match(/\w*$/)[0] : $ ]
29
- const data = {}
30
- if (target) { //> it's a bound action
31
- def = target.actions[event]; args.shift()
32
- // In case of bound actions, first argument(s) are expected to be the target's primary key,
33
- // which we need to fill in to named properties in data
34
- // object variant of keys, ensure keys are present in args[0] because of complex param
35
- if (typeof args[0] === 'object' && Object.keys(target.keys).every(k => k in args[0])) {
36
- const keys = args.shift()
37
- for (const p in target.keys) {
38
- data[p] = keys[p]
39
- }
40
- } else { // positional
41
- for (let p in target.keys) {
42
- data[p] = args.shift()
43
- }
44
- }
45
-
46
- }
47
-
48
- // object variant of params, ensure at least one param is present in args[0]
49
- // REVISIT: still not bullet proof, but parameters might be optional
50
- if (def.params) {
51
- if (args[0] !== null && typeof args[0] === 'object' && args.length === 1 && Object.keys(def.params).some(p => p in args[0])) {
52
- const params = args.shift()
53
- for (const p in def.params) data[p] = params[p]
54
- } else { // positional
55
- for (let p in def.params) data[p] = args.shift()
56
- }
57
- }
26
+ }
58
27
 
59
- // REVISIT: What happens with name clashes of keys and params?
60
- return this.send ({ event, target, data })
28
+ // Add stub methods to send request via typed API
29
+ const stub = srv[event] = function (...args) {
30
+ const req = { event, data:{} }, $ = args[0]
31
+ const target = this.entities [ $ && $.name ? $.name.match(/\w*$/)[0] : $ ]
32
+ if (target) { //> bound action/function?
33
+ req.target = target; args.shift() // first argument is the target entity name
34
+ req.params = [ args.shift() ] // second argument is the target's primary key
61
35
  }
62
- method._handled = true
36
+ const {params} = target ? target.actions[event] : def
37
+ if (params) req.data = _named(args,params) || _positional(args,params)
38
+ return this.send (req)
63
39
  }
40
+ stub._is_stub = true
64
41
  }
42
+
43
+
44
+ const _named = (args, declared) => {
45
+ if (args.length > 1) return
46
+ const a = args[0]
47
+ if (! a || typeof a !== 'object') return
48
+ if (Object.keys(a).every (k => k in declared)) return a
49
+ }
50
+
51
+ const _positional = (args, declared) => Object.keys(declared).reduce (
52
+ (data,k,i) => { data[k] = args[i]; return data }, {}
53
+ )
@@ -1,5 +1,7 @@
1
1
  const cds = require('../index'), { cds_tx_protection } = cds.env.features
2
2
  const EventContext = require('../req/context')
3
+ class RootContext extends EventContext {}
4
+
3
5
 
4
6
  /**
5
7
  * This is the implementation of the `srv.tx(req)` method. It constructs
@@ -28,12 +30,10 @@ module.exports = function tx (ctx,fn) { const srv = this
28
30
  // `ctx` is a plain context object or undefined
29
31
  if (ctx) { // REVISIT: This is for compatibility with AFC only
30
32
  if (ctx._txed_before) return NestedTransaction.for (srv, ctx._txed_before)
31
- else {
32
- Object.defineProperty(ctx, '_txed_before', { value: EventContext.for(ctx) }) // > must be non-enumerable
33
- return RootTransaction.for (srv, ctx._txed_before)
34
- }
33
+ else Object.defineProperty(ctx, '_txed_before', { value: RootContext.for(ctx) }) // > must be non-enumerable
34
+ return RootTransaction.for (srv, ctx._txed_before)
35
35
  }
36
- return RootTransaction.for (srv, EventContext.for(ctx))
36
+ return RootTransaction.for (srv, RootContext.for(ctx))
37
37
  }
38
38
 
39
39
 
@@ -174,8 +174,11 @@ const _begin = async function (req) {
174
174
  return this.ready = this.__proto__.dispatch.call (this,req)
175
175
  // Protection against unintended tx.run() after tx.commit/rollback()
176
176
  if (typeof this.ready === 'string' || !this.ready && this.context._done) {
177
- if (cds_tx_protection) throw Object.assign(new Error (`Transaction is ${this.ready || this.context._done}, no subsequent .run allowed, without prior .begin`), { code: 'TRANSACTION_CLOSED' })
178
- else this.ready = this.begin() // compatibiliy to former behavior, which allowed tx.run() after commit/rollback
177
+ if (!cds_tx_protection) this.ready = this.begin() // compatibiliy to former behavior, which allowed tx.run() after commit/rollback
178
+ else throw cds.error (
179
+ `Transaction is ${this.ready || this.context._done}, no subsequent .run allowed, without prior .begin`,
180
+ { code: 'TRANSACTION_CLOSED' }
181
+ )
179
182
  }
180
183
  else if (!this.ready) this.ready = this.begin()
181
184
  await this.ready
@@ -1,16 +1,18 @@
1
1
  const lib = require('../../libx/_runtime')
2
2
  const registry = {
3
- rest: lib.to.rest,
4
- odata: lib.to.odata_v4,
5
- odata_v2: lib.to.odata_v4,
6
- odata_v4: lib.to.odata_v4,
7
- fiori: lib.to.odata_v4,
3
+ get rest() { return lib.to.old_rest },
4
+ get new_rest() { return lib.to.new_rest },
5
+ get odata() { return lib.to.odata_v4 },
6
+ get odata_v2() { return lib.to.odata_v4 },
7
+ get odata_v4() { return lib.to.odata_v4 },
8
+ get fiori() { return lib.to.odata_v4 },
8
9
  }
9
10
 
10
11
 
11
12
  class ProtocolAdapter {
12
13
 
13
14
  static at (protocol) {
15
+ if (protocol === 'rest' && global.cds.env.features.rest_new_adapter) protocol = 'new_rest'
14
16
  const factory = registry[protocol]; if (factory) return factory
15
17
  else throw new Error (`Service protocol ${protocol} is not supported`)
16
18
  }
@@ -30,12 +32,12 @@ class ProtocolAdapter {
30
32
  /**
31
33
  * Mounts the adapter to an express app.
32
34
  */
33
- in (app) {
34
- const srv = this.service
35
+ in (app, at) {
36
+ const srv = this.service, path = at || srv.path
35
37
  lib.perf (app)
36
38
  lib.auth (srv, app, srv.options)
37
- app.use (srv.path+'/webapp/', (_,res)=> res.sendStatus(404))
38
- app.use (srv.path, this)
39
+ app.use (path+'/webapp/', (_,res)=> res.sendStatus(404))
40
+ app.use (path, this)
39
41
  return srv
40
42
  }
41
43