@sap/cds 5.6.2 → 5.7.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 (183) hide show
  1. package/CHANGELOG.md +133 -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 +7 -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 +13 -4
  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 -65
  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 +26 -0
  73. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +5 -5
  74. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +2 -2
  75. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +13 -10
  76. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/ConditionalRequestControlCommand.js +0 -7
  77. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/ConditionalRequestValidator.js +0 -8
  78. package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +54 -76
  79. package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +0 -7
  80. package/libx/_runtime/cds-services/adapter/rest/handlers/create.js +3 -6
  81. package/libx/_runtime/cds-services/adapter/rest/handlers/delete.js +3 -6
  82. package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +3 -6
  83. package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +3 -6
  84. package/libx/_runtime/cds-services/adapter/rest/handlers/update.js +3 -6
  85. package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +2 -2
  86. package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +8 -4
  87. package/libx/_runtime/cds-services/services/Service.js +0 -6
  88. package/libx/_runtime/cds-services/services/utils/columns.js +10 -3
  89. package/libx/_runtime/cds-services/services/utils/compareJson.js +4 -7
  90. package/libx/_runtime/cds-services/services/utils/differ.js +4 -1
  91. package/libx/_runtime/cds-services/services/utils/handlerUtils.js +1 -41
  92. package/libx/_runtime/cds-services/util/assert.js +1 -262
  93. package/libx/_runtime/cds.js +6 -9
  94. package/libx/_runtime/common/aspects/entity.js +1 -1
  95. package/libx/_runtime/common/composition/delete.js +4 -2
  96. package/libx/_runtime/common/composition/update.js +22 -35
  97. package/libx/_runtime/common/composition/utils.js +3 -7
  98. package/libx/_runtime/common/error/standardError.js +11 -0
  99. package/libx/_runtime/common/generic/auth.js +63 -33
  100. package/libx/_runtime/common/generic/crud.js +11 -23
  101. package/libx/_runtime/common/generic/input.js +20 -0
  102. package/libx/_runtime/common/generic/paging.js +2 -2
  103. package/libx/_runtime/common/generic/put.js +4 -10
  104. package/libx/_runtime/common/generic/sorting.js +12 -30
  105. package/libx/_runtime/common/perf/index.js +24 -0
  106. package/libx/_runtime/common/utils/cqn.js +58 -1
  107. package/libx/_runtime/common/utils/cqn2cqn4sql.js +297 -121
  108. package/libx/_runtime/common/utils/csn.js +38 -56
  109. package/libx/_runtime/common/utils/entityFromCqn.js +6 -6
  110. package/libx/_runtime/common/utils/resolveView.js +4 -5
  111. package/libx/_runtime/common/utils/rewriteAsterisks.js +46 -5
  112. package/libx/_runtime/common/utils/search2cqn4sql.js +21 -9
  113. package/libx/_runtime/common/utils/structured.js +35 -25
  114. package/libx/_runtime/db/Service.js +0 -6
  115. package/libx/_runtime/db/expand/expand-v2.js +130 -0
  116. package/libx/_runtime/db/expand/expandCQNToJoin.js +38 -52
  117. package/libx/_runtime/db/expand/index.js +3 -1
  118. package/libx/_runtime/db/generic/arrayed.js +3 -1
  119. package/libx/_runtime/db/generic/input.js +52 -10
  120. package/libx/_runtime/db/generic/integrity.js +367 -26
  121. package/libx/_runtime/db/generic/virtual.js +51 -13
  122. package/libx/_runtime/db/query/update.js +9 -3
  123. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +8 -9
  124. package/libx/_runtime/{common → db}/utils/propagateForeignKeys.js +11 -14
  125. package/libx/_runtime/fiori/generic/activate.js +1 -0
  126. package/libx/_runtime/fiori/generic/before.js +2 -1
  127. package/libx/_runtime/fiori/generic/edit.js +1 -0
  128. package/libx/_runtime/fiori/generic/patch.js +1 -1
  129. package/libx/_runtime/fiori/generic/read.js +155 -57
  130. package/libx/_runtime/fiori/uiflex/handler/transformRESULT.js +0 -4
  131. package/libx/_runtime/fiori/uiflex/index.js +1 -1
  132. package/libx/_runtime/fiori/uiflex/{extensibility/index.js → service.js} +6 -4
  133. package/libx/_runtime/fiori/utils/delete.js +7 -1
  134. package/libx/_runtime/hana/Service.js +1 -8
  135. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +5 -14
  136. package/libx/_runtime/hana/execute.js +10 -4
  137. package/libx/_runtime/hana/pool.js +55 -45
  138. package/libx/_runtime/hana/search.js +7 -6
  139. package/libx/_runtime/hana/search2cqn4sql.js +8 -5
  140. package/libx/_runtime/hana/searchToContains.js +3 -1
  141. package/libx/_runtime/index.js +5 -5
  142. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +3 -3
  143. package/libx/_runtime/messaging/Outbox.js +53 -0
  144. package/libx/_runtime/messaging/common-utils/AMQPClient.js +17 -10
  145. package/libx/_runtime/messaging/common-utils/connections.js +14 -9
  146. package/libx/_runtime/messaging/common-utils/waitingTime.js +2 -0
  147. package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -3
  148. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -2
  149. package/libx/_runtime/messaging/enterprise-messaging.js +21 -15
  150. package/libx/_runtime/messaging/file-based.js +5 -5
  151. package/libx/_runtime/messaging/message-queuing.js +2 -3
  152. package/libx/_runtime/messaging/outbox/OutboxRunner.js +75 -0
  153. package/libx/_runtime/messaging/outbox/utils.js +192 -0
  154. package/libx/_runtime/messaging/service.js +16 -30
  155. package/libx/_runtime/remote/Service.js +15 -0
  156. package/libx/_runtime/remote/utils/client.js +15 -3
  157. package/libx/_runtime/remote/utils/{dataConversion.js → data.js} +12 -2
  158. package/libx/_runtime/sqlite/Service.js +7 -10
  159. package/libx/_runtime/sqlite/customBuilder/CustomExpressionBuilder.js +19 -0
  160. package/libx/_runtime/sqlite/execute.js +18 -12
  161. package/libx/_runtime/types/api.js +2 -1
  162. package/libx/gql/resolvers/parse/ast2cqn/columns.js +1 -1
  163. package/libx/odata/{odata2cqn/afterburner.js → afterburner.js} +28 -16
  164. package/libx/odata/{cqn2odata/index.js → cqn2odata.js} +1 -1
  165. package/libx/odata/{odata2cqn/grammar.pegjs → grammar.pegjs} +182 -118
  166. package/libx/odata/index.js +18 -15
  167. package/libx/odata/parser.js +1 -0
  168. package/libx/odata/utils.js +57 -0
  169. package/libx/rest/RestAdapter.js +2 -6
  170. package/libx/rest/utils/data.js +1 -6
  171. package/package.json +4 -3
  172. package/server.js +4 -5
  173. package/srv/audit-log.cds +87 -0
  174. package/{libx/_runtime/fiori/uiflex/extensibility/index.cds → srv/flex.cds} +0 -0
  175. package/srv/flex.js +1 -0
  176. package/srv/outbox.cds +11 -0
  177. package/srv/outbox.js +0 -0
  178. package/libx/_runtime/cds-services/adapter/perf/performance.js +0 -104
  179. package/libx/_runtime/cds-services/adapter/perf/performanceMeasurement.js +0 -33
  180. package/libx/odata/odata2cqn/index.js +0 -3
  181. package/libx/odata/odata2cqn/parser.js +0 -1
  182. package/libx/odata/readme.md +0 -1
  183. package/libx/odata/utils/index.js +0 -64
