@sap/cds 7.9.2 → 8.0.3

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 (279) hide show
  1. package/CHANGELOG.md +139 -3656
  2. package/_i18n/i18n_en_US_saptrc.properties +113 -0
  3. package/_i18n/i18n_zh_CN.properties +7 -4
  4. package/app/index.css +129 -0
  5. package/app/index.html +16 -64
  6. package/app/index.js +14 -9
  7. package/bin/args.js +34 -0
  8. package/bin/serve.js +18 -24
  9. package/bin/test.js +97 -0
  10. package/common.cds +5 -12
  11. package/eslint.config.mjs +133 -0
  12. package/lib/auth/basic-auth.js +16 -20
  13. package/lib/auth/dummy-auth.js +1 -1
  14. package/lib/auth/ias-auth.js +12 -30
  15. package/lib/auth/index.js +1 -14
  16. package/lib/auth/jwt-auth.js +14 -30
  17. package/lib/compile/cds-compile.js +1 -2
  18. package/lib/compile/cdsc.js +21 -26
  19. package/lib/compile/etc/_localized.js +1 -6
  20. package/lib/compile/etc/csv.js +1 -1
  21. package/lib/compile/etc/properties.js +1 -1
  22. package/lib/compile/for/java.js +1 -1
  23. package/lib/compile/for/lean_drafts.js +4 -6
  24. package/lib/compile/for/nodejs.js +1 -1
  25. package/lib/compile/parse.js +4 -0
  26. package/lib/compile/resolve.js +4 -4
  27. package/lib/compile/to/edm-files.js +16 -23
  28. package/lib/compile/to/hana.js +27 -0
  29. package/lib/compile/to/json.js +1 -1
  30. package/lib/compile/to/sql.js +5 -1
  31. package/lib/compile/to/srvinfo.js +1 -1
  32. package/lib/compile/to/yaml.js +3 -3
  33. package/lib/dbs/cds-deploy.js +4 -2
  34. package/lib/env/cds-env.js +10 -14
  35. package/lib/env/cds-requires.js +29 -13
  36. package/lib/env/defaults.js +46 -16
  37. package/lib/env/plugins.js +1 -1
  38. package/lib/env/schemas/cds-rc.js +8 -4
  39. package/lib/env/schemas/index.js +7 -7
  40. package/lib/env/serviceBindings.js +1 -1
  41. package/lib/index.js +12 -10
  42. package/lib/lazy.js +1 -1
  43. package/lib/linked/classes.js +36 -8
  44. package/lib/linked/entities.js +2 -10
  45. package/lib/linked/models.js +2 -1
  46. package/lib/linked/validate.js +292 -0
  47. package/lib/log/cds-error.js +0 -6
  48. package/lib/log/cds-log.js +3 -3
  49. package/lib/log/format/json.js +1 -1
  50. package/lib/log/service/index.js +0 -1
  51. package/lib/plugins.js +3 -3
  52. package/lib/ql/Query.js +2 -10
  53. package/lib/ql/SELECT.js +1 -1
  54. package/lib/ql/Whereable.js +3 -2
  55. package/lib/req/cds-context.js +14 -25
  56. package/lib/req/context.js +23 -25
  57. package/lib/req/request.js +1 -34
  58. package/lib/req/user.js +47 -35
  59. package/lib/srv/bindings.js +1 -1
  60. package/lib/srv/cds-connect.js +4 -4
  61. package/lib/srv/cds-serve.js +2 -2
  62. package/lib/srv/factory.js +1 -1
  63. package/lib/srv/middlewares/cds-context.js +11 -22
  64. package/lib/srv/middlewares/ctx-model.js +2 -3
  65. package/lib/srv/middlewares/errors.js +41 -8
  66. package/lib/srv/middlewares/index.js +3 -3
  67. package/lib/srv/middlewares/trace.js +0 -2
  68. package/lib/srv/protocols/hcql.js +15 -10
  69. package/lib/srv/protocols/http.js +44 -49
  70. package/lib/srv/protocols/index.js +1 -23
  71. package/lib/srv/protocols/odata-v4.js +12 -74
  72. package/lib/srv/protocols/rest.js +1 -13
  73. package/lib/srv/srv-api.js +0 -20
  74. package/lib/srv/srv-dispatch.js +3 -2
  75. package/lib/srv/srv-handlers.js +22 -11
  76. package/lib/srv/srv-methods.js +2 -2
  77. package/lib/srv/srv-models.js +3 -36
  78. package/lib/test/expect.js +343 -0
  79. package/lib/test/index.js +2 -0
  80. package/lib/test/reporter.js +176 -0
  81. package/lib/utils/axios.js +10 -9
  82. package/lib/utils/cds-test.js +86 -37
  83. package/lib/utils/cds-utils.js +54 -7
  84. package/lib/utils/check-version.js +0 -4
  85. package/lib/utils/colors.js +49 -0
  86. package/lib/utils/data.js +5 -4
  87. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +2 -7
  88. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +3 -30
  89. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +6 -12
  90. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +1 -3
  91. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +0 -1
  92. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +4 -7
  93. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +12 -6
  94. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +2 -4
  95. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +1 -0
  96. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -1
  97. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +0 -1
  98. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +1 -3
  99. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +1 -1
  100. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/AbstractEdmStructuredType.js +1 -2
  101. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +5 -0
  102. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ContextURLFactory.js +1 -1
  103. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +9 -43
  104. package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +0 -1
  105. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +8 -3
  106. package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +4 -2
  107. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +1 -3
  108. package/libx/_runtime/cds-services/util/assert.js +1 -1
  109. package/libx/_runtime/cds.js +10 -3
  110. package/libx/_runtime/common/Service.js +12 -32
  111. package/libx/_runtime/common/aspects/any.js +1 -0
  112. package/libx/_runtime/common/code-ext/execute.js +1 -1
  113. package/libx/_runtime/common/code-ext/worker.js +0 -1
  114. package/libx/_runtime/common/composition/data.js +0 -1
  115. package/libx/_runtime/common/composition/delete.js +0 -1
  116. package/libx/_runtime/common/composition/insert.js +2 -2
  117. package/libx/_runtime/common/composition/tree.js +0 -1
  118. package/libx/_runtime/common/composition/update.js +3 -3
  119. package/libx/_runtime/common/error/frontend.js +21 -12
  120. package/libx/_runtime/common/error/log.js +36 -0
  121. package/libx/_runtime/common/error/utils.js +2 -5
  122. package/libx/_runtime/common/generic/auth/autoexpose.js +18 -17
  123. package/libx/_runtime/common/generic/auth/expand.js +1 -1
  124. package/libx/_runtime/common/generic/auth/readOnly.js +1 -2
  125. package/libx/_runtime/common/generic/auth/restrict.js +23 -42
  126. package/libx/_runtime/common/generic/auth/restrictions.js +2 -7
  127. package/libx/_runtime/common/generic/auth/utils.js +91 -88
  128. package/libx/_runtime/common/generic/crud.js +6 -5
  129. package/libx/_runtime/common/generic/etag.js +7 -12
  130. package/libx/_runtime/common/generic/input.js +70 -68
  131. package/libx/_runtime/common/generic/paging.js +1 -0
  132. package/libx/_runtime/common/generic/sorting.js +1 -0
  133. package/libx/_runtime/common/generic/temporal.js +8 -2
  134. package/libx/_runtime/common/i18n/index.js +1 -1
  135. package/libx/_runtime/common/i18n/messages.properties +3 -1
  136. package/libx/_runtime/common/utils/binary.js +8 -2
  137. package/libx/_runtime/common/utils/compareJson.js +5 -1
  138. package/libx/_runtime/common/utils/copy.js +6 -11
  139. package/libx/_runtime/common/utils/cqn2cqn4sql.js +16 -14
  140. package/libx/_runtime/common/utils/differ.js +3 -6
  141. package/libx/_runtime/common/utils/keys.js +77 -18
  142. package/libx/_runtime/common/utils/postProcess.js +12 -15
  143. package/libx/_runtime/common/utils/propagateForeignKeys.js +0 -1
  144. package/libx/_runtime/common/utils/resolveView.js +2 -3
  145. package/libx/_runtime/common/utils/restrictions.js +45 -17
  146. package/libx/_runtime/common/utils/rewriteAsterisks.js +1 -8
  147. package/libx/_runtime/common/utils/stream.js +3 -16
  148. package/libx/_runtime/common/utils/streamProp.js +8 -18
  149. package/libx/_runtime/common/utils/structured.js +1 -1
  150. package/libx/_runtime/common/utils/ucsn.js +0 -2
  151. package/libx/_runtime/db/Service.js +0 -72
  152. package/libx/_runtime/db/data-conversion/post-processing.js +0 -1
  153. package/libx/_runtime/db/expand/expandCQNToJoin.js +9 -9
  154. package/libx/_runtime/db/expand/rawToExpanded.js +0 -8
  155. package/libx/_runtime/db/generic/input.js +3 -8
  156. package/libx/_runtime/db/generic/rewrite.js +27 -4
  157. package/libx/_runtime/db/query/read.js +2 -2
  158. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +0 -1
  159. package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -1
  160. package/libx/_runtime/db/utils/columns.js +2 -6
  161. package/libx/_runtime/fiori/lean-draft.js +138 -56
  162. package/libx/_runtime/hana/Service.js +0 -1
  163. package/libx/_runtime/hana/driver.js +1 -1
  164. package/libx/_runtime/hana/dynatrace.js +1 -2
  165. package/libx/_runtime/hana/pool.js +11 -21
  166. package/libx/_runtime/hana/streaming.js +0 -1
  167. package/libx/_runtime/messaging/common-utils/AMQPClient.js +0 -1
  168. package/libx/_runtime/messaging/common-utils/authorizedRequest.js +1 -1
  169. package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +1 -1
  170. package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +1 -1
  171. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -33
  172. package/libx/_runtime/messaging/event-broker.js +0 -12
  173. package/libx/_runtime/messaging/file-based.js +3 -3
  174. package/libx/_runtime/messaging/http-utils/token.js +1 -1
  175. package/libx/_runtime/messaging/kafka.js +2 -2
  176. package/libx/_runtime/messaging/redis-messaging.js +0 -1
  177. package/libx/_runtime/remote/Service.js +25 -25
  178. package/libx/_runtime/remote/utils/client.js +4 -5
  179. package/libx/_runtime/remote/utils/cloudSdkProvider.js +0 -3
  180. package/libx/_runtime/remote/utils/data.js +0 -1
  181. package/libx/_runtime/sqlite/Service.js +1 -2
  182. package/libx/_runtime/ucl/Service.js +37 -78
  183. package/libx/common/assert/index.js +22 -21
  184. package/libx/common/assert/type-relaxed.js +39 -0
  185. package/libx/common/assert/utils.js +3 -2
  186. package/libx/common/assert/validation.js +3 -8
  187. package/libx/common/utils/index.js +5 -0
  188. package/libx/common/utils/path.js +51 -0
  189. package/libx/odata/ODataAdapter.js +126 -0
  190. package/libx/odata/index.js +15 -2
  191. package/libx/odata/middleware/batch.js +261 -72
  192. package/libx/odata/middleware/body-parser.js +33 -0
  193. package/libx/odata/middleware/create.js +44 -59
  194. package/libx/odata/middleware/delete.js +23 -12
  195. package/libx/odata/middleware/error.js +30 -6
  196. package/libx/odata/middleware/metadata.js +38 -26
  197. package/libx/odata/middleware/operation.js +93 -69
  198. package/libx/odata/middleware/parse.js +6 -8
  199. package/libx/odata/middleware/read.js +117 -93
  200. package/libx/odata/middleware/service-document.js +22 -19
  201. package/libx/odata/middleware/stream.js +54 -56
  202. package/libx/odata/middleware/update.js +79 -87
  203. package/libx/odata/parse/afterburner.js +191 -175
  204. package/libx/odata/parse/cqn2odata.js +8 -8
  205. package/libx/odata/parse/grammar.peggy +27 -20
  206. package/libx/odata/parse/multipartToJson.js +17 -9
  207. package/libx/odata/parse/parser.js +1 -1
  208. package/libx/odata/utils/etag.js +14 -6
  209. package/libx/odata/utils/index.js +84 -12
  210. package/libx/odata/utils/metadata.js +161 -0
  211. package/libx/odata/utils/postProcess.js +89 -0
  212. package/libx/odata/utils/readAfterWrite.js +134 -17
  213. package/libx/odata/utils/result.js +36 -142
  214. package/libx/outbox/index.js +5 -4
  215. package/libx/rest/RestAdapter.js +115 -182
  216. package/libx/rest/middleware/create.js +28 -24
  217. package/libx/rest/middleware/delete.js +7 -10
  218. package/libx/rest/middleware/error.js +19 -16
  219. package/libx/rest/middleware/operation.js +48 -41
  220. package/libx/rest/middleware/parse.js +128 -126
  221. package/libx/rest/middleware/read.js +20 -27
  222. package/libx/rest/middleware/update.js +26 -31
  223. package/package.json +16 -12
  224. package/server.js +4 -2
  225. package/tasks/enterprise-messaging-deploy.js +1 -1
  226. package/apis/cds.d.ts +0 -3
  227. package/apis/core.d.ts +0 -21
  228. package/apis/cqn.d.ts +0 -18
  229. package/apis/csn.d.ts +0 -21
  230. package/apis/events.d.ts +0 -18
  231. package/apis/internal/inference.d.ts +0 -18
  232. package/apis/linked.d.ts +0 -18
  233. package/apis/log.d.ts +0 -20
  234. package/apis/models.d.ts +0 -18
  235. package/apis/ql.d.ts +0 -18
  236. package/apis/reflect.d.ts +0 -32
  237. package/apis/server.d.ts +0 -18
  238. package/apis/services.d.ts +0 -22
  239. package/bin/cds-serve.js +0 -56
  240. package/lib/compile/to/gql.js +0 -15
  241. package/lib/srv/protocols/_legacy.js +0 -44
  242. package/lib/utils/jest.js +0 -43
  243. package/libx/_runtime/auth/index.js +0 -193
  244. package/libx/_runtime/auth/strategies/JWT.js +0 -37
  245. package/libx/_runtime/auth/strategies/basic.js +0 -20
  246. package/libx/_runtime/auth/strategies/dummy.js +0 -14
  247. package/libx/_runtime/auth/strategies/ias-auth.js +0 -1
  248. package/libx/_runtime/auth/strategies/mock.js +0 -77
  249. package/libx/_runtime/auth/strategies/xssecUtils.js +0 -93
  250. package/libx/_runtime/auth/strategies/xsuaa.js +0 -38
  251. package/libx/_runtime/common/perf/index.js +0 -19
  252. package/libx/_runtime/common/utils/ensureIEEE754.js +0 -29
  253. package/libx/_runtime/fiori/draft.js +0 -2
  254. package/libx/_runtime/fiori/generic/activate.js +0 -190
  255. package/libx/_runtime/fiori/generic/before.js +0 -201
  256. package/libx/_runtime/fiori/generic/cancel.js +0 -19
  257. package/libx/_runtime/fiori/generic/delete.js +0 -21
  258. package/libx/_runtime/fiori/generic/edit.js +0 -157
  259. package/libx/_runtime/fiori/generic/index.js +0 -25
  260. package/libx/_runtime/fiori/generic/new.js +0 -82
  261. package/libx/_runtime/fiori/generic/patch.js +0 -101
  262. package/libx/_runtime/fiori/generic/prepare.js +0 -57
  263. package/libx/_runtime/fiori/generic/read.js +0 -1340
  264. package/libx/_runtime/fiori/generic/readOverDraft.js +0 -146
  265. package/libx/_runtime/fiori/utils/csn.js +0 -13
  266. package/libx/_runtime/fiori/utils/delete.js +0 -114
  267. package/libx/_runtime/fiori/utils/handler.js +0 -264
  268. package/libx/_runtime/fiori/utils/lockInfo.js +0 -27
  269. package/libx/_runtime/fiori/utils/req.js +0 -23
  270. package/libx/_runtime/fiori/utils/stream.js +0 -36
  271. package/libx/_runtime/fiori/utils/where.js +0 -254
  272. package/libx/_runtime/index.js +0 -22
  273. package/libx/odata/utils/handler.js +0 -120
  274. package/libx/odata/utils/metaInfo.js +0 -410
  275. package/libx/odata/utils/path.js +0 -75
  276. package/libx/rest/RestRequest.js +0 -32
  277. package/libx/rest/index.js +0 -3
  278. package/libx/rest/readme.md +0 -1
  279. /package/libx/common/assert/{type.js → type-strict.js} +0 -0
