@sap/cds 6.1.3 → 6.2.2

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 (206) hide show
  1. package/CHANGELOG.md +83 -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/util/assert.js +4 -0
  113. package/libx/_runtime/common/aspects/relation.js +1 -1
  114. package/libx/_runtime/common/composition/data.js +61 -15
  115. package/libx/_runtime/common/composition/delete.js +0 -1
  116. package/libx/_runtime/common/composition/insert.js +0 -1
  117. package/libx/_runtime/common/composition/tree.js +4 -10
  118. package/libx/_runtime/common/composition/update.js +44 -21
  119. package/libx/_runtime/common/generic/auth/capabilities.js +8 -10
  120. package/libx/_runtime/common/generic/crud.js +1 -2
  121. package/libx/_runtime/common/generic/etag.js +4 -4
  122. package/libx/_runtime/common/generic/input.js +4 -4
  123. package/libx/_runtime/common/generic/paging.js +3 -3
  124. package/libx/_runtime/common/generic/put.js +3 -3
  125. package/libx/_runtime/common/generic/sorting.js +4 -4
  126. package/libx/_runtime/common/generic/temporal.js +3 -3
  127. package/libx/_runtime/common/i18n/messages.properties +0 -7
  128. package/libx/_runtime/common/utils/cqn2cqn4sql.js +11 -6
  129. package/libx/_runtime/common/utils/csn.js +0 -28
  130. package/libx/_runtime/common/utils/draft.js +8 -1
  131. package/libx/_runtime/common/utils/path.js +7 -1
  132. package/libx/_runtime/common/utils/resolveView.js +2 -3
  133. package/libx/_runtime/db/data-conversion/post-processing.js +3 -44
  134. package/libx/_runtime/db/generic/input.js +3 -3
  135. package/libx/_runtime/db/sql-builder/dataTypes.js +4 -0
  136. package/libx/_runtime/fiori/generic/activate.js +2 -2
  137. package/libx/_runtime/fiori/generic/before.js +40 -72
  138. package/libx/_runtime/fiori/generic/cancel.js +2 -2
  139. package/libx/_runtime/fiori/generic/delete.js +2 -2
  140. package/libx/_runtime/fiori/generic/edit.js +2 -2
  141. package/libx/_runtime/fiori/generic/new.js +2 -2
  142. package/libx/_runtime/fiori/generic/patch.js +49 -37
  143. package/libx/_runtime/fiori/generic/prepare.js +2 -2
  144. package/libx/_runtime/fiori/generic/read.js +27 -37
  145. package/libx/_runtime/fiori/utils/where.js +4 -2
  146. package/libx/_runtime/hana/Service.js +1 -3
  147. package/libx/_runtime/hana/conversion.js +3 -0
  148. package/libx/_runtime/hana/driver.js +33 -3
  149. package/libx/_runtime/hana/dynatrace.js +1 -0
  150. package/libx/_runtime/hana/search2Contains.js +12 -1
  151. package/libx/_runtime/hana/search2cqn4sql.js +10 -27
  152. package/libx/_runtime/hana/streaming.js +1 -0
  153. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +4 -2
  154. package/libx/_runtime/messaging/common-utils/AMQPClient.js +1 -0
  155. package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +5 -2
  156. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -0
  157. package/libx/_runtime/messaging/enterprise-messaging.js +62 -3
  158. package/libx/_runtime/messaging/outbox/utils.js +1 -1
  159. package/libx/_runtime/messaging/redis-messaging.js +1 -0
  160. package/libx/_runtime/remote/Service.js +2 -2
  161. package/libx/_runtime/remote/utils/client.js +8 -3
  162. package/libx/_runtime/remote/utils/data.js +7 -2
  163. package/libx/_runtime/sqlite/Service.js +18 -7
  164. package/libx/_runtime/sqlite/conversion.js +3 -0
  165. package/libx/_runtime/sqlite/convertAssocToOneManaged.js +3 -3
  166. package/libx/_runtime/sqlite/localized.js +8 -8
  167. package/libx/odata/afterburner.js +39 -7
  168. package/libx/odata/cqn2odata.js +6 -3
  169. package/libx/odata/grammar.pegjs +66 -18
  170. package/libx/odata/index.js +3 -2
  171. package/libx/odata/parser.js +1 -1
  172. package/libx/odata/utils.js +2 -0
  173. package/libx/rest/RestAdapter.js +62 -43
  174. package/libx/rest/middleware/parse.js +2 -1
  175. package/libx/rest/middleware/update.js +1 -1
  176. package/package.json +2 -2
  177. package/server.js +5 -4
  178. package/srv/mtx.cds +1 -1
  179. package/srv/mtx.js +4 -33
  180. package/lib/srv/adapters.js +0 -85
  181. package/lib/utils/resources/index.js +0 -48
  182. package/lib/utils/resources/tar.js +0 -49
  183. package/lib/utils/resources/utils.js +0 -11
  184. package/libx/_runtime/extensibility/activate.js +0 -69
  185. package/libx/_runtime/extensibility/add.js +0 -50
  186. package/libx/_runtime/extensibility/addExtension.js +0 -72
  187. package/libx/_runtime/extensibility/defaults.js +0 -34
  188. package/libx/_runtime/extensibility/handler/transformREAD.js +0 -121
  189. package/libx/_runtime/extensibility/handler/transformRESULT.js +0 -51
  190. package/libx/_runtime/extensibility/handler/transformWRITE.js +0 -64
  191. package/libx/_runtime/extensibility/linter/allowlist_checker.js +0 -373
  192. package/libx/_runtime/extensibility/linter/annotations_checker.js +0 -113
  193. package/libx/_runtime/extensibility/linter/checker_base.js +0 -20
  194. package/libx/_runtime/extensibility/linter/namespace_checker.js +0 -180
  195. package/libx/_runtime/extensibility/linter.js +0 -32
  196. package/libx/_runtime/extensibility/push.js +0 -118
  197. package/libx/_runtime/extensibility/service.js +0 -38
  198. package/libx/_runtime/extensibility/token.js +0 -57
  199. package/libx/_runtime/extensibility/utils.js +0 -131
  200. package/libx/_runtime/extensibility/validation.js +0 -50
  201. package/libx/_runtime/extensibility/views.js +0 -12
  202. package/srv/extensibility-service.cds +0 -60
  203. package/srv/extensibility-service.js +0 -1
  204. package/srv/extensions.cds +0 -8
  205. package/srv/model-provider.cds +0 -61
  206. package/srv/model-provider.js +0 -143
