@sap/cds 5.8.3 → 5.9.1

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 (246) hide show
  1. package/CHANGELOG.md +193 -77
  2. package/app/fiori/preview.js +16 -11
  3. package/app/fiori/routes.js +15 -8
  4. package/app/index.js +1 -1
  5. package/bin/build/buildTaskFactory.js +3 -3
  6. package/bin/build/buildTaskProviderFactory.js +1 -1
  7. package/bin/build/constants.js +1 -1
  8. package/bin/build/provider/buildTaskHandlerEdmx.js +12 -7
  9. package/bin/build/provider/buildTaskHandlerInternal.js +1 -1
  10. package/bin/build/provider/buildTaskProviderInternal.js +8 -2
  11. package/bin/build/provider/hana/2migration.js +27 -24
  12. package/bin/build/provider/hana/index.js +17 -18
  13. package/bin/build/provider/hana/migrationtable.js +9 -10
  14. package/bin/build/provider/java-cf/index.js +4 -5
  15. package/bin/build/provider/node-cf/index.js +99 -6
  16. package/bin/cds.js +17 -18
  17. package/bin/deploy/to-hana/cfUtil.js +16 -19
  18. package/bin/deploy/to-hana/hana.js +7 -24
  19. package/bin/deploy/to-hana/hdiDeployUtil.js +8 -4
  20. package/bin/mtx/in-cds.js +2 -2
  21. package/bin/serve.js +10 -3
  22. package/bin/utils/modules.js +7 -0
  23. package/bin/version.js +56 -3
  24. package/lib/compile/cdsc.js +7 -2
  25. package/lib/compile/etc/_localized.js +36 -25
  26. package/lib/compile/etc/csv.js +8 -8
  27. package/lib/compile/for/drafts.js +9 -0
  28. package/lib/compile/for/java.js +16 -0
  29. package/lib/compile/for/nodejs.js +12 -0
  30. package/lib/compile/index.js +3 -0
  31. package/lib/compile/minify.js +16 -2
  32. package/lib/compile/parse.js +2 -2
  33. package/lib/compile/resolve.js +35 -18
  34. package/lib/compile/to/json.js +3 -1
  35. package/lib/compile/to/sql.js +2 -2
  36. package/lib/compile/to/srvinfo.js +4 -2
  37. package/lib/connect/index.js +1 -1
  38. package/lib/core/entities.js +15 -14
  39. package/lib/core/index.js +39 -36
  40. package/lib/core/reflect.js +4 -2
  41. package/lib/deploy.js +114 -127
  42. package/lib/env/defaults.js +1 -0
  43. package/lib/env/index.js +165 -165
  44. package/lib/env/presets.js +1 -0
  45. package/lib/env/requires.js +120 -49
  46. package/lib/index.js +1 -0
  47. package/lib/log/format/kibana.js +2 -2
  48. package/lib/ql/SELECT.js +10 -0
  49. package/lib/ql/parse.js +1 -0
  50. package/lib/req/cds-context.js +4 -1
  51. package/lib/req/context.js +50 -56
  52. package/lib/req/event.js +1 -6
  53. package/lib/req/locale.js +6 -5
  54. package/lib/req/request.js +2 -0
  55. package/lib/req/user.js +7 -5
  56. package/lib/serve/Service-api.js +10 -7
  57. package/lib/serve/Service-dispatch.js +9 -11
  58. package/lib/serve/Service-methods.js +30 -41
  59. package/lib/serve/Transaction.js +10 -7
  60. package/lib/serve/adapters.js +7 -5
  61. package/lib/serve/index.js +24 -12
  62. package/lib/utils/data.js +1 -1
  63. package/lib/utils/index.js +27 -30
  64. package/lib/utils/resources/index.js +101 -0
  65. package/lib/utils/resources/tar.js +71 -0
  66. package/lib/utils/resources/utils.js +11 -0
  67. package/libx/_runtime/audit/Service.js +36 -39
  68. package/libx/_runtime/audit/generic/personal/access.js +3 -4
  69. package/libx/_runtime/audit/generic/personal/modification.js +3 -4
  70. package/libx/_runtime/audit/utils/v2.js +1 -2
  71. package/libx/_runtime/auth/index.js +126 -84
  72. package/libx/_runtime/auth/strategies/JWT.js +12 -19
  73. package/libx/_runtime/auth/strategies/dummy.js +1 -5
  74. package/libx/_runtime/auth/strategies/dwc.js +11 -9
  75. package/libx/_runtime/auth/strategies/mock.js +0 -4
  76. package/libx/_runtime/auth/strategies/{utils/xssec.js → xssecUtils.js} +7 -4
  77. package/libx/_runtime/auth/strategies/xsuaa.js +12 -19
  78. package/libx/_runtime/auth/utils.js +22 -1
  79. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +104 -98
  80. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +2 -2
  81. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
  82. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -1
  83. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/language.js +2 -8
  84. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +4 -29
  85. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +2 -1
  86. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +3 -2
  87. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +2 -2
  88. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +4 -6
  89. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +24 -21
  90. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +8 -2
  91. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +2 -0
  92. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/DispatcherCommand.js +2 -6
  93. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -12
  94. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +33 -9
  95. package/libx/_runtime/cds-services/adapter/odata-v4/utils/dispatcherUtils.js +51 -0
  96. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +2 -2
  97. package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +10 -3
  98. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +9 -11
  99. package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +6 -3
  100. package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +4 -2
  101. package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/utils.js +1 -1
  102. package/libx/_runtime/cds-services/adapter/rest/utils/binary.js +1 -1
  103. package/libx/_runtime/cds-services/adapter/rest/utils/key-value-utils.js +2 -3
  104. package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +6 -4
  105. package/libx/_runtime/cds-services/adapter/rest/utils/result.js +1 -0
  106. package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +8 -5
  107. package/libx/_runtime/cds-services/services/Service.js +40 -0
  108. package/libx/_runtime/cds-services/services/utils/columns.js +4 -3
  109. package/libx/_runtime/cds-services/services/utils/compareJson.js +4 -4
  110. package/libx/_runtime/cds-services/services/utils/differ.js +3 -3
  111. package/libx/_runtime/cds-services/services/utils/handlerUtils.js +4 -4
  112. package/libx/_runtime/cds-services/services/utils/restrictions.js +78 -0
  113. package/libx/_runtime/cds-services/util/assert.js +20 -14
  114. package/libx/_runtime/cds.js +9 -1
  115. package/libx/_runtime/common/aspects/any.js +5 -0
  116. package/libx/_runtime/common/aspects/entity.js +25 -7
  117. package/libx/_runtime/common/aspects/utils.js +2 -2
  118. package/libx/_runtime/common/composition/data.js +6 -0
  119. package/libx/_runtime/common/composition/insert.js +3 -2
  120. package/libx/_runtime/common/composition/tree.js +4 -10
  121. package/libx/_runtime/common/composition/update.js +4 -4
  122. package/libx/_runtime/common/constants/draft.js +29 -26
  123. package/libx/_runtime/common/error/constants.js +2 -2
  124. package/libx/_runtime/common/error/frontend.js +7 -15
  125. package/libx/_runtime/common/generic/auth/capabilities.js +59 -0
  126. package/libx/_runtime/common/generic/auth/constants.js +20 -0
  127. package/libx/_runtime/common/generic/auth/expand.js +54 -0
  128. package/libx/_runtime/common/generic/auth/index.js +32 -0
  129. package/libx/_runtime/common/generic/auth/insertOnly.js +15 -0
  130. package/libx/_runtime/common/generic/auth/readOnly.js +26 -0
  131. package/libx/_runtime/common/generic/auth/requires.js +34 -0
  132. package/libx/_runtime/common/generic/auth/restrict.js +296 -0
  133. package/libx/_runtime/common/generic/auth/utils.js +213 -0
  134. package/libx/_runtime/common/generic/crud.js +14 -10
  135. package/libx/_runtime/common/generic/etag.js +1 -1
  136. package/libx/_runtime/common/generic/input.js +35 -35
  137. package/libx/_runtime/common/generic/sorting.js +2 -3
  138. package/libx/_runtime/common/generic/temporal.js +2 -2
  139. package/libx/_runtime/common/i18n/messages.properties +1 -1
  140. package/libx/_runtime/common/toggles/handler.js +21 -0
  141. package/libx/_runtime/common/utils/copy.js +10 -1
  142. package/libx/_runtime/common/utils/cqn2cqn4sql.js +100 -29
  143. package/libx/_runtime/common/utils/csn.js +63 -1
  144. package/libx/_runtime/common/utils/dollar.js +10 -1
  145. package/libx/_runtime/common/utils/draft.js +46 -7
  146. package/libx/_runtime/common/utils/entityFromCqn.js +13 -9
  147. package/libx/_runtime/common/utils/extensibilityUtils.js +18 -0
  148. package/libx/_runtime/common/utils/foreignKeyPropagations.js +88 -104
  149. package/libx/_runtime/common/utils/generateOnCond.js +5 -2
  150. package/libx/_runtime/common/utils/quotingStyles.js +2 -0
  151. package/libx/_runtime/common/utils/resolveStructured.js +25 -9
  152. package/libx/_runtime/common/utils/resolveView.js +4 -1
  153. package/libx/_runtime/common/utils/rewriteAsterisks.js +3 -16
  154. package/libx/_runtime/common/utils/structured.js +33 -37
  155. package/libx/_runtime/common/utils/template.js +17 -8
  156. package/libx/_runtime/common/utils/templateProcessor.js +28 -28
  157. package/libx/_runtime/db/data-conversion/post-processing.js +118 -412
  158. package/libx/_runtime/db/expand/expandCQNToJoin.js +45 -41
  159. package/libx/_runtime/db/expand/rawToExpanded.js +29 -8
  160. package/libx/_runtime/db/generic/index.js +1 -3
  161. package/libx/_runtime/db/generic/input.js +5 -10
  162. package/libx/_runtime/db/generic/rewrite.js +5 -2
  163. package/libx/_runtime/db/generic/structured.js +2 -2
  164. package/libx/_runtime/db/query/delete.js +2 -2
  165. package/libx/_runtime/db/query/insert.js +1 -1
  166. package/libx/_runtime/db/query/update.js +9 -14
  167. package/libx/_runtime/db/sql-builder/CreateBuilder.js +4 -3
  168. package/libx/_runtime/db/sql-builder/FunctionBuilder.js +8 -8
  169. package/libx/_runtime/db/sql-builder/InsertBuilder.js +14 -1
  170. package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -2
  171. package/libx/_runtime/db/sql-builder/dataTypes.js +3 -3
  172. package/libx/_runtime/db/utils/columns.js +3 -3
  173. package/libx/_runtime/db/utils/normalizeTimeData.js +2 -2
  174. package/libx/_runtime/db/utils/propagateForeignKeys.js +6 -2
  175. package/libx/_runtime/extensibility/mps/index.js +5 -0
  176. package/libx/_runtime/extensibility/mps/service.js +111 -0
  177. package/libx/_runtime/extensibility/mps/tar.js +42 -0
  178. package/libx/_runtime/extensibility/mps/utils.js +11 -0
  179. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformREAD.js +0 -0
  180. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformRESULT.js +17 -5
  181. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformWRITE.js +1 -0
  182. package/libx/_runtime/extensibility/uiflex/index.js +54 -0
  183. package/libx/_runtime/extensibility/uiflex/service.js +276 -0
  184. package/libx/_runtime/{fiori → extensibility}/uiflex/utils.js +22 -7
  185. package/libx/_runtime/fiori/generic/activate.js +2 -2
  186. package/libx/_runtime/fiori/generic/before.js +4 -4
  187. package/libx/_runtime/fiori/generic/new.js +3 -3
  188. package/libx/_runtime/fiori/generic/patch.js +1 -1
  189. package/libx/_runtime/fiori/generic/read.js +58 -66
  190. package/libx/_runtime/fiori/generic/readOverDraft.js +71 -16
  191. package/libx/_runtime/fiori/utils/handler.js +6 -13
  192. package/libx/_runtime/fiori/utils/where.js +6 -5
  193. package/libx/_runtime/hana/Service.js +4 -10
  194. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +1 -1
  195. package/libx/_runtime/hana/driver.js +2 -2
  196. package/libx/_runtime/hana/execute.js +27 -74
  197. package/libx/_runtime/hana/pool.js +1 -1
  198. package/libx/_runtime/hana/streaming.js +2 -1
  199. package/libx/_runtime/index.js +6 -6
  200. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +5 -21
  201. package/libx/_runtime/messaging/Outbox.js +2 -2
  202. package/libx/_runtime/messaging/common-utils/AMQPClient.js +4 -14
  203. package/libx/_runtime/messaging/common-utils/connections.js +5 -7
  204. package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +30 -0
  205. package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -1
  206. package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +36 -30
  207. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -12
  208. package/libx/_runtime/messaging/enterprise-messaging.js +8 -8
  209. package/libx/_runtime/messaging/file-based.js +5 -5
  210. package/libx/_runtime/messaging/message-queuing.js +14 -12
  211. package/libx/_runtime/messaging/outbox/utils.js +18 -19
  212. package/libx/_runtime/messaging/redis-messaging.js +91 -0
  213. package/libx/_runtime/messaging/service.js +8 -6
  214. package/libx/_runtime/remote/Service.js +44 -8
  215. package/libx/_runtime/remote/utils/client.js +24 -19
  216. package/libx/_runtime/remote/utils/data.js +11 -11
  217. package/libx/_runtime/sqlite/Service.js +6 -9
  218. package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +5 -2
  219. package/libx/_runtime/types/api.js +10 -2
  220. package/libx/common/utils/ucsn.js +109 -0
  221. package/libx/gql/resolvers/crud/update.js +5 -0
  222. package/libx/gql/resolvers/parse/ast2cqn/columns.js +3 -1
  223. package/libx/gql/schema/typeDefMap.js +2 -2
  224. package/libx/odata/afterburner.js +110 -16
  225. package/libx/odata/cqn2odata.js +24 -27
  226. package/libx/odata/grammar.pegjs +9 -1
  227. package/libx/odata/parseToCqn.js +39 -0
  228. package/libx/odata/parser.js +1 -1
  229. package/libx/rest/RestAdapter.js +9 -1
  230. package/libx/rest/middleware/input.js +54 -0
  231. package/libx/rest/middleware/operation.js +14 -1
  232. package/libx/rest/middleware/parse.js +11 -7
  233. package/package.json +2 -2
  234. package/server.js +34 -19
  235. package/srv/audit-log.cds +2 -2
  236. package/srv/flex.cds +8 -2
  237. package/srv/flex.js +1 -1
  238. package/srv/mps.cds +23 -0
  239. package/srv/mps.js +1 -0
  240. package/libx/_runtime/auth/strategies/utils/uaa.js +0 -21
  241. package/libx/_runtime/common/generic/auth.js +0 -874
  242. package/libx/_runtime/common/toggles/alpha.js +0 -43
  243. package/libx/_runtime/db/generic/arrayed.js +0 -33
  244. package/libx/_runtime/fiori/uiflex/index.js +0 -35
  245. package/libx/_runtime/fiori/uiflex/service.js +0 -150
  246. package/libx/rest/utils/data.js +0 -60
