@sap/cds 5.4.6 → 5.5.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 (228) hide show
  1. package/CHANGELOG.md +208 -2
  2. package/apis/ql.d.ts +17 -15
  3. package/app/index.js +1 -1
  4. package/bin/build/buildTaskEngine.js +26 -42
  5. package/bin/build/buildTaskFactory.js +6 -10
  6. package/bin/build/buildTaskHandler.js +2 -4
  7. package/bin/build/buildTaskProvider.js +3 -1
  8. package/bin/build/buildTaskProviderFactory.js +9 -15
  9. package/bin/build/constants.js +15 -3
  10. package/bin/build/index.js +5 -4
  11. package/bin/build/mtaUtil.js +8 -11
  12. package/bin/build/provider/buildTaskHandlerEdmx.js +63 -6
  13. package/bin/build/provider/buildTaskHandlerInternal.js +2 -34
  14. package/bin/build/provider/buildTaskProviderInternal.js +16 -42
  15. package/bin/build/provider/fiori/index.js +13 -24
  16. package/bin/build/provider/hana/2migration.js +17 -15
  17. package/bin/build/provider/hana/2tabledata.js +52 -48
  18. package/bin/build/provider/hana/index.js +27 -25
  19. package/bin/build/provider/hana/migrationtable.js +91 -67
  20. package/bin/build/provider/java-cf/index.js +14 -24
  21. package/bin/build/provider/mtx/index.js +12 -14
  22. package/bin/build/provider/node-cf/index.js +18 -32
  23. package/bin/cds.js +5 -5
  24. package/bin/serve.js +29 -23
  25. package/bin/version.js +0 -1
  26. package/lib/compile/etc/_localized.js +4 -9
  27. package/lib/compile/for/sql.js +5 -2
  28. package/lib/compile/parse.js +25 -17
  29. package/lib/compile/to/srvinfo.js +2 -1
  30. package/lib/connect/bindings.js +2 -1
  31. package/lib/connect/index.js +48 -49
  32. package/lib/core/classes.js +1 -1
  33. package/lib/core/reflect.js +10 -2
  34. package/lib/deploy.js +26 -23
  35. package/lib/env/defaults.js +13 -6
  36. package/lib/env/index.js +73 -78
  37. package/lib/env/requires.js +38 -19
  38. package/lib/index.js +9 -10
  39. package/lib/lazy.js +2 -2
  40. package/lib/log/index.js +33 -45
  41. package/lib/log/service/index.js +2 -2
  42. package/lib/ql/CREATE.js +14 -9
  43. package/lib/ql/DELETE.js +6 -5
  44. package/lib/ql/DROP.js +12 -9
  45. package/lib/ql/INSERT.js +40 -16
  46. package/lib/ql/Query.js +67 -40
  47. package/lib/ql/SELECT.js +162 -127
  48. package/lib/ql/UPDATE.js +74 -42
  49. package/lib/ql/Whereable.js +77 -87
  50. package/lib/ql/index.js +36 -24
  51. package/lib/ql/parse.js +35 -0
  52. package/lib/req/context.js +44 -8
  53. package/lib/req/locale.js +7 -7
  54. package/lib/serve/Service-api.js +21 -14
  55. package/lib/serve/Service-dispatch.js +28 -12
  56. package/lib/serve/Transaction.js +22 -10
  57. package/lib/serve/index.js +16 -11
  58. package/lib/utils/axios.js +23 -16
  59. package/lib/utils/data.js +35 -0
  60. package/lib/utils/tests.js +27 -18
  61. package/libx/_runtime/audit/generic/personal/access.js +81 -0
  62. package/libx/_runtime/audit/generic/personal/constants.js +4 -0
  63. package/libx/_runtime/audit/generic/personal/index.js +50 -0
  64. package/libx/_runtime/audit/generic/personal/modification.js +138 -0
  65. package/libx/_runtime/audit/generic/personal/utils.js +186 -0
  66. package/libx/_runtime/audit/utils/v2.js +10 -4
  67. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +5 -5
  68. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +6 -7
  69. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +5 -7
  70. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +5 -7
  71. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +2 -3
  72. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +4 -0
  73. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +7 -4
  74. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +59 -8
  75. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +11 -1
  76. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +6 -10
  77. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +3 -46
  78. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +2 -5
  79. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/createToCQN.js +2 -2
  80. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/deleteToCQN.js +4 -3
  81. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -2
  82. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +0 -1
  83. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +1 -1
  84. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +2 -2
  85. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +16 -18
  86. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/EdmEntityType.js +6 -3
  87. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/format/RepresentationKind.js +4 -1
  88. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/core/OdataRequest.js +1 -0
  89. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/SerializerFactory.js +15 -2
  90. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/OperationValidator.js +1 -0
  91. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
  92. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +8 -1
  93. package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +6 -1
  94. package/libx/_runtime/cds-services/adapter/odata-v4/utils/omitValues.js +12 -5
  95. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +1 -7
  96. package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +7 -7
  97. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +14 -18
  98. package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +13 -13
  99. package/libx/_runtime/cds-services/adapter/rest/handlers/create.js +0 -1
  100. package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +2 -1
  101. package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +2 -2
  102. package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +2 -4
  103. package/libx/_runtime/cds-services/adapter/rest/utils/result.js +4 -2
  104. package/libx/_runtime/cds-services/services/Service.js +40 -5
  105. package/libx/_runtime/cds-services/services/utils/columns.js +13 -7
  106. package/libx/_runtime/cds-services/services/utils/compareJson.js +88 -4
  107. package/libx/_runtime/cds-services/services/utils/differ.js +24 -6
  108. package/libx/_runtime/cds-services/services/utils/handlerUtils.js +2 -2
  109. package/libx/_runtime/common/composition/data.js +44 -55
  110. package/libx/_runtime/common/composition/delete.js +97 -71
  111. package/libx/_runtime/common/composition/index.js +2 -1
  112. package/libx/_runtime/common/composition/insert.js +34 -11
  113. package/libx/_runtime/common/composition/tree.js +119 -92
  114. package/libx/_runtime/common/composition/update.js +4 -1
  115. package/libx/_runtime/common/composition/utils.js +1 -3
  116. package/libx/_runtime/common/constants/draft.js +12 -1
  117. package/libx/_runtime/common/generic/auth.js +6 -22
  118. package/libx/_runtime/common/generic/crud.js +14 -13
  119. package/libx/_runtime/common/generic/input.js +23 -26
  120. package/libx/_runtime/common/generic/put.js +1 -1
  121. package/libx/_runtime/common/generic/sorting.js +16 -16
  122. package/libx/_runtime/common/i18n/index.js +1 -1
  123. package/libx/_runtime/common/i18n/messages.properties +4 -0
  124. package/libx/_runtime/common/utils/backlinks.js +12 -5
  125. package/libx/_runtime/common/utils/cqn.js +6 -1
  126. package/libx/_runtime/common/utils/cqn2cqn4sql.js +102 -101
  127. package/libx/_runtime/common/utils/csn.js +47 -4
  128. package/libx/_runtime/common/utils/data.js +0 -37
  129. package/libx/_runtime/common/utils/enrichWithKeysFromWhere.js +1 -1
  130. package/libx/_runtime/common/utils/entityFromCqn.js +7 -24
  131. package/libx/_runtime/common/utils/foreignKeyPropagations.js +39 -7
  132. package/libx/_runtime/common/utils/generateOnCond.js +11 -12
  133. package/libx/_runtime/common/utils/onlyKeysRemain.js +10 -0
  134. package/libx/_runtime/common/utils/path.js +35 -0
  135. package/libx/_runtime/common/utils/postProcessing.js +86 -0
  136. package/libx/_runtime/common/utils/quotingStyles.js +37 -26
  137. package/libx/_runtime/common/utils/resolveView.js +223 -171
  138. package/libx/_runtime/common/utils/rewriteAsterisk.js +46 -26
  139. package/libx/_runtime/common/utils/structured.js +6 -12
  140. package/libx/_runtime/common/utils/template.js +10 -5
  141. package/libx/_runtime/common/utils/templateDelimiter.js +1 -0
  142. package/libx/_runtime/common/utils/templateProcessor.js +22 -30
  143. package/libx/_runtime/common/utils/union.js +31 -0
  144. package/libx/_runtime/common/utils/unionCqnTemplate.js +184 -0
  145. package/libx/_runtime/db/Service.js +1 -1
  146. package/libx/_runtime/db/data-conversion/timestamp.js +2 -9
  147. package/libx/_runtime/db/expand/expandCQNToJoin.js +204 -297
  148. package/libx/_runtime/db/expand/index.js +3 -3
  149. package/libx/_runtime/db/expand/rawToExpanded.js +36 -7
  150. package/libx/_runtime/db/generic/index.js +1 -1
  151. package/libx/_runtime/db/generic/input.js +5 -7
  152. package/libx/_runtime/db/generic/integrity.js +1 -1
  153. package/libx/_runtime/db/generic/rewrite.js +2 -10
  154. package/libx/_runtime/db/generic/update.js +13 -5
  155. package/libx/_runtime/db/generic/virtual.js +22 -58
  156. package/libx/_runtime/db/query/delete.js +7 -4
  157. package/libx/_runtime/db/query/insert.js +6 -4
  158. package/libx/_runtime/db/query/read.js +13 -20
  159. package/libx/_runtime/db/query/run.js +4 -1
  160. package/libx/_runtime/db/query/update.js +5 -4
  161. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +35 -2
  162. package/libx/_runtime/db/sql-builder/FunctionBuilder.js +17 -2
  163. package/libx/_runtime/db/sql-builder/InsertBuilder.js +6 -5
  164. package/libx/_runtime/db/sql-builder/ReferenceBuilder.js +10 -0
  165. package/libx/_runtime/db/sql-builder/SelectBuilder.js +35 -24
  166. package/libx/_runtime/db/sql-builder/UpdateBuilder.js +14 -4
  167. package/libx/_runtime/db/sql-builder/arrayed.js +4 -0
  168. package/libx/_runtime/db/utils/deep.js +8 -0
  169. package/libx/_runtime/db/utils/generateAliases.js +2 -1
  170. package/libx/_runtime/fiori/generic/activate.js +19 -15
  171. package/libx/_runtime/fiori/generic/before.js +3 -11
  172. package/libx/_runtime/fiori/generic/cancel.js +1 -1
  173. package/libx/_runtime/fiori/generic/delete.js +3 -1
  174. package/libx/_runtime/fiori/generic/edit.js +12 -2
  175. package/libx/_runtime/fiori/generic/new.js +5 -5
  176. package/libx/_runtime/fiori/generic/patch.js +0 -18
  177. package/libx/_runtime/fiori/generic/read.js +241 -189
  178. package/libx/_runtime/fiori/utils/delete.js +36 -7
  179. package/libx/_runtime/fiori/utils/handler.js +43 -44
  180. package/libx/_runtime/fiori/utils/where.js +30 -15
  181. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +4 -6
  182. package/libx/_runtime/hana/execute.js +2 -2
  183. package/libx/_runtime/hana/localized.js +4 -4
  184. package/libx/_runtime/hana/pool.js +29 -14
  185. package/libx/_runtime/hana/search2cqn4sql.js +2 -1
  186. package/libx/_runtime/hana/searchToContains.js +18 -14
  187. package/libx/_runtime/index.js +0 -5
  188. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +13 -5
  189. package/libx/_runtime/messaging/common-utils/naming-conventions.js +4 -1
  190. package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +31 -19
  191. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +1 -2
  192. package/libx/_runtime/messaging/enterprise-messaging.js +6 -4
  193. package/libx/_runtime/messaging/service.js +7 -6
  194. package/libx/_runtime/odata/cqn2odata.js +110 -43
  195. package/libx/_runtime/odata/index.js +26 -48
  196. package/libx/_runtime/odata/odata2cqn.js +1 -6154
  197. package/libx/_runtime/odata/odata2cqn.pegjs +559 -0
  198. package/libx/_runtime/odata/readToCqn.js +94 -64
  199. package/libx/_runtime/remote/Service.js +74 -21
  200. package/libx/_runtime/remote/cqn2odata/index.js +1 -5
  201. package/libx/_runtime/remote/utils/client.js +24 -101
  202. package/libx/_runtime/remote/utils/dataConversion.js +27 -12
  203. package/libx/_runtime/sqlite/Service.js +3 -5
  204. package/libx/_runtime/sqlite/execute.js +23 -24
  205. package/libx/_runtime/sqlite/localized.js +12 -7
  206. package/libx/_runtime/types/api.js +10 -0
  207. package/package.json +1 -1
  208. package/server.js +16 -2
  209. package/lib/ql/grammar.pegjs +0 -208
  210. package/lib/ql/parser.js +0 -1
  211. package/lib/ql/rt/DELETE.js +0 -29
  212. package/lib/ql/rt/INSERT.js +0 -23
  213. package/lib/ql/rt/Query.js +0 -84
  214. package/lib/ql/rt/SELECT.js +0 -174
  215. package/lib/ql/rt/UPDATE.js +0 -119
  216. package/lib/ql/rt/_helpers.js +0 -91
  217. package/lib/ql/rt/index.js +0 -32
  218. package/libx/_runtime/audit/generic/personal.js +0 -260
  219. package/libx/_runtime/cds-services/statements/BaseStatement.js +0 -72
  220. package/libx/_runtime/cds-services/statements/Create.js +0 -57
  221. package/libx/_runtime/cds-services/statements/Delete.js +0 -33
  222. package/libx/_runtime/cds-services/statements/Drop.js +0 -42
  223. package/libx/_runtime/cds-services/statements/Insert.js +0 -201
  224. package/libx/_runtime/cds-services/statements/Select.js +0 -826
  225. package/libx/_runtime/cds-services/statements/Update.js +0 -181
  226. package/libx/_runtime/cds-services/statements/Where.js +0 -726
  227. package/libx/_runtime/cds-services/statements/index.js +0 -25
  228. package/libx/_runtime/common/generic/resolve-mock.js +0 -9
