@sap/cds 6.1.2 → 6.2.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 (212) hide show
  1. package/CHANGELOG.md +92 -8
  2. package/apis/cds.d.ts +18 -6
  3. package/apis/connect.d.ts +1 -1
  4. package/apis/cqn.d.ts +1 -1
  5. package/apis/log.d.ts +23 -5
  6. package/apis/ql.d.ts +128 -61
  7. package/apis/services.d.ts +11 -0
  8. package/apis/test.d.ts +61 -0
  9. package/apis/utils.d.ts +15 -0
  10. package/app/fiori/preview.js +1 -0
  11. package/bin/build/buildTaskEngine.js +70 -22
  12. package/bin/build/buildTaskFactory.js +18 -11
  13. package/bin/build/buildTaskHandler.js +1 -1
  14. package/bin/build/buildTaskProviderFactory.js +3 -13
  15. package/bin/build/constants.js +0 -1
  16. package/bin/build/index.js +14 -6
  17. package/bin/build/provider/buildTaskHandlerEdmx.js +2 -3
  18. package/bin/build/provider/buildTaskHandlerFeatureToggles.js +2 -2
  19. package/bin/build/provider/buildTaskHandlerInternal.js +3 -6
  20. package/bin/build/provider/buildTaskProviderInternal.js +51 -39
  21. package/bin/build/provider/fiori/index.js +3 -3
  22. package/bin/build/provider/hana/2migration.js +1 -1
  23. package/bin/build/provider/hana/index.js +34 -27
  24. package/bin/build/provider/java/index.js +6 -7
  25. package/bin/build/provider/mtx/index.js +20 -18
  26. package/bin/build/provider/mtx/resourcesTarBuilder.js +8 -11
  27. package/bin/build/provider/mtx-sidecar/index.js +13 -17
  28. package/bin/build/provider/nodejs/index.js +8 -7
  29. package/bin/build/util.js +22 -4
  30. package/bin/cds.js +8 -4
  31. package/bin/deploy/to-hana/cfUtil.js +53 -18
  32. package/bin/mtx/in-cds.js +1 -0
  33. package/bin/serve.js +37 -30
  34. package/lib/auth/basic-auth.js +33 -0
  35. package/lib/auth/dummy-auth.js +7 -0
  36. package/lib/auth/ias-auth.js +2 -0
  37. package/lib/auth/index.js +31 -0
  38. package/lib/auth/jwt-auth.js +3 -0
  39. package/lib/auth/mocked-users.js +72 -0
  40. package/lib/auth/passport-basic.js +12 -0
  41. package/lib/auth/passport-digest.js +14 -0
  42. package/lib/auth/xsuaa-auth.js +3 -0
  43. package/lib/compile/cds-compile.js +3 -3
  44. package/lib/compile/to/cdl.js +5 -1
  45. package/lib/compile/to/edm.js +8 -0
  46. package/lib/compile/to/gql.js +1 -0
  47. package/lib/compile/to/json.js +30 -5
  48. package/lib/compile/to/sql.js +3 -1
  49. package/lib/core/index.js +5 -1
  50. package/lib/dbs/cds-deploy.js +36 -6
  51. package/lib/env/cds-env.js +15 -5
  52. package/lib/env/cds-requires.js +51 -58
  53. package/lib/env/defaults.js +1 -0
  54. package/lib/env/schemas/cds-package.json +4 -0
  55. package/lib/env/schemas/cds-rc.json +63 -77
  56. package/lib/i18n/localize.js +16 -5
  57. package/lib/index.js +9 -4
  58. package/lib/log/cds-error.js +4 -6
  59. package/lib/log/cds-log.js +89 -53
  60. package/lib/log/service/index.js +1 -0
  61. package/lib/ql/CREATE.js +2 -5
  62. package/lib/ql/DELETE.js +1 -1
  63. package/lib/ql/DROP.js +1 -3
  64. package/lib/ql/INSERT.js +3 -3
  65. package/lib/ql/Query.js +10 -23
  66. package/lib/ql/SELECT.js +1 -2
  67. package/lib/ql/UPDATE.js +2 -2
  68. package/lib/ql/Whereable.js +7 -15
  69. package/lib/ql/cds-ql.js +9 -3
  70. package/lib/req/cds-context.js +11 -3
  71. package/lib/req/context.js +29 -23
  72. package/lib/req/locale.js +9 -5
  73. package/lib/req/request.js +1 -0
  74. package/lib/req/user.js +2 -1
  75. package/lib/srv/cds-connect.js +1 -1
  76. package/lib/srv/cds-serve.js +21 -14
  77. package/lib/srv/middlewares/cds-context.js +29 -0
  78. package/lib/srv/middlewares/ctx-model.js +24 -0
  79. package/lib/srv/middlewares/errors.js +9 -0
  80. package/lib/srv/middlewares/index.js +22 -0
  81. package/lib/srv/middlewares/sap-statistics.js +13 -0
  82. package/lib/srv/middlewares/trace.js +102 -0
  83. package/lib/srv/protocols/_legacy.js +42 -0
  84. package/lib/srv/protocols/graphql.js +39 -0
  85. package/lib/srv/protocols/hcql.js +37 -0
  86. package/lib/srv/protocols/index.js +86 -0
  87. package/lib/srv/protocols/odata-v2-proxy.js +3767 -0
  88. package/lib/srv/protocols/odata-v2.js +26 -0
  89. package/lib/srv/protocols/odata-v4.js +16 -0
  90. package/lib/srv/protocols/rest.js +13 -0
  91. package/lib/srv/srv-api.js +5 -0
  92. package/lib/srv/srv-models.js +4 -6
  93. package/lib/utils/axios.js +3 -2
  94. package/lib/utils/cds-test.js +27 -21
  95. package/lib/utils/cds-utils.js +19 -20
  96. package/lib/utils/tar.js +175 -0
  97. package/libx/_runtime/audit/generic/personal/utils.js +18 -7
  98. package/libx/_runtime/audit/utils/v2.js +1 -0
  99. package/libx/_runtime/auth/index.js +4 -0
  100. package/libx/_runtime/auth/strategies/ias-auth.js +76 -0
  101. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +8 -3
  102. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +15 -4
  103. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -1
  104. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/orderByToCQN.js +1 -1
  105. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +1 -1
  106. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/ResourcePathParser.js +9 -0
  107. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriInfo.js +5 -1
  108. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +12 -0
  109. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/utils/BufferedWriter.js +6 -2
  110. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/RequestValidator.js +47 -7
  111. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
  112. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +0 -2
  113. package/libx/_runtime/cds-services/services/utils/compareJson.js +3 -1
  114. package/libx/_runtime/cds-services/util/assert.js +7 -0
  115. package/libx/_runtime/common/aspects/relation.js +1 -1
  116. package/libx/_runtime/common/composition/data.js +61 -15
  117. package/libx/_runtime/common/composition/delete.js +0 -1
  118. package/libx/_runtime/common/composition/insert.js +0 -1
  119. package/libx/_runtime/common/composition/tree.js +4 -10
  120. package/libx/_runtime/common/composition/update.js +44 -21
  121. package/libx/_runtime/common/generic/auth/capabilities.js +8 -10
  122. package/libx/_runtime/common/generic/crud.js +1 -2
  123. package/libx/_runtime/common/generic/etag.js +4 -4
  124. package/libx/_runtime/common/generic/input.js +21 -6
  125. package/libx/_runtime/common/generic/paging.js +3 -3
  126. package/libx/_runtime/common/generic/put.js +7 -4
  127. package/libx/_runtime/common/generic/sorting.js +4 -4
  128. package/libx/_runtime/common/generic/temporal.js +3 -6
  129. package/libx/_runtime/common/i18n/messages.properties +0 -7
  130. package/libx/_runtime/common/utils/cqn2cqn4sql.js +11 -6
  131. package/libx/_runtime/common/utils/csn.js +0 -28
  132. package/libx/_runtime/common/utils/draft.js +8 -1
  133. package/libx/_runtime/common/utils/path.js +7 -1
  134. package/libx/_runtime/common/utils/propagateForeignKeys.js +122 -0
  135. package/libx/_runtime/common/utils/resolveView.js +2 -3
  136. package/libx/_runtime/common/utils/template.js +2 -3
  137. package/libx/_runtime/db/data-conversion/post-processing.js +3 -44
  138. package/libx/_runtime/db/generic/input.js +6 -6
  139. package/libx/_runtime/db/sql-builder/dataTypes.js +4 -0
  140. package/libx/_runtime/fiori/generic/activate.js +2 -2
  141. package/libx/_runtime/fiori/generic/before.js +40 -72
  142. package/libx/_runtime/fiori/generic/cancel.js +2 -2
  143. package/libx/_runtime/fiori/generic/delete.js +2 -2
  144. package/libx/_runtime/fiori/generic/edit.js +2 -2
  145. package/libx/_runtime/fiori/generic/new.js +3 -5
  146. package/libx/_runtime/fiori/generic/patch.js +49 -43
  147. package/libx/_runtime/fiori/generic/prepare.js +2 -2
  148. package/libx/_runtime/fiori/generic/read.js +27 -37
  149. package/libx/_runtime/fiori/utils/where.js +4 -2
  150. package/libx/_runtime/hana/Service.js +1 -3
  151. package/libx/_runtime/hana/conversion.js +3 -0
  152. package/libx/_runtime/hana/driver.js +33 -3
  153. package/libx/_runtime/hana/dynatrace.js +1 -0
  154. package/libx/_runtime/hana/search2Contains.js +12 -1
  155. package/libx/_runtime/hana/search2cqn4sql.js +10 -27
  156. package/libx/_runtime/hana/streaming.js +1 -0
  157. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +4 -2
  158. package/libx/_runtime/messaging/common-utils/AMQPClient.js +1 -0
  159. package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +5 -2
  160. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -0
  161. package/libx/_runtime/messaging/enterprise-messaging.js +62 -3
  162. package/libx/_runtime/messaging/outbox/utils.js +1 -1
  163. package/libx/_runtime/messaging/redis-messaging.js +1 -0
  164. package/libx/_runtime/remote/Service.js +2 -2
  165. package/libx/_runtime/remote/utils/client.js +35 -11
  166. package/libx/_runtime/remote/utils/data.js +7 -2
  167. package/libx/_runtime/sqlite/Service.js +18 -7
  168. package/libx/_runtime/sqlite/conversion.js +3 -0
  169. package/libx/_runtime/sqlite/convertAssocToOneManaged.js +3 -3
  170. package/libx/_runtime/sqlite/localized.js +8 -8
  171. package/libx/odata/afterburner.js +39 -7
  172. package/libx/odata/cqn2odata.js +6 -3
  173. package/libx/odata/grammar.pegjs +66 -18
  174. package/libx/odata/index.js +3 -2
  175. package/libx/odata/parser.js +1 -1
  176. package/libx/odata/utils.js +2 -0
  177. package/libx/rest/RestAdapter.js +62 -43
  178. package/libx/rest/middleware/input.js +2 -3
  179. package/libx/rest/middleware/parse.js +2 -1
  180. package/libx/rest/middleware/update.js +1 -1
  181. package/package.json +2 -2
  182. package/server.js +5 -4
  183. package/srv/mtx.cds +1 -1
  184. package/srv/mtx.js +4 -24
  185. package/lib/srv/adapters.js +0 -85
  186. package/lib/utils/resources/index.js +0 -48
  187. package/lib/utils/resources/tar.js +0 -49
  188. package/lib/utils/resources/utils.js +0 -11
  189. package/libx/_runtime/db/utils/propagateForeignKeys.js +0 -93
  190. package/libx/_runtime/extensibility/activate.js +0 -69
  191. package/libx/_runtime/extensibility/add.js +0 -50
  192. package/libx/_runtime/extensibility/addExtension.js +0 -72
  193. package/libx/_runtime/extensibility/defaults.js +0 -34
  194. package/libx/_runtime/extensibility/handler/transformREAD.js +0 -121
  195. package/libx/_runtime/extensibility/handler/transformRESULT.js +0 -51
  196. package/libx/_runtime/extensibility/handler/transformWRITE.js +0 -64
  197. package/libx/_runtime/extensibility/linter/allowlist_checker.js +0 -373
  198. package/libx/_runtime/extensibility/linter/annotations_checker.js +0 -113
  199. package/libx/_runtime/extensibility/linter/checker_base.js +0 -20
  200. package/libx/_runtime/extensibility/linter/namespace_checker.js +0 -180
  201. package/libx/_runtime/extensibility/linter.js +0 -32
  202. package/libx/_runtime/extensibility/push.js +0 -118
  203. package/libx/_runtime/extensibility/service.js +0 -38
  204. package/libx/_runtime/extensibility/token.js +0 -57
  205. package/libx/_runtime/extensibility/utils.js +0 -131
  206. package/libx/_runtime/extensibility/validation.js +0 -50
  207. package/libx/_runtime/extensibility/views.js +0 -12
  208. package/srv/extensibility-service.cds +0 -59
  209. package/srv/extensibility-service.js +0 -1
  210. package/srv/extensions.cds +0 -8
  211. package/srv/model-provider.cds +0 -61
  212. package/srv/model-provider.js +0 -143