@@ -2,6 +2,7 @@ const { resolve, join, sep } = require('path')
2
2
  const { readdirSync } = require('fs')
3
3
  const suffixes = [ '.csn', '.cds', sep+'index.csn', sep+'index.cds', sep+'csn.json' ]
4
4
 
5
+
5
6
  /**
6
7
  * Resolves given model references to an array of absolute filenames.
7
8
  * For the model references, all these are accepted:
@@ -16,18 +17,20 @@ module.exports = exports = function cds_resolve (model, o={}) { // NOSONAR
16
17
 
17
18
  if (!model || model === '--') return
18
19
  if (model._resolved) return model
19
- if (model === '*') return exports.cache['*'] || _resolve_all(this,o)
20
- if (Array.isArray(model)) return _resolved (
21
- [... new Set(model)] .reduce ((prev,next) => prev.concat (this.resolve(next,o)||[]), [])
22
- )
20
+ if (model === '*') return _resolve_all(o,this)
21
+ if (Array.isArray(model)) {
22
+ const resolved = [... new Set(model)] .reduce ((prev,next) => prev.concat (this.resolve(next,o)||[]), [])
23
+ return o.dry || o === false ? resolved : _resolved (resolved)
24
+ }
25
+ if (model.endsWith('/*')) return _resolve_subdirs_in(model,o,this)
23
26
 
24
- const cwd = o.root || global.cds && global.cds.root, local = resolve (cwd,model)
27
+ const cwd = o.root || this.root, local = resolve (cwd,model)
25
28
  const context = _paths(cwd), {cached} = context
26
29
  let id = model.startsWith('.') ? local : model
27
- if (id in cached && !o.skipModelCache) return cached[id]
30
+ if (id in cached) return cached[id]
28
31
 
29
32
  // expand @sap/cds by cds.home
30
- if (id.startsWith('@sap/cds/')) id = global.cds.home + id.slice(8)
33
+ if (id.startsWith('@sap/cds/')) id = this.home + id.slice(8)
31
34
 
32
35
  // fetch file with .cds/.csn suffix as is
33
36
  if (/\.(csn|cds)$/.test(id)) try {
@@ -58,27 +61,41 @@ module.exports = exports = function cds_resolve (model, o={}) { // NOSONAR
58
61
 
59
62
  }
60
63
 
64
+
61
65
  exports.cache = {}
62
66
 
63
67
 
68
+ const _required = cds => Object.values(cds.env.requires) .map (r => r.model) .filter(x=>x)
64
69
  const _resolve = require('module')._resolveFilename
65
70
 
66
- function _resolve_all (cds,o) {
67
- const {roots,requires} = cds.env, {cache} = exports
68
- const required = Object.values(requires) .map (r => r.model) .filter(x=>x)
69
- if (o.dry || o === false) return [ ...roots, ...required ]
70
- cache['*'] = [] // important to avoid endless recursion on '*'
71
- const resolved = cds.resolve (roots,o) || []
72
- if (!(resolved.length === 1 && resolved[0].endsWith('csn.json')))
73
- resolved.push (...cds.resolve (required,o)||[])
74
- return cache['*'] = _resolved ([ ...new Set (resolved) ])
71
+ function _resolve_all (o,cds) {
72
+ const {roots} = cds.env; if (o.dry || o === false) return [ ...roots, ..._required(cds) ]
73
+ const cached = exports.cache['*']; if (cached) return cached
74
+ exports.cache['*'] = [] // important to avoid endless recursion on '*'
75
+ const sources = cds.resolve (roots,o) || []
76
+ if (!(sources.length === 1 && sources[0].endsWith('csn.json'))) // REVISIT: why is that? -> pre-compiled gen/csn.json?
77
+ sources.push (...cds.resolve (_required(cds),o)||[])
78
+ return exports.cache['*'] = _resolved (sources)
79
+ }
80
+
81
+ function _resolve_subdirs_in (pattern='fts/*',o,cds) {
82
+ const cached = exports.cache[pattern]; if (cached && !o.dry && o !== false) return cached
83
+ const folder = pattern.slice(0,-2), dir = resolve (cds.root, folder)
84
+ try {
85
+ const dirs = readdirSync(dir) .map (e => folder+'/'+e+'/') .filter (cds.utils.isdir)
86
+ if (o.dry || o === false) return dirs
87
+ return exports.cache[pattern] = cds.resolve (dirs,o) || undefined
88
+ } catch(e) {
89
+ if (e.code === 'ENOENT')
90
+ return exports.cache[pattern] = undefined
91
+ }
75
92
  }
76
93
 
77
94
  function _paths (dir) {
78
- const {cache} = exports; if (dir in cache) return cache[dir]
95
+ const cached = exports.cache[dir]; if (cached) return cached
79
96
  const a = dir.split(sep), n = a.length, nm = sep+'node_modules'
80
97
  const paths = [ dir, ...a.map ((_,i,a)=> a.slice(0,n-i).join(sep)+nm) ]
81
- return cache[dir] = { paths, cached:{} }
98
+ return exports.cache[dir] = { paths, cached:{} }
82
99
  }
83
100
 
84
101
  function _resolved (array) {
@@ -8,12 +8,14 @@ module.exports = (csn,o={}) => JSON.stringify (csn, (_,v) => {
8
8
 
9
9
  else if (v.grant && v.where) try {
10
10
  // Add a parsed _where clause for @restrict.{grant,where} annotations
11
+ // Note: This has moved to cds.compile.for.java meanwhile, but is kept
12
+ // here for compatibility, at least temporarily.
11
13
  return {...v, _where: JSON.stringify (cds.parse.xpr(v.where)) }
12
14
  } catch(e){/* ignored */}