@@ -0,0 +1,292 @@
1
+ const cds = require('..')
2
+
3
+ /** Validates given input data against a request target definition.
4
+ * @param {entity} target the linked definition to check against, usually an entity definition
5
+ * @returns {Error[]|undefined} an array of errors or undefined if no errors occurred
6
+ */
7
+ const conf = module.exports = exports = function validate (data, target, options={}) {
8
+ const vc = new Validation (data, target, options)
9
+ target.validate (data, null, vc)
10
+ return vc.errors
11
+ }
12
+
13
+
14
+ /** Instances represent single validations and are mainly used to record errors during validation. */
15
+ class Validation {
16
+
17
+ constructor (data, target, options={}) {
18
+ this.data = data
19
+ this.target = target
20
+ this.options = options
21
+ this.insert = options.insert ?? options.mandatories
22
+ this.cleanse = options.cleanse !== false
23
+ }
24
+
25
+ error (code, path, leaf, val, ...args) {
26
+ const err = (this.errors ??= new ValidationErrors).add (code)
27
+ if (this.options.path) path = [ this.options.path, ...path ] // e.g. used to prefic 'in/' for actions
28
+ if (path) err.target = (!leaf ? path : path.concat(leaf)).reduce?.((p,n)=> (
29
+ n?.row ? p + this.filter4(n) : //> some/entity(ID=1)...
30
+ typeof n === 'number' ? p + `[${n}]` : //> some/array[1]...
31
+ p && n ? p+'/'+n : n //> some/element/...
32
+ ),'')
33
+ if (val) err.args = [ val, ...args ]
34
+ return err
35
+ }
36
+
37
+ filter4 ({ def, row, index }) {
38
+ const entity = def._target || def, filter=[]
39
+ for (let k in entity.keys) {
40
+ let v = row[k]
41
+ if (v === undefined) if (k === 'IsActiveEntity') v = false; else continue
42
+ else if (typeof v === 'string' && !entity.elements[k].isUUID || entity.elements[k]['@odata.Type'] === 'Edm.String') v = `'${v}'`
43
+ filter.push (`${k}=${v}`)
44
+ }
45
+ return filter.length ? `(${filter})` : `[${index}]`
46
+ }
47
+
48
+ unknown(e,d) {
49
+ d['@open'] || cds.error (`Property "${e}" does not exist in ${d.name}`, {status:400})
50
+ }
51
+ }
52
+
53
+
54
+ /** ValidationErrors avoid expensive creation of stack traces */
55
+ class ValidationErrors extends Array {
56
+ add (error) {
57
+ const err = Object.create (ValidationErrors.proto)
58
+ err.message = err.stack = error
59
+ this.push (err)
60
+ return err
61
+ }
62
+ static proto = Object.create (Error.prototype, {
63
+ message: { writable:true, configurable:true },
64
+ stack: { writable:true, configurable:true, value: '<none>' },
65
+ code: { value: '400', writable:true }, // REVISIT: should be 'ASSERT_'... (i.e. msg) but we need to adjust all tests, and have a code catalogue
66
+ statusCode: { value: 400 }, // REVISIT: should go into mappings in adapter's error handlers -> requires a code catalogue // REVISIT: .statusCode vs .status?
67
+ numericSeverity: { value: 4, enumerable: true }, // REVISIT: that is OData-specific
68
+ })
69
+ }
70
+ exports.ValidationErrors = ValidationErrors
71
+
72
+
73
+ /** Adding basic validation capabilities to linked definitions. */
74
+ const $any = class any {
75
+
76
+ /**
77
+ * Central method for validating input data against CSN definitions.
78
+ * @param {any} value the input value to validate
79
+ * @param {Array} path the path prefix to use for error messages
80
+ * @param {Validation} ctx the request object used to record errors
81
+ */
82
+ validate (value, path, ctx) {
83
+ this.check_asserts (value, path, ctx)
84
+ }
85
+
86
+ /**
87
+ * Checks the type of provided input values as well as @asserts specified.
88
+ * On first call, it constructs an optimized instance-specific override of
89
+ * this method for subsequent usages, with statically determined checks.
90
+ */
91
+ check_asserts (val, path, /** @type {Validation} */ ctx) {
92
+ // if (this['@cds.validate'] === false) return this.set ('check_asserts', ()=>{})
93
+ const asserts = []
94
+ const type_check = conf.strict && this.strict_check || this.type_check
95
+ if (type_check) {
96
+ asserts.push ((v,p,ctx) => v == null || type_check(v) || ctx.error ('ASSERT_DATA_TYPE', p, this.name, v, this ))
97
+ }
98
+ if (this._is_mandatory()) {
99
+ asserts.push ((v,p,ctx) => v != null && v.trim?.() !== '' || ctx.error ('ASSERT_NOT_NULL', p, this.name, v)) // ASSERT_NOT_NULL is misleading -> should be ASSERT_REQUIRED
100
+ }
101
+ if (this['@assert.format']) {
102
+ const format = new RegExp(this['@assert.format'],'u')
103
+ asserts.push ((v,p,ctx) => v == null || format.test(v) || ctx.error ('ASSERT_FORMAT', p, this.name, v, format))
104
+ }
105
+ if (this['@assert.range'] && !this.enum) {
106
+ const [ min, max ] = this['@assert.range']
107
+ asserts.push ((v,p,ctx) => v == null || min <= v && v <= max || ctx.error ('ASSERT_RANGE', p, this.name, v, min, max))
108
+ }
109
+ if (this['@assert.enum'] || this['@assert.range'] && this.enum) {
110
+ const vals = Object.entries(this.enum).map(([k,v]) => 'val' in v ? v.val : k)
111
+ const enums = vals.reduce((a,v) => (a[v]=true, a),{})
112
+ asserts.push ((v,p,ctx) => v == null || v in enums || vals.some(x => x == v) || ctx.error ('ASSERT_ENUM', p, this.name, typeof v === 'string' ? `"${v}"` : v, vals.join(', ')))
113
+ }
114
+ if (!asserts.length) return this.check_asserts = ()=>{} // nothing to do
115
+ this.set ('check_asserts', (v,p,ctx) => asserts.forEach (a => a(v,p,ctx)))
116
+ this.check_asserts (val, path, ctx) // call first time
117
+ }
118
+
119
+ _is_mandatory (d=this) {
120
+ return d.own('_mandatory', ()=> {
121
+ if (d['@readonly']) return false // readonly -> not mandatory
122
+ if (d['@mandatory']) return true
123
+ if (d['@Common.FieldControl']?.['#'] === 'Mandatory') return true
124
+ else return false
125
+ })
126
+ }
127
+
128
+ _is_readonly (d=this) {
129
+ return d.own('_readonly', ()=> {
130
+ if (d['@readonly']) return true
131
+ if (d['@cds.on.insert']) return true
132
+ if (d['@cds.on.update']) return true
133
+ if (d['@Core.Computed']) return true
134
+ if (d['@Common.FieldControl']?.['#'] === 'ReadOnly') return true
135
+ if (d['@cds.api.ignore']) return true
136
+ else return false
137
+ })
138
+ }
139
+
140
+ /**
141
+ * Checks if a nested row of a deep update is in turn to be inserted or updated.
142
+ * This is the case if the row date does not contain all primary key elements of the target entity.
143
+ */
144
+ _is_insert (row) {
145
+ const entity = this._target || this
146
+ const keys = Object.keys (entity.keys||{})
147
+ if (!keys.length) return this.set('_is_insert', ()=> true), true
148
+ else this.set('_is_insert', data => typeof data === 'object' && !keys.every(k => k in data))
149
+ return this._is_insert (row)
150
+ }
151
+
152
+ _required (elements) {
153
+ const _required = Object.values(elements).filter(this._is_mandatory)
154
+ this.set('_required', ()=> _required)
155
+ return _required
156
+ }
157
+
158
+ /** Forward declaration for universal CSN */
159
+ get $struct() { return this['@odata.foreignKey4'] }
160
+ }
161
+
162
+ /** Structs iterate over their elements to validate them. */
163
+ class struct extends $any {
164
+ validate (data, path, /** @type {Validation} */ ctx, elements = this.elements, skip={}) {
165
+ const path_ = !path ? [] : [...path, this.name]; if (path?.row) path_.push({...path})
166
+ // check for required elements in case of inserts -- note: null values are handled in the payload loop below
167
+ if (ctx.insert || data && path_.length && this._is_insert(data)) for (let each of this._required (elements)) {
168
+ if (each.name in data) continue // got value for required element
169
+ if (each.name in skip) continue // skip uplinks in deep inserts -> see Composition.validate()
170
+ if (each.$struct in data) continue // got struct for flattened element/fk, e.g. {author:{ID:1}}
171
+ if (each.elements || each.foreignKeys) continue // skip struct-likes as we check flat payloads above, and deep payloads via struct.validate()
172
+ else ctx.error ('ASSERT_NOT_NULL', path_, each.name) // ASSERT_NOT_NULL should be ASSERT_REQUIRED
173
+ }
174
+ // check values of given data
175
+ for (let each in data) { // will work for structured payloads as well as flattened ones with universal CSN
176
+ let /** @type {$any} */ d = elements[each]
177
+ if (!d || typeof d === 'function') ctx.unknown (each, this, data) // `each` might be a method of LinkedDefinitions, like filter, map, some, every, ...
178
+ else if (ctx.cleanse && d._is_readonly()) delete data[each]
179
+ else if (d['@cds.validate'] !== false) d.validate (data[each], path_, ctx)
180
+ }
181
+ }
182
+ }
183
+
184
+ /** Array definitions validate the entries of an array against their items definition. */
185
+ class array extends $any {
186
+ validate (data, path, /** @type {Validation} */ ctx) {
187
+ if (data == null) return super.validate (data, path, ctx)
188
+ if (!Array.isArray(data)) return ctx.error ('ASSERT_ARRAY', path, this.name)
189
+ const path_ = path?.concat(this.name)
190
+ const /** @type {$any} */ items = { __proto__:this.items, name: undefined }
191
+ data.forEach ((entry,i) => items.validate (entry, path_.concat(i), ctx))
192
+ }
193
+ }
194
+
195
+ /** Entities support both as input: single records as well as arrays of which. */
196
+ class entity extends struct {
197
+ validate (data, path, ctx, ...more) {
198
+ const _path4 = !path ? ()=>path : (row,i) => ({__proto__:path, index:i, row, def:this})
199
+ if (!Array.isArray(data)) return super.validate (data, _path4(data), ctx, ...more)
200
+ return data.forEach ((row,i) => super.validate (row, _path4(row,i), ctx, ...more))
201
+ }
202
+ }
203
+
204
+ /** Actions are struct-like, with their parameters as elements to validate. */
205
+ class action extends struct {
206
+ validate (data, path, ctx) {
207
+ if (this.params) super.validate (data, path, ctx, this.params)
208
+ }
209
+ }
210
+
211
+ /** Managed associations are struct-like, with foreign keys as elements to validate. */
212
+ class Association extends struct {
213
+ validate (data, path, ctx) {
214
+ if (this.foreignKeys) super.validate (data, path, ctx, this.foreignKeys)
215
+ }
216
+ }
217
+
218
+ /** Compositions are like nested entities, validating deep input against their target entity definitions. */
219
+ class Composition extends entity {
220
+ validate (data, path, ctx) { if (!data) return
221
+ const elements = this._target.elements
222
+ const uplinks = {} // statically determine the uplinks for this composition
223
+ if (this.on) for (let {ref} of this.on) if (ref?.[0] === this.name) {
224
+ const fk = ref[1], fk_ = fk+'_'; uplinks[fk] = true
225
+ for (let e in elements) if (e.startsWith(fk_)) uplinks[e] = true
226
+ }
227
+ this.validate = (data, path, ctx) => super.validate (data, path, ctx, elements, uplinks)
228
+ this.validate (data, path, ctx) // call first time
229
+ }
230
+ }
231
+
232
+
233
+ // Type checks ---------------------------------------------------------------
234
+
235
+ $any.prototype.type_check = undefined
236
+
237
+ /**
238
+ * This getter constructs and returns a type check function for the declared precision and scale.
239
+ * Precision is the total number of digits, scale the number of digits after the decimal point.
240
+ */
241
+ class Decimal extends $any { get type_check() {
242
+ const { precision:p, scale:s } = this, rx = RegExp (
243
+ !p ? `^[+-]?\\d+(?:\\.\\d+)?$` :
244
+ !s ? `^[+-]?\\d{1,${p}}$` :
245
+ p === s ? `^[+-]?0(?:\\.\\d{1,${s}})?$` :
246
+ /* p,s */ `^[+-]?\\d{1,${p-s}}(?:\\.\\d{1,${s}})?$`
247
+ )
248
+ return v => rx.test(v)
249
+ }}
250
+
251
+ class string extends $any { get type_check() {
252
+ const { length:l } = this; return l
253
+ ? v => typeof v === 'string' && v.length <= l
254
+ : v => typeof v === 'string'
255
+ }}
256
+
257
+ const {Readable} = require('stream')
258
+ const _range_check = (range, min=-range) => v => min <= v && v < range
259
+ const _regex_check = (rx) => v => rx.test(v)
260
+ const _date_check = (...parts) => {
261
+ const rx = RegExp('^'+parts.map(p => p.source||p).join('')+'$')
262
+ return v => v instanceof Date || rx.test(v)
263
+ }
264
+ const YYYY = /\d{4}/
265
+ const MM = /-(0[1-9]|1[0-2])/
266
+ const DD = /-(0[1-9]|[12]\d|3[01])/
267
+ const hh = /[0-2]\d/
268
+ const mm = /:[0-5]\d/
269
+ const ss = /(?::[0-5]\d)?/
270
+ const ms = /(?::[0-5]\d(?:\.\d+)?)?/
271
+ const tz = /(?:Z|[+-][0-2]\d:?[0-5]\d)?/
272
+
273
+ const $ = cds.linked.classes
274
+ $.UUID.prototype .strict_check = _regex_check (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)
275
+ $.boolean.prototype .strict_check = v => typeof v === 'boolean'
276
+ $.boolean.prototype .type_check = v => typeof v === 'boolean' || v === 0 || v === 1
277
+ $.number.prototype .type_check = v => !isNaN(v)
278
+ $.Integer.prototype .type_check = _range_check (2**53)
279
+ $.Int16.prototype .type_check = _range_check (2**15)
280
+ $.Int32.prototype .type_check = _range_check (2**31)
281
+ $.Int64.prototype .type_check = _range_check (2n**63n)
282
+ $.UInt8.prototype .type_check = _range_check (256+1,0)
283
+ $.Time.prototype .type_check = _date_check (hh,mm,ss)
284
+ $.Date.prototype .type_check = _date_check (YYYY,MM,DD)
285
+ $.DateTime.prototype .type_check = _date_check (YYYY,MM,DD,'(?:T',hh,mm,ss,tz,')?')
286
+ $.Timestamp.prototype .type_check = _date_check (YYYY,MM,DD,'(?:T',hh,mm,ms,tz,')?')
287
+ $.Binary.prototype .type_check = v => Buffer.isBuffer(v) || typeof v === 'string'
288
+ $.LargeBinary.prototype .type_check = v => Buffer.isBuffer(v) || typeof v === 'string' || v instanceof Readable
289
+ $.LargeString.prototype .type_check = v => Buffer.isBuffer(v) || typeof v === 'string' || v instanceof Readable
290
+
291
+ // Mixin above class extensions to cds.linked.classes
292
+ $.mixin ( Decimal, string, $any, action, array, struct, entity, Association, Composition )
@@ -85,9 +85,3 @@ exports._no_primary_db = new Proxy ({},{ get: function fn(_,p) { error (`Not con
85
85
  cds ${process.argv[2]} --in-memory` : ''}`
86
86
 
87
87
  ,{},fn) }})
88
-
89
-
90
- exports._outdated_dk = () => error `
91
- This application uses @sap/cds version >= 7, which is not compatible with the installed @sap/cds-dk version 6.
92
- Either update @sap/cds-dk to version 7 or downgrade @sap/cds to version 6 instead.
93
- `
@@ -1,6 +1,7 @@
1
1
  const cds = require('../index'), conf = cds.env.log
2
2
  const log = module.exports = exports = cds_log
3
3
  const path = require('path')
4
+ /* eslint-disable no-console */
4
5
 
5
6
 
6
7
  /**
@@ -88,7 +89,6 @@ exports.debug = function cds_debug (id, options) {
88
89
  * @param {string} [label] the module for which a logger is requested
89
90
  * @param {number} [level] the log level to enable -> 0=off, 1=error, 2=warn, 3=info, 4=debug, 5=trace
90
91
  */
91
- /* eslint-disable no-console */
92
92
  exports.Logger = (label, level) => {
93
93
  const fmt = (level,args) => logger.format (label,level,...args)
94
94
  const logger = {
@@ -112,7 +112,7 @@ function Logger (label, level) { return exports.Logger (label, level) }
112
112
  * .debug(), .info(), .warn(), .error(), etc.
113
113
  */
114
114
  exports.winstonLogger = (options) => (label, level) => {
115
- const winston = require("winston") // eslint-disable-line cds/no-missing-dependencies
115
+ const winston = require("winston")
116
116
  const logger = winston.createLogger({
117
117
  levels: log.levels, level: Object.keys(log.levels)[level],
118
118
  transports: [new winston.transports.Console()],
@@ -175,7 +175,7 @@ const { ERROR, WARN, INFO, DEBUG, TRACE } = exports.levels = {
175
175
  const conf = cds.env.log
176
176
  if (conf.Logger) {
177
177
  let resolvedPath
178
- try { resolvedPath = require.resolve(conf.Logger) } catch {
178
+ try { resolvedPath = require.resolve(conf.Logger) } catch {
179
179
  try { resolvedPath = require.resolve(path.join(cds.root, conf.Logger)) } catch {
180
180
  throw new Error(`Cannot find logger at "${conf.Logger}"`)
181
181
  }
@@ -100,7 +100,7 @@ module.exports = function format(module, level, ...args) {
100
100
  // return array with the stringified toLog (to avoid multiple log lines) as the sole element
101
101
  try {
102
102
  return [JSON.stringify(toLog)]
103
- } catch (e) {
103
+ } catch {
104
104
  // try again with removed circular references
105
105
  return [JSON.stringify(toLog, _getCircularReplacer())]
106
106
  }
@@ -23,7 +23,6 @@ module.exports = class LogService extends cds.Service {
23
23
  }
24
24
 
25
25
  // serve embedded UI
26
- // eslint-disable-next-line cds/no-missing-dependencies
27
26
  const express = require('express')
28
27
  app.use (path+'/ui', express.static(__dirname+'/vue.html'))
29
28
 
package/lib/plugins.js CHANGED
@@ -29,8 +29,8 @@ exports.fetch = function (DEV = process.env.NODE_ENV !== 'production') {
29
29
  */
30
30
  exports.activate = async function () {
31
31
  const DEBUG = cds.debug ('plugins', {label:'cds'})
32
- DEBUG && console.time ('[cds] - loaded plugins in')
33
- const plugins = exports.fetch(), { local } = cds.utils
32
+ DEBUG?.time ('[cds] - loaded plugins in')
33
+ const { plugins } = cds.env, { local } = cds.utils
34
34
  await Promise.all (Object.entries(plugins) .map (async ([ plugin, conf ]) => {
35
35
  DEBUG?.(`loading plugin ${plugin}:`, { impl: local(conf.impl) })
36
36
  // TODO: support ESM plugins. But see cap/cds/pull/1838#issuecomment-1177200 !
@@ -44,6 +44,6 @@ exports.activate = async function () {
44
44
  }
45
45
  return p
46
46
  }))
47
- DEBUG && console.timeEnd ('[cds] - loaded plugins in')
47
+ DEBUG?.timeEnd ('[cds] - loaded plugins in')
48
48
  return plugins
49
49
  }
package/lib/ql/Query.js CHANGED
@@ -36,9 +36,8 @@ class Query {
36
36
  /** Turns all queries into Thenables which execute with primary db by default */
37
37
  get then() {
38
38
  const srv = this._srv || cds.db || cds.error `Can't execute query as no primary database is connected.`
39
- const q = new AsyncResource('await cds.query')
40
- // Temporary solution for cds.stream in .then. Remove with the next major release.
41
- return (r,e) => q.runInAsyncScope (srv.run, srv, this) .then(rt => { rt = this._stream && rt ? Object.values(rt)[0] : rt; return r(rt) }, e)
39
+ const q = new AsyncResource('await cds.query')
40
+ return (r,e) => q.runInAsyncScope (srv.run, srv, this).then (r,e)
42
41
  }
43
42
 
44
43
  _target4 (...args) {
@@ -120,11 +119,4 @@ class Query {
120
119
 
121
120
  const _name = cds.env.sql.names === 'quoted' ? n =>`"${n}"` : n => n.replace(/[.:]/g,'_')
122
121
 
123
- if (cds.env.ql.quirks_mode) Object.defineProperty (Query.prototype, '_target4', {
124
- value: function (...args) {
125
- const { ref, as } = this._target_ref4 (...args)
126
- return ref.length === 1 && typeof ref[0] === 'string' && !as ? ref[0] : as ? {ref, as} : {ref}
127
- }
128
- })
129
-
130
122
  module.exports = Query
package/lib/ql/SELECT.js CHANGED
@@ -48,7 +48,7 @@ module.exports = class Query extends Whereable {
48
48
  if (c) return this._add('columns',c)
49
49
  if (typeof cols === 'string') {
50
50
  try { parse.path(cols) }
51
- catch(e) { //> it can't be a from
51
+ catch { //> it can't be a from
52
52
  try { return this.columns(...arguments) }
53
53
  catch(e) {
54
54
  if (!e.message.startsWith('CDS compilation failed')) throw e
@@ -44,9 +44,9 @@ class Query extends require('./Query') {
44
44
  if (typeof key !== 'object' || key === null) key = { [Object.keys(this._target.keys||{ID:1})[0]]: key }
45
45
  if (this.SELECT) this.SELECT.one = true
46
46
  if (cds.env.features.keys_into_where) return this.where(key)
47
- if (this.UPDATE) { this.UPDATE.entity = { ref: [{ id: cds.env.ql.quirks_mode ? this.UPDATE.entity : this.UPDATE.entity.ref.at(-1), where: predicate4([key]) }] }; return this }
47
+ if (this.UPDATE) { this.UPDATE.entity = { ref: [{ id: this.UPDATE.entity.ref.at(-1), where: predicate4([key]) }] }; return this }
48
48
  if (this.SELECT) { this.SELECT.from.ref[this.SELECT.from.ref.length-1] = { id: this.SELECT.from.ref.at(-1), where: predicate4([key]) }; return this }
49
- if (this.DELETE) { this.DELETE.from = { ref: [{ id: cds.env.ql.quirks_mode ? this.DELETE.from : this.DELETE.from.ref.at(-1), where: predicate4([key]) }] }; return this }
49
+ if (this.DELETE) { this.DELETE.from = { ref: [{ id: this.DELETE.from.ref.at(-1), where: predicate4([key]) }] }; return this }
50
50
  return this.where(key)
51
51
  }
52
52
  }
@@ -97,6 +97,7 @@ const _object_predicate = ([arg], _clause) => { // e.g. .where ({ID:4711, stock:
97
97
  else if (is_cqn(x)) pred.push('=', x)
98
98
  else if (x instanceof Buffer) pred.push('=', {val:x})
99
99
  else if (x instanceof RegExp) pred.push('like', {val:x})
100
+ else if (x instanceof Date) pred.push('=', {val:x})
100
101
  else if (typeof x === 'object') for (let op in x) pred.push(op, val(x[op]))
101
102
  else if (_clause === 'on' && typeof x === 'string') pred.push('=', { ref: x.split('.') })
102
103
  else pred.push('=', {val:x})
@@ -1,26 +1,15 @@
1
1
  const { AsyncLocalStorage } = require ('async_hooks')
2
2
  const { EventEmitter } = require('events')
3
- const EventContext = require('./context')
3
+ const EventContext = require('./context'), ec4 = v => {
4
+ if (v instanceof EventContext || typeof v !== 'object') return v
5
+ if (v.context) return v.context
6
+ else return EventContext.for(v)
7
+ }
4
8
 
5
9
  module.exports = new class extends AsyncLocalStorage {
6
10
 
7
- run(v,fn,...args) { return super.run (this._context4(v),fn,...args) }
8
- enterWith(v) { return super.enterWith (this._context4(v)) }
9
-
10
- _context4(v) {
11
- if (v instanceof EventContext || typeof v !== 'object') return v
12
- if (v.context) return v.context
13
- return EventContext.for(v)
14
- }
15
-
16
- /** @returns {EventContext} */
17
- _for (cds,v) {
18
- Reflect.defineProperty (cds,'context', { enumerable:1, ... {
19
- set:(v) => this.enterWith(v),
20
- get:()=> this.getStore(),
21
- }})
22
- return cds.context = v // IMPORTANT: we need to set it initially, to get it all wired up correctly
23
- }
11
+ run(v,fn,...args) { return super.run (ec4(v),fn,...args) }
12
+ enterWith(v) { return super.enterWith (ec4(v)) }
24
13
 
25
14
  spawn (o,fn, /** @type {import('../index')} cds */ cds=this) {
26
15
  if (typeof o === 'function') [fn,o] = [o,fn] //> for compatibility
@@ -37,22 +26,22 @@ module.exports = new class extends AsyncLocalStorage {
37
26
  tx.model = cds.context.model
38
27
  }
39
28
  return Promise.resolve(fn(tx))
40
- .then (tx.commit, e => tx.rollback(_error(e, cds)))
29
+ .then (tx.commit, e => {
30
+ cds.log().error(`ERROR occurred in background job:`, e)
31
+ return tx.rollback(e)
32
+ })
41
33
  .then (res => Promise.all(em.listeners('succeeded').map(each => each(res))))
42
34
  .catch (err => Promise.all(em.listeners('failed').map(each => each(err))))
43
35
  .finally (() => Promise.all(em.listeners('done').map(each => each())))
44
36
  })
45
37
  }
46
38
  const em = new EventEmitter
47
- const { every, after } = o || {}
48
- if (every) {
49
- em.timer = setInterval(fx, every)
39
+ if (o?.every) {
40
+ em.timer = setInterval(fx, o.every)
50
41
  cds.on('shutdown', () => clearInterval(em.timer))
51
42
  } else {
52
- em.timer = (after ? setTimeout(fx, after) : setImmediate(fx)).unref()
43
+ em.timer = (o?.after ? setTimeout(fx, o.after) : setImmediate(fx)).unref()
53
44
  }
54
45
  return em
55
46
  }
56
47
  }
57
-
58
- const _error = (err, cds) => { cds.log().error(`ERROR occurred in background job:`, err); return err }
@@ -3,10 +3,6 @@ const async_events = { succeeded:1, failed:1, done:1, commit:1 }
3
3
  const req_locale = require('./locale')
4
4
  const { EventEmitter } = require('events')
5
5
 
6
- // getter functions extracted to show deprecation warning only once
7
- const _getTenant = req => req.tenant
8
- const _getLocale = req => req.locale
9
-
10
6
  /**
11
7
  * This is the base class for `cds.Events` and `cds.Requests`,
12
8
  * providing the transaction context nature to all instances.
@@ -20,7 +16,6 @@ class EventContext {
20
16
  const ctx = new this (_)
21
17
  const base = cds.context
22
18
  if (base) {
23
- // we inherit from former cds.currents (except for the timestamp if new root ctx)
24
19
  if (_as_root) ctx._set('_propagated', Object.create(base, { timestamp: { value: undefined } }))
25
20
  else {
26
21
  ctx._set('_propagated', base)
@@ -90,26 +85,14 @@ class EventContext {
90
85
  if (t) super.tenant = t
91
86
  }
92
87
  get tenant() {
93
- return super.tenant = this._propagated.tenant
88
+ return super.tenant = this._propagated.tenant || this._.req?.tenant
94
89
  }
95
90
 
96
91
  set user(u) {
97
- if (u && typeof u === 'object') for (let p of ['tenant','locale']) {
98
- let pd = Reflect.getOwnPropertyDescriptor(u,p)
99
- if (pd?.value) this[p] = pd.value
100
- }
101
- let user = u instanceof cds.User ? Object.create(u,{
102
- tenant: {get:()=> cds.utils.deprecated (_getTenant, {kind: 'Property', old: 'req.user.tenant', use: 'req.tenant'})(this)},
103
- locale: {get:()=> cds.utils.deprecated (_getLocale, {kind: 'Property', old: 'req.user.locale', use: 'req.locale'})(this)},
104
- }) : Object.defineProperties (new cds.User(u), {
105
- tenant: {get:()=> cds.utils.deprecated (_getTenant, {kind: 'Property', old: 'req.user.tenant', use: 'req.tenant'})(this)},
106
- locale: {get:()=> cds.utils.deprecated (_getLocale, {kind: 'Property', old: 'req.user.locale', use: 'req.locale'})(this)},
107
- })
108
- super.user = user
92
+ super.user = cds.User(u)
109
93
  }
110
94
  get user() {
111
- this.user = this._propagated.user || _anonymous
112
- return this.user // IMPORTANT: first set this.user then return it separately to ensure we return the compat-wrapped objects
95
+ return super.user = this._propagated.user || cds.User (this._.req?.user)
113
96
  }
114
97
 
115
98
  set locale(l) {
@@ -124,13 +107,13 @@ class EventContext {
124
107
  }
125
108
 
126
109
  get _features() {
127
- return super._features = this._propagated._features || Features.for (this.http?.req?.features || this.user?.features || this.http?.req?.user?.features)
110
+ return super._features = this._propagated._features || Features.for (this._.req?.features || this.user.features)
128
111
  }
129
112
  get features() {
130
113
  return super.features = this._features || Features.none
131
114
  }
132
115
  set features(v) {
133
- super.features = Features.for(v)
116
+ super.features = Features.for(v) || Features.none
134
117
  }
135
118
 
136
119
  get model() {
@@ -184,11 +167,26 @@ class EventContext {
184
167
  get _tx() { return this.tx } // REVISIT: for compatibility to bade usages of req._tx
185
168
  }
186
169
 
187
- const _anonymous = new cds.User.default
188
-
189
170
 
190
171
  class Features {
191
- static for (x) { // normalizes features to an object
172
+ /**
173
+ * Returns an instance of this class for different supported input variants to specify features:
174
+ *
175
+ * - an `array` of feature names of features to be enabled
176
+ * - a `string` with a comma-separated list of feature names
177
+ * - the string `'*'` to generically enable _all_ features
178
+ * - an `object` with feature names as keys and boolean values true/false
179
+ * - `null` or `undefined` or an _empty_ `string` or `array` or `object` to indicate no features
180
+ *
181
+ * Note that the returned instance is effectively an object that has all enabled feature
182
+ * names as keys with the value true. In particualar, that means if the input is an object,
183
+ * with some flags set to false, the returned instance will not have these keys at all.
184
+ *
185
+ * Hence, users of cds.context.features can simply check for the presence of a feature
186
+ * with the like of `if ('foo' in cds.context.features)`...
187
+ * @returns {Features}
188
+ */
189
+ static for (x) {
192
190
  if (x == null) return
193
191
  if (x === '*') return this.all
194
192
  if (Array.isArray(x)) ; //> go on below
@@ -90,33 +90,7 @@ class Request extends require('./event') {
90
90
 
91
91
  const ref = subject.ref || [ subject ]
92
92
 
93
- if(!target.drafts || cds.env.fiori.lean_draft) return super.subject = { ref }
94
-
95
- // special handling for old draft logic
96
- let subjectRef = []
97
- ref.forEach ((item, i) => {
98
- // to one assoc & comp
99
- if (typeof item === 'string') {
100
- subjectRef.push(item)
101
- return
102
- }
103
-
104
- // create key value pair without IsActiveEntity & adjust root target
105
- const keys = {}
106
- let id = item.id
107
- if (!id) return
108
- for (let j = 0; j < item?.where?.length; j = j + 4) {
109
- const key = item.where[j].ref[0]
110
- const value = item.where[j + 2].val
111
- if (key !== 'IsActiveEntity') keys[key] = value
112
- else if (i === 0 && key === 'IsActiveEntity' && value === false) id = item.id + '_drafts'
113
- }
114
-
115
- // create copy
116
- const {SELECT:{from:{ref}}} = SELECT.from(id,keys)
117
- subjectRef.push(...ref)
118
- })
119
- return super.subject = {ref: subjectRef}
93
+ return super.subject = { ref }
120
94
  }
121
95
 
122
96
  reply (results) { return this.results = results }
@@ -135,7 +109,6 @@ class Request extends require('./event') {
135
109
  ).join('')
136
110
  throw me
137
111
  }
138
- if (args[0] === 401 && this._.req?._login) this._.req._login()
139
112
  let e = this.error(...args)
140
113
  if (!e.stack) Error.captureStackTrace (e = Object.assign(new Error,e), this.reject)
141
114
  throw e
@@ -144,12 +117,6 @@ class Request extends require('./event') {
144
117
  // Lazily create message collectors for .errors and .messages
145
118
  /** @private */ get _messages() { return this.messages = this._set ('_messages', new Responses) }
146
119
  /** @private */ get _errors() { return this.errors = this._set ('_errors', new Errors) }
147
-
148
- // REVISIT: Used for request logging in cds.server
149
- // REVISIT: _.odataReq stuff should go into subclass ODataRequest
150
- get _path() { return this._set ('_path', this._.odataReq ? this._.odataReq._url.pathname : this._.req && this._.req.path) }
151
- get _query() { return this._set ('_query', this._.odataReq ? this._.odataReq._queryOptions : this._.req && this._.req.query) }
152
-
153
120
  }
154
121
 
155
122
  module.exports = Request