@@ -1,112 +1,102 @@
1
+ const { error } = require ('../index')
2
+ const parse = require('./parse')
3
+
1
4
  class Whereable extends require('./Query') {
2
5
 
3
- where(...x) {
4
- const pred = _where(x)
5
- if (pred.length > 0)
6
- this._.where = !this._.where ? pred : ['(', ...this._.where, ')', 'and', ...pred]
7
- return this
8
- }
9
- and(...x) {
10
- this._.where = [...this._.where, 'and', ..._where(x)]
11
- return this
12
- }
13
- or(...x) {
14
- this._.where = [...this._.where, 'or', ..._where(x)]
6
+ where(...x) { return this._where (x,'and','where') }
7
+ and(...x) { return this._where (x,'and') }
8
+ or(...x) { return this._where (x,'or') }
9
+ _where (args, and_or, _clause) {
10
+ let pred = predicate4(args, _clause)
11
+ if (pred && pred.length > 0) {
12
+ let _ = this[this.cmd]
13
+ if (!_clause) _clause = (
14
+ _.having ? 'having' :
15
+ _.where ? 'where' :
16
+ _.from && _.from.on ? 'on' :
17
+ error (`Invalid attempt to call '${this.cmd}.${and_or}()' before a prior call to '${this.cmd}.where()'`)
18
+ )
19
+ if (_clause === 'on') _ = _.from
20
+ let left = this._own(_clause,_)
21
+ if (!left) {
22
+ if (pred.includes('or')) Object.defineProperty(pred,'_includes_or__',{value:1})
23
+ _[_clause] = pred
24
+ } else {
25
+ if (and_or === 'and') {
26
+ if (left._includes_or__) left = [{xpr:left}]
27
+ if (pred.includes('or')) pred = [{xpr:pred}]
28
+ }
29
+ _[_clause] = [ ...left, and_or, ...pred ]
30
+ }
31
+ }
15
32
  return this
16
33
  }
17
- byKey(ID) {
18
- if (this.SELECT) this._.one = true
19
- return this.where(typeof ID === 'object' ? ID : { ID })
20
- }
21
- }
22
34
 
23
- const _where = (args) => {
24
- if (is_array(args[0])) return args[0]
25
- if (is_xpr(args)) return args
26
- if (is_object(args)) return _where_object(args)
27
- else return _where_fluid(args)
35
+ byKey(key) {
36
+ if (typeof key !== 'object') key = { [Object.keys(this._target.keys||{ID:1})[0]]: key }
37
+ if (this.SELECT) this.SELECT.one = true
38
+ return this.where(key)
39
+ }
28
40
  }
29
41
 
30
- // .where ('ID=',4711, 'and stock >=',1)
31
- const _where_fluid = (args) => {
32
- // 1. construct a full expr string with ? for values collected in vals
33
- let expr = args.filter((_, i) => i % 2 === 0).join(' ? ')
34
- let vals = args.filter((_, i) => i % 2 === 1)
35
- if (args.length % 2 === 0) expr += ' ?'
36
- // 2. parse the expr string -> returns cxn ...
37
- let {xpr} = parse.expr(expr), flat=[]
38
- // 3. and replace {params} in there with collected vals
39
- for (let x of xpr) {
40
- const v = x.param ? val(vals.shift()) : x
41
- is_array(v) ? flat.push(...v) : flat.push(v)
42
+ const predicate4 = (args, _clause) => {
43
+ if (args.length === 0) return; const x = args[0]
44
+ if (x.raw) return parse.CXL(...args).xpr
45
+ if (args.length === 1 && typeof x === 'object') {
46
+ if (is_array(x)) return x
47
+ if (is_cqn(x)) return args
48
+ else return _object_predicate(args,_clause)
42
49
  }
43
- return flat
50
+ else return _fluid_predicate(args)
44
51
  }
45
52
 
46
- // .where ({ID:4711, stock: {'>=':1})
47
- const _where_object = ([arg]) => {
53
+ const _object_predicate = ([arg], _clause) => { // e.g. .where ({ID:4711, stock: {'>=':1})
48
54
  const pred = []
49
- for (let k in arg) {
55
+ for (const k in arg) {
50
56
  const x = arg[k]
51
57
  if (k === 'and') {
52
- let p2 = _where([x])
53
- if (x.or) p2 = ['(',...p2,')']
54
- pred.push('and', ...p2)
58
+ if (x.or) pred.push('and', {xpr:predicate4([x],_clause)})
59
+ else pred.push('and', ...predicate4([x],_clause))
55
60
  continue
56
61
  }
57
62
  if (k === 'or') {
58
- pred.push('or', ..._where([x]))
63
+ pred.push('or', ...predicate4([x],_clause))
59
64
  continue
60
- } else pred.push('and', parse.expr(k))
61
- // if (x === null || x === undefined) pred.push('is null')
62
- if (!x) pred.push('=', { val: x })
63
- else if (is_array(x)) pred.push('in', { val: x })
64
- else if (x.SELECT) pred.push('in', x)
65
- else if (x.ref || x.val || x.xpr) pred.push('=', x)
66
- else if (x instanceof RegExp) pred.push('like', { val: x })
67
- else if (typeof x === 'object') for (let op in x) pred.push(op, { val: x[op] })
68
- else pred.push('=', { val: x })
65
+ }
66
+ if (k === 'exists') {
67
+ pred.push(null, 'exists', ...predicate4([x],_clause))
68
+ continue
69
+ }
70
+ else pred.push('and', parse.expr(k))
71
+ if (!x || x==='*') pred.push('=', {val:x})
72
+ else if (x.SELECT || x.list) pred.push('in', x)
73
+ else if (is_array(x)) pred.push('in', {list:x.map(val)})
74
+ else if (is_cqn(x)) pred.push('=', x)
75
+ else if (x instanceof RegExp) pred.push('like', {val:x})
76
+ else if (typeof x === 'object') for (let op in x) pred.push(op, val(x[op]))
77
+ else if (_clause === 'on' && typeof x === 'string') pred.push('=', { ref: x.split('.') })
78
+ else pred.push('=', {val:x})
69
79
  }
70
80
  return pred.slice(1)
71
81
  }
72
82
 
73
- const parser = require('./parser')
74
- const parse = {
75
- columns:(x) => simple(x) || parser.parse(x,{startRule:'columns'}),
76
- column:(x) => simple(x) || parser.parse(x,{startRule:'column'}),
77
- expr:(x) => simple(x) || parser.parse(x,{startRule:'expr'}),
78
- }
79
-
80
- const simple = (x) => {
81
- if (typeof x !== 'string') return {val:x}
82
- const t = /^\s*([\w.'?]+)(?:\s*([!?\\/:=\-+<~>]+|like)\s*([\w.'?]+))?\s*$/.exec(x); if (!t) return
83
- const [,ref,op,rhs] = t
84
- return op ? {xpr:[rv(ref),op,rv(rhs)]} : rv(ref)
85
- }
86
-
87
- const rv = (x) => {
88
- if (x[0] === '?') return { param: true, ref: x }
89
- if (x[0] === "'") return { val: x.slice(1, -1).replace(/''/g, "'") }
90
- if (x === 'null') return { val: null }
91
- if (x === 'true') return { val: true }
92
- if (x === 'false') return { val: false }
93
- if (!isNaN(x)) return { val: Number(x) }
94
- else return { ref: x.split('.') }
95
- }
96
-
97
- const val = (x) => {
98
- if (x === undefined || x.xpr || x.ref || x.val || x.func || x.SELECT) return x
99
- if (is_array(x)) {
100
- const xpr = []
101
- for (let each of x) xpr.push(',',rv(each))
102
- xpr[0]='('; xpr.push (')')
103
- return xpr
104
- }
105
- return {val:x}
83
+ const _fluid_predicate = (args) => { // e.g. .where ('ID=',4711, 'and stock >=',1)
84
+ if (args.length === 3 && args[1] in operators) return [ ref(args[0]), args[1], val(args[2]) ] // REVISIT: Legacy!
85
+ if (args.length % 2 === 0) args.push('')
86
+ const expr = args.filter((_, i) => i % 2 === 0).join(' ? ')
87
+ const vals = args.filter((_, i) => i % 2 === 1)
88
+ const {xpr} = parse.expr(expr)
89
+ ;(function _fill_in_vals_into (xpr) { xpr.forEach ((x,i) => {
90
+ if (x.xpr) _fill_in_vals_into (x.xpr)
91
+ if (x.param) xpr[i] = val(vals.shift())
92
+ })})(xpr)
93
+ return xpr
106
94
  }
107
95
 
108
- const is_object = (args) => args.length === 1 && typeof args[0] === 'object'
109
- const is_xpr = (x) => x.length === 1 && (is_array(x[0]) || x[0].xpr || x[0].ref || x[0].val || x[0].func || x[0].SELECT)
96
+ const ref = x => is_cqn(x) ? x : {ref:x.split('.')}
97
+ const val = x => !x ? {val:x} : is_array(x) ? {list:x.map(val)} : is_cqn(x) ? x : {val:x}
98
+ const is_cqn = x => x.val !== undefined || x.xpr || x.ref || x.list || x.func || x.SELECT
110
99
  const is_array = Array.isArray
100
+ const operators = { '=':1, '<':2, '<=':2, '>':2, '>=':2, '!=':3, '<>':3, in:4, like:4, IN:4, LIKE:4 }
111
101
 
112
- module.exports = Object.assign (Whereable, { _where, parse })
102
+ module.exports = Object.assign (Whereable, { predicate4, parse })
package/lib/ql/index.js CHANGED
@@ -1,28 +1,40 @@
1
- if (/\b(y|ql)\b/.test(process.env.cdr)) {
1
+ const cds = require('../index')
2
+ require = path => { // eslint-disable-line no-global-assign
3
+ const clazz = module.require (path); if (!clazz._api) return clazz
4
+ Object.defineProperty (clazz.prototype, 'cmd', { value: path.match(/\w+$/)[0] })
5
+ return clazz._api()
6
+ }
7
+
8
+ module.exports = Object.assign (_deprecated_srv_ql, { cdr: true,
9
+ SELECT: require('./SELECT'),
10
+ INSERT: require('./INSERT'),
11
+ UPDATE: require('./UPDATE'),
12
+ DELETE: require('./DELETE'),
13
+ CREATE: require('./CREATE'),
14
+ DROP: require('./DROP'),
15
+ })
2
16
 
3
- const SELECT = require('./SELECT')
4
- const INSERT = require('./INSERT')
5
- const UPDATE = require('./UPDATE')
6
- const DELETE = require('./DELETE')
7
- const CREATE = require('./CREATE')
8
- const DROP = require('./DROP')
17
+ function _deprecated_srv_ql() { // eslint-disable-next-line no-console
18
+ console.trace(`
19
+ Method 'srv.ql(req)' is deprecated and superceded by 'cds.context'.
20
+ Please use global SELECT instead of 'const { SELECT } = srv.ql(req)'.
21
+ `)
22
+ return module.exports
23
+ }
9
24
 
10
- module.exports = {
11
- SELECT: SELECT._,
12
- INSERT: INSERT._,
13
- UPDATE: UPDATE._,
14
- DELETE: DELETE._,
15
- CREATE: CREATE._,
16
- DROP: DROP._,
17
- classes: {
18
- SELECT, INSERT, UPDATE, DELETE,
19
- CREATE, DROP,
20
- Query: require('./Query'),
21
- Whereable: require('./Whereable'),
22
- },
23
- }
25
+ if (cds.env.features.cls && cds.env.features.debug_queries) {
26
+ const Query = module.exports, { then } = Query.prototype
27
+ const { AsyncResource } = require('async_hooks')
28
+ Object.defineProperty (Query,'then',{ get(){
29
+ const q = new AsyncResource('cds.Query')
30
+ return (r,e) => q.runInAsyncScope (then,this,r,e)
31
+ }})
32
+ }
24
33
 
25
- } else {
26
- // Using monkey-patched implementations from cds-runtime
27
- Object.assign (module, {exports: require('./rt')})
34
+ module.exports._reset = ()=>{ // for strange tests only
35
+ const _name = cds.env.sql.names === 'quoted' ? n =>`"${n}"` : n => n.replace(/[.:]/g,'_')
36
+ Object.defineProperty (require('./Query').prototype,'valueOf',{ configurable:1, value: function(cmd=this.cmd) {
37
+ return `${cmd} ${_name(this._target.name)} `
38
+ }})
39
+ return this
28
40
  }
@@ -0,0 +1,35 @@
1
+ // please keep all comments!
2
+
3
+ const cds = require('../index')
4
+ module.exports = {
5
+ column:(x) => _simple(x) /* || _parse('column',x) */ || cds.parse.column(x),
6
+ expr:(x) => _simple(x) /* || _parse('expr',x) */ || cds.parse.expr(x),
7
+ CQL: (..._) => cds.parse.CQL (..._),
8
+ CXL: (..._) => cds.parse.CXL (..._),
9
+ cql: (..._) => cds.parse.cql (..._),
10
+ }
11
+
12
+ const _simple = (x) => {
13
+ if (typeof x !== 'string') return {val:x}
14
+ const t = /^\s*([\w.'?]+)(?:\s*([!?\\/:=\-+<~>]+|like)\s*([\w.'?]+))?\s*$/.exec(x); if (!t) return
15
+ const [,lhs,op,rhs] = t
16
+ return op ? {xpr:[_rv(lhs),op,_rv(rhs)]} : _rv(lhs)
17
+ }
18
+
19
+ const _rv = (x) => {
20
+ if (x[0] === '?') return { param: true, ref: x }
21
+ if (x[0] === "'") return { val: x.slice(1,-1).replace(/''/g, "'") }
22
+ if (x === 'null') return { val: null }
23
+ if (x === 'true') return { val: true }
24
+ if (x === 'false') return { val: false }
25
+ if (!isNaN(x)) return { val: Number(x) }
26
+ else return { ref: x.split('.') }
27
+ }
28
+
29
+ // const _parse = (startRule,x) => {
30
+ // try {
31
+ // return parser.parse(x,{startRule})
32
+ // // } catch (e) { e.message += ' in: \n' + x; throw e }
33
+ // } catch {/* ignored */}
34
+ // }
35
+ // const parser = require('./parser')
@@ -1,8 +1,6 @@
1
+ const cds = require ('../index'), { features } = cds.env, { uuid } = cds.utils
1
2
  const async_events = { succeeded:1, failed:1, done:1 }
2
- const { features } = require('../env')
3
3
  const { EventEmitter } = require('events')
4
- const { uuid } = require('../utils')
5
- const User = require ('./user')
6
4
 
7
5
  /**
8
6
  * This is the base class for `cds.Events` and `cds.Requests`,
@@ -14,6 +12,43 @@ class EventContext {
14
12
 
15
13
  toString() { return `${this.event} ${this.path}` }
16
14
 
15
+ static new (data) {
16
+ const base = cds.context
17
+ if (base && features.cds_tx_inheritance) {
18
+ const { tenant, user, locale, _ } = base
19
+ data = { user: user && Object.create(user), tenant, locale, _, ...data }
20
+ // REVISIT: Do we have to propagate hidden subdomain as well?
21
+ // let subdomain = _ && _.req && _.req && _.req.authInfo && _.req.authInfo.getSubdomain()
22
+ // if (subdomain) {
23
+ // if (!data._) data._ = {}
24
+ // if (!data._.req) data._.req = {}
25
+ // if (!data._.req.authInfo) data._.req.authInfo = { setSubdomain(v){ this.subdomain = v } }
26
+ // if (!data._.req.authInfo.getSubdomain()) data._.req.authInfo.setSubdomain (subdomain)
27
+ // }
28
+ } else if (data && 'user' in data) {
29
+ data = { user:1, ...data } // IMPORTANT: ensure user is first to be assigned to call setter
30
+ }
31
+ return new this (data)
32
+ }
33
+
34
+ static spawn (background_job,o) {
35
+ const fn = async ()=> {
36
+ // create a new transaction for each run of the background job
37
+ // which inherits from the current event context by default
38
+ const tx = cds.context = cds.tx({...o})
39
+ try {
40
+ await background_job(tx)
41
+ await tx.commit()
42
+ } catch(e) {
43
+ console.trace (`ERROR occured in background job:`, e) // eslint-disable-line no-console
44
+ await tx.rollback()
45
+ }
46
+ }
47
+ if (o && o.after) return setTimeout (fn, o.after)
48
+ if (o && o.every) return setInterval (fn, o.every)
49
+ else return setImmediate (fn)
50
+ }
51
+
17
52
  constructor(_={}) {
18
53
  Object.assign (this, this._set('_',_))
19
54
  }
@@ -76,12 +111,13 @@ class EventContext {
76
111
  }
77
112
 
78
113
  set user(u) {
79
- if (!u) return; else super.user = u
80
- // REVISIT: The following is to support user.locale
81
- if (this._.req) Object.defineProperty(u,'_req',{value:this._.req})
114
+ if (!u) return
115
+ if (typeof u === 'string') u = new cds.User(u)
116
+ if (this._.req) Object.defineProperty(u,'_req',{value:this._.req}) // REVISIT: The following is to support req.user.locale
117
+ super.user = u
82
118
  }
83
119
  get user() {
84
- return this.user = this._propagated('user') || new User
120
+ return this.user = this._propagated('user') || new cds.User
85
121
  }
86
122
 
87
123
  set locale(l) {
@@ -97,7 +133,7 @@ class EventContext {
97
133
 
98
134
  set headers(h) { if (h) super.headers = h }
99
135
  get headers() {
100
- if (this._.req && this._.req.headers) {
136
+ if (this._ && this._.req && this._.req.headers) {
101
137
  return super.headers = this._.req.headers
102
138
  } else {
103
139
  const headers={}, outer = this._propagated('headers')
package/lib/req/locale.js CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- const {i18n} = require('../env')
2
+ const {i18n} = require('..').env
3
3
  const INCLUDE_LIST = i18n.preserved_locales.reduce((p,n)=>{
4
4
  p[n]=n; p[n.toUpperCase()]=n; return p
5
5
  },{
@@ -7,16 +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
- module.exports = function req_locale (req) {
10
+ const from_req = req => req.query['sap-language'] || req.headers['x-sap-request-language'] || req.headers['accept-language']
11
+
12
+ function req_locale (req) {
11
13
  if (!req) return i18n.default_language
12
- const locale = (
13
- req.query['sap-language'] ||
14
- req.headers['x-sap-request-language'] ||
15
- req.headers['accept-language']
16
- )
14
+ const locale = from_req(req)
17
15
  if (!locale) return i18n.default_language
18
16
  const loc = locale.replace(/-/g,'_')
19
17
  return INCLUDE_LIST[loc]
20
18
  || /([a-z]+)/i.test(loc) && RegExp.$1.toLowerCase()
21
19
  || i18n.default_language
22
20
  }
21
+
22
+ module.exports = Object.assign(req_locale, { from_req })
@@ -1,15 +1,13 @@
1
1
  const add_methods_to = require ('./Service-methods')
2
2
  const cds = require('..')
3
- const { is_array } = require('../ql/rt/_helpers')
4
3
 
5
4
 
6
5
  class Service extends require('./Service-handlers') {
7
6
 
8
- constructor (name, model, options={}) {
9
- super (name || new.target.name)
10
- if (options.kind) this.kind = options.kind // shortcut
7
+ constructor (name, model, o={}) {
8
+ super (name || new.target.name) .options = o
9
+ if (o.kind) this.kind = o.kind // shortcut
11
10
  if (model) this.model = model
12
- this.options = options
13
11
  }
14
12
 
15
13
  /**
@@ -71,15 +69,21 @@ class Service extends require('./Service-handlers') {
71
69
  /**
72
70
  * Model Reflection API...
73
71
  */
74
- get definition() { return super.definition = this.model && this.model.definitions [this.name] }
72
+ get definition() {
73
+ const defs = this.model && this.model.definitions, o = this.options
74
+ return super.definition = defs && (o && defs[o.service] || defs[this.name] )
75
+ }
76
+
77
+ get namespace() {
78
+ return super.namespace = this.definition && this.definition.name
79
+ || this.model && this.model.namespace
80
+ || this.name !== 'db' && !/\W/.test(this.name) && this.name || undefined
81
+ }
82
+
75
83
  get operations() { return super.operations = _reflect (this, d => d.kind === 'action' || d.kind === 'function') }
76
84
  get entities() { return super.entities = _reflect (this, d => d.kind === 'entity') }
77
85
  get events() { return super.events = _reflect (this, d => d.kind === 'event') }
78
86
  get types() { return super.types = _reflect (this, d => !d.kind || d.kind === 'type') }
79
- get namespace() { return super.namespace = this.definition && this.name
80
- || this.model && this.model.namespace
81
- || this.name !== 'db' && !/\W/.test(this.name) && this.name || undefined
82
- }
83
87
 
84
88
  /**
85
89
  * Subclasses may override this to free private resources
@@ -90,15 +94,18 @@ class Service extends require('./Service-handlers') {
90
94
  }
91
95
 
92
96
  }
93
- module.exports = Service
97
+
98
+ const { dispatch, handle } = require('./Service-dispatch')
99
+ Service.prototype.dispatch = dispatch
100
+ Service.prototype.handle = handle
94
101
  Service.prototype.transaction = Service.prototype.tx = require('./Transaction')
95
- Service.prototype.dispatch = require('./Service-dispatch')
96
102
  Service.prototype._compat_sync = require('./Service-compat')
97
103
  Service.prototype._implicit_next = cds.env.features.implicit_next
98
104
  Service.prototype._is_service_instance = Service._is_service_class = true //> for factory
99
-
105
+ module.exports = Service
100
106
 
101
107
  // Helpers...
102
108
  const _reflect = (srv,filter) => !srv.model ? [] : srv.model.childrenOf (srv.namespace,filter)
103
109
  const is_rest = x => x && typeof x === 'string' && x[0] === '/'
104
- const is_query = x => x && x.bind || is_array(x) && !x.raw
110
+ const is_query = x => x && x.bind || is_array(x) && !x.raw
111
+ const is_array = (x) => Array.isArray(x) && !x.raw
@@ -1,17 +1,20 @@
1
1
  const cds = require ('../index')
2
2
 
3
3
  /**
4
- * This is the implementation of the `srv.dispatch(req)` method.
5
- * It dispatches the request through the registered event handlers.
6
- * @param {import('../req/event') & import('../req/impl')} req
4
+ * The default implementation of the `srv.dispatch(req)` ensures everything
5
+ * is prepared before calling `srv.handle(req)`
6
+ * @typedef {import('./Service-api')} Service
7
+ * @typedef {import('../req/impl')} Request
8
+ * @this {Service}
9
+ * @param {Request} req
7
10
  * @returns {Promise} resolving to the outcome/return value of last .on handler
8
11
  */
9
- module.exports = async function dispatch (req) { //NOSONAR
12
+ exports.dispatch = async function dispatch (req) { //NOSONAR
10
13
 
11
14
  // Ensure we are in a proper transaction
12
15
  if (!this.context) {
13
16
  const txc = cds.context //> join an outer tx.context, if any, with a nested tx
14
- if (txc) return this.tx(txc).dispatch(req)
17
+ if (txc && !txc._done) return this.tx(txc).dispatch(req)
15
18
  else try { //> start a new top-level tx, which we need to commit/rollback
16
19
  const tx = cds.context = this.tx(req)
17
20
  return tx.dispatch(req) .then (tx.commit, tx.rollback)
@@ -28,28 +31,41 @@ module.exports = async function dispatch (req) { //NOSONAR
28
31
  return Promise.all (req.query.map (q => this.dispatch ({query:q,__proto__:req})))
29
32
 
30
33
  // Ensure target and fqns
31
- const srv = this; let handlers //...
32
- if (!req.target) _ensure_target (srv,req)
34
+ if (!req.target) _ensure_target (this,req)
33
35
  if (req.query && typeof req.query === 'object' && !req.query._srv) Object.defineProperty (req.query,'_srv',{value:this})
34
36
 
37
+ return this.handle(req)
38
+ }
39
+
40
+
41
+ /**
42
+ * The default implementation of the `srv.handle(req)` method dispatches
43
+ * requests through registered event handlers.
44
+ * Subclasses should overload this method instead of `srv.dispatch`.
45
+ * @param {Request} req
46
+ * @this {Service}
47
+ */
48
+ exports.handle = async function handle (req) {
49
+ const srv=this; let handlers //...
50
+
35
51
  // ._initial handlers run in sequence
36
52
  handlers = this._handlers._initial.filter (h => h.for(req))
37
53
  if (handlers.length) {
38
- for (const each of handlers) await each.handler.call (srv,req)
54
+ for (const each of handlers) await each.handler.call (this,req)
39
55
  if (req.errors) throw req.errors.throwable()
40
56
  }
41
57
 
42
58
  // .before handlers run in parallel
43
59
  handlers = this._handlers.before.filter (h => h.for(req))
44
60
  if (handlers.length) {
45
- await Promise.all (handlers.map (each => each.handler.call (srv,req)))
61
+ await Promise.all (handlers.map (each => each.handler.call (this,req)))
46
62
  if (req.errors) throw req.errors.throwable()
47
63
  }
48
64
 
49
65
  // .on handlers run in parallel for async events, and as interceptors stack for sync requests
50
66
  handlers = this._handlers.on.filter (h => h.for(req))
51
67
  if (handlers.length) {
52
- if (!req.reply) await Promise.all (handlers.map (each => each.handler.call (srv,req,_dummy)))
68
+ if (!req.reply) await Promise.all (handlers.map (each => each.handler.call (this,req,_dummy)))
53
69
  else await async function next (r=req) { //> handlers may pass a new req object into next()
54
70
  const each = handlers.shift(); if (!each) return //> unhandled silently
55
71
  const x = await each.handler.call (srv,r,next)
@@ -59,13 +75,13 @@ module.exports = async function dispatch (req) { //NOSONAR
59
75
  }()
60
76
  if (req.errors) throw req.errors.throwable()
61
77
  }
62
- else if (req.query) throw _unhandled (srv,req)
78
+ else if (req.query) throw _unhandled (this,req)
63
79
 
64
80
  // .after handlers run in parallel
65
81
  handlers = this._handlers.after.filter (h => h.for(req))
66
82
  if (handlers.length) {
67
83
  const results = cds.env.features.arrayed_after && req.event === 'READ' && !_is_array(req.results) ? [req.results] : req.results // REVISIT: remove this in a future release after some grace period
68
- await Promise.all (handlers.map (each => each.handler.call (srv, results, req)))
84
+ await Promise.all (handlers.map (each => each.handler.call (this, results, req)))
69
85
  if (req.errors) throw req.errors.throwable()
70
86
  }
71
87
 
@@ -1,4 +1,4 @@
1
- const cds = require('../index'), { Context } = cds.Request
1
+ const cds = require('../index'), { Context } = cds.Request, { cds_tx_protection } = cds.env.features
2
2
  const _context = Symbol()
3
3
 
4
4
  /**
@@ -6,11 +6,11 @@ const _context = Symbol()
6
6
  * a new Transaction as a derivate of the `srv` (i.e. {__proto__:srv})
7
7
  * @returns { Transaction & import('./Service-api') }
8
8
  */
9
- module.exports = function tx (req) { const srv = this
9
+ module.exports = function tx (req,o) { const srv = this
10
10
  if (srv.context) return srv
11
11
  if (!req) {
12
12
  // called as srv.tx() -> new root transaction
13
- return RootTransaction.for (srv, new Context)
13
+ return RootTransaction.for (srv, Context.new())
14
14
  }
15
15
  if (req instanceof Context) {
16
16
  // called for a nested req -> nested tx
@@ -20,12 +20,17 @@ module.exports = function tx (req) { const srv = this
20
20
  // called for a top-level req -> root tx
21
21
  else return RootTransaction.for (srv, req)
22
22
  }
23
+ if (typeof req === 'function') {
24
+ // auto-committed transaction, i.e. cds.tx (tx => {...})
25
+ const tx = srv.tx(o)
26
+ return Promise.resolve().then (()=> req(tx)) .then (tx.commit,tx.rollback)
27
+ }
23
28
  if (req[_context]) {
24
29
  // called again for an arbitrary context object -> see below
25
30
  return NestedTransaction.for (srv, req[_context])
26
31
  } else {
27
32
  // called first time for an arbitrary context object
28
- const root = new Context(req); Object.defineProperty (req, _context, {value:root})
33
+ const root = Context.new(req); Object.defineProperty (req, _context, {value:root})
29
34
  return RootTransaction.for (srv, root)
30
35
  }
31
36
  }
@@ -59,7 +64,7 @@ class Transaction {
59
64
  async commit (res) {
60
65
  if (this.ready) { //> nothing to do if no transaction started at all
61
66
  if (this.__proto__.commit) await this.__proto__.commit.call (this,res)
62
- _init (this)
67
+ _init(this).ready = 'committed'
63
68
  }
64
69
  return res
65
70
  }
@@ -78,7 +83,7 @@ class Transaction {
78
83
 
79
84
  if (this.ready) { //> nothing to do if no transaction started at all
80
85
  if (this.__proto__.rollback) await this.__proto__.rollback.call (this,err)
81
- _init (this)
86
+ _init(this).ready = 'rolled back'
82
87
  }
83
88
  if (err) throw err
84
89
  }
@@ -100,6 +105,7 @@ class RootTransaction extends Transaction {
100
105
  * are informed by emitting 'succeesed' event to them all.
101
106
  */
102
107
  async commit (res) {
108
+ if (cds_tx_protection) this.context._done = 'committed'
103
109
  try {
104
110
  await this.context.emit ('succeeded',res)
105
111
  await super.commit (res)
@@ -115,6 +121,7 @@ class RootTransaction extends Transaction {
115
121
  * are informed by emitting 'failed' event to them all.
116
122
  */
117
123
  async rollback (err) {
124
+ if (cds_tx_protection) this.context._done = 'rolled back'
118
125
  try {
119
126
  await this.context.emit ('failed',err)
120
127
  await super.rollback (err)
@@ -148,14 +155,19 @@ class NestedTransaction extends Transaction {
148
155
  * before any .dispatch.
149
156
  */
150
157
  const _init = (tx) => {
151
- if ('begin' in tx) tx.dispatch = begin_and_dispatch
152
- tx.ready = false
158
+ if ('begin' in tx) tx.dispatch = _begin
159
+ else tx.ready = true //> to allow subclasses w/o .begin
153
160
  return tx
154
161
  }
155
- async function begin_and_dispatch (req) {
162
+ const _begin = async function (req) {
156
163
  if (!req.query && req.method === 'BEGIN') // IMPORTANT: !req.query is to exclude batch requests
157
164
  return this.ready = this.__proto__.dispatch.call (this,req)
158
- if (!this.ready) this.ready = this.begin()
165
+ // Protection against unintended tx.run() after tx.commit/rollback()
166
+ if (typeof this.ready === 'string' || !this.ready && this.context._done) {
167
+ 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' })
168
+ else this.ready = this.begin() // compatibiliy to former behavior, which allowed tx.run() after commit/rollback
169
+ }
170
+ else if (!this.ready) this.ready = this.begin()
159
171
  await this.ready
160
172
  delete this.dispatch
161
173
  return this.dispatch (req)