13
15
 
14
16
  else if (v.kind === "service" && !v['@source'] && v.$location && v.$location.file) {
15
17
  // Preserve original sources for services so we can use them for finding
16
- // sibling implementation filed when reloaded from csn.json.
18
+ // sibling implementation files when reloaded from csn.json.
17
19
  const file = (o.cwd !== o.src) ? path.relative(o.src, path.join(o.cwd, v.$location.file)) : v.$location.file
18
20
  return { '@source': file.replace(/\\/g,'/'), ...v }
19
21
  }
@@ -1,4 +1,4 @@
1
- const cds = require ('../..'), { unfold_ddl: cds_localized } = cds.compile._localized
1
+ const cds = require ('../..')
2
2
  const cdsc = require ('../cdsc')
3
3
 
4
4
 
@@ -6,7 +6,7 @@ function cds_compile_to_sql (csn,_o) {
6
6
  csn = _extended(cds.minify(csn))
7
7
  const o = cdsc._options.for.sql(_o) //> used twice below...
8
8
  const all = cdsc.to.sql(csn,o) .map (each => each.replace(/^-- .+\n/,'')) //> strip comments
9
- const sql = cds_localized(all, csn, o)
9
+ const sql = cds.compile._localized.unfold_ddl(all, csn, o)
10
10
  if (o.as === 'str') return `\n${sql.join('\n\n')}\n`
11
11
  return sql
12
12
  }