@@ -1,6 +1,5 @@
1
1
  const { types, classes:{ service, entity, action, event, any, struct, array, context, annotation } } = require('.')
2
2
  const _kinds = { annotation, context, service, action, event, entity, view:entity }
3
- const _minified = Symbol('minified')
4
3
 
5
4
  class LinkedCSN extends any {
6
5
 
@@ -48,38 +47,6 @@ class LinkedCSN extends any {
48
47
  return Object.setPrototypeOf (x, new.target.prototype)
49
48
  }
50
49
 
51
- minified (skip = global.cds.env.features.skip_unused) {
52
- if (!skip) return this
53
- if (this[_minified]) return this; else _set (this,_minified,true)
54
- const csn = this, all = csn.definitions, reached = new Set
55
- const roots = skip === 'all' ? this.services : this.each(_root)
56
- for (let each of roots) _visit (each)
57
- function _visit (d) {
58
- if (reached.has(d)) return; else reached.add(d)
59
- if (d.kind === 'service') for (let e of csn.childrenOf(d)) _visit(e)
60
- if (d.includes) d.includes.forEach(i => _visit(all[i])) // Note: with delete d.includes, redirects in AFC broke
61
- if (d.query) d.query._target && _visit (d.query._target)
62
- if (d.type) _builtin(d.type) || _visit (d.__proto__)
63
- if (d.target) _visit (d._target) ; else if (d.targetAspect) _visit (typeof d.targetAspect === 'object' ? d.targetAspect : all[d.targetAspect])
64
- if (d.returns) _visit (d.returns)
65
- if (d.items) _visit (d.items)
66
- if (d.parent) _visit (d.parent)
67
- for (let e in d.elements) _visit (d.elements[e])
68
- for (let a in d.actions) _visit (d.actions[a])
69
- for (let p in d.params) _visit (d.params[p])
70
- }
71
- for (let n in all) {
72
- if (n.endsWith('.texts') && reached.has(all[n.slice(0,-6)])) continue
73
- if (reached.has(all[n])) continue
74
- else {
75
- delete all[n]
76
- // also delete the legacy _texts proxy (not enumerable, installed by _localized.unfold_csn)
77
- if (n.endsWith('.texts')) delete all[n.replace('.texts','_texts')]
78
- }
79
- }
80
- return this
81
- }
82
-
83
50
  *each (x, defs=this.definitions) {
84
51
  const pick=_is(x); for (let d in defs) if (pick(defs[d])) yield defs[d]
85
52
  }
@@ -131,7 +98,6 @@ class LinkedCSN extends any {
131
98
  const _unresolved = (x,unknown=any) => ({name:x, __proto__:unknown.prototype, _unresolved:true})
132
99
  const _builtin = x => types[x] || typeof x === 'string' && x.startsWith('cds.hana.') && any.prototype
133
100
  const _infer = require('./infer'), _not_inferred = _unresolved('<query>',entity)
134
- const _root = d => d instanceof service || d instanceof entity && !d.name.endsWith('.texts') && d['@cds.persistence.skip'] !== 'if-unused'
135
101
  const _set = (o,p,v) => Object.defineProperty (o,p,{value:v,enumerable:false,configurable:1,writable:1})
136
102
  const _own = (o,p) => { const pd = Reflect.getOwnPropertyDescriptor(o,p); return pd && pd.value }
137
103
  const _is = x => {
package/lib/deploy.js CHANGED
@@ -14,7 +14,16 @@ exports = module.exports = function cds_deploy (model,options) { return {
14
14
  async to (db, o=options||{}) { // NOSONAR
15
15
  LOG = cds.log('deploy')
16
16
 
17
- if (model && !model.definitions) model = await cds.load (model)
17
+ if (model && !model.definitions) {
18
+ model = await cds.load (model) .then (cds.minify)
19
+ if (DEBUG) try {
20
+ DEBUG (`model loaded from ${model.$sources.length} file(s):\n\x1b[2m`)
21
+ for (let each of model.$sources) console.log (' ', local(each))
22
+ } finally {
23
+ console.log ('\x1b[0m')
24
+ }
25
+ }
26
+
18
27
  if (o.mocked) exports.include_external_entities_in (model)
19
28
  else exports.exclude_external_entities_in (model)
20
29
 
@@ -120,37 +129,36 @@ function init_from_csv (db, csn, SILENT) {
120
129
 
121
130
  function _csvs (filename,_,allFiles) {
122
131
  if (filename[0] === '-' || !filename.endsWith ('.csv')) return false
123
- if (/[._]texts\.csv$/.test (filename) && check_lang_file(filename, allFiles)) {
124
- return false
125
- }
132
+ if (prefer_translated_texts(filename, allFiles)) return false
126
133
  return true
127
134
  }
128
135
  }
129
136
 
130
137
  function init_from_json (db, csn, SILENT) {
131
138
  return init_from_ (['data'], _jsons, db, csn, SILENT, (entity, src) => {
132
- let json = JSON.parse (src)
133
- return json[0] && INSERT.into (entity) .entries (json)
139
+ let json = JSON.parse (src)
140
+ return json[0] && INSERT.into (entity) .entries (json)
134
141
  });
135
142
 
136
143
  function _jsons (filename,_,allFiles) {
137
144
  if (filename[0] === '-' || !filename.endsWith ('.json')) return false
138
- if (/[._]texts\.json$/.test (filename) && check_lang_file(filename, allFiles)) {
139
- return false
140
- }
145
+ if (prefer_translated_texts(filename, allFiles)) return false
141
146
  return true
142
147
  }
143
148
  }
144
149
 
145
- function check_lang_file(filename, allFiles) {
146
- // ignores 'Books_texts.csv/json' if there is any 'Books_texts_LANG.csv/json'
147
- const basename = path.parse(filename).name
148
- const monoLangFiles = allFiles.filter (file => new RegExp('^'+basename+'_').test (file))
149
- if (monoLangFiles.length > 0) {
150
- DEBUG && DEBUG (`ignoring '${filename}' in favor of [${monoLangFiles}]`) // eslint-disable-line
151
- return true
150
+ /**
151
+ * ignores 'Books_texts.csv/json' if there is any 'Books_texts_LANG.csv/json'
152
+ */
153
+ function prefer_translated_texts (file, all) {
154
+ if (/[._]texts\.(json|csv)$/.test (file)) {
155
+ const pattern = new RegExp('^'+ path.basename(file) +'_')
156
+ const translated = all.filter (f => pattern.test(f))
157
+ if (translated.length > 0) {
158
+ DEBUG && DEBUG (`ignoring '${file}' in favor of [${translated}]`) // eslint-disable-line
159
+ return true
160
+ }
152
161
  }
153
- return false
154
162
  }
155
163
 
156
164
  async function init_from_ (locations, filter, db, csn, SILENT, INSERT_into) { // NOSONAR
@@ -13,12 +13,14 @@ module.exports = {
13
13
  in_memory_db: !production,
14
14
  test_data: !production,
15
15
  test_mocks: !production,
16
+ with_mocks: !production,
16
17
  mocked_bindings: !production,
17
18
  // skip_unused: 'all',
18
19
  skip_unused: true,
19
20
  one_model: true,
20
21
  localized: true,
21
- // assert_integrity: true,
22
+ assert_integrity: true,
23
+ assert_integrity_type: 'RT',
22
24
  cds_tx_protection: true,
23
25
  cds_tx_inheritance: true,
24
26
  },
package/lib/env/index.js CHANGED
@@ -2,6 +2,7 @@ const { isfile, fs } = require('../utils')
2
2
  const DEFAULTS = require('./defaults'), defaults = require.resolve ('./defaults')
3
3
  const os_user_home = require('os').homedir()
4
4
  const compat = require('./compat')
5
+ const presets = require('./presets')
5
6
  const path = require('path')
6
7
 
7
8
  /**
@@ -73,6 +74,9 @@ class Config {
73
74
  else if (this.requires.db.multiTenant !== undefined) this.requires.multitenancy = this.requires.db.multiTenant
74
75
  }
75
76
 
77
+ // 9. apply presets
78
+ presets (this)
79
+
76
80
  // Only if feature is enabled
77
81
  this._emulate_vcap_services()
78
82
  }
@@ -214,14 +218,15 @@ _loadFromPath (_path, _basePath) {
214
218
  _add_process_env (prefix, basePath) {
215
219
  const {env} = process
216
220
  const PREF = prefix.toUpperCase(), my = { CONFIG: PREF+'_CONFIG', ENV: PREF+'_ENV' }
217
- const configEnvValue = env[my.CONFIG]
218
221
  let config
219
- try {
222
+
223
+ const configEnvValue = env[my.CONFIG]
224
+ if (configEnvValue) try {
220
225
  // CDS_CONFIG={ /* json */}
221
226
  config = JSON.parse (configEnvValue)
222
227
  } catch (e) {
223
228
  // CDS_CONFIG=/path/to/config.json *OR* CDS_CONFIG=/path/to/config/dir
224
- if (configEnvValue && typeof configEnvValue === "string") this._loadFromPath (configEnvValue, basePath)
229
+ if (typeof configEnvValue === "string") this._loadFromPath (configEnvValue, basePath)
225
230
  }
226
231
 
227
232
  if (!config) config = {}
@@ -242,7 +247,11 @@ _loadFromPath (_path, _basePath) {
242
247
 
243
248
  _link_required_services () {
244
249
  const { requires } = this, protos = requires && requires._prototypes || {}
245
- for (let each in requires) requires[each] = _merged (each)
250
+ for (let each in requires) {
251
+ requires[each] = _merged (each)
252
+ // if we got an invalid value, remove it (would anyways cause trouble down the road)
253
+ if (!requires[each]) delete requires[each]
254
+ }
246
255
  function _merged (key) {
247
256
  const entry = requires[key] || protos[key]
248
257
  if (!entry || entry._is_merged || entry.kind === key || !(entry.kind in requires) && !(entry.kind in protos)) return entry
@@ -0,0 +1,38 @@
1
+ module.exports = function (conf) {
2
+ const { features } = conf
3
+ if (!features) return
4
+
5
+ // integrity checks
6
+ if (typeof features.assert_integrity === 'string') {
7
+ switch (features.assert_integrity) {
8
+ case 'individual':
9
+ // > keep as is
10
+ break
11
+ case 'off':
12
+ features.assert_integrity = false
13
+ break
14
+ case 'app':
15
+ features.assert_integrity = true
16
+ features.assert_integrity_type = 'RT'
17
+ break
18
+ case 'db':
19
+ features.assert_integrity = true
20
+ features.assert_integrity_type = 'DB'
21
+ break
22
+ // *-opt-in options not officially documented!
23
+ case 'app-opt-in':
24
+ features.assert_integrity = 'individual'
25
+ features.assert_integrity_type = 'RT'
26
+ break
27
+ case 'db-opt-in':
28
+ features.assert_integrity = 'individual'
29
+ features.assert_integrity_type = 'DB'
30
+ break
31
+ default:
32
+ // > invalid -> set to default
33
+ features.assert_integrity = true
34
+ features.assert_integrity_type = 'RT'
35
+ }
36
+ }
37
+
38
+ }
@@ -73,16 +73,16 @@ module.exports = {
73
73
  local: true
74
74
  },
75
75
  "file-based-messaging": {
76
- outbox: true,
76
+ outbox: {},
77
77
  impl: `${_runtime}/messaging/file-based.js`,
78
78
  credentials: { file:'~/.cds-msg-box' }
79
79
  },
80
80
  "default-messaging": {
81
- "[development]": { "kind": "local-messaging" },
82
- "[hybrid]": { "kind": "enterprise-messaging-amqp" },
81
+ "[development]": { kind: "local-messaging" },
82
+ "[hybrid]": { kind: "enterprise-messaging-amqp" },
83
83
  "[production]": {
84
- "kind": "enterprise-messaging-amqp",
85
- "[multitenant]": { "kind": "enterprise-messaging-http" }
84
+ kind: "enterprise-messaging-amqp",
85
+ "[multitenant]": { kind: "enterprise-messaging-http" }
86
86
  }
87
87
  },
88
88
  "enterprise-messaging": {
@@ -92,17 +92,17 @@ module.exports = {
92
92
  kind: "enterprise-messaging-amqp",
93
93
  },
94
94
  "enterprise-messaging-http": {
95
- outbox: true,
95
+ outbox: {},
96
96
  impl: `${_runtime}/messaging/enterprise-messaging.js`,
97
97
  vcap: { label: "enterprise-messaging" },
98
98
  },
99
99
  "enterprise-messaging-amqp": {
100
- outbox: true,
100
+ outbox: {},
101
101
  impl: `${_runtime}/messaging/enterprise-messaging-shared.js`,
102
102
  vcap: { label: "enterprise-messaging" },
103
103
  },
104
104
  'message-queuing': {
105
- outbox: true,
105
+ outbox: {},
106
106
  impl: `${_runtime}/messaging/message-queuing.js`
107
107
  },
108
108
  "composite-messaging": {
@@ -110,15 +110,20 @@ module.exports = {
110
110
  },
111
111
  "audit-log": {
112
112
  impl: `${_runtime}/audit/Service.js`,
113
- // REVISIT: how to load model?
113
+ // REVISIT: how to load model? -> see _prototypes below
114
114
  // model: 'AuditLogService.cds',
115
- outbox: true,
115
+ outbox: {},
116
116
  vcap: { label: "auditlog" },
117
117
  },
118
118
 
119
119
  _prototypes: {
120
120
  "uiflex": {
121
- "model": "@sap/cds/libx/_runtime/fiori/uiflex/extensibility"
121
+ model: "@sap/cds/srv/flex"
122
+ },
123
+ "persistent-outbox": {
124
+ model: "@sap/cds/srv/outbox",
125
+ maxAttempts: 20,
126
+ chunkSize: 100
122
127
  },
123
128
  }
124
129
  }
package/lib/index.js CHANGED
@@ -3,19 +3,20 @@ if (global.cds) Object.assign(module,{exports:global.cds}) ; else {
3
3
 
4
4
  const facade = class cds extends require('events') {
5
5
 
6
- get version() { return super.version = require('../package.json').version }
7
- get env() { return super.env = require('./env') }
6
+ get env() { return super.env = require('./env') }
8
7
  get requires() { return super.requires = this.env.required_services_or_defs }
9
- get builtin() { return super.builtin = require ('./core') }
10
- get service() { return super.service = extend (this.builtin.classes.service) .with ({
8
+ get version() { return super.version = require('../package.json').version }
9
+ get builtin() { return super.builtin = require ('./core') }
10
+ get service() { return super.service = extend (this.builtin.classes.service) .with ({
11
11
  /** @param x {(this:Service, srv:Service) => any} */ impl: x=>x,
12
12
  /** @type Service[] */ providers: [],
13
13
  factory: require ('./serve/factory'),
14
14
  bindings: require ('./connect/bindings'),
15
15
  })}
16
- get context() { return require('./req/context').for(this) }
17
- set context(_){ require('./req/context').for(this,_) }
18
- get spawn() { return super.spawn = require('./req/context').spawn }
16
+ /** @type {import './req/context'} */
17
+ get context() { return require('./req/cds-context').for(this) }
18
+ set context(_){ require('./req/cds-context').for(this,_) }
19
+ get spawn() { return super.spawn = require('./req/cds-context').spawn }
19
20
 
20
21
  emit (eve, ...args) {
21
22
  if (eve === 'served') return Promise.all (this.listeners(eve).map (l => l.call(this,...args)))
@@ -46,11 +47,13 @@ if (global.cds) Object.assign(module,{exports:global.cds}) ; else {
46
47
 
47
48
  // Loading and Compiling Models
48
49
  model: undefined,
50
+ compiler: require ('./compile/cdsc'),
51
+ compile: require ('./compile'),
49
52
  resolve: require ('./compile/resolve'),
50
53
  load: require ('./compile/load'), get: lazy => cds.load.parsed,
51
54
  parse: require ('./compile/parse'),
52
- compile: require ('./compile'),
53
- compiler: require ('./compile/cdsc'),
55
+ minify: require ('./compile/minify'),
56
+ extend: require ('./compile/extend'),
54
57
  deploy: require ('./deploy'),
55
58
 
56
59
  // Providing and Consuming Services
@@ -87,7 +90,7 @@ if (global.cds) Object.assign(module,{exports:global.cds}) ; else {
87
90
  log: require ('./log'), debug: lazy => cds.log.debug,
88
91
  exec: require ('../bin/cds'),
89
92
  clone: m => JSON.parse (JSON.stringify(m)),
90
- lazified, lazify, extend,
93
+ lazified, lazify,
91
94
 
92
95
  // Configuration & Information
93
96
  home: __dirname.slice(0,-4),
@@ -117,7 +120,6 @@ if (global.cds) Object.assign(module,{exports:global.cds}) ; else {
117
120
  // legacy and to be moved stuff -> hidden for tools in cds.__proto__
118
121
  extend (cds.__proto__) .with (lazified ({
119
122
  /** @deprecated */ in: (cwd) => !cwd ? cds : {__proto__:cds, cwd, env: cds.env.for('cds',cwd) },
120
- alpha_localized: lazy => require('./compile/etc/_localized'),
121
123
  mtx: lazy => require('../bin/mtx/in-cds'),
122
124
  build: lazy => require('../bin/build'),
123
125
  }))
@@ -26,7 +26,9 @@ module.exports = (module, level, ...args) => {
26
26
  if (user && log_user) toLog.remote_user = user.id
27
27
  // add headers, if available, with _ instead of -
28
28
  const req = cds.context._ && cds.context._.req
29
- if (req && req.headers) for (const k in req.headers) toLog[k.replace(/-/g, '_')] = req.headers[k]
29
+ if (req && req.headers)
30
+ for (const k in req.headers)
31
+ toLog[k.replace(/-/g, '_')] = k.match(/authorization/i) ? `${req.headers[k].split(' ')[0]} ***` : req.headers[k]
30
32
  }
31
33
  toLog.timestamp = new Date()
32
34
 
@@ -35,7 +37,7 @@ module.exports = (module, level, ...args) => {
35
37
  const err = args.shift()
36
38
  toLog.msg = err.message
37
39
  if (err instanceof Error) toLog.stacktrace = err.stack.split(/\s*\r?\n\s*/)
38
- Object.assign(toLog, err)
40
+ Object.assign(toLog, err, { level: toLog.level })
39
41
  }
40
42
 
41
43
  // append remaining args via util.format()
package/lib/log/index.js CHANGED
@@ -4,8 +4,8 @@ const cds = require ('../index'), { log } = cds.env
4
4
  if (log.Logger || log.service) {
5
5
  if (log.Logger) exports.Logger = require (log.Logger)
6
6
  if (log.service) {
7
- const {app} = cds, srv = require('./service')
8
- app ? setImmediate(()=>srv.serveIn(app)) : cds.on('bootstrap', app => srv.serveIn(app))
7
+ const {app} = cds, serveIn = app => require('./service').serveIn(app)
8
+ app ? setImmediate(() => serveIn(app)) : cds.on('bootstrap', app => serveIn(app))
9
9
  }
10
10
  }
11
11
 
@@ -77,6 +77,7 @@ const _object_predicate = ([arg], _clause) => { // e.g. .where ({ID:4711, stock:
77
77
  else if (x.SELECT || x.list) pred.push('in', x)
78
78
  else if (is_array(x)) pred.push('in', {list:x.map(val)})
79
79
  else if (is_cqn(x)) pred.push('=', x)
80
+ else if (x instanceof Buffer) pred.push('=', {val:x})
80
81
  else if (x instanceof RegExp) pred.push('like', {val:x})
81
82
  else if (typeof x === 'object') for (let op in x) pred.push(op, val(x[op]))
82
83
  else if (_clause === 'on' && typeof x === 'string') pred.push('=', { ref: x.split('.') })
@@ -0,0 +1,79 @@
1
+ const cds = require('../index'), { EventContext } = cds
2
+ const { EventEmitter } = require('events')
3
+
4
+
5
+ /**
6
+ * Continuation Local Storage for cds.context
7
+ */
8
+ exports.for = (cds,v) => {
9
+
10
+ if (cds.env.features.cls) {
11
+
12
+ const { executionAsyncResource:current, createHook } = module.require ('async_hooks')
13
+ const _context = Symbol('cds.context')
14
+
15
+ createHook ({ init(id,t,tid, res) {
16
+ const cr = current(); if (!cr) return
17
+ const ctx = cr[_context]
18
+ if (ctx) res[_context] = ctx
19
+ }}).enable()
20
+
21
+ Reflect.defineProperty (cds,'context',{ enumerable:1,
22
+ set(v) {
23
+ const cr = current(); if (!cr) return
24
+ const ctx = typeof v !== 'object' ? v : v.context || (v instanceof EventContext ? v : EventContext.for(v,false))
25
+ cr[_context] = ctx
26
+ },
27
+ get() {
28
+ const cr = current(); if (!cr) return undefined
29
+ return cr[_context]
30
+ },
31
+ })
32
+
33
+ } else {
34
+
35
+ Reflect.defineProperty (cds,'context',{ enumerable:1,
36
+ get:()=> undefined,
37
+ set:()=> {},
38
+ })
39
+
40
+ }
41
+
42
+ return v ? cds.context = v : cds.context
43
+ }
44
+
45
+
46
+
47
+ exports.spawn = function cds_spawn (o,fn) {
48
+
49
+ if (typeof o === 'function') [fn,o] = [o,fn] //> for compatibility
50
+ if (o instanceof cds.EventContext) throw new Error('The passed options must not be an instance of cds.EventContext.')
51
+ else if (!o) o = {}
52
+
53
+ const em = new EventEmitter()
54
+ const fx = () => {
55
+ // create a new transaction for each run of the background job
56
+ // which inherits from the current event context by default
57
+ const o2 = { ...o }; delete o2.timestamp //> spawned txes set their own timestamps
58
+ const tx = cds.context = cds.tx(o2)
59
+ const commit = async (res) => {
60
+ await tx.commit()
61
+ for (const handler of em.listeners('succeeded')) await handler(res)
62
+ for (const handler of em.listeners('done')) await handler()
63
+ }
64
+ fn(tx).then(commit, tx.rollback) .catch (async e => {
65
+ // tx.rollback throws passed error -> we will arrive here for any error
66
+ cds.log().error(`ERROR occured in background job:`, e)
67
+ for (const handler of em.listeners('failed')) await handler(e)
68
+ for (const handler of em.listeners('done')) await handler()
69
+ })
70
+ }
71
+
72
+ const timer = (
73
+ (o && o.after) ? setTimeout(fx, o.after) :
74
+ (o && o.every) ? setInterval(fx, o.every) :
75
+ setImmediate(fx)
76
+ )
77
+
78
+ return Object.assign (em, { timer })
79
+ }
@@ -12,8 +12,9 @@ class EventContext {
12
12
 
13
13
  toString() { return `${this.event} ${this.path}` }
14
14
 
15
- static new (data={}, base = cds.context) {
16
- if (data && base && features.cds_tx_inheritance) {
15
+ static for (data={}, base = cds.context) {
16
+ if (data instanceof EventContext) return data
17
+ if (base && features.cds_tx_inheritance) {
17
18
  let u = _inherit('user', u => typeof u === 'object' ? Object.create(u) : u)
18
19
  if (!u || !u.tenant) _inherit('tenant')
19
20
  if (!u || !u.locale) _inherit('locale')
@@ -156,78 +157,5 @@ class EventContext {
156
157
  }
157
158
  }
158
159
 
159
-
160
- /**
161
- * Continuation Local Storage for cds.context
162
- */
163
- const cls = (cds,v) => {
164
-
165
- if (cds.env.features.cls) {
166
-
167
- const { executionAsyncResource:current, createHook } = module.require ('async_hooks')
168
- const _context = Symbol('cds.context')
169
-
170
- createHook ({ init(id,t,tid, res) {
171
- const cr = current(); if (!cr) return
172
- const ctx = cr[_context]
173
- if (ctx) res[_context] = ctx
174
- }}).enable()
175
-
176
- Reflect.defineProperty (cds,'context',{ enumerable:1,
177
- set(v) {
178
- const cr = current(); if (!cr) return
179
- const ctx = typeof v !== 'object' ? v : v.context || (v instanceof EventContext ? v : EventContext.new(v,false))
180
- cr[_context] = ctx
181
- },
182
- get() {
183
- const cr = current(); if (!cr) return undefined
184
- return cr[_context]
185
- },
186
- })
187
-
188
- } else {
189
-
190
- Reflect.defineProperty (cds,'context',{ enumerable:1,
191
- get:()=> undefined,
192
- set:()=> {},
193
- })
194
-
195
- }
196
-
197
- return v ? cds.context = v : cds.context
198
- }
199
-
200
-
201
- function cds_spawn (o,fn) {
202
- if (typeof o === 'function') [ fn, o ] = [ o, fn ]
203
- if (!o) o = {}
204
- const em = new EventEmitter()
205
- const fx = async ()=> {
206
- // create a new transaction for each run of the background job
207
- // which inherits from the current event context by default
208
- const tx = cds.context = cds.tx({...o})
209
- try {
210
- const res = await fn(tx)
211
- await tx.commit()
212
- for (const handler of em.listeners('succeeded')) await handler(res)
213
- for (const handler of em.listeners('done')) await handler()
214
- } catch(e) {
215
- console.trace (`ERROR occured in background job:`, e) // eslint-disable-line no-console
216
- await tx.rollback()
217
- for (const handler of em.listeners('failed')) await handler(e)
218
- for (const handler of em.listeners('done')) await handler()
219
- }
220
- }
221
- const timer = (
222
- (o && o.after) ? setTimeout (fx, o.after) :
223
- (o && o.every) ? setInterval (fx, o.every) :
224
- setImmediate (fx) )
225
- return Object.assign(em, { timer })
226
- }
227
-
228
-
229
- module.exports = Object.assign (EventContext,{
230
- /** @type {(cds,v)=>EventContext} */ for: cls,
231
- spawn: cds_spawn,
232
- propagateHeaders: [ 'x-correlation-id' ]
233
- })
160
+ EventContext.propagateHeaders = [ 'x-correlation-id' ]
161
+ module.exports = EventContext
@@ -112,7 +112,7 @@ const SQL2Crud = {
112
112
  }
113
113
 
114
114
  const Query2Crud = (q) => {
115
- if (typeof q === 'string') return SQL2Crud[q] || /^\s*(\w+)/.test(q) && SQL2Crud[RegExp.$1] || q
115
+ if (typeof q === 'string') return SQL2Crud[q] || /^\s*(\w+)/i.test(q) && SQL2Crud[RegExp.$1.toUpperCase()] || q
116
116
  else for (let each in q) if (each in SQL2Crud) return SQL2Crud[each]
117
117
  }
118
118
 
@@ -1,5 +1,5 @@
1
1
  const add_methods_to = require ('./Service-methods')
2
- const cds = require('..')
2
+ const cds = require('..'), { unfold_csn: cds_localized } = cds.compile._localized
3
3
 
4
4
 
5
5
  class Service extends require('./Service-handlers') {
@@ -13,9 +13,13 @@ class Service extends require('./Service-handlers') {
13
13
  /**
14
14
  * Subclasses may override this to prepare the given model appropriately
15
15
  */
16
- set model(m) {
17
- super.model = m && cds.linked(m)
18
- if (m) add_methods_to (this)
16
+ set model (csn) {
17
+ if (csn) {
18
+ super.model = cds_localized(cds.linked(cds.compile.for.odata(csn)))
19
+ add_methods_to (this)
20
+ } else {
21
+ super.model = undefined
22
+ }
19
23
  }
20
24
 
21
25
  /**
@@ -37,13 +37,6 @@ exports.dispatch = async function dispatch (req) { //NOSONAR
37
37
  if (!req.query._srv) Object.defineProperty (req.query,'_srv',{ value:this, configurable:true, writable:true })
38
38
  }
39
39
 
40
- // REVISIT: Ensure req._.req and req._.res in case of srv.run(query)?!
41
- /*
42
- if (this instanceof cds.ApplicationService && !req._.req) {
43
- // TODO: add req and res to req._ from tx
44
- }
45
- */
46
-
47
40
  return this.handle(req)
48
41
  }
49
42
 
@@ -47,14 +47,12 @@ const add_handler_for = (srv, def) => {
47
47
 
48
48
  // object variant of params, ensure at least one param is present in args[0]
49
49
  // REVISIT: still not bullet proof, but parameters might be optional
50
- if ( args[0] !== null && typeof args[0] === 'object' && args.length === 1 && Object.keys(def.params).some(p => p in args[0])) {
51
- const params = args.shift()
52
- for (const p in def.params) {
53
- data[p] = params[p]
54
- }
55
- } else { // positional
56
- for (let p in def.params) {
57
- data[p] = args.shift()
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()
58
56
  }
59
57
  }
60
58