@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,4 +1,4 @@
1
- const cds = require('..'), {one_model} = cds.env.features
1
+ const cds = require('..'), {one_model} = cds.env.features, LOG = cds.log('cds.connect')
2
2
  const factory = require('../serve/factory')
3
3
  const _pending = cds.services._pending || {} // used below to chain parallel connect.to(<same>)
4
4
 
@@ -6,12 +6,12 @@ const _pending = cds.services._pending || {} // used below to chain parallel con
6
6
  * Connect to a service as primary datasource, i.e. cds.db.
7
7
  */
8
8
  const connect = module.exports = async function cds_connect (options) {
9
- if (typeof options === 'object' && cds.db) throw cds.error (
10
- `You need to disconnect before creating a new primary connection with different options!`
11
- )
12
- if (typeof options === 'string') cds.db = await connect.to (options)
13
- else await connect.to ('db',options)
14
- return cds
9
+ if (typeof options === 'object' && cds.db) throw cds.error (
10
+ `You need to disconnect before creating a new primary connection with different options!`
11
+ )
12
+ if (typeof options === 'string') cds.db = await connect.to (options)
13
+ else await connect.to ('db',options)
14
+ return cds
15
15
  }
16
16
 
17
17
  /**
@@ -21,51 +21,50 @@ const connect = module.exports = async function cds_connect (options) {
21
21
  * @param {{ kind?:String, impl?:String }} [options]
22
22
  * @returns { Promise<import('../serve/Service-api')> }
23
23
  */