@@ -74,8 +74,10 @@ module.exports = (model, options={}) => {
74
74
  if (file) {
75
75
  const yaml = cds.load.yaml(file)
76
76
  for (let {cds} of Array.isArray(yaml) ? yaml : [yaml]) {
77
- if (cds && cds['odata-v4.endpoint.path']) return cds['odata-v4.endpoint.path']
78
- if (cds && cds['odata-v2.endpoint.path']) return cds['odata-v2.endpoint.path']
77
+ if (!cds) continue
78
+ return cds['odataV4.endpoint.path'] || cds['odataV2.endpoint.path'] // https://cap.cloud.sap/docs/java/application-services#configure-base-path
79
+ || cds['odata-v4.endpoint.path'] || cds['odata-v2.endpoint.path'] // older/intermediate config, keep for backward compatibility
80
+
79
81
  }
80
82
  return 'odata/v4/'
81
83
  }
@@ -38,7 +38,7 @@ connect.to = async (datasource, options) => {
38
38
  // check if required service definition exists
39
39
  const required = cds.requires[datasource]
40
40
  if (required && required.model && datasource !== 'db' && !m.definitions[required.service||datasource]) {
41
- LOG._error && LOG.error(`No service definition found for '${required.service || datasource}', as required by 'cds.requires.${datasource}':`, required)
41
+ LOG.error(`No service definition found for '${required.service || datasource}', as required by 'cds.requires.${datasource}':`, required)
42
42
  throw new Error (`No service definition found for '${required.service || datasource}'`)
43
43
  }
44
44
  // construct new service instance
@@ -34,23 +34,24 @@ class Association extends type {
34
34
  return !c || c.max === 1 || (!c.max && !c.targetMax) || c.targetMax === 1
35
35
  }
36
36
 
37
- set _elements(k) { this.set('_elements', k) }
38
- get _elements() {
39
- const { keys } = this
40
- if (!keys) return (this._elements = undefined)
41
- const fks = {},{ elements } = this._target
42
- for (let k of keys) fks[k.as || k.ref[0]] = elements[k.ref[0]]
43
- return (this._elements = fks)
37
+ set foreignKeys(k) { this.set('foreignKeys', k) }
38
+ get foreignKeys() {
39
+ const keys = this.keys; if (!keys) return this.foreignKeys = undefined
40
+ const foreignKeys = {}
41
+ for (const k of keys) {
42
+ const el = k.ref.reduce((target,n)=> target.elements[n], this._target)
43
+ const as = k.as || k.ref[k.ref.length-1]
44
+ foreignKeys [as] = el
45
+ }
46
+ return this.foreignKeys = foreignKeys
44
47
  }
45
48
 
46
- set _keys(k) { this.set('keys', k) }
47
- get _keys() {
48
- if (this.keys) return (this._keys = this.keys)
49
- if (this.on || this.is2many || !this._target) return (this._keys = undefined)
50
- const keys = [],
51
- tks = this._target.keys
49
+ set keys(k) { super.keys = k }
50
+ get keys() {
51
+ if (this.on || this.is2many || !this._target) return this._keys = undefined
52
+ const keys=[], tks = this._target.keys
52
53
  for (let k in tks) keys.push({ ref: [tks[k].name] })
53
- return (this._keys = keys)
54
+ return this.keys = keys
54
55
  }
55
56
  }
56
57
 
package/lib/core/index.js CHANGED
@@ -8,8 +8,9 @@ const classes = Object.assign (
8
8
  require('./entities'),
9
9
  )
10
10
 
11
- // Type system roots -> can be used with instanceof
12
- const roots = _bootstrap ({
11
+
12
+ /** Type system roots -> can be used with instanceof */
13
+ const roots = _roots ({
13
14
  context: {},
14
15
  type: {},
15
16
  scalar: {type:'type'},
@@ -27,9 +28,28 @@ const roots = _bootstrap ({
27
28
  service: {type:'context'},
28
29
  })
29
30
 
30
- // Adding common recommended types
31
- const common = { __proto__:roots,
32
- UUID: {type:'string',length:36},
31
+ /**
32
+ * Turns the given CSN definitions into linked definitions with classes.
33
+ * @type <T>(csn:T) => T
34
+ */
35
+ function _roots (defs) {
36
+ const linked = { any: classes.any.prototype }
37
+ for (const t in defs) {
38
+ if (t in classes) {
39
+ linked[t] = classes[t].prototype
40
+ continue
41
+ }
42
+ const c = class extends classes[defs[t].type || 'any'] {}
43
+ linked[t] = Object.defineProperty (c.prototype, 'name', {value:t})
44
+ classes[t] = Object.defineProperty (c, 'name', {value:t})
45
+ }
46
+ return linked
47
+ }
48
+
49
+
50
+ /** Construct builtin.types as dictionary of all roots and common types */
51
+ const types = _common ({ __proto__: roots,
52
+ UUID: {type:'string',length:36,isUUID:true},
33
53
  Boolean: {type:'boolean'},
34
54
  Integer: {type:'number'},
35
55
  Integer16: {type:'Integer'},
@@ -47,41 +67,24 @@ const common = { __proto__:roots,
47
67
  Binary: {type:'string'},
48
68
  LargeString: {type:'string'},
49
69
  LargeBinary: {type:'string'},
50
- }
70
+ })
51
71
 
52
72
  /**
53
- * Construct builtin.types as dictionary of all roots and common types
54
- * @type { typeof roots & typeof common }
73
+ * Link all definitions, essentially by: d.__proto__ = resolved (d.type),
74
+ * and prefixes all common types with a namespace 'cds'.
75
+ * @type <T>(csn:T) => T & roots
55
76
  */
56
- const types = { __proto__:common }
57
-
58
- // Link all definitions, essentially by: d.__proto__ = resolved (d.type)
59
- for (let [name,d] of Object.entries(common)) {
60
- common[name] = Object.defineProperty({ ...d, __proto__: types[d.type] }, 'name', {value:name})
77
+ function _common (defs) {
78
+ const prefixed = {__proto__:defs}
79
+ for (let [name,d] of Object.entries(defs)) {
80
+ defs[name] = Object.defineProperty({ ...d, __proto__: defs[d.type] }, 'name', {value:name})
81
+ Object.defineProperty (prefixed['cds.'+name] = defs[name], '_type', {value:'cds.'+name})
82
+ }
83
+ for (let name of ['Association','Composition']) {
84
+ Object.defineProperty (prefixed['cds.'+name] = defs[name], '_type', {value:'cds.'+name})
85
+ }
86
+ return prefixed
61
87
  }
62
88
 
63
- // Prefix all common types with a namespace 'cds'
64
- for (let t of Object.keys(common)) types['cds.'+t] = types[t]
65
- types['cds.Association'] = types.Association
66
- types['cds.Composition'] = types.Composition
67
89
 
68
90
  module.exports = { types, classes }
69
-
70
-
71
- /**
72
- * Turns the given CSN definitions into linked definitions.
73
- * @type <T>(csn:T) => T
74
- */
75
- function _bootstrap (csn) {
76
- const defs = { any: classes.any.prototype }
77
- for (const t in csn) {
78
- if (t in classes) {
79
- defs[t] = classes[t].prototype
80
- continue
81
- }
82
- const c = class extends classes[csn[t].type || 'any'] {}
83
- defs[t] = Object.defineProperty(c.prototype, 'name', { value: t })
84
- classes[t] = Object.defineProperty(c, 'name', { value: t })
85
- }
86
- return defs
87
- }
@@ -95,7 +95,7 @@ class LinkedCSN extends any {
95
95
  }
96
96
 
97
97
 
98
- const _unresolved = (x,unknown=any) => ({name:x, __proto__:unknown.prototype, _unresolved:true})
98
+ const _unresolved = (x,unknown=any) => ({ name:x, __proto__:unknown.prototype, _unresolved:true })
99
99
  const _builtin = x => types[x] || typeof x === 'string' && x.startsWith('cds.hana.') && any.prototype
100
100
  const _infer = require('./infer'), _not_inferred = _unresolved('<query>',entity)
101
101
  const _set = (o,p,v) => Object.defineProperty (o,p,{value:v,enumerable:false,configurable:1,writable:1})
@@ -107,4 +107,6 @@ const _is = x => {
107
107
  }
108
108
 
109
109
  /** @returns {LinkedCSN} */
110
- module.exports = x => x instanceof any ? x : new LinkedCSN(x)
110
+ module.exports = x => x instanceof any ? x : new LinkedCSN (
111
+ typeof x === 'string' ? global.cds.parse(x) : x
112
+ )
package/lib/deploy.js CHANGED
@@ -1,6 +1,6 @@
1
- const cds = require('./index'), { local } = cds.utils
1
+ const cds = require('./index'), { local, inspect } = cds.utils
2
2
  const DEBUG = cds.debug('deploy')
3
- let LOG
3
+ /* eslint-disable no-console */
4
4
 
5
5
  /**
6
6
  * Implementation of `cds.deploy` common to all databases.
@@ -11,8 +11,8 @@ let LOG
11
11
  exports = module.exports = function cds_deploy (model,options) { return {
12
12
 
13
13
  /** @param {cds.Service} db */
14
- async to (db, o=options||{}) { // NOSONAR
15
- LOG = cds.log('deploy')
14
+ async to (db, o = options || cds.options || {}) {
15
+ const LOG = o.silent || !cds.log('deploy')._info ? ()=>{} : console.log
16
16
 
17
17
  if (model && !model.definitions) {
18
18
  model = await cds.load (model) .then (cds.minify)
@@ -31,25 +31,22 @@ exports = module.exports = function cds_deploy (model,options) { return {
31
31
  if (!cds.db) cds.db = cds.services.db = db
32
32
  if (!db.model) db.model = model
33
33
 
34
- if (o.ddl !== false) {
35
- const any = await db.deploy (model,o) //> CREATE TABLE, ...
36
- if (!any) return
37
- }
34
+ // create tables & views...
35
+ const any = await exports.create (db,model,o)
36
+ if (!any) return db
38
37
 
39
38
  // fill in initial data...
40
- const SILENT = o.silent || !LOG._info
41
- await init_from_code (db,model,SILENT)
42
- await init_from_csv (db,model,SILENT)
43
- await init_from_json (db,model,SILENT)
44
-
45
- const {credentials} = db.options, file = credentials && (credentials.database || credentials.url)
46
- if (!SILENT) {
47
- if (file !== ':memory:') console.log (`/> successfully deployed to ./${file}\n`)
48
- else console.log (`/> successfully deployed to sqlite in-memory db\n`)
49
- }
50
-
39
+ await exports.init (db,model, file => LOG(
40
+ `\x1b[2m > init from ${local(file)} \x1b[0m`
41
+ ))
42
+
43
+ // done
44
+ const {credentials:c} = db.options, file = c && (c.database || c.url)
45
+ if (file !== ':memory:') LOG (`/> successfully deployed to ./${file}\n`)
46
+ else LOG (`/> successfully deployed to sqlite in-memory db\n`)
51
47
  return db
52
48
  },
49
+
53
50
  // continue to support cds.deploy() as well...
54
51
  then(n,e) { return this.to (cds.db||'db') .then (n,e) },
55
52
  catch(e) { return this.to (cds.db||'db') .catch (e) },
@@ -57,9 +54,10 @@ exports = module.exports = function cds_deploy (model,options) { return {
57
54
 
58
55
 
59
56
 
60
-
61
- const { fs, path, isdir, isfile, read } = cds.utils
57
+ const { fs, path, read } = cds.utils
62
58
  const { readdir } = fs.promises
59
+ const isdir = (..._) => fs.isdir(path.join(..._))
60
+ const isfile = (..._) => fs.isfile(path.join(..._))
63
61
 
64
62
  exports.include_external_entities_in = function (model) {
65
63
  if (model._mocked) return model; else Object.defineProperty(model,'_mocked',{value:true})
@@ -101,125 +99,114 @@ exports.exclude_external_entities_in = function (csn) { // NOSONAR
101
99
  }
102
100
 
103
101
 
104
- function init_from_code (db, csn, SILENT) {
105
-
106
- if (!csn.$sources) return
107
- const folders = new Set([ path.resolve(cds.root,'db'), ...csn.$sources.map (path.dirname)])
108
- const inits = []
109
-
110
- for (let each of folders) {
111
- let file;
112
- if (process.env.CDS_TYPESCRIPT === 'true') file = isfile (path.resolve(each,'init.ts'));
113
- if (!file) file = isfile (path.resolve(each,'init.js'));
114
- if (!file) continue
115
- SILENT || console.log (` > initializing database from ${local(file)}`) // eslint-disable-line
116
- let init = require(file); if (!init) continue
117
- if (!init.then && typeof init === 'function') inits.push (init(db))
118
- else inits.push (init)
119
- }
120
-
121
- return Promise.all (inits)
102
+ exports.create = async function (db, csn=db.model, o) {
103
+ if (!db.deploy || o.lean) {
104
+ const creates = cds.compile.to.sql (csn,o); if (!creates || creates.length === 0) return
105
+ const drops = creates.map (each => {
106
+ let [, kind, entity] = each.match(/^CREATE (TABLE|VIEW) "?([^\s"(]+)/im) || []
107
+ return `DROP ${kind} IF EXISTS ${entity};`
108
+ }).reverse()
109
+ if (o.dry) {
110
+ console.log(); for (let each of drops) console.log(each)
111
+ console.log(); for (let each of creates) console.log(each,'\n')
112
+ return
113
+ } else await db.tx (async tx => {
114
+ await tx.run(drops)
115
+ await tx.run(creates)
116
+ return true
117
+ })
118
+ } else return db.deploy (csn,o)
122
119
  }
123
120
 
124
- function init_from_csv (db, csn, SILENT) {
125
- return init_from_ (['data','csv'], _csvs, db, csn, SILENT, (entity, src) => {
126
- let [ cols, ...rows ] = cds.parse.csv (src)
127
- return rows && rows[0] && INSERT.into (entity) .columns (cols) .rows (rows)
128
- });
129
121
 
130
- function _csvs (filename,_,allFiles) {
131
- if (filename[0] === '-' || !filename.endsWith ('.csv')) return false
132
- if (prefer_translated_texts(filename, allFiles)) return false
133
- return true
122
+ exports.init = (db, csn=db.model, log=()=>{}) => db.tx (async tx => {
123
+ cds.context = tx //> registers tx as root transaction
124
+ const resources = await exports.resources(csn), inits=[], err = new Error
125
+ for (let [file,e] of Object.entries(resources)) {
126
+ if (e === '*') { // init.js/ts
127
+ const x = require(file); if (!x) continue
128
+ log (file)
129
+ inits.push (!x.then && typeof x === 'function' ? x(db) : x)
130
+ } else { // from .csv or .json
131
+ const INSERT_into = _from_csv_or_json [path.extname(file)]
132
+ const src = await read(file,'utf8'); if (!src) continue
133
+ const q = INSERT_into (e,src); if (!q) continue
134
+ log (file,e)
135
+ inits.push (tx.run(q) .catch (e => { throw Object.assign (e,{
136
+ message: 'in cds.deploy(): ' + e.message +'\n'+ inspect(q),
137
+ })}))
138
+ }
134
139
  }
135
- }
136
-
137
- function init_from_json (db, csn, SILENT) {
138
- return init_from_ (['data'], _jsons, db, csn, SILENT, (entity, src) => {
139
- let json = JSON.parse (src)
140
- return json[0] && INSERT.into (entity) .entries (json)
141
- });
142
-
143
- function _jsons (filename,_,allFiles) {
144
- if (filename[0] === '-' || !filename.endsWith ('.json')) return false
145
- if (prefer_translated_texts(filename, allFiles)) return false
146
- return true
140
+ await Promise.all (inits)
141
+ })
142
+
143
+
144
+ exports.resources = async function (csn) {
145
+ if (!csn || !csn.definitions) csn = await cds.load (csn||'*') .then (cds.minify)
146
+ const folders = await exports.resources.folders(csn)
147
+ const found={}, ts = process.env.CDS_TYPESCRIPT
148
+ for (let folder of folders) {
149
+ // fetching init.js files
150
+ const init_js = ts && isfile(folder,'init.ts') || isfile(folder,'init.js')
151
+ if (init_js) found[init_js] = '*'
152
+ // fetching .csv and .json files
153
+ for (let each of ['data','csv']) {
154
+ const subdir = isdir(folder,each); if (!subdir) continue
155
+ const files = await readdir (subdir)
156
+ for (let fx of files) {
157
+ if (fx[0] === '-') continue
158
+ const ext = path.extname(fx); if (ext in _from_csv_or_json) {
159
+ const f = fx.slice(0,-ext.length)
160
+ if (/[._]texts$/.test(f) && files.some(g => g.startsWith(f+'_'))) {
161
+ // ignores 'Books_texts.csv/json' if there is any 'Books_texts_LANG.csv/json'
162
+ DEBUG && DEBUG (`ignoring '${fx}' in favor of translated ones`); continue
163
+ }
164
+ const e = _entity4(f,csn); if (_skip(e)) continue
165
+ found[path.join(subdir,fx)] = e.name
166
+ }
167
+ }
168
+ }
147
169
  }
170
+ return found
148
171
  }
149
172
 
150
- /**
151
- * ignores 'Books_texts.csv/json' if there is any 'Books_texts_LANG.csv/json'
152
- */
153
- function prefer_translated_texts (file, all) {
154
- if (/[._]texts\.(json|csv)$/.test (file)) {
155
- const pattern = new RegExp('^'+ path.parse(file).name +'_')
156
- const translated = all.filter (f => pattern.test(f))
157
- if (translated.length > 0) {
158
- DEBUG && DEBUG (`ignoring '${file}' in favor of [${translated}]`) // eslint-disable-line
159
- return true
160
- }
161
- }
173
+
174
+ exports.resources.folders = async function (csn) {
175
+ if (!csn || !csn.definitions) csn = await cds.load (csn||'*') .then (cds.minify)
176
+ const folders = new Set (csn.$sources.map (path.dirname) .filter (f => f !== cds.home))
177
+ if (cds.env.folders.db) folders.add (path.resolve(cds.root, cds.env.folders.db))
178
+ if (cds.env.features.test_data) folders.add (path.resolve(cds.root,'test/'))
179
+ return folders
162
180
  }
163
181
 
164
- async function init_from_ (locations, filter, db, csn, SILENT, INSERT_into) { // NOSONAR
165
182
 
166
- const folders = new Set
167
- let roots = Object.values(cds.env.folders); if (cds.env.features.test_data) roots.push('test/')
168
- for (let root of roots) {
169
- for (let data of locations) {
170
- let each = path.resolve(root,data)
171
- if (isdir (each)) folders.add(each)
183
+ const _entity4 = (file,csn) => {
184
+ const name = file.replace(/-/g,'.')
185
+ const entity = csn.definitions [name]
186
+ if (!entity) {
187
+ if (/(.+)[._]texts_?/.test(name)) { // 'Books.texts', 'Books.texts_de'
188
+ const base = csn.definitions [RegExp.$1]
189
+ return base && _entity4 (base.elements.texts.target,csn)
172
190
  }
191
+ else return DEBUG && DEBUG (`warning: ${name} not in model`)
173
192
  }
174
- if (csn.$sources) for (let model of csn.$sources) {
175
- for (let data of locations) {
176
- let each = path.resolve(model,'..',data)
177
- if (isdir (each)) folders.add(each)
178
- }
193
+ // We also support insert into simple views if they have no projection
194
+ const p = entity.query && entity.query.SELECT || entity.projection
195
+ if (p && !p.columns && p.from.ref && p.from.ref.length === 1) {
196
+ if (csn.definitions [p.from.ref[0]]) return entity
179
197
  }
198
+ return entity.name ? entity : { name, __proto__:entity }
199
+ }
180
200
 
181
- if (folders.size === 0) return
182
-
183
- const {local} = cds.utils, inits = [], err = new Error
184
- await db.tx (async tx => {
185
- for (let folder of folders) {
186
- const files = await readdir (folder)
187
- for (let each of files.filter (filter)) {
188
- let name = each.replace(/-/g,'.').slice(0, -path.extname(each).length)
189
- let entity = _entity4 (name)
190
- if (!entity) { DEBUG && DEBUG (`warning: ${name} not in model`); continue }
191
- if (entity['@cds.persistence.skip'] === true) continue
192
- const file = path.join(folder,each)
193
- const src = await read (file,'utf8'); if (!src) continue
194
- const q = INSERT_into (entity,src)
195
- if (!q) { DEBUG && DEBUG (`skipping empty ${local(file)}`); continue }
196
- SILENT || console.log (`\x1b[2m > filling ${entity.name} from ${local(file)} \x1b[0m`) // eslint-disable-line
197
- inits.push (tx.run(q).catch(e=>{ const ex = new Error
198
- e.stack = e.message +'\n'+ require('util').inspect(q) + err.stack.slice(5)
199
- .replace (/deploy\.js:\d+:/, ex.stack.slice(5).match(/deploy\.js:\d+:/)[0])
200
- throw e
201
- }))
202
- }
203
- }
204
- await Promise.all(inits)
205
- })
206
-
207
- function _entity4 (name) {
208
- let entity = csn.definitions [name]
209
- if (!entity) {
210
- if (/(.+)[._]texts_?/.test (name)) { // 'Books.texts', 'Books.texts_de'
211
- const base = csn.definitions [RegExp.$1]
212
- return base && _entity4 (base.elements.texts.target)
213
- }
214
- else return
215
- }
216
- // We also support insert into simple views if they have no projection
217
- const p = entity.query && entity.query.SELECT || entity.projection
218
- if (p && !p.columns && p.from.ref && p.from.ref.length === 1) {
219
- if (csn.definitions [p.from.ref[0]]) return entity
220
- }
221
- return entity.name ? entity : { name, __proto__:entity }
222
- }
201
+ const INSERT_from_csv = (entity, csv) => {
202
+ let [ cols, ...rows ] = cds.parse.csv (csv)
203
+ if (rows.length > 0) return INSERT.into (entity) .columns (cols) .rows (rows)
204
+ }
223
205
 
206
+ const INSERT_from_json = (entity, json) => {
207
+ let records = JSON.parse (json)
208
+ if (records.length > 0) return INSERT.into (entity) .entries (records)
224
209
  }
225
- /* eslint no-console: off */
210
+
211
+ const _from_csv_or_json = { '.json': INSERT_from_json, '.csv': INSERT_from_csv, }
212
+ const _skip = e => !e || e['@cds.persistence.skip'] === true
@@ -6,6 +6,7 @@ module.exports = {
6
6
  requires: require('./requires'),
7
7
 
8
8
  features: {
9
+ folders: 'fts/*', // where to find feature toggles -> switch on by default when released
9
10
  cls: major > 12 || major == 12 && minor >= 18,
10
11
  live_reload: !production,
11
12
  fiori_preview: !production,