@@ -1,5 +1,5 @@
1
1
  const cds = require ('..')
2
- const { ProtocolAdapter } = cds.service.adapters
2
+ const { ProtocolAdapter } = cds.service.protocols
3
3
  const { Service } = cds.service.factory
4
4
  const _ready = Symbol(), _pending = cds.services._pending || {}
5
5
 
@@ -63,7 +63,7 @@ module.exports = function cds_serve (som, _options) { // NOSONAR
63
63
  // skip all services marked to be ignored
64
64
  d['@cds.ignore'] || d['@cds.serve.ignore'] ||
65
65
  // skip external services, unless asked to mock them and unbound
66
- (d['@cds.external'] || required[d.name]?.external) && (!o.mocked || required[d.name].credentials)
66
+ (d['@cds.external'] || required[d.name]?.external) && (!o.mocked || required[d.name]?.credentials)
67
67
  ))
68
68
  if (services.length > 1 && o.at) {
69
69
  throw cds.error `You cannot specify 'path' for multiple services`
@@ -80,7 +80,7 @@ module.exports = function cds_serve (som, _options) { // NOSONAR
80
80
  if (o.service && o.service._is_service_instance) return srv
81
81
  srv.init && await srv.prepend (srv.init)
82
82
  srv.options.impl && await srv.prepend (srv.options.impl)
83
- cds.services[srv.name] = cds.service.paths[srv.path] = srv
83
+ cds.services[srv.name] = srv
84
84
  cds.service.providers.push (srv)
85
85
  if (srv[_ready]) srv[_ready](srv)
86
86
  return srv
@@ -100,24 +100,30 @@ module.exports = function cds_serve (som, _options) { // NOSONAR
100
100
  ready = ready.then (()=> all.forEach (each => {
101
101
  const d = each.definition
102
102
  if (d['@protocol'] === 'none' || d['@cds.api.ignore']) return each._is_dark = true
103
- ProtocolAdapter.serve(each,o.to).in(app)
103
+ ProtocolAdapter.serve (each, /*in:*/ app)
104
104
  if (!o.silent) cds.emit ('serving',each)
105
105
  }))
106
106
  return this
107
107
  },
108
108
 
109
109
  /** Finally resolve to a single picked provider or a map of all */
110
- then: (resolve, failed) => ready.then (()=>{
111
- if (all.length === 0) return resolve()
112
- let response={}
113
- for (let each of all) {
114
- response[each.name] = each._is_dark || !each.definition ? each : ProtocolAdapter.serve(each,o.to).asRouter()
110
+ then: (_resolve, _error) => ready.then (()=>{
111
+ if (all.length === 0) return _resolve()
112
+ const response = all.length === 1 ? all[0] : {}
113
+ for (const each of all) {
114
+ Object.defineProperty (response, each.name, { enumerable:true, configurable:true, get(){
115
+ let chimera = each
116
+ if (each.definition && !each._is_dark) {
117
+ chimera = ProtocolAdapter.for (each)
118
+ Object.setPrototypeOf (chimera, each)
119
+ Object.defineProperty (chimera, 'name', { value: each.name })
120
+ }
121
+ Object.defineProperty (response, each.name, { enumerable:true, value: chimera })
122
+ return chimera
123
+ }})
115
124
  }
116
- if (all.length === 1) {
117
- response = Object.assign (all[0], response)
118
- }
119
- return resolve (response)
120
- }, failed),
125
+ return _resolve (response)
126
+ }, _error),
121
127
 
122
128
  catch: (e) => ready.catch(e)
123
129
  }
@@ -140,6 +146,7 @@ async function _new (Service, d,m,o) {
140
146
  let service = cds.env.requires[srv.name]?.service
141
147
  if (service && !cds.services[service]) Object.defineProperty (cds.services, service, {value:srv})
142
148
  }
149
+ if (!o.silent) cds.emit (`serving:${srv.name}`, srv)
143
150
  })