24
- connect.to = async (datasource, options) => { //NOSONAR
25
- let Service = factory, _done = x=>x
26
- if (typeof datasource === 'object') [options,datasource] = [datasource]
27
- else if (datasource) {
28
- if (datasource._is_service_class) [ Service, datasource ] = [ datasource, datasource.name ]
29
- if (!options) { //> specifying ad-hoc options disallows caching
30
- if (datasource in cds.services) return cds.services[datasource]
31
- if (datasource in _pending) return _pending[datasource]
32
- }
33
- // queue parallel requests to a single promise, to avoid creating multiple services
34
- _pending[datasource] = new Promise (r=>_done=r).finally(()=>{ delete _pending[datasource] })
35
- }
36
- const o = Service === factory ? options4 (datasource, options) : {}
37
- const m = await model4 (o)
38
- const srv = new Service (datasource,m,o)
39
- await srv.prepend (srv.init, srv.options.impl)
40
- if (datasource === 'db') cds.db = srv
41
- _done (cds.services[datasource] = srv)
42
- cds.emit ('connect',srv)
43
- return srv
24
+ connect.to = async (datasource, options) => {
25
+ let Service = factory, _done = x=>x
26
+ if (typeof datasource === 'object') [options,datasource] = [datasource]
27
+ else if (datasource) {
28
+ if (datasource._is_service_class) [ Service, datasource ] = [ datasource, datasource.name ]
29
+ if (!options) { //> specifying ad-hoc options disallows caching
30
+ if (datasource in cds.services) return cds.services[datasource]
31
+ if (datasource in _pending) return _pending[datasource]
32
+ }
33
+ // queue parallel requests to a single promise, to avoid creating multiple services
34
+ _pending[datasource] = new Promise (r=>_done=r).finally(()=>{ delete _pending[datasource] })
35
+ }
36
+ const o = Service === factory ? options4 (datasource, options) : {}
37
+ const m = await model4 (o)
38
+ // check if required service definition exists
39
+ const required = cds.requires[datasource]
40
+ if (required && required.model && datasource !== 'db' && !m.definitions[required.service||datasource]) {
41
+ LOG.error (`No service definition found for '${required.service || datasource}', as required by 'cds.requires.${datasource}':`, required)
42
+ throw new Error (`No service definition found for '${required.service || datasource}'`)
43
+ }
44
+ // construct new service instance
45
+ const srv = new Service (datasource,m,o)
46
+ await srv.prepend (srv.init, srv.options.impl)
47
+ if (datasource === 'db') cds.db = srv
48
+ _done (cds.services[datasource] = srv)
49
+ cds.emit ('connect',srv)
50
+ return srv
44
51
  }
45
52
 
46
- function options4 (name, _o) { // NOSONAR
47
- const [, kind=_o && (_o.use || _o.kind || _o.driver), url] = /^(\w+):(.*)/.exec(name) || []
48
- const conf = cds.requires [name] || cds.requires [kind]
49
- const o = { kind, ...conf, ..._o }
50
- if (!(o.kind = o.use || o.driver || o.kind) && !o.silent && !o.impl) throw cds.error(
51
- conf ? `Configuration for 'cds.requires.${name}' lacks mandatory property 'kind' or 'impl'` :
52
- name ? `Didn't find a configuration for 'cds.requires.${name}'` :
53
- `Provided options object lacks mandatory property 'kind' or 'impl'`
54
- )
55
- if (url) o.credentials = { ...o.credentials, url, database: url }
56
- if (o.kind === 'sqlite') { // REVISIT: should move to sqlite module
57
- const creds = o.credentials
58
- for (let p of ['url','database']) {
59
- const each = creds[p]
60
- if (!each || each === ':memory:') continue
61
- else creds[p] = cds.utils.path.resolve(cds.root,each)
62
- }
63
- }
64
- return o
53
+ function options4 (name, _o) {
54
+ const [, kind=_o && _o.kind, url] = /^(\w+):(.*)/.exec(name) || []
55
+ const conf = cds.requires[name] || cds.requires[kind]
56
+ const o = { kind, ...conf, ..._o }
57
+ if (!o.kind && !o.impl && !o.silent) throw cds.error(
58
+ conf ? `Configuration for 'cds.requires.${name}' lacks mandatory property 'kind' or 'impl'` :
59
+ name ? `Didn't find a configuration for 'cds.requires.${name}'` :
60
+ `Provided options object lacks mandatory property 'kind' or 'impl'`
61
+ )
62
+ if (url) o.credentials = { ...o.credentials, url }
63
+ return o
65
64
  }
66
65
 
67
66
  function model4 (o) {
68
- if (o.model && o.model.definitions) return o.model
69
- if (one_model && cds.model) return cds.model
70
- else return o.model && cds.load(o.model)
67
+ if (o.model && o.model.definitions) return o.model
68
+ if (one_model && cds.model) return cds.model
69
+ else return o.model && cds.load(o.model)
71
70
  }
@@ -6,7 +6,7 @@ class any {
6
6
  set name(n) { this.set('name', n, false) }
7
7
  set kind(k) { this.set('kind', k, true) }
8
8
  get kind() { return this.set('kind', this.parent ? 'element' : 'type') }
9
- is (kind) { return this.kind === kind }
9
+ is (kind) { return this.kind === kind || kind === 'any' }
10
10
  valueOf() { return this.name }
11
11
 
12
12
  own (property) {
@@ -57,7 +57,7 @@ class LinkedCSN extends any {
57
57
  if (d.includes) d.includes.forEach(i => _visit(all[i])) // Note: with delete d.includes, redirects in AFC broke
58
58
  if (d.query) d.query._target && _visit (d.query._target)
59
59
  if (d.type) _builtin(d.type) || _visit (d.__proto__)
60
- if (d.target) _visit (d._target)
60
+ if (d.target) _visit (d._target) ; else if (d.targetAspect) _visit (typeof d.targetAspect === 'object' ? d.targetAspect : all[d.targetAspect])
61
61
  if (d.returns) _visit (d.returns)
62
62
  if (d.items) _visit (d.items)
63
63
  if (d.parent) _visit (d.parent)
@@ -65,7 +65,15 @@ class LinkedCSN extends any {
65
65
  for (let a in d.actions) _visit (d.actions[a])
66
66
  for (let p in d.params) _visit (d.params[p])
67
67
  }
68
- for (let d in all) if (!reached.has(all[d])) delete all[d]
68
+ for (let n in all) {
69
+ if (n.endsWith('.texts') && reached.has(all[n.slice(0,-6)])) continue
70
+ if (reached.has(all[n])) continue
71
+ else {
72
+ delete all[n]
73
+ // also delete the legacy _texts proxy (not enumerable, installed by _localized.unfold_csn)
74
+ if (n.endsWith('.texts')) delete all[n.replace('.texts','_texts')]
75
+ }
76
+ }
69
77
  return csn
70
78
  }
71
79
 
package/lib/deploy.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const cds = require('./index'), { local } = cds.utils
2
2
  const DEBUG = cds.debug('deploy')
3
+ let LOG
3
4
 
4
5
  /**
5
6
  * Implementation of `cds.deploy` common to all databases.
@@ -11,6 +12,7 @@ exports = module.exports = function cds_deploy (model,options) { return {
11
12
 
12
13
  /** @param {cds.Service} db */
13
14
  async to (db, o=options||{}) { // NOSONAR
15
+ LOG = cds.log('deploy')
14
16
 
15
17
  if (model && !model.definitions) model = await cds.load (model)
16
18
  if (o.mocked) exports.include_external_entities_in (model)
@@ -26,12 +28,12 @@ exports = module.exports = function cds_deploy (model,options) { return {
26
28
  }
27
29
 
28
30
  // fill in initial data...
29
- const SILENT = o.silent || global.it || process.env.NODE_ENV === 'test'
31
+ const SILENT = o.silent || !LOG._info
30
32
  await init_from_code (db,model,SILENT)
31
33
  await init_from_csv (db,model,SILENT)
32
34
  await init_from_json (db,model,SILENT)
33
35
 
34
- const {credentials} = db.options, file = credentials && credentials.database
36
+ const {credentials} = db.options, file = credentials && (credentials.database || credentials.url)
35
37
  if (!SILENT) {
36
38
  if (file !== ':memory:') console.log (`/> successfully deployed to ./${file}\n`)
37
39
  else console.log (`/> successfully deployed to sqlite in-memory db\n`)
@@ -170,28 +172,29 @@ async function init_from_ (locations, filter, db, csn, SILENT, INSERT_into) { //
170
172
 
171
173
  if (folders.size === 0) return
172
174
 
173
- const tx = await db.tx(), err = new Error
174
- const inits = [], {local} = cds.utils
175
- for (let folder of folders) {
176
- const files = await readdir (folder)
177
- for (let each of files.filter (filter)) {
178
- let name = each.replace(/-/g,'.').slice(0, -path.extname(each).length)
179
- let entity = _entity4 (name)
180
- if (!entity) { DEBUG && DEBUG (`warning: ${name} not in model`); continue }
181
- if (entity['@cds.persistence.skip'] === true) continue
182
- const file = path.join(folder,each)
183
- const src = await read (file,'utf8'); if (!src) continue
184
- const q = INSERT_into (entity,src); if (!q) continue
185
- SILENT || console.log (`\x1b[2m > filling ${entity.name} from ${local(file)} \x1b[0m`) // eslint-disable-line
186
- inits.push (tx.run(q).catch(e=>{ const ex = new Error
187
- e.stack = e.message +'\n'+ require('util').inspect(q) + err.stack.slice(5)
188
- .replace (/deploy\.js:\d+:/, ex.stack.slice(5).match(/deploy\.js:\d+:/)[0])
189
- throw e
190
- }))
175
+ const {local} = cds.utils, inits = [], err = new Error
176
+ await db.tx (async tx => {
177
+ for (let folder of folders) {
178
+ const files = await readdir (folder)
179
+ for (let each of files.filter (filter)) {
180
+ let name = each.replace(/-/g,'.').slice(0, -path.extname(each).length)
181
+ let entity = _entity4 (name)
182
+ if (!entity) { DEBUG && DEBUG (`warning: ${name} not in model`); continue }
183
+ if (entity['@cds.persistence.skip'] === true) continue
184
+ const file = path.join(folder,each)
185
+ const src = await read (file,'utf8'); if (!src) continue
186
+ const q = INSERT_into (entity,src)
187
+ if (!q) { DEBUG && DEBUG (`skipping empty ${local(file)}`); continue }
188
+ SILENT || console.log (`\x1b[2m > filling ${entity.name} from ${local(file)} \x1b[0m`) // eslint-disable-line
189
+ inits.push (tx.run(q).catch(e=>{ const ex = new Error
190
+ e.stack = e.message +'\n'+ require('util').inspect(q) + err.stack.slice(5)
191
+ .replace (/deploy\.js:\d+:/, ex.stack.slice(5).match(/deploy\.js:\d+:/)[0])
192
+ throw e
193
+ }))
194
+ }
191
195
  }
192
- }
193
- await Promise.all(inits)
194
- if (!db.context) await tx.commit()
196
+ await Promise.all(inits)
197
+ })
195
198
 
196
199
  function _entity4 (name) {
197
200
  let entity = csn.definitions [name]
@@ -17,7 +17,10 @@ module.exports = {
17
17
  // skip_unused: 'all',
18
18
  skip_unused: true,
19
19
  one_model: true,
20
- localized: true
20
+ localized: true,
21
+ // assert_integrity: true,
22
+ cds_tx_protection: true,
23
+ cds_tx_inheritance: true,
21
24
  },
22
25
 
23
26
  log: {
@@ -25,6 +28,9 @@ module.exports = {
25
28
  levels: {
26
29
  compile: 'warn',
27
30
  cli: 'warn',
31
+ deploy: 'info',
32
+ serve: 'info',
33
+ server: 'info',
28
34
  },
29
35
  service: false,
30
36
  // adds custom fields in kibana's error rendering (unknown fields are ignored); key: index
@@ -119,16 +125,17 @@ module.exports = {
119
125
  api: {
120
126
  model: true,
121
127
  provisioning: true,
122
- metadata: true
128
+ metadata: true,
129
+ diagnose: true
123
130
  },
124
131
  domain: '__default__'
125
132
  },
126
133
 
127
134
  cdsc: {
128
- cv2: {
129
- _localized_entries: true,
130
- _texts_entries: true,
131
- }
135
+ // cv2: {
136
+ // _localized_entries: true,
137
+ // _texts_entries: true,
138
+ // }
132
139
  // toSql: { associations: 'joins' },
133
140
  // newCsn: true,
134
141
  },
package/lib/env/index.js CHANGED
@@ -3,7 +3,6 @@ const DEFAULTS = require('./defaults'), defaults = require.resolve ('./defaults'
3
3
  const user_home = require('os').homedir()
4
4
  const compat = require('./compat')
5
5
  const path = require('path')
6
- const cache = {}
7
6
 
8
7
 
9
8
  /**
@@ -15,20 +14,11 @@ class Config {
15
14
  * This is the one and only way to construct new instances.
16
15
  * Public API is through `cds.env.for (<context>)`
17
16
  * @param context - the app context, like 'cds' or 'your-app'
18
- * @returns {Config}
17
+ * @returns {Config & typeof DEFAULTS}
19
18
  */
20
- for (context, cwd, _force_cache_update=false, _filter_default_values=false) {
19
+ for (context, cwd, _defaults=true) {
21
20
  if (!cwd) cwd = global.cds && global.cds.root || process.cwd()
22
- const cached = cache[cwd] || (cache[cwd] = {})
23
-
24
- if (_filter_default_values) {
25
- // never update cache without default values
26
- return new Config (context, cwd, false)
27
- }
28
- if(!cached[context] || _force_cache_update) {
29
- cached[context] = new Config (context, cwd)
30
- }
31
- return cached[context]
21
+ return new Config (context, cwd, _defaults)
32
22
  }
33
23
 
34
24
 
@@ -37,8 +27,8 @@ class Config {
37
27
  */
38
28
  constructor (_context, _home, _defaults=true) {
39
29
  Object.assign (this, { _context, _home, _sources:[] })
40
- this.profiles = _determineProfilesFrom (process.env)
41
- this._profiles_defined = new Set()
30
+ this._profiles = _determineProfilesFrom (process.env)
31
+ this._profiles._defined = new Set()
42
32
 
43
33
  // compat requires default values
44
34
  if (_context === 'cds' && _defaults) this.add (DEFAULTS, defaults)
@@ -74,6 +64,20 @@ class Config {
74
64
  }
75
65
  }
76
66
 
67
+ /**
68
+ * This is `this.requires` plus additional entries for all cds.required.<name>.service
69
+ */
70
+ get required_services_or_defs() {
71
+ const dict = Object.create (this.requires)
72
+ for (let [name,e] of Object.entries (this.requires)) if (e.service) {
73
+ if (e.service in dict && e.service !== name) {
74
+ console.error (`Datasource name '${e.service}' conflicts with 'service' definition referred to in 'cds.requires.${name}':`, e)
75
+ throw new Error (`Datasource name '${e.service}' conflicts with service definition`)
76
+ }
77
+ else dict[e.service] = { ...e, name }
78
+ }
79
+ return super.required_services_or_defs = dict
80
+ }
77
81
 
78
82
  set roots(v) { set (this, 'roots', v) }
79
83
  get roots() {
@@ -118,7 +122,11 @@ class Config {
118
122
  * For BAS only: get all defined profiles (could include some from the defaults)
119
123
  */
120
124
  get "defined-profiles" () {
121
- return Array.from (this._profiles_defined)
125
+ return Array.from (this._profiles._defined)
126
+ }
127
+
128
+ get profiles() {
129
+ return super.profiles = Array.from (this._profiles)
122
130
  }
123
131
 
124
132
 
@@ -129,24 +137,15 @@ class Config {
129
137
  //
130
138
 
131
139
 
132
- /** @returns {Config} */
133
- _load (cwd, res, _conf=o=>o) {
134
- const json = _readJson (res = path.join(cwd, res)) // only support JSON
135
- if (json) {
136
- const conf = _conf (json)
137
- if (conf) {
138
- this._sources.push (res)
139
- _merge (this, conf, this.profiles, this._profiles_defined)
140
- }
141
- }
142
- return this
140
+ _load (cwd, file, _conf=o=>o) {
141
+ const json = _readJson (file = path.join(cwd, file)) // only support JSON
142
+ if (json) this.add (_conf (json), file)
143
143
  }
144
144
 
145
- /** @returns {Config} */
146
145
  add (conf, /*from:*/ _src) {
147
146
  if (!conf) return this
148
147
  if (_src) this._sources.push (_src)
149
- _merge (this, conf, this.profiles, this._profiles_defined)
148
+ _merge (this, conf, this._profiles)
150
149
  return this
151
150
  }
152
151
 
@@ -170,12 +169,11 @@ class Config {
170
169
  const { requires } = this
171
170
  for (let each in requires) requires[each] = _merged (each)
172
171
  function _merged (key) {
173
- let entry = requires [key]
174
- if (entry._is_merged || entry.kind === key || !(entry.kind in requires)) return entry
175
- else return Object.defineProperty (
176
- _merge ({..._merged(entry.kind)}, entry, false, false, 'cloned'),
177
- '_is_merged', {value:true}
178
- )
172
+ const entry = requires[key]
173
+ if (entry._is_merged || entry.kind === key || !(entry.kind in requires)) return entry
174
+ const clone = _merge ({}, _merged (entry.kind)) // first apply inherited data
175
+ _merge (clone, entry, false, false, o => _merge({},o)) // then apply overridden data
176
+ return Object.defineProperty (clone, '_is_merged', {value:true})
179
177
  }
180
178
  }
181
179
 
@@ -207,12 +205,12 @@ class Config {
207
205
  const env = new Config('cds')
208
206
  this._for_tests.vcaps = (vcaps) => { _add_vcap_services_to (env, vcaps)}
209
207
  // merge all configs, then resolve profiles (same as in 'for' function above)
210
- for (let c of [...conf].reverse()) _merge(env, c, env.profiles, env._profiles_defined)
208
+ for (let c of [...conf].reverse()) _merge(env, c, env._profiles)
211
209
  return env
212
210
  }
213
211
  // FOR TESTS ONLY! --> PLEASE: tests should test public APIs (only)
214
212
  _merge_with (src) {
215
- _merge (this, src, this.profiles, this._profiles_defined)
213
+ _merge (this, src, this._profiles)
216
214
  return this
217
215
  }
218
216
  }
@@ -228,31 +226,37 @@ class Config {
228
226
  /**
229
227
  * @returns {Config} dst
230
228
  */
231
- function _merge (dst, src, _profiles, _profiles_defined, _clone) { // NOSONAR
232
- const profiled = []
233
- for (let p of Object.getOwnPropertyNames(src)) {
234
- const v = src[p]
235
- if (_profiles_defined && p[0] === '[') _profiles_defined.add (p.slice(1,-1))
236
- if (_profiles && p[0] === '[') {
237
- if (_profiles.includes(p.slice(1,-1)))
238
- profiled.push (()=> _merge (dst, v, _profiles, _profiles_defined, _clone))
229
+ function _merge (dst, src, _profiles, _cloned) {
230
+ const profiled = [], descr = Object.getOwnPropertyDescriptors(src)
231
+ for (let p in descr) {
232
+ const pd = descr[p]
233
+
234
+ if ('get' in pd || !pd.enumerable) {
235
+ Object.defineProperty(dst,p,pd)
236
+ continue
239
237
  }
240
- else if (typeof v === 'object' && !Array.isArray(v)) {
241
- if (!dst[p]) dst[p] = {}
242
- else if (_clone) dst[p] = {...dst[p]}
243
- _merge (dst[p], v, _profiles, _profiles_defined)
238
+
239
+ if (_profiles && p[0] === '[') {
240
+ if (_profiles._defined) _profiles._defined.add (p.slice(1,-1))
241
+ if (_profiles.has(p.slice(1,-1)))
242
+ profiled.push (()=> _merge (dst, src[p], _profiles, _cloned))
243
+ continue
244
244
  }
245
- else if (v !== undefined) {
246
- // eslint-disable-next-line no-prototype-builtins
247
- if (src.propertyIsEnumerable(p)) dst[p] = v
248
- else if (dst[p] === undefined) Object.defineProperty(dst, p, {value: v})
245
+
246
+ const v = pd.value
247
+ if (typeof v === 'object' && !Array.isArray(v)) {
248
+ if (!dst[p]) dst[p] = {}; else if (_cloned) dst[p] = _cloned(dst[p])
249
+ _merge (dst[p], v, _profiles, _cloned)
250
+ continue
249
251
  }
252
+
253
+ if (v !== undefined) dst[p] = v
250
254
  }
251
255
  for (let each of profiled) each()
252
256
  return dst
253
257
  }
254
258
 
255
- function _process_env4 (prefix) { // NOSONAR
259
+ function _process_env4 (prefix) {
256
260
  const {env} = process
257
261
  const PREF = prefix.toUpperCase(), my = { CONFIG: PREF+'_CONFIG', ENV: PREF+'_ENV' }
258
262
  const out = JSON.parse (env[my.CONFIG] || '{}')
@@ -280,7 +284,7 @@ function _value4 (val) {
280
284
  }
281
285
 
282
286
 
283
- function _add_vcap_services_to (env, vcaps={}) { //NOSONAR
287
+ function _add_vcap_services_to (env, vcaps={}) {
284
288
  let any
285
289
  for (let service in env.requires) {
286
290
  const conf = env.requires [service]
@@ -288,40 +292,31 @@ function _add_vcap_services_to (env, vcaps={}) { //NOSONAR
288
292
  conf.vcap && _fetch (conf.vcap) || //> alternatives, e.g. { name:'foo', tag:'foo' }
289
293
  _fetch ({ name: service }) ||
290
294
  _fetch ({ tag: env._context+':'+service }) ||
291
- _fetch ({ tag: conf.use || conf.kind }) || // important for hanatrial, labeled 'hanatrial', tagged 'hana'
292
- _fetch ({ label: conf.use || conf.kind }) ||
295
+ _fetch ({ tag: conf.dialect || conf.kind }) || // important for hanatrial, labeled 'hanatrial', tagged 'hana'
296
+ _fetch ({ label: conf.dialect || conf.kind }) ||
293
297
  {/* not found */}
294
298
  )
295
- // `credentials.database` is used as indicator for sqlite, so must not appear for other DBs
296
- if (conf.credentials && conf.use !== 'sqlite') delete conf.credentials.database
297
299
  // Merge `credentials`. Needed because some app-defined things like `credentials.destination` must survive.
298
300
  if (credentials) any = conf.credentials = Object.assign ({}, conf.credentials, credentials)
299
301
  }
300
302
  return any
301
303
 
302
- function _fetch (predicate) { //NOSONAR
303
- const filter = _filter4 (predicate)
304
- for (let stype in vcaps) {
305
- const found = _array(vcaps,stype) .find (filter)
306
- if (found) return found
307
- }
308
- }
309
-
310
- function _filter4 (predicate) {
311
- let filter; for (let key in predicate) {
312
- const val = predicate[key], prev=filter, next=(
313
- key === 'tag' ? e => _array(e,'tags').includes (val)
314
- : e => val !== undefined ? e[key] === val : false
315
- )
316
- filter = prev ? e => prev(e) || next(e) : next
304
+ function _fetch (predicate) {
305
+ for (let k of Object.keys(predicate).reverse()) {
306
+ const v = predicate[k]; if (!v) continue
307
+ const filter = k === 'tag' ? e => _array(e,'tags').includes(v) : e => e[k] === v
308
+ for (let stype in vcaps) {
309
+ const found = _array(vcaps,stype) .find (filter)
310
+ if (found) return found
311
+ }
317
312
  }
318
- return filter
319
313
  }
320
314
 
321
315
  function _array(o,p) {
322
316
  const v = o[p]
323
- if (!v || Array.isArray(v)) return v || []
324
- throw new Error(`Expected '${p}' to be an array, but was: ${require('util').inspect(vcaps)}`)
317
+ if (!v) return []
318
+ if (Array.isArray(v)) return v
319
+ throw new Error(`Expected VCAP entry '${p}' to be an array, but was: ${require('util').inspect(vcaps)}`)
325
320
  }
326
321
 
327
322
  }
@@ -343,7 +338,7 @@ function _determineProfilesFrom (env = process.env) {
343
338
  }
344
339
  const split = (x) => env[x] ? env[x].split (/\s*,\s*/) : []
345
340
  const profiles = [ ...split ('NODE_ENV'), ...split ('CDS_ENV') ]
346
- return [...new Set (profiles)]
341
+ return new Set (profiles)
347
342
  }
348
343
 
349
344
 
@@ -30,20 +30,17 @@ module.exports = {
30
30
  logging: undefined,
31
31
  audit: undefined,
32
32
  "sql": {
33
- '[development]': { kind: 'sqlite', credentials: { database: ':memory:' } },
33
+ '[development]': { kind: 'sqlite', credentials: { url: ':memory:' } },
34
34
  '[production]': { kind: 'hana' },
35
35
  },
36
- "sqlite": {
37
- // use: '@sap/cds-sqlite', // enforce using sqlite when accessed through <sql>
38
- use: 'sqlite', // enforce using sqlite when accessed through <sql>
39
- credentials: { database: 'sqlite.db' },
40
- impl: `${_runtime}/sqlite/Service.js`
41
- },
42
- "hana": {
43
- // use: '@sap/cds-hana', // enforce using hana when accessed through <sql>
44
- use: 'hana', // enforce using hana when accessed through <sql>
45
- impl: `${_runtime}/hana/Service.js`
46
- },
36
+ "sqlite": _compat_to_use({
37
+ dialect: 'sqlite', credentials: { url: 'sqlite.db' },
38
+ impl: `${_runtime}/sqlite/Service.js`,
39
+ }),
40
+ "hana": _compat_to_use ({
41
+ dialect: 'hana',
42
+ impl: `${_runtime}/hana/Service.js`,
43
+ }),
47
44
  "rest": {
48
45
  impl: `${_runtime}/remote/Service.js`
49
46
  },
@@ -57,21 +54,37 @@ module.exports = {
57
54
  kind: 'odata'
58
55
  },
59
56
  "local-messaging": {
60
- impl: `${_runtime}/messaging/service.js`
57
+ impl: `${_runtime}/messaging/service.js`,
58
+ local: true
61
59
  },
62
60
  "file-based-messaging": {
63
61
  outbox: true,
64
62
  impl: `${_runtime}/messaging/file-based.js`,
65
63
  credentials: { file:'~/.cds-msg-box' }
66
64
  },
67
- "enterprise-messaging-shared": {
68
- outbox: true,
69
- impl: `${_runtime}/messaging/enterprise-messaging-shared.js`,
70
- use: "enterprise-messaging" // make sure to search for the correct label in config
65
+ "default-messaging": {
66
+ "[development]": { "kind": "local-messaging" },
67
+ "[hybrid]": { "kind": "enterprise-messaging-amqp" },
68
+ "[production]": {
69
+ "kind": "enterprise-messaging-amqp",
70
+ "[multitenant]": { "kind": "enterprise-messaging-http" }
71
+ }
71
72
  },
72
73
  "enterprise-messaging": {
74
+ kind: "enterprise-messaging-http",
75
+ },
76
+ "enterprise-messaging-shared": { // for temporary compat only
77
+ kind: "enterprise-messaging-amqp",
78
+ },
79
+ "enterprise-messaging-http": {
80
+ outbox: true,
81
+ impl: `${_runtime}/messaging/enterprise-messaging.js`,
82
+ vcap: { label: "enterprise-messaging" },
83
+ },
84
+ "enterprise-messaging-amqp": {
73
85
  outbox: true,
74
- impl: `${_runtime}/messaging/enterprise-messaging.js`
86
+ impl: `${_runtime}/messaging/enterprise-messaging-shared.js`,
87
+ vcap: { label: "enterprise-messaging" },
75
88
  },
76
89
  'message-queuing': {
77
90
  outbox: true,
@@ -85,6 +98,12 @@ module.exports = {
85
98
  // REVISIT: how to load model?
86
99
  // model: 'AuditLogService.cds',
87
100
  outbox: true,
88
- use: "auditlog" // > without "-" for credentials lookup
101
+ vcap: { label: "auditlog" },
89
102
  }
90
103
  }
104
+
105
+
106
+ function _compat_to_use(o) { return Object.defineProperties (o,{
107
+ // NOTE: Property .use is for compatibility only -> use .dialect instead!
108
+ use: { get(){ return this.dialect }, enumerable:true },
109
+ })}