@sap/cds 6.6.2 → 6.7.0

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 (130) hide show
  1. package/CHANGELOG.md +59 -2
  2. package/README.md +1 -1
  3. package/apis/connect.d.ts +11 -4
  4. package/apis/core.d.ts +1 -1
  5. package/apis/csn.d.ts +1 -0
  6. package/apis/internal/inference.d.ts +15 -2
  7. package/apis/log.d.ts +10 -0
  8. package/apis/serve.d.ts +4 -9
  9. package/apis/services.d.ts +86 -19
  10. package/bin/build/buildTaskEngine.js +16 -42
  11. package/bin/build/constants.js +4 -2
  12. package/bin/build/provider/buildTaskProviderInternal.js +117 -85
  13. package/bin/build/provider/hana/index.js +6 -1
  14. package/bin/build/provider/mtx-extension/index.js +74 -34
  15. package/bin/build/provider/mtx-sidecar/index.js +3 -3
  16. package/bin/build/provider/nodejs/index.js +2 -2
  17. package/bin/build/util.js +63 -14
  18. package/bin/cds-serve.js +6 -0
  19. package/bin/cds.js +20 -4
  20. package/bin/deploy/to-hana/cfUtil.js +15 -1
  21. package/bin/mtx/in-cds.js +2 -9
  22. package/bin/plugins.js +31 -0
  23. package/bin/serve.js +12 -12
  24. package/lib/compile/etc/_localized.js +1 -1
  25. package/lib/compile/for/lean_drafts.js +22 -6
  26. package/lib/compile/for/nodejs.js +4 -1
  27. package/lib/compile/load.js +4 -2
  28. package/lib/core/index.js +35 -15
  29. package/lib/dbs/cds-deploy.js +129 -133
  30. package/lib/env/cds-env.js +25 -17
  31. package/lib/env/cds-requires.js +10 -40
  32. package/lib/env/compat.js +12 -0
  33. package/lib/env/defaults.js +17 -9
  34. package/lib/env/plugins.js +29 -0
  35. package/lib/env/schemas/cds-rc.json +14 -0
  36. package/lib/index.js +3 -0
  37. package/lib/log/cds-log.js +7 -4
  38. package/lib/ql/CREATE.js +1 -1
  39. package/lib/ql/DELETE.js +1 -1
  40. package/lib/ql/DROP.js +3 -3
  41. package/lib/ql/INSERT.js +1 -1
  42. package/lib/ql/Query.js +14 -6
  43. package/lib/ql/SELECT.js +8 -2
  44. package/lib/ql/UPDATE.js +1 -1
  45. package/lib/ql/Whereable.js +1 -1
  46. package/lib/ql/cds-ql.js +1 -9
  47. package/lib/req/cds-context.js +1 -4
  48. package/lib/req/request.js +63 -2
  49. package/lib/req/response.js +3 -2
  50. package/lib/srv/bindings.js +69 -71
  51. package/lib/srv/cds-connect.js +4 -1
  52. package/lib/srv/cds-serve.js +4 -0
  53. package/lib/srv/middlewares/index.js +37 -6
  54. package/lib/srv/protocols/_legacy.js +1 -1
  55. package/lib/srv/protocols/index.js +1 -1
  56. package/lib/srv/srv-api.js +4 -6
  57. package/lib/srv/srv-dispatch.js +4 -3
  58. package/lib/srv/srv-handlers.js +1 -1
  59. package/lib/srv/srv-methods.js +8 -2
  60. package/lib/utils/cds-test.js +4 -1
  61. package/libx/_runtime/audit/Service.js +8 -9
  62. package/libx/_runtime/audit/generic/personal/index.js +1 -1
  63. package/libx/_runtime/audit/generic/personal/utils.js +1 -1
  64. package/libx/_runtime/audit/utils/v2.js +17 -20
  65. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +2 -0
  66. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +11 -4
  67. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +0 -1
  68. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +3 -3
  69. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +1 -1
  70. package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +4 -4
  71. package/libx/_runtime/cds-services/adapter/odata-v4/utils/oDataConfiguration.js +2 -2
  72. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +1 -1
  73. package/libx/_runtime/cds-services/services/Service.js +1 -1
  74. package/libx/_runtime/cds-services/util/assert.js +41 -65
  75. package/libx/_runtime/common/code-ext/WorkerPool.js +90 -0
  76. package/libx/_runtime/common/code-ext/WorkerReq.js +0 -4
  77. package/libx/_runtime/common/code-ext/execute.js +28 -18
  78. package/libx/_runtime/common/code-ext/handlers.js +5 -4
  79. package/libx/_runtime/common/code-ext/worker.js +45 -3
  80. package/libx/_runtime/common/code-ext/workerQueryExecutor.js +8 -7
  81. package/libx/_runtime/common/composition/delete.js +1 -1
  82. package/libx/_runtime/common/composition/update.js +3 -5
  83. package/libx/_runtime/common/generic/auth/expand.js +1 -1
  84. package/libx/_runtime/common/generic/auth/readOnly.js +5 -4
  85. package/libx/_runtime/common/generic/auth/restrict.js +7 -2
  86. package/libx/_runtime/common/generic/crud.js +12 -1
  87. package/libx/_runtime/common/generic/etag.js +11 -3
  88. package/libx/_runtime/common/generic/input.js +8 -6
  89. package/libx/_runtime/common/generic/paging.js +25 -8
  90. package/libx/_runtime/common/generic/put.js +1 -1
  91. package/libx/_runtime/common/generic/sorting.js +0 -1
  92. package/libx/_runtime/common/i18n/messages.properties +1 -0
  93. package/libx/_runtime/common/utils/cqn.js +5 -1
  94. package/libx/_runtime/common/utils/cqn2cqn4sql.js +2 -2
  95. package/libx/_runtime/common/utils/resolveView.js +14 -10
  96. package/libx/_runtime/common/utils/rewriteAsterisks.js +2 -3
  97. package/libx/_runtime/common/utils/templateProcessor.js +15 -17
  98. package/libx/_runtime/common/utils/templateProcessorPathSerializer.js +18 -6
  99. package/libx/_runtime/db/Service.js +1 -0
  100. package/libx/_runtime/db/data-conversion/post-processing.js +0 -18
  101. package/libx/_runtime/db/expand/expand-v2.js +2 -2
  102. package/libx/_runtime/db/expand/rawToExpanded.js +6 -6
  103. package/libx/_runtime/db/generic/integrity.js +1 -1
  104. package/libx/_runtime/db/utils/columns.js +5 -5
  105. package/libx/_runtime/fiori/generic/activate.js +3 -3
  106. package/libx/_runtime/fiori/generic/edit.js +1 -1
  107. package/libx/_runtime/fiori/generic/new.js +4 -0
  108. package/libx/_runtime/fiori/lean-draft.js +138 -46
  109. package/libx/_runtime/hana/execute.js +3 -1
  110. package/libx/_runtime/hana/pool.js +10 -2
  111. package/libx/_runtime/messaging/common-utils/AMQPClient.js +6 -1
  112. package/libx/_runtime/messaging/enterprise-messaging.js +1 -0
  113. package/libx/_runtime/remote/Service.js +16 -13
  114. package/libx/_runtime/remote/utils/client.js +6 -1
  115. package/libx/_runtime/sqlite/Service.js +5 -59
  116. package/libx/_runtime/sqlite/convertDraftAdminPathExpression.js +1 -0
  117. package/libx/_runtime/sqlite/customBuilder/CustomUpsertBuilder.js +2 -2
  118. package/libx/_runtime/sqlite/execute.js +3 -1
  119. package/libx/_runtime/types/api.js +12 -3
  120. package/libx/odata/afterburner.js +36 -0
  121. package/libx/odata/cqn2odata.js +1 -1
  122. package/libx/odata/grammar.pegjs +5 -3
  123. package/libx/odata/parser.js +1 -1
  124. package/libx/odata/utils.js +1 -1
  125. package/libx/rest/RestAdapter.js +1 -1
  126. package/libx/rest/RestRequest.js +1 -0
  127. package/package.json +5 -2
  128. package/libx/_runtime/common/code-ext/workerQuery.js +0 -45
  129. package/libx/_runtime/common/constants/limit.js +0 -12
  130. package/libx/_runtime/common/utils/page.js +0 -39