144
151
  return srv
145
152
  }
@@ -0,0 +1,29 @@
1
+ module.exports = ()=> {
2
+
3
+ const cds = require ('../../index')
4
+
5
+ /** @type { import('express').Handler } */
6
+ async function cds_context_provider (req, res, next) {
7
+ const ctx = new cds.EventContext
8
+ ctx.http = { req, res }
9
+ ctx.id = _id4(req)
10
+ ctx.user = req.user
11
+ ctx.tenant = req.tenant || ctx.user?.tenant
12
+ cds._context.run (ctx, next)
13
+ }
14
+
15
+ const { uuid } = cds.utils
16
+ const _id4 = (req) => {
17
+ let id = req.headers['x-correlation-id'] = (
18
+ req.headers['x-correlation-id'] ||
19
+ req.headers['x-correlationid'] ||
20
+ req.headers['x-request-id'] ||
21
+ req.headers['x-vcap-request-id'] ||
22
+ uuid()
23
+ )
24
+ req.res.set('X-Correlation-ID', id)
25
+ return id
26
+ }
27
+
28
+ return cds_context_provider
29
+ }
@@ -0,0 +1,24 @@
1
+ module.exports = ()=>{
2
+ const cds = require ('../../index')
3
+ if (cds.requires.extensibility || cds.requires.toggles || cds.mtx) {
4
+ const { model4 } = require('../srv-models')
5
+ return async function cds_context_model (req,res, next) {
6
+ if (req.baseUrl.startsWith('/-/')) return next() //> our own tech services cannot be extended
7
+ const ctx = cds.context
8
+ if (ctx.tenant) try {
9
+ // if (req.headers.features) ctx.user.features = req.headers.features //> currently done in basic-auth only
10
+ ctx.model = req.__model = await model4 (ctx.tenant, ctx.features) // REVISIT: req.__model is because of Okra
11
+ } catch (e) {
12
+ console.error(e)
13
+ return res.status(503) .json ({ // REVISIT: we should throw a simple error, nothing else! -> this is overly OData-specific!
14
+ error: { code: '503', message:
15
+ process.env.NODE_ENV === 'production' ? 'Service Unavailable' :
16
+ 'Unable to get context-specific model due to: ' + e.message
17
+ }
18
+ })
19
+ }
20
+ next()
21
+ }
22
+ }
23
+ else return []
24
+ }
@@ -0,0 +1,9 @@
1
+ module.exports = ()=> function cds_error_handler (err, req, res, next) {
2
+ // console.error (err)
3
+ // const n = Number(err.status || err.statusCode)
4
+ // res.status (n >= 400 && n < 600 ? n : 500)
5
+ // err.status = 400
6
+ // res.json ({ ...err, message: err.message, stack: err.stack })
7
+ // if (err.status === 401 && req.login) return req.login()
8
+ next (err)
9
+ }
@@ -0,0 +1,22 @@
1
+ const auth = exports.auth = require('../../auth')
2
+ const context = exports.context = require('./cds-context')
3
+ const ctx_model = exports.ctx_model = require('./ctx-model')
4
+ const errors = exports.errors = require('./errors')
5
+ const trace = exports.trace = require('./trace')
6
+
7
+ // middlewares running before protocol adapters
8
+ exports.before = [
9
+ trace(), // provides detailed trace logs when DEBUG=trace
10
+ auth(), // provides req.user & tenant
11
+ context(), // provides cds.context
12
+ ctx_model(), // fills in cds.context.model
13
+ ]
14
+
15
+ exports.after = [
16
+ // usually error middlewares
17
+ errors(),
18
+ ]
19
+
20
+ exports.bootstrap = ()=>{
21
+ require('../protocols')()
22
+ }
@@ -0,0 +1,13 @@
1
+ const { performance:{now} } = require ('perf_hooks')
2
+
3
+ module.exports = (prec = 1000) => function sap_statistics (req, res, next) {
4
+ if (req.query['sap-statistics'] || req.headers['sap-statistics']) {
5
+ const { writeHead } = res, t0 = now()
6
+ res.writeHead = function (...args) {
7
+ const total = ((now() - t0) / prec).toFixed(2)
8
+ if (res.statusCode < 400) res.setHeader('sap-statistics', `total=${total}`)
9
+ writeHead.call(this, ...args)
10
+ }
11
+ }
12
+ next()
13
+ }
@@ -0,0 +1,102 @@
1
+ const cds = require ('../../index'), { decodeURIComponent } = cds.utils
2
+ const LOG = cds.log('trace')
3
+ if (!LOG._debug) module.exports = ()=>[]; else {
4
+
5
+ module.exports = (o={}) => {
6
+
7
+ // normalize options
8
+ let { truncate:t = 111, skip = { BEGIN:1, COMMIT:2, ROLLBACK:3 } } = o || {}
9
+ if (typeof t !== 'function') o.truncate = !t ? s=>s : s => s.length <= t ? s : s.slice(0,t)+' ...'
10
+ if (typeof skip !== 'function') o.skip = !skip ? ()=>false : x => (x.event||x) in skip
11
+ const _perf = e => !o.skip(e) && cds.context?.http?.req._perf
12
+
13
+ // instrument framework services
14
+ _instrument_cds_services (_perf)
15
+ _instrument_better_sqlite (_perf)
16
+ _instrument_sqlite (_perf)
17
+
18
+ // the express middleware function
19
+ return function cap_perf_logger (req, res, next) {
20
+ let perf = req._perf || (req._perf = new PerfTrace)
21
+ perf.log (req.method, decodeURIComponent(req.originalUrl))
22
+ res.on('finish', ()=> LOG.debug ('elapsed times:', perf.toString(o)))
23
+ next()
24
+ }
25
+ }
26
+
27
+ const { performance:{now} } = require ('perf_hooks')
28
+ const { format } = require ('util')
29
+
30
+ class PerfTrace extends Array {
31
+ log (...details) {
32
+ const e = { details, start:now() }
33
+ return this.push(e), e
34
+ }
35
+ done (e) {
36
+ return e.stop = now()
37
+ }
38
+ toString ({truncate}) {
39
+ const t0 = this[0].start; if (!this[0].stop) this[0].stop = now()
40
+ return '\n'+ this.map (e => truncate (format (
41
+ (e.start - t0).toFixed(2).padStart(6), '→',
42
+ (e.stop - t0).toFixed(2).padEnd(6), '= ',
43
+ (e.stop - e.start).toFixed(2).padStart(6), 'ms',
44
+ '-', ...e.details))
45
+ ).join('\n')
46
+ }
47
+ }
48
+ }
49
+
50
+
51
+ function _instrument_cds_services (_get_perf) {
52
+ const me = _instrument_cds_services; if (me.done) return; else me.done = true
53
+ const { handle } = cds.Service.prototype
54
+ cds.Service.prototype.handle = function (req) {
55
+ const perf = _get_perf(req)
56
+ if (perf) {
57
+ const pe = perf.log (this.name, '-', req.event, req.path||'')
58
+ var _done = r => { perf.done(pe); return r }
59
+ }
60
+ return handle.apply (this, arguments) .then (_done)
61
+ }
62
+ }
63
+
64
+ function _instrument_sqlite (_get_perf) {
65
+ const me = _instrument_sqlite; if (me.done) return; else me.done = true
66
+ try { require.resolve('sqlite3') } catch { return }
67
+ // eslint-disable-next-line cds/no-missing-dependencies
68
+ const sqlite = require('sqlite3').Database.prototype
69
+ for (let each of ['all', 'get', 'run', 'prepare']) {
70
+ const _super = sqlite[each]
71
+ sqlite[each] = function (q, ..._) {
72
+ const perf = _get_perf(q)
73
+ if (perf) {
74
+ const pe = perf.log ('sqlite -', q)
75
+ const callback = _[_.length-1]; _[_.length-1] = function(){
76
+ perf.done(pe)
77
+ callback.apply (this, arguments)
78
+ }
79
+ }
80
+ return _super.call (this, q, ..._)
81
+ }
82
+ }
83
+ }
84
+
85
+ function _instrument_better_sqlite (_get_perf) {
86
+ const me = _instrument_better_sqlite; if (me.done) return; else me.done = true
87
+ try { require.resolve('better-sqlite3') } catch { return }
88
+ // eslint-disable-next-line cds/no-missing-dependencies
89
+ const sqlite = require('better-sqlite3').prototype
90
+ for (let each of ['exec', 'prepare']) {
91
+ const _super = sqlite[each]
92
+ sqlite[each] = function (q, ..._) {
93
+ const perf = _get_perf(q)
94
+ if (perf) {
95
+ const pe = perf.log ('sqlite -', q)
96
+ try { return _super.call (this, q, ..._) }
97
+ finally { perf.done(pe) }
98
+ }
99
+ else return _super.call (this, q, ..._)
100
+ }
101
+ }
102
+ }
@@ -0,0 +1,42 @@
1
+ const libx = require('../../../libx/_runtime')
2
+ const cds_context_model = require('../srv-models')
3
+ const { ProtocolAdapter } = require('.')
4
+
5
+ class LegacyProtocolAdapter extends ProtocolAdapter {
6
+
7
+ static init() {
8
+ return this.protocols = {
9
+ "odata": { get impl() { return libx.to.odata_v4 } },
10
+ "rest": { get impl() { return libx.to.rest } },
11
+ }
12
+ }
13
+
14
+ static serve (srv, /* in: */ app) {
15
+ return super.serve (srv, app, { before: [
16
+ // async (req, res, next) => { await 1; next() }, // REVISIT: AsyncResource.bind() -> enable to break cds/tests/_runtime/odata/__tests__/integration/crud-with-mtx.test.js with existing, non-middleware mode, *w/o* fix to BufferedWriter
17
+ cap_req_logger,
18
+ libx.perf,
19
+ libx.auth(srv),
20
+ cds_context_model.middleware4(srv)
21
+ ], after:[] })
22
+ }
23
+
24
+ }
25
+
26
+ const cds = require('../../index')
27
+ const LOG = cds.log(), DEBUG = cds.debug()
28
+ function cap_req_logger (req,_,next) {
29
+ let url = req.originalUrl
30
+ try { url = decodeURI(req.originalUrl) } catch (e) { /* decodeURI throws error for invalid urls */ }
31
+ LOG && LOG (req.method, url, req.body||'')
32
+ if (/\$batch/.test(req.url)) req.on ('dispatch', (req) => {
33
+ let path = req._path
34
+ try { path = decodeURI(req._path) } catch (e) { /* decodeURI throws error for invalid urls */ }
35
+ LOG && LOG ('>', req.event, path, req._query||'')
36
+ if (DEBUG && req.query) DEBUG (req.query)
37
+ })
38
+ next()
39
+ }
40
+
41
+ LegacyProtocolAdapter.init()
42
+ module.exports = LegacyProtocolAdapter
@@ -0,0 +1,39 @@
1
+ const cds = require ('../../index'), { decodeURIComponent } = cds.utils
2
+ const LOG = cds.log('graphql')
3
+
4
+ const GraphQLAdapter = require('@sap/cds-graphql/lib') // eslint-disable-line cds/no-missing-dependencies
5
+ const express = require ('express') // eslint-disable-line cds/no-missing-dependencies
6
+
7
+ function CDSGraphQLAdapter (options) {
8
+
9
+ const {services} = options
10
+
11
+ return express.Router()
12
+ .use (express.json()) //> required in the slug handlers and logger below
13
+
14
+ /** Convenience slug route for /graphql/srv/entity/id */
15
+ .use ('/:path/:entity/:id?(%20:query)?', (req,_,next) => {
16
+ // TODO: add filter by id -> then remove the // eslint-disable-line
17
+ let { entity, id, query } = req.params // eslint-disable-line no-unused-vars
18
+ if (query) req.body = { query }
19
+ if (entity) req.body.query = req.body.query.replace(/{/, `{ ${entity} {`) +'}'
20
+ next() //> goes on below
21
+ })
22
+
23
+ /** Convenience slug route for /graphql/srv */
24
+ .use ('/:path', (req, res, next) => {
25
+ let srv = services [req.params.path]
26
+ if (req.body) req.body.query = req.body.query.replace(/{/, `{ ${srv.name} {`) +'}'
27
+ next() //> goes on below
28
+ })
29
+
30
+ .use ((req,_,next)=>{
31
+ LOG.info (req.method, req.body?.query || decodeURIComponent(req.query.query))
32
+ next()
33
+ })
34
+
35
+ /** The global /graphql route */
36
+ .use (new GraphQLAdapter (services, options))
37
+ }
38
+
39
+ module.exports = CDSGraphQLAdapter
@@ -0,0 +1,37 @@
1
+ const cds = require('../../index'), { decodeURIComponent } = cds.utils
2
+ const LOG = cds.log('hcql')
3
+ const express = require('express') // eslint-disable-line cds/no-missing-dependencies
4
+
5
+ module.exports = function HCQLAdapter (srv) {
6
+
7
+ return express.Router()
8
+ .use(express.json()) //> for application/json -> cqn
9
+ .use(express.text()) //> for text/plain -> cql -> cqn
10
+
11
+ /** Returns CSN schema in response to /<srv>/$csn requests */
12
+ .get('/\\$csn', (_, res) => {
13
+ let csn = cds.minify (cds.model, { service: srv.name })
14
+ res.json(csn)
15
+ })
16
+
17
+ /** Convenience route for REST-style request formats like that: */
18
+ .get('/:entity/:id?(%20:tail)?', (req, _, next) => {
19
+ let { entity, id, tail } = req.params, q = SELECT.from(entity, id)
20
+ if (is_string(req.body)) tail = req.body
21
+ else if (is_array(req.body)) q.columns(req.body)
22
+ else Object.assign(q.SELECT, req.body)
23
+ if (tail) q = { SELECT: { ...CQL(`SELECT from _ ${tail}`).SELECT, ...q.SELECT } }
24
+ req.body = q; next() // delegating to main handler
25
+ })
26
+
27
+ /** The actual protocol adapter. */
28
+ .use((req, res, next) => {
29
+ let q = req.body; if (is_string(q)) q = CQL(q)
30
+ LOG.info (req.method, decodeURIComponent(req.originalUrl), { ...q })
31
+ return srv.run(q).then(r => res.json(r)).catch(next)
32
+ })
33
+
34
+ }
35
+
36
+ const is_string = x => typeof x === 'string'
37
+ const is_array = Array.isArray
@@ -0,0 +1,86 @@
1
+ const cds = require('../../index')
2
+ const {join} = cds.utils.path
3
+
4
+ class ProtocolAdapter {
5
+
6
+ /**
7
+ * Provides canonicalized protocols configurations
8
+ */
9
+ static init (protocols = { ...cds.env.protocols }) {
10
+ for (let [k,o] of Object.entries(protocols)) if (typeof o === 'string') protocols[k] = {path:o}
11
+ if (!protocols.odata) protocols.odata = { impl: join(__dirname,'odata-v4') }
12
+ if (!protocols.rest) protocols.rest = { impl: join(__dirname,'rest') }
13
+ return this.protocols = protocols
14
+ }
15
+
16
+ /**
17
+ * Returns the middleware impl for the given protocol
18
+ */
19
+ static middlewareFor (p) {
20
+ const conf = this.protocols[p] || (this.protocols[p] = {})
21
+ let { impl = join(__dirname,p) } = conf; if (typeof impl !== 'function') {
22
+ try { require.resolve(impl) } catch { cds.error `Cannot find protocol adapter implementation: ${impl}` }
23
+ impl = conf.impl = require(impl)
24
+ }
25
+ return impl
26
+ }
27
+
28
+ /**
29
+ * Constructs a new adapter for the given service, or returns a formerly constructed one
30
+ */
31
+ static for (srv, p = srv.options?.to || protocol4(srv.definition)) {
32
+ const cache = srv._adapters || (srv._adapters={}); if (p in cache) return cache[p]
33
+ const impl = this.middlewareFor(p), conf = this.protocols[p]
34
+ return cache[p] = impl (srv, conf)
35
+ }
36
+
37
+ /**
38
+ * Constructs a new adapter for the given service, and mounts it to an express app.
39
+ */
40
+ static serve (srv, /* in: */ app, { before, after } = cds.middlewares) {
41
+ const adapter = this.for(srv); if (!adapter) return
42
+ app.use (srv.path+'/webapp/', (_,res) => res.sendStatus(404))
43
+ app.use (srv.path, before, adapter, after)
44
+ return adapter
45
+ }
46
+
47
+ /**
48
+ * Serve protocols at configured paths, if any; e.g. /hcql/browse, /graphql/...
49
+ */
50
+ static serveAll (protocols = Object.entries(ProtocolAdapter.protocols).filter(([,o]) => o.path)) {
51
+ if (protocols?.length) cds.once ('served', ()=>{
52
+ const LOG = cds.log(), DEBUG = cds.debug('adapters')
53
+ for (let [ protocol, options ] of protocols) {
54
+ let globalAdapter = false
55
+ for (let srv of cds.service.providers) {
56
+ if (!protocol4(srv.definition,null)) {
57
+ let adapter = ProtocolAdapter.middlewareFor(protocol); if (!adapter) continue
58
+ if (is_global(adapter)) { globalAdapter = adapter; break }
59
+ _serve (adapter(srv,options), options.path + srv.path, protocol, options)
60
+ }
61
+ }
62
+ if (globalAdapter) {
63
+ options = { ...options, services: cds.service.providers
64
+ .filter (srv => { let p = protocol4(srv.definition,null); return !p || p === protocol })
65
+ .reduce ((all,srv) => (all[srv.path.slice(1)] = srv, all), {})
66
+ }
67
+ _serve (globalAdapter(options), options.path, protocol, options)
68
+ }
69
+ }
70
+ function _serve (adapter, path, protocol, options) {
71
+ if (!_serve.first) { _serve.first = true; console.log() }
72
+ if (!_serve[protocol]) LOG.info ('serving', _serve[protocol] = { protocol, at: options.path })
73
+ DEBUG?.('app.use(', path, ', ... )')
74
+ cds.app.use (path, cds.middlewares.before, adapter, cds.middlewares.after)
75
+ }
76
+ })
77
+ }
78
+ }
79
+
80
+
81
+ const protocols = Object.keys(ProtocolAdapter.init())
82
+ const protocol4 = (def, _default = protocols[0]) => def['@protocol'] || protocols.find(p => def['@'+p]) || _default
83
+ const is_global = adapter => adapter.length === 1 && !/^(function )?(\w+\s+)?\((srv|service)/.test(adapter)
84
+
85
+ module.exports = Object.assign (ProtocolAdapter.serveAll, { ProtocolAdapter, protocol4 })
86
+ if (!cds.requires.middlewares) module.exports.ProtocolAdapter = require('./_legacy')