package/bin/mtx/in-cds.js CHANGED
@@ -7,6 +7,7 @@ const _is_streamlined_mtx = ()=>{
7
7
 
8
8
  if (!cds.requires.multitenancy || _is_streamlined_mtx()) module.exports = undefined
9
9
  else try {
10
+ // eslint-disable-next-line cds/no-missing-dependencies
10
11
  const mtx = module.exports = require ('@sap/cds-mtx')()
11
12
  mtx.inject (cds)
12
13
  cds.on('served', () => cds.emit('mtx'))
package/bin/serve.js CHANGED
@@ -129,6 +129,7 @@ const cds = require('../lib'), { exists, isfile, local, path } = cds.utils
129
129
  let log = console.log
130
130
  let debug = false
131
131
 
132
+
132
133
  /**
133
134
  * The main function which dispatches into the respective usage variants.
134
135
  * @param {string[]} all - project folder, model filenames, or service name
@@ -148,6 +149,9 @@ async function serve (all=[], o={}) { // NOSONAR
148
149
  // handle --watch and --project
149
150
  if (o.watch) return _watch.call(this, o.project,o) // cds serve --watch <project>
150
151
  if (o.project) _chdir_to (o.project) // cds run --project <project>
152
+
153
+ // Load local server.js early in order to allow setting custom cds.log.Loggers
154
+ const cds_server = await _local_server_js() || cds.server
151
155
  if (!o.silent) _prepare_logging ()
152
156
 
153
157
  // The following things are meant for dev mode, which can be overruled by feature flagse...
@@ -185,30 +189,15 @@ async function serve (all=[], o={}) { // NOSONAR
185
189
  }
186
190
 
187
191
  // bootstrap server from project-local server.js or from @sap/cds/server.js
188
- const cds_server = await _local_server_js() || cds.server
189
192
  const server = await cds_server(o)
190
193
 
191
194
  // return a promise which resolves to the created http server when listening
192
195
  return cds.server.listening = new Promise ((_resolve,_reject) => {
193
- const _started = ()=>{
194
- const url = cds.server.url = `http://localhost:${server.address().port}`
195
- cds.emit ('listening', {server,url}) //> inform local listeners
196
- _resolve (server)
197
- }
196
+
198
197
  server.listening ? _started(server) : server.once('listening',_started)
199
198
  server.on ('error',_reject) // startup errors like EADDRINUSE
200
199
  server.on ('close', ()=> shutdown()) // in case of server.close() was called, like in cds.test
201
200
 
202
- let shutdownCalled = false
203
- const shutdown = async sig => {
204
- if (shutdownCalled) return
205
- shutdownCalled = true
206
- global.it || cds.watched || console.log() // blank line makes the ^C look pretty in terminals
207
- debug && debug(`${sig}, shutting down, calling ${cds.listeners('shutdown').length} listeners`)
208
- await Promise.all(cds.listeners('shutdown').map((fn) => fn()))
209
- if (process.env.NODE_ENV !== 'test' && !global.it) process.exit()
210
- }
211
-
212
201
  process.once('SIGTERM', shutdown)
213
202
  process.once('SIGINT', shutdown)
214
203
  process.once('SIGHUP', shutdown)
@@ -217,6 +206,21 @@ async function serve (all=[], o={}) { // NOSONAR
217
206
  process.on('message', (msg) => { if (msg.close||msg.exit) shutdown() }) // by `cds watch` on Windows
218
207
 
219
208
  return server
209
+
210
+ async function _started() {
211
+ _warn_if_cds_was_loaded_from_different_locations()
212
+ const url = cds.server.url = `http://localhost:${server.address().port}`
213
+ cds.emit ('listening', {server,url}) //> inform local listeners
214
+ _resolve (server)
215
+ }
216
+
217
+ async function shutdown (sig) {
218
+ if (shutdown.called) return; else shutdown.called = true // only do that once
219
+ global.it || cds.watched || console.log() // blank line makes the ^C look pretty in terminals
220
+ debug && debug(`${sig}, shutting down, calling ${cds.listeners('shutdown').length} listeners`)
221
+ await Promise.all(cds.listeners('shutdown').map((fn) => fn()))
222
+ if (process.env.NODE_ENV !== 'test' && !global.it) process.exit()
223
+ }
220
224
  })
221
225
  }
222
226
 
@@ -224,7 +228,7 @@ async function _local_server_js() {
224
228
  const _local = file => isfile(file) || isfile (path.join(cds.env.folders.srv,file))
225
229
  let server_js = process.env.CDS_TYPESCRIPT && _local('server.ts') || _local('server.js')
226
230
  if (server_js) {
227
- log ('Loading server from', { file: local(server_js) })
231
+ console.log ('[cds] - loading server from', { file: local(server_js) })
228
232
  let fn = await cds.utils._import(server_js)
229
233
  if (fn && fn.default) fn = fn.default // default ESM export
230
234
  return typeof fn === 'function' ? fn : cds.server
@@ -233,25 +237,21 @@ async function _local_server_js() {
233
237
 
234
238
 
235
239
  function _prepare_logging () { // NOSONAR
236
- // change `log` function to cds.log
237
- const LOG = cds.log('serve', { prefix:'cds' })
238
- log = LOG._info && LOG.info
239
- if (!log) return log = ()=>{}
240
- debug = cds.debug('cli')
241
240
 
242
- const _timer = `[cds] - launched at ${new Date().toLocaleString()}, in`
241
+ const LOG = cds.log('serve|server',{label:'cds'}); if (!LOG._info) return; else log = LOG.info
242
+ const _timer = `[cds] - launched at ${new Date().toLocaleString()}, version: ${cds.version}, in`
243
243
  console.time (_timer)
244
244
 
245
245
  // print information when model is loaded
246
246
  cds.on ('loaded', (model)=>{
247
- log (`model loaded from ${model.$sources.length} file(s):\n\x1b[2m`)
247
+ LOG.info (`loaded model from ${model.$sources.length} file(s):\n\x1b[2m`)
248
248
  for (let each of model.$sources) console.log (' ', local(each))
249
249
  console.log ('\x1b[0m')
250
250
  })
251
251
 
252
252
  // print information about each connected service
253
253
  cds.on ('connect', ({name,kind,options:{use,credentials,impl}})=>{
254
- log (`connect to ${name} > ${use||kind||impl}`, credentials ? _redacted(credentials) : '')
254
+ LOG.info (`connect to ${name} > ${use||kind||impl}`, credentials ? _redacted(credentials) : '')
255
255
  })
256
256
 
257
257
  // print information about each provided service
@@ -260,17 +260,15 @@ function _prepare_logging () { // NOSONAR
260
260
  if (srv.path) details.path = srv.path
261
261
  if (srv._source && !srv._source.startsWith('@sap'))
262
262
  details.impl = local(srv._source)
263
- log (`${srv.mocked ? 'mocking' : 'serving'} ${srv.name}`, details)
263
+ LOG.info (`${srv.mocked ? 'mocking' : 'serving'} ${srv.name}`, details)
264
264
  })
265
265
 
266
- cds.on ('served', ()=> console.log())
267
-
268
266
  // print info when we are finally on air
269
267
  cds.once ('listening', ({url})=>{
270
268
  console.log()
271
- log ('server listening on',{url})
269
+ LOG.info ('server listening on',{url})
272
270
  _timer && console.timeEnd (_timer)
273
- if (process.stdin.isTTY) log (`[ terminate with ^C ]\n`)
271
+ if (process.stdin.isTTY) LOG.info (`[ terminate with ^C ]\n`)
274
272
  })
275
273
  }
276
274
 
@@ -336,4 +334,13 @@ function _redacted(cred) {
336
334
  return cred
337
335
  }
338
336
 
337
+
338
+ const _warn_if_cds_was_loaded_from_different_locations = ()=> global.__cds_loaded_from?.size > 1 && console.warn(`
339
+ -----------------------------------------------------------------------
340
+ WARNING: Package '@sap/cds' was loaded from different installations:`,
341
+ [ ...global.__cds_loaded_from ],
342
+ `Rather ensure a single install only to avoid hard-to-resolve errors.
343
+ -----------------------------------------------------------------------
344
+ `)
345
+
339
346
  /* eslint no-console:off */
@@ -0,0 +1,33 @@
1
+ module.exports = function basic_auth (options) {
2
+
3
+ const cds = require ('../index'), LOG = cds.log('auth'), { decodeURIComponent } = cds.utils
4
+ const users = require ('./mocked-users') (options)
5
+ const login_required = options.login === 'required' || process.env.NODE_ENV === 'production' && options.credentials || cds.requires.multitenancy
6
+
7
+ /** @type { import('express').Handler } express_handler */
8
+ return async function basic_auth (req, res, next) {
9
+ // allow subsequent code to request a user login
10
+ req.login = login
11
+ // get basic authorization header
12
+ let auth = req.headers.authorization
13
+ // enforce login if requested
14
+ if (!auth) return login_required ? req.login('Logged in user required!') : next()
15
+ // decode user credentials from autorization header
16
+ let [id,pwd] = Buffer.from(auth.slice(6),'base64').toString().split(':')
17
+ // verify user credentials and set req.user
18
+ let u = req.user = await users.verify (id, pwd)
19
+ // re-request login in case of wrong credentials
20
+ if (u.failed) return req.login (u)
21
+ // support for feature toggles via req.headers.features
22
+ if (req.headers.features) u = req.user = { ...u, features: req.headers.features } // NOTE: need to clone u
23
+ // done...
24
+ if (LOG._debug) LOG.debug('authenticated user:', u)
25
+ next()
26
+ }
27
+
28
+ function login (reason='') {
29
+ const req=this, res=req.res
30
+ res.set ('WWW-Authenticate', `Basic realm="Users"`) .sendStatus (401)
31
+ LOG.info (req.method, decodeURIComponent(req.path), '>', res.statusCode, res.statusMessage, ...(!reason ? [] : ['-', reason]))
32
+ }
33
+ }
@@ -0,0 +1,7 @@
1
+ const { User: { privileged }} = require ('../index')
2
+ module.exports = function dummy_auth() {
3
+ return function dummy_auth (req, res, next) {
4
+ req.user = privileged
5
+ next()
6
+ }
7
+ }
@@ -0,0 +1,2 @@
1
+ module.exports = require('../../libx/_runtime/auth/strategies/ias-auth')
2
+ // TODO: could move that implementation over here and link from libx/_runtime
@@ -0,0 +1,31 @@
1
+
2
+ function auth_factory (options) {
3
+ const cds = require ('../index'), { path, local } = cds.utils
4
+ const o = { ...options, ...cds.requires.auth }
5
+ let kind = o.kind || o.strategy
6
+ let middleware = cds.auth[kind]
7
+ if (middleware) {
8
+ cds.log().info ('using auth strategy:', { kind }, '\n')
9
+ } else {
10
+ let impl = o.impl || path.resolve (__dirname, kind)
11
+ try { impl = require.resolve (impl) } catch {
12
+ throw cds.error `Cannot find auth impl: ${impl}`
13
+ }
14
+ cds.log().info ('using auth strategy:', { kind, impl: local(impl) }, '\n')
15
+ middleware = require(impl)
16
+ }
17
+ return middleware(o)
18
+ }
19
+
20
+ const { lazified } = require('../lazy')
21
+ const _require = require; require = lazified (module) // eslint-disable-line no-global-assign
22
+
23
+ module.exports = lazified (Object.assign (auth_factory, {
24
+ mocked: require('./basic-auth'),
25
+ basic: require('./basic-auth'),
26
+ dummy: require('./dummy-auth'),
27
+ ias: require('./ias-auth'),
28
+ xsuaa: require('./xsuaa-auth'),
29
+ }))
30
+
31
+ require = _require // eslint-disable-line no-global-assign
@@ -0,0 +1,3 @@
1
+ // TODO...
2
+ console.trace ('JWT auth is not yet implemented')
3
+ module.exports = ()=> (req,res,next)=> next()
@@ -0,0 +1,72 @@
1
+ const cds = require ('../index'), { User } = cds
2
+ const LOG = cds.log('auth')
3
+
4
+ class MockedUsers {
5
+
6
+ constructor (options) {
7
+ const tenants = this.tenants = options.tenants || {}
8
+ const users = this.users = options.users || {}
9
+ for (let [k,v] of Object.entries(users)) {
10
+ if (typeof v === 'boolean') continue
11
+ if (typeof v === 'string') v = { password:v }
12
+ let id = _configured(v).id || k
13
+ let u = users[id] = new User ({ id, ...v })
14
+ let fts = tenants[u.tenant]?.features
15
+ if (fts && !u.features) u.features = fts
16
+ }
17
+ }
18
+
19
+ /**
20
+ * Verifies a username / password combination against configured users.
21
+ * @returns { {id:string} | {failed:string} }
22
+ * - `{id,...}` &rarr; a user object for successfully authenticated users
23
+ * - `{failed}` &rarr; for failed authentication, i.e., invalid credentials
24
+ */
25
+ verify (id, pwd) {
26
+ let u = this.users[id]
27
+ if (!u) return id && this.users['*'] ? { id } : { failed: `User '${id}' not found` }
28
+ if (u.password && pwd !== u.password) return { failed: `Wrong password for user '${id}'` }
29
+ return u
30
+ }
31
+ }
32
+
33
+ const _configured = (u,x) => {
34
+ if ((x = _deprecated (u.ID, 'ID','id'))) {
35
+ u.id = x
36
+ }
37
+ if ((x = _deprecated (u.userAttributes, 'userAttributes','attr'))) {
38
+ u.attr = { ...u.attr, ...x }
39
+ }
40
+ if (u.jwt) {
41
+ if ((x = _deprecated (u.jwt.zid, 'jwt.zid','tenant'))) {
42
+ u.tenant = u.jwt.zid
43
+ }
44
+ if ((x = _deprecated (u.jwt.attributes, 'jwt.attributes','attr'))) {
45
+ u.attr = { ...u.attr, ...x }
46
+ }
47
+ if ((x = _deprecated (u.jwt.userInfo, 'jwt.attributes','attr'))) {
48
+ u.attr = { ...u.attr, ...x }
49
+ }
50
+ if ((x = _deprecated (u.jwt.scope || u.jwt.scopes, 'jwt.scopes','roles'))) {
51
+ const {aud} = u.jwt; if (aud) x = x.map (s => {
52
+ for (const each of aud) s = s.replace(`${each}.`, '')
53
+ return s
54
+ })
55
+ u.roles = [ ...u.roles||[], ...x ]
56
+ }
57
+ }
58
+ return u
59
+ }
60
+
61
+ const _deprecated = (v,x,y) => {
62
+ if (!v || x in _deprecated) return v
63
+ else LOG.warn(`WARNING: \n
64
+ Usage of '${x}' in user configurations is deprecated and won't be
65
+ supported in future releases. → Please use property '${y}' instead.
66
+ `)
67
+ return _deprecated[x] = v
68
+ }
69
+
70
+
71
+ // allows calling with or without new
72
+ module.exports = function(o) { return new MockedUsers(o) }
@@ -0,0 +1,12 @@
1
+ /* eslint-disable cds/no-missing-dependencies */
2
+ module.exports = function passport_basic_auth (options) {
3
+ // const session = require('express-session')({ secret:'secret', resave:false, saveUninitialized:true, })
4
+ const { BasicStrategy } = require('passport-http')
5
+ const users = require ('./mocked-users') (options)
6
+ const passport = require('passport') .use (new BasicStrategy ((id, pwd, done) => {
7
+ let user = users.verify (id,pwd)
8
+ if (user.failed) return done (null, false, { message: user.failed })
9
+ else return done (null, user)
10
+ }))
11
+ return passport.authenticate('basic', {session:false})
12
+ }
@@ -0,0 +1,14 @@
1
+ /* eslint-disable cds/no-missing-dependencies */
2
+ module.exports = function passport_digest_auth (options) {
3
+ // const session = require('express-session')({ secret:'secret', resave:false, saveUninitialized:true, })
4
+ const { users } = require ('./mocked-users') (options)
5
+ const { DigestStrategy } = require('passport-http')
6
+ const passport = require('passport') .use (new DigestStrategy ((id, done) => {
7
+ // REVISIT: this is never called -> no clue why
8
+ console.trace (id)
9
+ const u = users[id]
10
+ if (!u) return done (null, false)
11
+ else return done (null, u, u.password)
12
+ }))
13
+ return passport.authenticate('digest', {session:false})
14
+ }
@@ -0,0 +1,3 @@
1
+ // TODO...
2
+ console.trace ('XSUAA auth is not yet implemented')
3
+ module.exports = ()=> (req,res,next)=> next()
@@ -51,8 +51,8 @@ function cds_compile (model, options, _flavor) {
51
51
  if (!model) throw cds.error (`Argument 'model' must be specified`)
52
52
  if (_is_csn(model) && _assert_flavor(model,_flavor,options)) return _fluent(model) //> already parsed csn
53
53
  const o = _options4 (options,_flavor)
54
- const files = _is_files (model)
55
54
  const cwd = o.cwd || cds.root
55
+ const files = _is_files (model,cwd)
56
56
  if (files) {
57
57
  if (o.sync) return _fluent (_finalize (cdsc.compileSync(files,cwd,o))) //> compile files synchroneously
58
58
  else return _fluent (cdsc.compile(files,cwd,o) .then (_finalize)) //> compile files asynchroneously
@@ -75,9 +75,9 @@ function cds_compile (model, options, _flavor) {
75
75
 
76
76
 
77
77
  const _is_csn = (x) => (x.definitions || x.extensions) && !x.$builtins
78
- const _is_files = m => {
78
+ const _is_files = (m,root) => {
79
79
  if (Array.isArray(m) || /^file:/.test(m) && (m = m.slice(5)))
80
- return cds.resolve(m) || cds.error ( `Couldn't find a CDS model for '${m}' in ${cds.root}`,{ code:'MODEL_NOT_FOUND', model: m })
80
+ return cds.resolve(m,{root}) || cds.error ( `Couldn't find a CDS model for '${m}' in ${root||cds.root}`,{ code:'MODEL_NOT_FOUND', model: m })
81
81
  }
82
82
  const _assert_flavor = (m,_flavor,options) => {
83
83
  if (!m.meta) return true; const f = _flavor || _flavor4 (options)
@@ -1,4 +1,5 @@
1
1
  const cdsc = require ('../cdsc')
2
+ const keywords = require("@sap/cds-compiler").to.cdl.keywords
2
3
 
3
4
  function cds_compile_to_cdl (csn,o) {
4
5
  const results = cdsc.to.cdl (csn,o)
@@ -15,4 +16,7 @@ function* _many(all) {
15
16
  }
16
17
 
17
18
  const _cdl = cdl => cdl.replace(/^\/\/ generated.+\n/,'')
18
- module.exports = cds_compile_to_cdl
19
+ module.exports = Object.assign(
20
+ cds_compile_to_cdl,
21
+ { keywords }
22
+ )
@@ -1,6 +1,14 @@
1
1
  const cdsc = require ('../cdsc')
2
2
  const cds = require ('../../index')
3
3
 
4
+ if (cds.env.features.precompile_edms !== false) {
5
+ const _precompiled = new WeakMap
6
+ cdsc.to.edm = Object.assign ((csn,o)=>{
7
+ if (!_precompiled.has(csn)) _precompiled.set (csn, cdsc.to.edm.all (csn,o))
8
+ return _precompiled.get(csn) [o.service]
9
+ }, { all: cdsc.to.edm.all })
10
+ }
11
+
4
12
  function cds_compile_to_edm (csn,_o) {
5
13
  const o = cdsc._options.for.edm(_o) //> used twice below...
6
14
  csn = _4odata(csn,o)
@@ -1,4 +1,5 @@
1
1
  const cds = require ('../..')
2
+ // eslint-disable-next-line cds/no-missing-dependencies -- needs to be added by app dev
2
3
  const { SchemaGenerator } = require('@sap/cds-graphql/lib/schema')
3
4
 
4
5
  function cds_compile_to_gql (csn) {
@@ -4,6 +4,7 @@ const path = require('path')
4
4
  module.exports = (csn,o={}) => {
5
5
  const relative = filename => (o.src !== o.cwd) ? path.relative(o.src, path.join(o.cwd, filename)) : filename
6
6
  const relative_cds_home = RegExp ('^' + path.relative (o.src || o.cwd || cds.root, cds.home) + '/')
7
+
7
8
  const resolver = (_,v) => {
8
9
 
9
10
  if (!v) return v
@@ -18,10 +19,15 @@ module.exports = (csn,o={}) => {
18
19
  else if (v.kind === "service" && !v['@source'] && v.$location?.file) {
19
20
  // Preserve original sources for services so we can use them for finding
20
21
  // sibling implementation files when reloaded from csn.json.
21
- const file = relative(v.$location.file)
22
- .replace(relative_cds_home,'@sap/cds/')
23
- .replace('node_modules/','')
24
- .replace(/\\/g,'/')
22
+ let file = relative(v.$location.file)
23
+ .replace(relative_cds_home,'@sap/cds/')
24
+ .replace('node_modules/','')
25
+ .replace(/\\/g,'/')
26
+ // If there is still a relative path pointing outside of cwd, convert it to a module path
27
+ // e.g. ../bookshop/srv/cat-service.cds -> @capire/bookshop/srv/cat-service.cds
28
+ if (file.startsWith('../')) {
29
+ file = to_module_path(file, o.cwd)
30
+ }
25
31
  return { '@source': file, ...v }
26
32
  }
27
33
 
@@ -29,4 +35,23 @@ module.exports = (csn,o={}) => {
29
35
 
30
36
  }
31
37
  return JSON.stringify (csn, resolver, o && o.indents || 2)
32
- }
38
+ }
39
+
40
+ // go upwards, find a package.json and try resolving with this module name
41
+ function to_module_path (file, cwd=cds.root) {
42
+ let dir = path.dirname(file)
43
+ while (dir && dir.length > 1) {
44
+ try {
45
+ const pkg = require(path.join(cwd, dir, 'package.json'))
46
+ const module_path = file.replace(dir, pkg.name)
47
+ require.resolve(module_path, { paths:[cwd] }) // check if result is resovable, note that this assumes NPM install
48
+ return module_path
49
+ } catch (err) {
50
+ if (err.code !== 'MODULE_NOT_FOUND') throw err
51
+ dir = path.dirname(dir)
52
+ }
53
+ }
54
+ return file
55
+ }
56
+
57
+ // module.exports.to_module_path = to_module_path
@@ -1,6 +1,6 @@
1
1
  const cds = require ('../..')
2
2
  const cdsc = require ('../cdsc')
3
-
3
+ const keywords = require("@sap/cds-compiler").to.sql.sqlite.keywords
4
4
 
5
5
  function cds_compile_to_sql (csn,_o) {
6
6
  csn = _extended(cds.minify(csn))
@@ -38,10 +38,12 @@ function* _2many (all,_file=f=>f) {
38
38
  module.exports = Object.assign (cds_compile_to_sql, {
39
39
  hdbcds: cds_compile_to_hdbcds,
40
40
  hdbtable: cds_compile_to_hdbtable,
41
+ sqlite: { keywords }
41
42
  })
42
43
 
43
44
 
44
45
 
46
+
45
47
  /////////////////////////////////////////////////////////////////////////////
46
48
  // UI Flex - read extensions__ to views, when ext fields are read
47
49
  const _extended = (csn) => {
package/lib/core/index.js CHANGED
@@ -52,6 +52,10 @@ const types = _common ({ __proto__: roots,
52
52
  UUID: {type:'string',length:36,isUUID:true},
53
53
  Boolean: {type:'boolean'},
54
54
  Integer: {type:'number'},
55
+ UInt8: {type:'Integer'},
56
+ Int16: {type:'Integer'},
57
+ Int32: {type:'Integer'},
58
+ Int64: {type:'Integer'},
55
59
  Integer16: {type:'Integer'},
56
60
  Integer32: {type:'Integer'},
57
61
  Integer64: {type:'Integer'},
@@ -66,7 +70,7 @@ const types = _common ({ __proto__: roots,
66
70
  String: {type:'string'},
67
71
  Binary: {type:'string'},
68
72
  LargeString: {type:'string'},
69
- LargeBinary: {type:'string'},
73
+ LargeBinary: {type:'string'}
70
74
  })
71
75
 
72
76
  /**
@@ -1,6 +1,12 @@
1
1
  const cds = require('../index'), { local, inspect } = cds.utils
2
2
  const DEBUG = cds.debug('deploy')
3
- /* eslint-disable no-console */
3
+
4
+ const colors = !!process.stdout.isTTY && !!process.stderr.isTTY
5
+ const term = {
6
+ x1b2: colors ? '\x1b[2m' : '',
7
+ x1b0: colors ? '\x1b[0m' : '',
8
+ info: colors ? (s => term.x1b2 + s + term.x1b0) : (s => s)
9
+ }
4
10
 
5
11
  /**
6
12
  * Implementation of `cds.deploy` common to all databases.
@@ -18,10 +24,10 @@ exports = module.exports = function cds_deploy (model,options) { return {
18
24
  if (model && !model.definitions) {
19
25
  model = await cds.load (model) .then (cds.minify)
20
26
  if (DEBUG) try {
21
- DEBUG (`model loaded from ${model.$sources.length} file(s):\n\x1b[2m`)
27
+ DEBUG (`loaded model from ${model.$sources.length} file(s):\n${term.x1b2}`)
22
28
  for (let each of model.$sources) console.log (' ', local(each))
23
29
  } finally {
24
- console.log ('\x1b[0m')
30
+ console.log (term.x1b0)
25
31
  }
26
32
  }
27
33
 
@@ -38,11 +44,11 @@ exports = module.exports = function cds_deploy (model,options) { return {
38
44
 
39
45
  // fill in initial data...
40
46
  await exports.init (db,model, file => LOG(
41
- `\x1b[2m > init from ${local(file)} \x1b[0m`
47
+ term.info(` > init from ${local(file)}`)
42
48
  ))
43
49
 
44
50
  // done
45
- const {credentials:c} = db.options, file = c && (c.database || c.url)
51
+ const file = db.getDbUrl(cds.context?.tenant)
46
52
  if (file !== ':memory:') LOG (`/> successfully deployed to ./${file}\n`)
47
53
  else LOG (`/> successfully deployed to sqlite in-memory db\n`)
48
54
  return db
@@ -116,7 +122,8 @@ exports.create = async function (db, csn=db.model, o) {
116
122
  await tx.run(creates)
117
123
  return true
118
124
  })
119
- } else return db.deploy (csn,o)
125
+ }
126
+ else return db.deploy (csn,o)
120
127
  }
121
128
 
122
129
 
@@ -133,6 +140,7 @@ exports.init = (db, csn=db.model, log=()=>{}) => db.run (async tx => {
133
140
  const INSERT_into = _from_csv_or_json [path.extname(file)]
134
141
  const src = await read(file,'utf8'); if (!src) continue
135
142
  const q = INSERT_into (e,src); if (!q) continue
143
+ if (db.kind === 'better-sqlite') _add_missing_pks2(q)
136
144
  log (file,e)
137
145
  inits.push (tx.run(q) .catch (e => {
138
146
  Error.captureStackTrace(e)
@@ -141,6 +149,19 @@ exports.init = (db, csn=db.model, log=()=>{}) => db.run (async tx => {
141
149
  }
142
150
  }
143
151
  await Promise.all (inits)
152
+
153
+ function _add_missing_pks2 (q) {
154
+ const {columns,rows} = q.INSERT // REVISIT: .entries are covered by current runtime. Should eventually also be handled here, as we likely don't do so in new db services
155
+ if (columns) {
156
+ const entity = csn.definitions[q._target.name], {uuid} = cds.utils
157
+ for (let k in entity.keys) if (!columns.includes(k)) {
158
+ columns.push(k)
159
+ const t = entity.keys[k]._type, pk = t === 'cds.UUID' ? uuid : index => index+1
160
+ rows.forEach ((row,index) => row.push(pk(index)))
161
+ }
162
+ }
163
+ }
164
+
144
165
  })
145
166
 
146
167
 
@@ -165,6 +186,13 @@ exports.resources = async function (csn, opts) {
165
186
  DEBUG && DEBUG (`ignoring '${fx}' in favor of translated ones`); continue
166
187
  }
167
188
  const e = _entity4(f,csn); if (_skip(e)) continue
189
+ if (cds.env.features.deploy_data_onconflict === 'replace' && !/[._]texts_/.test(f)) {
190
+ const seenBefore = Object.entries(found).find(([_, entity]) => entity === e.name )
191
+ if (seenBefore) {
192
+ DEBUG && DEBUG(`Conflict for '${e.name}': replacing '${local(seenBefore[0])}' with '${local(path.join(subdir,fx))}'`)
193
+ continue
194
+ }
195
+ }
168
196
  found[path.join(subdir,fx)] = e.name
169
197
  }
170
198
  }
@@ -213,3 +241,5 @@ const INSERT_from_json = (entity, json) => {
213
241
 
214
242
  const _from_csv_or_json = { '.json': INSERT_from_json, '.csv': INSERT_from_csv, }
215
243
  const _skip = e => !e || e['@cds.persistence.skip'] === true
244
+
245
+ /* eslint-disable no-console */
@@ -106,7 +106,11 @@ class Config {
106
106
  let sources = [
107
107
  { name: 'USER_HOME', path: user_home, file: '.cdsrc.json' },
108
108
  { name: 'PROJECT', path: home, file: '.cdsrc.json' },
109
- { name: 'PACKAGE', path: home, file: 'package.json', mapper: p => p[context] },
109
+ { name: 'PACKAGE', path: home, file: 'package.json', mapper: p => {
110
+ const obj = p[context] || {}
111
+ obj.extends = obj.extends || p.extends // fill cds.extends from .extends
112
+ return obj
113
+ }},
110
114
  { name: 'PRIVATE', path: home, file: '.cdsrc-private.json' }
111
115
  ]
112
116
 
@@ -429,9 +433,11 @@ function _merge (dst, src, _profiles, _cloned, _profiles_only = false) {
429
433
  }
430
434
 
431
435
  if (_profiles && p[0] === '[') {
432
- if (_profiles._defined) _profiles._defined.add (p.slice(1,-1))
433
- if (_profiles.has(p.slice(1,-1)))
434
- profiled.push (()=> _merge (dst, src[p], _profiles, _cloned, false))
436
+ const profile = p.slice(1,-1)
437
+ if (_profiles._defined) _profiles._defined.add (profile)
438
+ if (_profiles.has(profile)) {
439
+ profiled.push ({ profile, merge: () => _merge (dst, src[p], _profiles, _cloned, false)})
440
+ }
435
441
  continue
436
442
  }
437
443
 
@@ -445,7 +451,11 @@ function _merge (dst, src, _profiles, _cloned, _profiles_only = false) {
445
451
 
446
452
  if (!_profiles_only && v !== undefined) dst[p] = v
447
453
  }
448
- for (let each of profiled) each()
454
+ if (profiled.length > 0 && !_profiles.has('production')) {
455
+ const profiles = Array.from(_profiles)
456
+ profiled.sort((a,b) => profiles.indexOf(b.profile) - profiles.indexOf(a.profile))
457
+ }
458
+ for (let each of profiled) each.merge()
449
459
  return dst
450
460
  }
451
461