@@ -32,6 +32,11 @@ class Config {
32
32
  const { NODE_ENV, CDS_ENV } = process.env, profiles = []
33
33
  if (NODE_ENV) profiles.push (NODE_ENV)
34
34
  if (CDS_ENV) profiles.push (...CDS_ENV.split(/\s*,\s*/))
35
+ if (_home) try {
36
+ const { cds } = require(path.join(_home,'package.json'))
37
+ if (cds?.profiles) profiles.push(...cds.profiles)
38
+ if (cds?.profile) profiles.push(cds.profile)
39
+ } catch {/* ignore */}
35
40
  if (!profiles.includes('production')) profiles.push('development')
36
41
  this._profiles = new Set (profiles)
37
42
  this._profiles._defined = new Set()
@@ -127,8 +132,9 @@ class Config {
127
132
  * Retrieves the value for a config option, specified as a property path.
128
133
  */
129
134
  get (option) {
130
- if (!option) return
131
- return option.split('.').reduce ((p,n)=> p && p[n], this)
135
+ if (!option) return
136
+ let path = option.includes('/') ? option.split('/') : option.split('.')
137
+ return path.reduce ((p,n)=> p && p[n], this)
132
138
  }
133
139
 
134
140
  get profiles() {
@@ -244,7 +250,7 @@ class Config {
244
250
  }
245
251
 
246
252
  _link_required_services () {
247
- const { requires } = this; if (!requires) return
253
+ const { requires, _profiles } = this; if (!requires) return
248
254
  const kinds = requires.kinds || {}
249
255
  Object.defineProperty (requires, 'kinds', { value:kinds, enumerable:false }) // for cds env
250
256
  // Object.setPrototypeOf (requires, kinds)
@@ -254,27 +260,28 @@ class Config {
254
260
  if (!val || val._is_linked) return val
255
261
  if (val === true) {
256
262
  let x = kinds[key]
257
- if (!x) return val
258
- else val = x
263
+ if (x) val = x; else return val
259
264
  }
260
265
  if (typeof val === 'string') {
261
266
  let x = kinds[val] || kinds[val+'-'+key] || kinds[key+'-'+val]
262
- if (!x) return val
263
- else val = { kind: val }
267
+ if (x) val = {kind:val}; else return val
264
268
  }
265
- const preset1 = kinds[key]
266
- if (typeof preset1 === 'object' && preset1 !== val) {
267
- const top = val, base = _merge ({},_linked(key,preset1)), {kind} = base
268
- val = _merge (base, top, false, false) // apply/override with top-level data
269
- if (kind) val.kind = kind // but inherited kind wins
269
+ let k = val.kind, p, preset = kinds[p=k] || kinds[p=k+'-'+key] || kinds[p=key+'-'+k]
270
+ if (!preset?.$root) {
271
+ const preset1 = kinds[key]
272
+ if (typeof preset1 === 'object' && preset1 !== val) {
273
+ const top = val, base = _merge ({},_linked(key,preset1)), {kind} = base
274
+ val = _merge (base, top, false, false) // apply/override with top-level data
275
+ if (kind) val.kind = kind // but inherited kind wins
276
+ }
270
277
  }
271
- let k = val.kind, p, preset = k && kinds[p=k] || kinds[p=k+'-'+key] || kinds[p=key+'-'+k]
272
278
  if (typeof preset === 'object' && preset !== val) {
273
- const top = val, base = _merge ({},_linked(p,preset)), {kind} = base
274
- val = _merge (base, top, false, false) // apply/override with top-level data
279
+ const top = val, base = _merge ({},_linked(p,preset), _profiles), {kind} = base
280
+ val = _merge (base, top, _profiles, false) // apply/override with top-level data
275
281
  if (kind) val.kind = kind // but inherited kind wins
276
282
  }
277
- return Object.defineProperty (val, '_is_linked', {value:true})
283
+ if (typeof val === 'object') Object.defineProperty (val, '_is_linked', {value:true})
284
+ return val
278
285
  }
279
286
  }
280
287
 
@@ -435,7 +442,8 @@ function _merge (dst, src, _profiles, _cloned, _profiles_only = false) {
435
442
  const profile = p.slice(1,-1)
436
443
  if (_profiles._defined) _profiles._defined.add (profile)
437
444
  if (_profiles.has(profile)) {
438
- profiled.push ({ profile, merge: () => _merge (dst, src[p], _profiles, _cloned, false)})
445
+ let o = src[p]; if (typeof o === 'string') o = {kind:o}
446
+ profiled.push ({ profile, merge: () => _merge (dst, o, _profiles, _cloned, false)})
439
447
  }
440
448
  continue
441
449
  }
@@ -2,16 +2,8 @@ const _runtime = '@sap/cds/libx/_runtime'
2
2
 
3
3
  exports = module.exports = {
4
4
 
5
- middlewares: false,
6
- "[middlewares]": {
7
- middlewares: true
8
- },
9
-
10
- "[schevo]": {
11
- db: { schema_evolution: 'auto' },
12
- },
13
-
14
- db: undefined,
5
+ middlewares: undefined, "[middlewares]": { middlewares: true },
6
+ db: undefined, "[schevo]": { db: { schema_evolution: 'auto' }, },
15
7
  messaging: undefined,
16
8
  multitenancy: undefined,
17
9
  extensibility: undefined,
@@ -127,19 +119,20 @@ const _databases = {
127
119
  "sql": {
128
120
  '[development]': { kind: 'sqlite', credentials: { url: ':memory:' } },
129
121
  '[production]': { kind: 'hana' },
130
- '[better-sqlite]': { kind: 'better-sqlite' },
131
122
  },
132
- "better-sqlite": _compat_to_use({
123
+ "better-sqlite": {
133
124
  credentials: { url: ":memory:" },
134
125
  impl: "@cap-js/sqlite",
135
- }),
136
- "sql-mt": {
126
+ kind: 'sqlite'
127
+ },
128
+ "sql-mt": { // For compatibility only
137
129
  '[development]': { kind: 'sqlite' },
138
130
  '[production]': { kind: 'hana-mt' },
139
131
  },
140
132
  "sqlite": _compat_to_use({
141
- credentials: { url: 'db.sqlite' },
133
+ '[better-sqlite]': { impl: '@cap-js/sqlite' },
142
134
  impl: `${_runtime}/sqlite/Service.js`,
135
+ credentials: { url: 'db.sqlite' },
143
136
  }),
144
137
  "hana": _compat_to_use ({
145
138
  impl: `${_runtime}/hana/Service.js`,
@@ -246,37 +239,12 @@ const _platform_services = {
246
239
 
247
240
  }
248
241
 
249
- require = (id) => { // eslint-disable-line no-global-assign
250
- try {
251
- return module.require(id)
252
- } catch(e) {
253
- if (e.code !== 'MODULE_NOT_FOUND') throw e
254
- }
255
- }
256
-
257
- // REVISIT: we should have a real modular plugin technique for cds.env
258
- const utils = require ('../utils/cds-utils')
259
- const _mtxs = !utils._oldMtx()
260
- // eslint-disable-next-line cds/no-missing-dependencies
261
- && require('@sap/cds-mtxs/lib/env-requires') || {
262
- "toggles": {
263
- model: "@sap/cds/srv/mtx"
264
- },
265
- "multitenancy": {
266
- model: "@sap/cds/srv/mtx",
267
- kind: "saas-registry",
268
- },
269
- "extensibility": {
270
- model: "@sap/cds/srv/mtx",
271
- },
272
- }
273
242
 
274
243
  exports.kinds = {
275
244
  ..._authentication_strategies,
276
245
  ..._databases,
277
246
  ..._services,
278
247
  ..._messaging,
279
- ..._mtxs,
280
248
  ..._platform_services,
281
249
  }
282
250
 
@@ -285,3 +253,5 @@ function _compat_to_use(o) { return Object.defineProperties (o,{
285
253
  // NOTE: Property .use is for compatibility only -> use .dialect instead!
286
254
  use: { get(){ return this.dialect || this.kind }, configurable:true, enumerable:true },
287
255
  })}
256
+
257
+ Object.defineProperty(exports,'_resolved',{value:exports._resolved,enumerable:false}) // hide it in outputs
package/lib/env/compat.js CHANGED
@@ -89,4 +89,16 @@ module.exports = function (conf) {
89
89
  }
90
90
  }
91
91
  })
92
+
93
+ Object.defineProperties (conf.features, {
94
+ fiori_preview: {
95
+ get: ()=> _.fiori.preview,
96
+ set: (v) => { _.fiori.preview = v },
97
+ },
98
+ fiori_routes: {
99
+ get: ()=> _.fiori.routes,
100
+ set: (v) => { _.fiori.routes = v },
101
+ },
102
+ })
103
+
92
104
  }
@@ -1,8 +1,7 @@
1
- const path = require('path')
2
- const [,major,minor] = /v(\d+)\.(\d+)/.exec(process.version)
3
1
  const production = process.env.NODE_ENV === 'production'
2
+ const path = require('path')
4
3
 
5
- module.exports = {
4
+ const defaults = module.exports = {
6
5
 
7
6
  requires: require('./cds-requires'),
8
7
 
@@ -13,10 +12,7 @@ module.exports = {
13
12
 
14
13
  features: {
15
14
  folders: 'fts/*', // where to find feature toggles -> switch on by default when released
16
- cls: major > 12 || major == 12 && minor >= 18,
17
15
  live_reload: !production,
18
- fiori_preview: !production,
19
- fiori_routes: !production,
20
16
  in_memory_db: !production,
21
17
  test_data: !production,
22
18
  test_mocks: !production,
@@ -30,10 +26,20 @@ module.exports = {
30
26
  assert_integrity: false,
31
27
  cds_tx_protection: true,
32
28
  cds_tx_inheritance: true,
29
+ },
30
+
31
+ fiori: {
32
+ preview: !production,
33
+ routes: !production,
33
34
  lean_draft: false,
34
- '[lean-draft]': {
35
- lean_draft: true,
36
- }
35
+ draft_compat: false,
36
+ '[better-sqlite]': { lean_draft: true },
37
+ '[lean-draft]': { lean_draft: true },
38
+ '[draft-compat]': { draft_compat: true },
39
+ },
40
+
41
+ ql: {
42
+ quirks_mode: true, // IMPORTANT: Remove that for cds7 !!
37
43
  },
38
44
 
39
45
  log: {
@@ -162,3 +168,5 @@ module.exports = {
162
168
  },
163
169
 
164
170
  }
171
+
172
+ require('./plugins')(defaults)
@@ -0,0 +1,29 @@
1
+ // REVISIT: we should have a real modular plugin technique for cds.env
2
+ module.exports = function add_mtx_env (env) {
3
+
4
+ const mtx_env = old_mtx_env() || require('@sap/cds-mtxs/env') // eslint-disable-line cds/no-missing-dependencies
5
+ if (mtx_env) {
6
+ const {requires} = env, {kinds} = requires
7
+ Object.assign (env, mtx_env, {requires})
8
+ Object.assign (requires, mtx_env.requires, {kinds})
9
+ Object.assign (kinds, mtx_env.requires?.kinds)
10
+ }
11
+
12
+ function require (id) {
13
+ try { return module.require(id) }
14
+ catch(e) { if (e.code !== 'MODULE_NOT_FOUND') throw e }
15
+ }
16
+
17
+ function old_mtx_env() {
18
+ const {_oldMtx} = require('../utils/cds-utils')
19
+ if (_oldMtx()) return {
20
+ requires: { kinds: {
21
+ "multitenancy": { model: "@sap/cds/srv/mtx", kind: "saas-registry" },
22
+ "extensibility": { model: "@sap/cds/srv/mtx" },
23
+ // "toggles": { model: "@sap/cds/srv/mtx" },
24
+ }
25
+ }
26
+ }}
27
+
28
+ return env
29
+ }
@@ -5,6 +5,20 @@
5
5
  "type": "object",
6
6
  "additionalProperties": true,
7
7
  "properties": {
8
+ "[development]": { "type": "object" },
9
+ "[production]": { "type": "object" },
10
+ "[hybrid]": { "type": "object" },
11
+ "profile": {
12
+ "description": "A single static profile",
13
+ "anyOf": [
14
+ { "enum": [ "mtx-sidecar", "with-mtx-sidecar" ] },
15
+ { "type": "string" }
16
+ ]
17
+ },
18
+ "profiles": {
19
+ "description": "An array of profiles",
20
+ "type": "array", "items": { "$ref": "#/properties/profile" }
21
+ },
8
22
  "folders": {
9
23
  "type": "object",
10
24
  "description": "Only set folders if you don't want to use the defaults 'app/', 'db/', 'srv/'.",
package/lib/index.js CHANGED
@@ -77,6 +77,9 @@ const cds = module.exports = extend (new facade) .with ({
77
77
  User: require ('./req/user'),
78
78
  ql: lazy => require ('./ql/cds-ql'),
79
79
  tx: (..._) => (cds.db || cds.Service.prototype) .tx (..._),
80
+ inferred: lazy => require('@cap-js/sqlite/lib/ql/cds.infer'), // eslint-disable-line cds/no-missing-dependencies
81
+ cqn2sql: lazy => require('@cap-js/sqlite/lib/db/sql/cqn2sql'), // eslint-disable-line cds/no-missing-dependencies
82
+ cqn4sql: lazy => require('@cap-js/sqlite/lib/db/sql/cqn4sql'), // eslint-disable-line cds/no-missing-dependencies
80
83
  /** @type Service */ db: undefined,
81
84
 
82
85
  // Protocols and Periphery
@@ -65,9 +65,12 @@ function cds_log (module, options) { // NOSONAR
65
65
  * Shortcut to `cds.log(...).debug`, returning undefined if `cds.log(...)._debug` is false.
66
66
  * @param {string} [module] the module for which a logger is requested
67
67
  */
68
- exports.debug = function cds_debug (module) {
69
- const L = this.log (module)
70
- if (L._debug) return L.debug
68
+ exports.debug = function cds_debug (id, options) {
69
+ const L = cds_log (id, options)
70
+ if (L._debug) return Object.assign(L.debug, {
71
+ time: label => console.time (`[${id}] - ${label}`),
72
+ timeEnd: label => console.timeEnd (`[${id}] - ${label}`),
73
+ })
71
74
  }
72
75
 
73
76
 
@@ -87,7 +90,7 @@ exports.Logger = (label, level) => {
87
90
  const fmt = (level,args) => logger.format (label,level,...args)
88
91
  const logger = {
89
92
  format: exports.format, // use logger.format as this could be changed dynamically
90
- trace: level < TRACE ? ()=>{} : (...args) => console.trace (...fmt(TRACE,args)),
93
+ trace: level < DEBUG ? ()=>{} : (...args) => console.trace (...fmt(TRACE,args)),
91
94
  debug: level < DEBUG ? ()=>{} : (...args) => console.debug (...fmt(DEBUG,args)),
92
95
  log: level < INFO ? ()=>{} : (...args) => console.log (...fmt(INFO,args)),
93
96
  info: level < INFO ? ()=>{} : (...args) => console.info (...fmt(INFO,args)),
package/lib/ql/CREATE.js CHANGED
@@ -10,7 +10,7 @@ module.exports = class Query extends require('./Query') {
10
10
  if (elements)
11
11
  this.CREATE.entity = { elements: elements, kind: 'entity', name:e }
12
12
  else
13
- this.CREATE.entity = e && e.elements ? e : this._target_name4(e)
13
+ this.CREATE.entity = e && e.elements ? e : this._target4(e)
14
14
  return this
15
15
  }
16
16
 
package/lib/ql/DELETE.js CHANGED
@@ -9,7 +9,7 @@ module.exports = class Query extends Whereable {
9
9
  }
10
10
 
11
11
  from(entity, key) {
12
- this.DELETE.from = this._target_name4 (...arguments) // supporting tts
12
+ this.DELETE.from = this._target4 (...arguments) // supporting tts
13
13
  if (key) this.byKey(key)
14
14
  return this
15
15
  }
package/lib/ql/DROP.js CHANGED
@@ -7,17 +7,17 @@ module.exports = class Query extends require('./Query') {
7
7
  })
8
8
  }
9
9
  entity(e) {
10
- this.DROP.entity = this._target_name4 (e)
10
+ this.DROP.entity = this._target4 (e)
11
11
  return this
12
12
  }
13
13
  table(e) {
14
14
  const {DROP} = this
15
- DROP.entity = DROP.table = this._target_name4 (e)
15
+ DROP.entity = DROP.table = this._target4 (e)
16
16
  return this
17
17
  }
18
18
  view(e) {
19
19
  const {DROP} = this
20
- DROP.entity = DROP.view = this._target_name4 (e)
20
+ DROP.entity = DROP.view = this._target4 (e)
21
21
  return this
22
22
  }
23
23
  }
package/lib/ql/INSERT.js CHANGED
@@ -7,7 +7,7 @@ module.exports = class Query extends require('./Query') {
7
7
  }
8
8
 
9
9
  into (entity, ...data) {
10
- this[this.cmd].into = this._target_name4 (...arguments) // supporting tts
10
+ this[this.cmd].into = this._target4 (...arguments) // supporting tts
11
11
  if (data.length) this.entries(...data)
12
12
  return this
13
13
  }
package/lib/ql/Query.js CHANGED
@@ -52,12 +52,6 @@ class Query {
52
52
  || this._expected `${{target}} to be an entity path string, a CSN definition, a {ref}, a {SELECT}, or a {SET}`
53
53
  }
54
54
 
55
- //> REVISIT: should we rather have consistent .from/.entity/.into in CQN?
56
- _target_name4 (...args) {
57
- const {ref, as} = this._target_ref4 (...args)
58
- return ref.length === 1 && typeof ref[0] === 'string' && !as ? ref[0] : as ? {ref, as} : {ref}
59
- }
60
-
61
55
  _expected (...args) {
62
56
  return cds.error.expected (...args)
63
57
  }
@@ -72,8 +66,15 @@ class Query {
72
66
  return `${cmd} ${_name(this._target.name)} `
73
67
  }
74
68
 
69
+ forSQL (db = cds.db || cds) { return _flat(db.cqn4sql(this)) }
70
+ toSQL (db = cds.db || cds) { return _2sql(db.cqn2sql(this)) }
71
+ toSql (db = cds.db || cds) { return this.toSQL(db).sql }
72
+
75
73
  }
76
74
 
75
+ // skip .cqn property when in repl
76
+ const _2sql = cds.repl ? ({sql,values}) => ({sql,values}) : (x => x)
77
+ const _flat = Query.prototype.flat
77
78
 
78
79
  const _target4 = (target, arg2) => target && (
79
80
  typeof target === 'string' ? { name: target } :
@@ -85,5 +86,12 @@ const _target4 = (target, arg2) => target && (
85
86
 
86
87
  const _name = cds.env.sql.names === 'quoted' ? n =>`"${n}"` : n => n.replace(/[.:]/g,'_')
87
88
 
89
+ Object.defineProperty (Query.prototype, '_target4', { value: (
90
+ cds.env.ql.quirks_mode === false ? Query.prototype._target_ref4
91
+ : function (...args) {
92
+ const {ref, as} = this._target_ref4 (...args)
93
+ return ref.length === 1 && typeof ref[0] === 'string' && !as ? ref[0] : as ? {ref, as} : {ref}
94
+ }
95
+ )})
88
96
 
89
97
  module.exports = Query
package/lib/ql/SELECT.js CHANGED
@@ -104,8 +104,14 @@ module.exports = class Query extends Whereable {
104
104
  return this._where (args,'and','on')
105
105
  }
106
106
 
107
- having(...x) {
108
- return this._where (x,'and','having')
107
+ having(...args) {
108
+ return this._where (args,'and','having')
109
+ }
110
+
111
+ search (...args) {
112
+ let _xpr=[]; for (let val of args) _xpr.push('or',{val})
113
+ this.SELECT.search = _xpr.slice(1)
114
+ return this
109
115
  }
110
116
 
111
117
  groupBy (...args) {
package/lib/ql/UPDATE.js CHANGED
@@ -10,7 +10,7 @@ module.exports = class Query extends Whereable {
10
10
  }
11
11
 
12
12
  entity (e, key) {
13
- this.UPDATE.entity = this._target_name4 (...arguments) // supporting tts
13
+ this.UPDATE.entity = this._target4 (...arguments) // supporting tts
14
14
  if (key) this.byKey(key)
15
15
  return this
16
16
  }
@@ -18,7 +18,7 @@ class Query extends require('./Query') {
18
18
  error (`Invalid attempt to call '${this.cmd}.${and_or}()' before a prior call to '${this.cmd}.where()'`)
19
19
  )
20
20
  if (clause === 'on') _ = _.from
21
- let left = Reflect.getOwnPropertyDescriptor(_,clause)?.value
21
+ let left = _[clause]
22
22
  if (!left) { //> .where() called first time
23
23
  // SELECT.from `X` .where `x or y` .and `z` -> SELECT from X where (x or y) and z
24
24
  if (pred.includes('or')) this._left_has_or = true
package/lib/ql/cds-ql.js CHANGED
@@ -9,7 +9,7 @@ require = path => { // eslint-disable-line no-global-assign
9
9
  }, api)
10
10
  }
11
11
 
12
- module.exports = Object.assign (_deprecated_srv_ql, { cdr: true,
12
+ module.exports = {
13
13
  Query, clone: (q,_) => Query.prototype.clone.call(q,_),
14
14
  SELECT: require('./SELECT'),
15
15
  INSERT: require('./INSERT'),
@@ -18,14 +18,6 @@ module.exports = Object.assign (_deprecated_srv_ql, { cdr: true,
18
18
  DELETE: require('./DELETE'),
19
19
  CREATE: require('./CREATE'),
20
20
  DROP: require('./DROP'),
21
- })
22
-
23
- function _deprecated_srv_ql() { // eslint-disable-next-line no-console
24
- console.trace(`
25
- Method 'srv.ql(req)' is deprecated and superceded by 'cds.context'.
26
- Please use global SELECT instead of 'const { SELECT } = srv.ql(req)'.
27
- `)
28
- return module.exports
29
21
  }
30
22
 
31
23
  module.exports._reset = ()=>{ // for strange tests only
@@ -15,12 +15,9 @@ module.exports = new class extends AsyncLocalStorage {
15
15
 
16
16
  /** @returns {EventContext} */
17
17
  _for (cds,v) {
18
- Reflect.defineProperty (cds,'context', { enumerable:1, ... cds.env.features.cls ? {
18
+ Reflect.defineProperty (cds,'context', { enumerable:1, ... {
19
19
  set:(v) => this.enterWith(v),
20
20
  get:()=> this.getStore(),
21
- } : {
22
- get:()=> undefined,
23
- set:()=> {},
24
21
  }})
25
22
  return cds.context = v // IMPORTANT: we need to set it initially, to get it all wired up correctly
26
23
  }
@@ -1,4 +1,5 @@
1
1
  const { Responses, Errors } = require('./response')
2
+ const cds = require('../../lib')
2
3
 
3
4
  /**
4
5
  * Class Request represents requests received via synchronous protocols.
@@ -56,6 +57,67 @@ class Request extends require('./event') {
56
57
  return this._set ('data', {})
57
58
  }
58
59
 
60
+ set subject(d) { if (d) super.subject = d }
61
+ get subject() {
62
+ const q = this.query
63
+ let subject = (
64
+ q?.SELECT?.from ||
65
+ q?.INSERT?.into ||
66
+ q?.UPSERT?.into ||
67
+ q?.UPDATE?.entity ||
68
+ q?.DELETE?.from
69
+ )
70
+
71
+ const {target} = this; if (!target) return undefined
72
+
73
+ // programmatic action calls as of now only in format /Books(201)/action
74
+ if (!subject) {
75
+ let where = []
76
+ for (const param of this.params) {
77
+ if (typeof param === 'object') {
78
+ for (const key in param) {
79
+ if (key in target.keys) {
80
+ if (where.length > 1) where.push('and')
81
+ where.push({ ref: [key] }, '=', { val: param[key] })
82
+ }
83
+ }
84
+ } else {
85
+ where.push({ ref: [Object.keys(target.keys)[0]] }, '=', { val: param })
86
+ }
87
+ }
88
+ subject = { id: target.name, where }
89
+ }
90
+
91
+ const ref = subject.ref || [ subject ]
92
+
93
+ if(!target.drafts || cds.env.fiori.lean_draft) return super.subject = { ref }
94
+
95
+ // special handling for old draft logic
96
+ let subjectRef = []
97
+ ref.forEach ((item, i) => {
98
+ // to one assoc & comp
99
+ if (typeof item === 'string') {
100
+ subjectRef.push(item)
101
+ return
102
+ }
103
+
104
+ // create key value pair without IsActiveEntity & adjust root target
105
+ const keys = {}
106
+ let id = item.id
107
+ for (let j = 0; j < item?.where?.length; j = j + 4) {
108
+ const key = item.where[j].ref[0]
109
+ const value = item.where[j + 2].val
110
+ if (key !== 'IsActiveEntity') keys[key] = value
111
+ else if (i === 0 && key === 'IsActiveEntity' && value === false) id = item.id + '_drafts'
112
+ }
113
+
114
+ // create copy
115
+ const {SELECT:{from:{ref}}} = SELECT.from(id,keys)
116
+ subjectRef.push(...ref)
117
+ })
118
+ return super.subject = {ref: subjectRef}
119
+ }
120
+
59
121
  reply (results) { return this.results = results }
60
122
  notify (...args) { return this._messages.add (1, ...args) }
61
123
  info (...args) { return this._messages.add (2, ...args) }
@@ -116,8 +178,7 @@ const SQL2Crud = {
116
178
  }
117
179
 
118
180
  const Query2Crud = (q) => {
119
- if (typeof q === 'string') return SQL2Crud[q] || /^\s*(\w+)/i.test(q) && SQL2Crud[RegExp.$1.toUpperCase()] || q
120
- else for (let each in q) if (each in SQL2Crud) return SQL2Crud[each]
181
+ for (let each in q) if (each in SQL2Crud) return SQL2Crud[each]
121
182
  }
122
183
 
123
184
  const _path4 = (x,p) => {
@@ -4,8 +4,9 @@ const cds = require ('../index')
4
4
  * Messages Collector, used for `req.errors` and `req.messages`
5
5
  */
6
6
  class Responses extends Array {
7
- static get (severity, code, message, target, args) {
8
- let e // be filled in below...
7
+ static get (severity, code, message, target, args, ...more) {
8
+ if (code?.raw) [ message, code ] = [ String.raw (code, message, target, args, ...more) ]
9
+ let e // be filled in below...
9
10
  if (typeof code === 'object') e = code; else {
10
11
  if (typeof code === 'number') e = { code }; else [ code, message, target, args, e ] = [ undefined, code, message, target, {} ]
11
12
  if (typeof message === 'object') e = Object.assign(message,e); else {