@sap/cds 7.9.4 → 8.0.4

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 (276) hide show
  1. package/CHANGELOG.md +128 -3659
  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 +9 -41
  15. package/lib/auth/index.js +1 -14
  16. package/lib/auth/jwt-auth.js +10 -40
  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/yaml.js +3 -3
  32. package/lib/dbs/cds-deploy.js +4 -2
  33. package/lib/env/cds-env.js +10 -14
  34. package/lib/env/cds-requires.js +30 -13
  35. package/lib/env/defaults.js +46 -16
  36. package/lib/env/plugins.js +1 -1
  37. package/lib/env/schemas/cds-rc.js +8 -4
  38. package/lib/env/schemas/index.js +7 -7
  39. package/lib/env/serviceBindings.js +1 -1
  40. package/lib/index.js +12 -10
  41. package/lib/lazy.js +1 -1
  42. package/lib/linked/classes.js +36 -8
  43. package/lib/linked/entities.js +2 -10
  44. package/lib/linked/models.js +2 -1
  45. package/lib/linked/validate.js +292 -0
  46. package/lib/log/cds-error.js +0 -6
  47. package/lib/log/cds-log.js +3 -3
  48. package/lib/log/format/json.js +1 -1
  49. package/lib/log/service/index.js +0 -1
  50. package/lib/plugins.js +2 -2
  51. package/lib/ql/Query.js +2 -10
  52. package/lib/ql/SELECT.js +1 -1
  53. package/lib/ql/Whereable.js +3 -2
  54. package/lib/req/cds-context.js +14 -25
  55. package/lib/req/context.js +23 -25
  56. package/lib/req/request.js +1 -34
  57. package/lib/req/user.js +47 -35
  58. package/lib/srv/bindings.js +1 -1
  59. package/lib/srv/cds-connect.js +4 -4
  60. package/lib/srv/cds-serve.js +2 -2
  61. package/lib/srv/factory.js +1 -1
  62. package/lib/srv/middlewares/cds-context.js +11 -22
  63. package/lib/srv/middlewares/ctx-model.js +2 -3
  64. package/lib/srv/middlewares/errors.js +41 -8
  65. package/lib/srv/middlewares/index.js +3 -3
  66. package/lib/srv/middlewares/trace.js +0 -2
  67. package/lib/srv/protocols/hcql.js +15 -10
  68. package/lib/srv/protocols/http.js +44 -49
  69. package/lib/srv/protocols/index.js +1 -23
  70. package/lib/srv/protocols/odata-v4.js +12 -74
  71. package/lib/srv/protocols/rest.js +1 -13
  72. package/lib/srv/srv-api.js +0 -20
  73. package/lib/srv/srv-dispatch.js +3 -2
  74. package/lib/srv/srv-handlers.js +22 -11
  75. package/lib/srv/srv-methods.js +2 -2
  76. package/lib/srv/srv-models.js +3 -36
  77. package/lib/test/expect.js +343 -0
  78. package/lib/test/index.js +2 -0
  79. package/lib/test/reporter.js +176 -0
  80. package/lib/utils/axios.js +10 -9
  81. package/lib/utils/cds-test.js +85 -36
  82. package/lib/utils/cds-utils.js +54 -7
  83. package/lib/utils/check-version.js +0 -4
  84. package/lib/utils/colors.js +49 -0
  85. package/lib/utils/data.js +5 -4
  86. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +2 -7
  87. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +3 -30
  88. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +6 -12
  89. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +1 -3
  90. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +0 -1
  91. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +4 -7
  92. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +12 -6
  93. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +2 -4
  94. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +1 -0
  95. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -1
  96. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +0 -1
  97. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +1 -3
  98. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +1 -1
  99. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/AbstractEdmStructuredType.js +1 -2
  100. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +5 -0
  101. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ContextURLFactory.js +1 -1
  102. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +9 -43
  103. package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +0 -1
  104. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +8 -3
  105. package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +4 -2
  106. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +1 -3
  107. package/libx/_runtime/cds-services/util/assert.js +1 -1
  108. package/libx/_runtime/cds.js +10 -3
  109. package/libx/_runtime/common/Service.js +12 -32
  110. package/libx/_runtime/common/aspects/any.js +1 -0
  111. package/libx/_runtime/common/code-ext/execute.js +1 -1
  112. package/libx/_runtime/common/code-ext/worker.js +0 -1
  113. package/libx/_runtime/common/composition/data.js +0 -1
  114. package/libx/_runtime/common/composition/delete.js +0 -1
  115. package/libx/_runtime/common/composition/tree.js +0 -1
  116. package/libx/_runtime/common/composition/update.js +3 -3
  117. package/libx/_runtime/common/error/frontend.js +21 -12
  118. package/libx/_runtime/common/error/log.js +36 -0
  119. package/libx/_runtime/common/error/utils.js +2 -5
  120. package/libx/_runtime/common/generic/auth/autoexpose.js +18 -17
  121. package/libx/_runtime/common/generic/auth/expand.js +1 -1
  122. package/libx/_runtime/common/generic/auth/readOnly.js +1 -2
  123. package/libx/_runtime/common/generic/auth/restrict.js +23 -42
  124. package/libx/_runtime/common/generic/auth/restrictions.js +2 -7
  125. package/libx/_runtime/common/generic/auth/utils.js +91 -88
  126. package/libx/_runtime/common/generic/crud.js +6 -5
  127. package/libx/_runtime/common/generic/etag.js +7 -12
  128. package/libx/_runtime/common/generic/input.js +70 -68
  129. package/libx/_runtime/common/generic/paging.js +1 -0
  130. package/libx/_runtime/common/generic/sorting.js +1 -0
  131. package/libx/_runtime/common/generic/temporal.js +8 -2
  132. package/libx/_runtime/common/i18n/index.js +1 -1
  133. package/libx/_runtime/common/i18n/messages.properties +3 -1
  134. package/libx/_runtime/common/utils/binary.js +8 -2
  135. package/libx/_runtime/common/utils/compareJson.js +5 -1
  136. package/libx/_runtime/common/utils/copy.js +6 -11
  137. package/libx/_runtime/common/utils/cqn2cqn4sql.js +16 -14
  138. package/libx/_runtime/common/utils/differ.js +3 -6
  139. package/libx/_runtime/common/utils/keys.js +77 -18
  140. package/libx/_runtime/common/utils/postProcess.js +12 -15
  141. package/libx/_runtime/common/utils/propagateForeignKeys.js +0 -1
  142. package/libx/_runtime/common/utils/resolveView.js +2 -3
  143. package/libx/_runtime/common/utils/restrictions.js +45 -17
  144. package/libx/_runtime/common/utils/rewriteAsterisks.js +1 -8
  145. package/libx/_runtime/common/utils/stream.js +3 -16
  146. package/libx/_runtime/common/utils/streamProp.js +8 -18
  147. package/libx/_runtime/common/utils/structured.js +1 -1
  148. package/libx/_runtime/common/utils/ucsn.js +0 -2
  149. package/libx/_runtime/db/Service.js +0 -72
  150. package/libx/_runtime/db/data-conversion/post-processing.js +0 -1
  151. package/libx/_runtime/db/expand/expandCQNToJoin.js +9 -9
  152. package/libx/_runtime/db/expand/rawToExpanded.js +0 -8
  153. package/libx/_runtime/db/generic/input.js +3 -8
  154. package/libx/_runtime/db/generic/rewrite.js +1 -0
  155. package/libx/_runtime/db/query/read.js +2 -2
  156. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +0 -1
  157. package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -1
  158. package/libx/_runtime/db/utils/columns.js +2 -6
  159. package/libx/_runtime/fiori/lean-draft.js +138 -56
  160. package/libx/_runtime/hana/Service.js +0 -1
  161. package/libx/_runtime/hana/driver.js +1 -1
  162. package/libx/_runtime/hana/dynatrace.js +1 -2
  163. package/libx/_runtime/hana/pool.js +11 -21
  164. package/libx/_runtime/hana/streaming.js +0 -1
  165. package/libx/_runtime/messaging/common-utils/AMQPClient.js +0 -1
  166. package/libx/_runtime/messaging/common-utils/authorizedRequest.js +1 -1
  167. package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +1 -1
  168. package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +1 -1
  169. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -33
  170. package/libx/_runtime/messaging/event-broker.js +54 -27
  171. package/libx/_runtime/messaging/file-based.js +3 -3
  172. package/libx/_runtime/messaging/http-utils/token.js +1 -1
  173. package/libx/_runtime/messaging/kafka.js +2 -2
  174. package/libx/_runtime/messaging/redis-messaging.js +0 -1
  175. package/libx/_runtime/remote/Service.js +25 -25
  176. package/libx/_runtime/remote/utils/client.js +4 -5
  177. package/libx/_runtime/remote/utils/cloudSdkProvider.js +0 -3
  178. package/libx/_runtime/remote/utils/data.js +0 -1
  179. package/libx/_runtime/sqlite/Service.js +1 -2
  180. package/libx/_runtime/ucl/Service.js +37 -78
  181. package/libx/common/assert/index.js +22 -21
  182. package/libx/common/assert/type-relaxed.js +39 -0
  183. package/libx/common/assert/utils.js +3 -2
  184. package/libx/common/assert/validation.js +3 -8
  185. package/libx/common/utils/index.js +5 -0
  186. package/libx/common/utils/path.js +51 -0
  187. package/libx/odata/ODataAdapter.js +126 -0
  188. package/libx/odata/index.js +15 -2
  189. package/libx/odata/middleware/batch.js +320 -84
  190. package/libx/odata/middleware/body-parser.js +33 -0
  191. package/libx/odata/middleware/create.js +44 -59
  192. package/libx/odata/middleware/delete.js +23 -12
  193. package/libx/odata/middleware/error.js +30 -6
  194. package/libx/odata/middleware/metadata.js +38 -26
  195. package/libx/odata/middleware/operation.js +93 -69
  196. package/libx/odata/middleware/parse.js +6 -8
  197. package/libx/odata/middleware/read.js +117 -93
  198. package/libx/odata/middleware/service-document.js +22 -19
  199. package/libx/odata/middleware/stream.js +54 -56
  200. package/libx/odata/middleware/update.js +79 -87
  201. package/libx/odata/parse/afterburner.js +191 -175
  202. package/libx/odata/parse/cqn2odata.js +5 -5
  203. package/libx/odata/parse/grammar.peggy +27 -20
  204. package/libx/odata/parse/multipartToJson.js +17 -9
  205. package/libx/odata/parse/parser.js +1 -1
  206. package/libx/odata/utils/etag.js +14 -6
  207. package/libx/odata/utils/index.js +84 -12
  208. package/libx/odata/utils/metadata.js +161 -0
  209. package/libx/odata/utils/postProcess.js +89 -0
  210. package/libx/odata/utils/readAfterWrite.js +134 -17
  211. package/libx/odata/utils/result.js +36 -142
  212. package/libx/outbox/index.js +4 -3
  213. package/libx/rest/RestAdapter.js +115 -182
  214. package/libx/rest/middleware/create.js +28 -24
  215. package/libx/rest/middleware/delete.js +7 -10
  216. package/libx/rest/middleware/error.js +26 -16
  217. package/libx/rest/middleware/operation.js +48 -41
  218. package/libx/rest/middleware/parse.js +128 -126
  219. package/libx/rest/middleware/read.js +20 -27
  220. package/libx/rest/middleware/update.js +26 -31
  221. package/package.json +17 -8
  222. package/server.js +4 -2
  223. package/apis/cds.d.ts +0 -3
  224. package/apis/core.d.ts +0 -21
  225. package/apis/cqn.d.ts +0 -18
  226. package/apis/csn.d.ts +0 -21
  227. package/apis/events.d.ts +0 -18
  228. package/apis/internal/inference.d.ts +0 -18
  229. package/apis/linked.d.ts +0 -18
  230. package/apis/log.d.ts +0 -20
  231. package/apis/models.d.ts +0 -18
  232. package/apis/ql.d.ts +0 -18
  233. package/apis/reflect.d.ts +0 -32
  234. package/apis/server.d.ts +0 -18
  235. package/apis/services.d.ts +0 -22
  236. package/bin/cds-serve.js +0 -56
  237. package/lib/compile/to/gql.js +0 -15
  238. package/lib/srv/protocols/_legacy.js +0 -44
  239. package/lib/utils/jest.js +0 -43
  240. package/libx/_runtime/auth/index.js +0 -193
  241. package/libx/_runtime/auth/strategies/JWT.js +0 -37
  242. package/libx/_runtime/auth/strategies/basic.js +0 -20
  243. package/libx/_runtime/auth/strategies/dummy.js +0 -14
  244. package/libx/_runtime/auth/strategies/ias-auth.js +0 -1
  245. package/libx/_runtime/auth/strategies/mock.js +0 -77
  246. package/libx/_runtime/auth/strategies/xssecUtils.js +0 -93
  247. package/libx/_runtime/auth/strategies/xsuaa.js +0 -38
  248. package/libx/_runtime/common/perf/index.js +0 -19
  249. package/libx/_runtime/common/utils/ensureIEEE754.js +0 -29
  250. package/libx/_runtime/fiori/draft.js +0 -2
  251. package/libx/_runtime/fiori/generic/activate.js +0 -190
  252. package/libx/_runtime/fiori/generic/before.js +0 -201
  253. package/libx/_runtime/fiori/generic/cancel.js +0 -19
  254. package/libx/_runtime/fiori/generic/delete.js +0 -21
  255. package/libx/_runtime/fiori/generic/edit.js +0 -157
  256. package/libx/_runtime/fiori/generic/index.js +0 -25
  257. package/libx/_runtime/fiori/generic/new.js +0 -82
  258. package/libx/_runtime/fiori/generic/patch.js +0 -101
  259. package/libx/_runtime/fiori/generic/prepare.js +0 -57
  260. package/libx/_runtime/fiori/generic/read.js +0 -1340
  261. package/libx/_runtime/fiori/generic/readOverDraft.js +0 -146
  262. package/libx/_runtime/fiori/utils/csn.js +0 -13
  263. package/libx/_runtime/fiori/utils/delete.js +0 -114
  264. package/libx/_runtime/fiori/utils/handler.js +0 -264
  265. package/libx/_runtime/fiori/utils/lockInfo.js +0 -27
  266. package/libx/_runtime/fiori/utils/req.js +0 -23
  267. package/libx/_runtime/fiori/utils/stream.js +0 -36
  268. package/libx/_runtime/fiori/utils/where.js +0 -254
  269. package/libx/_runtime/index.js +0 -22
  270. package/libx/odata/utils/handler.js +0 -120
  271. package/libx/odata/utils/metaInfo.js +0 -410
  272. package/libx/odata/utils/path.js +0 -75
  273. package/libx/rest/RestRequest.js +0 -32
  274. package/libx/rest/index.js +0 -3
  275. package/libx/rest/readme.md +0 -1
  276. /package/libx/common/assert/{type.js → type-strict.js} +0 -0
@@ -1,14 +0,0 @@
1
- const cds = require('../../cds')
2
-
3
- class DummyStrategy {
4
- constructor() {
5
- this.name = 'dummy'
6
- }
7
-
8
- authenticate() {
9
- const user = new cds.User.Privileged()
10
- this.success(user)
11
- }
12
- }
13
-
14
- module.exports = DummyStrategy
@@ -1 +0,0 @@
1
- module.exports = require('../../../../lib/auth/ias-auth')
@@ -1,77 +0,0 @@
1
- const cds = require('../../cds')
2
- const CHALLENGE = 'Basic realm="Users"'
3
-
4
- class MockStrategy {
5
- constructor({ users, tenants }, name = 'mock') {
6
- this.name = name
7
- this.users = _init_users(users || cds.env.requires.auth.users || {}, tenants)
8
- }
9
-
10
- authenticate(req) {
11
- const { authorization } = req.headers
12
- if (!authorization) return this.fail(CHALLENGE)
13
-
14
- const [scheme, base64] = authorization.split(' ')
15
- if (!scheme || scheme.toLowerCase() !== 'basic') return this.fail(CHALLENGE)
16
- if (!base64) return this.fail(CHALLENGE)
17
-
18
- const [id, password] = Buffer.from(base64, 'base64').toString().split(':')
19
- const user = this.users[id] || (this.users['*'] && { id })
20
- if (!user) return this.fail(CHALLENGE)
21
- if (user.password && user.password !== password) return this.fail(CHALLENGE)
22
-
23
- const { features } = req.headers
24
- this.success(new cds.User(features ? { ...user, features } : user))
25
- }
26
- }
27
-
28
- // eslint-disable-next-line complexity
29
- const _init_users = (users, tenants = {}) => {
30
- if (!users._initialized) {
31
- Object.defineProperty(users, '_initialized', { value: true })
32
- for (let [id, user] of Object.entries(users)) {
33
- if (id !== '*') user.id = user.ID || user.id || id // fill in user ids
34
- if (id !== user.id) users[user.id] = user
35
- if (user.jwt) {
36
- let scopes = user.jwt.scope || user.jwt.scopes
37
- if (scopes) {
38
- const aud = user.jwt.aud
39
- if (aud)
40
- scopes = scopes.map(s => {
41
- for (const each of aud) s = s.replace(`${each}.`, '')
42
- return s
43
- })
44
- Array.isArray(user.roles) ? user.roles.push(...scopes) : (user.roles = scopes)
45
- }
46
- if (user.jwt.grant_type === 'client_credentials' || user.jwt.grant_type === 'client_x509') {
47
- Array.isArray(user.roles) ? user.roles.push('system-user') : (user.roles = ['system-user'])
48
- }
49
- if (!user.tenant && user.jwt.zid) user.tenant = user.jwt.zid
50
- }
51
-
52
- // optimization:
53
- if (user.roles)
54
- user.roles = user.roles.reduce((p, n) => {
55
- p[n] = 1
56
- return p
57
- }, {})
58
-
59
- // apply tenant info if any
60
- if (!user.features) {
61
- const features = tenants[user.tenant]?.features
62
- if (features) user.features = features
63
- }
64
-
65
- const attr = Object.assign(
66
- {},
67
- user.userAttributes,
68
- user.jwt && user.jwt.userInfo,
69
- user.jwt && user.jwt.attributes
70
- )
71
- if (Object.keys(attr).length > 0) user.attr = attr
72
- }
73
- }
74
- return users
75
- }
76
-
77
- module.exports = MockStrategy
@@ -1,93 +0,0 @@
1
- const CLIENT = { client_credentials: 1, client_x509: 1 }
2
-
3
- const getUserId = (user, info) => {
4
- // fallback for grant_type=client_credentials (xssec v3)
5
- return user.id || (info && info.getClientId && info.getClientId())
6
- }
7
-
8
- const addRolesFromGrantType = (user, info, credentials) => {
9
- const grantType = info && (info.grantType || (info.getGrantType && info.getGrantType()))
10
- if (grantType) {
11
- // > not "weak"
12
- user.roles['authenticated-user'] = true
13
- if (grantType in CLIENT) {
14
- user.roles['system-user'] = true
15
- if (info.getClientId() === credentials.clientid) user.roles['internal-user'] = true
16
- }
17
- }
18
- }
19
-
20
- const getRoles = (roles, info) => {
21
- // convert to object
22
- roles = Object.assign(...roles.map(ele => ({ [ele]: true })))
23
-
24
- if (info && info.checkLocalScope && typeof info.checkLocalScope === 'function') {
25
- // > xssec v3
26
- roles = new Proxy(roles, {
27
- get: function (t, role) {
28
- return role in t ? t[role] : info.checkLocalScope(role)
29
- }
30
- })
31
- }
32
-
33
- return roles
34
- }
35
-
36
- const getAttrForJWT = info => {
37
- if (!info) {
38
- return {}
39
- }
40
-
41
- if (info.getAttribute && typeof info.getAttribute === 'function') {
42
- // > xssec v3
43
- return new Proxy(
44
- {},
45
- {
46
- get: function (_, attr) {
47
- return info.getAttribute(attr)
48
- }
49
- }
50
- )
51
- }
52
-
53
- return {}
54
- }
55
-
56
- // xssec v3 only
57
- const getAttrForXSSEC = info => {
58
- if (!info) return {}
59
-
60
- return new Proxy(
61
- {},
62
- {
63
- get: function (_, attr) {
64
- // try to get saml attribute via API (getEmail, getFamilyName, etc.)
65
- try {
66
- const getter = `get${attr[0].toUpperCase()}${attr.slice(1)}`
67
- if (info[getter] && typeof info[getter] === 'function') {
68
- return info[getter]()
69
- }
70
- } catch (e) {
71
- // ignore
72
- }
73
-
74
- // default to getAttribute
75
- return info.getAttribute(attr)
76
- }
77
- }
78
- )
79
- }
80
-
81
- const getTenant = info => {
82
- // xssec v3
83
- return info && info.getZoneId && info.getZoneId()
84
- }
85
-
86
- module.exports = {
87
- getUserId,
88
- getRoles,
89
- getAttrForJWT,
90
- getAttrForXSSEC,
91
- getTenant,
92
- addRolesFromGrantType
93
- }
@@ -1,38 +0,0 @@
1
- const cds = require('../../cds')
2
-
3
- const _require = require('../../common/utils/require')
4
- const xssecUtils = require('./xssecUtils')
5
-
6
- // use _require for a better error message
7
- const { JWTStrategy: JS } = _require('@sap/xssec')
8
-
9
- class XSUAAStrategy extends JS {
10
- constructor(credentials) {
11
- super(credentials)
12
- this.credentials = credentials
13
- this.name = 'xsuaa'
14
- }
15
-
16
- authenticate(req, options) {
17
- const credentials = this.credentials
18
-
19
- // monkey patch success
20
- const _success = this.success
21
- this.success = (user, info) => {
22
- // create cds.User
23
- user = new cds.User({
24
- id: xssecUtils.getUserId(user, info),
25
- roles: xssecUtils.getRoles(['any', 'identified-user'], info, credentials),
26
- attr: xssecUtils.getAttrForXSSEC(info)
27
- })
28
- xssecUtils.addRolesFromGrantType(user, info, credentials)
29
- const tenant = xssecUtils.getTenant(info)
30
- if (tenant) user.tenant = tenant
31
- // call "super.success"
32
- _success(user, info)
33
- }
34
- super.authenticate(req, options)
35
- }
36
- }
37
-
38
- module.exports = XSUAAStrategy
@@ -1,19 +0,0 @@
1
- const { performance } = require('perf_hooks')
2
-
3
- const _statisticsRequested = req =>
4
- (req.query && req.query['sap-statistics'] === 'true') ||
5
- (req.headers && req.headers['sap-statistics'] === 'true' && (!req.query || !req.query['sap-statistics']))
6
-
7
- module.exports = function sap_statistics(req, res, next) {
8
- if (!_statisticsRequested(req)) return next()
9
-
10
- const t0 = performance.now()
11
- const { writeHead } = res
12
- res.writeHead = function (...args) {
13
- const total = Number((performance.now() - t0) / 1000).toFixed(2)
14
- if (res.statusCode < 400) res.setHeader('sap-statistics', `total=${total}`)
15
- writeHead.call(this, ...args)
16
- }
17
-
18
- next()
19
- }
@@ -1,29 +0,0 @@
1
- const getTemplate = require('./template')
2
- const templateProcessor = require('./templateProcessor')
3
-
4
- const _pick = element => {
5
- return element._type in { 'cds.Decimal': 1, 'cds.Integer64': 1 }
6
- }
7
-
8
- const _processorFn = () => elementInfo => {
9
- const { row, key } = elementInfo
10
- if (typeof row?.[key] !== 'number') return
11
- row[key] = `${row[key]}`
12
- }
13
-
14
- const ensureIEEE754 = (target, service, result) => {
15
- const { model } = service
16
- if (!target || !result || !model || !model.definitions[target.name]) return
17
-
18
- const template = getTemplate('ensureIEEE754', service, target, { pick: _pick })
19
- if (template.elements.size === 0) return
20
-
21
- if (typeof result === 'object' && result != null) {
22
- const processFn = _processorFn()
23
- for (const row of Array.isArray(result) ? result : [result]) {
24
- templateProcessor({ processFn, row, template })
25
- }
26
- }
27
- }
28
-
29
- module.exports = ensureIEEE754
@@ -1,2 +0,0 @@
1
- const cds = require('../cds')
2
- module.exports = cds.env.fiori.lean_draft ? require('./lean-draft') : require('./generic')
@@ -1,190 +0,0 @@
1
- const cds = require('../../cds')
2
- const { INSERT, SELECT, UPDATE, DELETE } = cds.ql
3
-
4
- const {
5
- ensureNoDraftsSuffix,
6
- ensureDraftsSuffix,
7
- entity_keys,
8
- getDeleteDraftAdminCqn,
9
- getCompositionTargets
10
- } = require('../utils/handler')
11
- const { readAndDeleteKeywords, isActiveEntityRequested, getKeyData } = require('../utils/where')
12
- const { isDraftRootEntity } = require('../../fiori/utils/csn')
13
- const { getColumns } = require('../../common/utils/columns')
14
- const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
15
-
16
- const _getRootCQN = (context, requestActiveData) => {
17
- const keys = entity_keys(context.target)
18
- const keyData = getKeyData(keys, context.query.SELECT.from.ref[0].where)
19
- const columns = getColumns(context.target, { onlyNames: true, filterVirtual: true })
20
- return SELECT.from(
21
- requestActiveData ? ensureNoDraftsSuffix(context.target.name) : ensureDraftsSuffix(context.target.name),
22
- columns
23
- ).where(keyData)
24
- }
25
-
26
- const _getExpandSubCqn = (model, parentEntityName, targets, isRoot = true) => {
27
- const result = []
28
- const parentEntity = model[parentEntityName]
29
-
30
- for (const element of Object.values(parentEntity.elements)) {
31
- const { name, target, cardinality } = element
32
- if (name in DRAFT_COLUMNS_MAP) {
33
- continue
34
- }
35
-
36
- const ref = { ref: [name] }
37
- if (element.isComposition && cardinality && !targets.includes(target)) {
38
- if (name === 'texts' && !parentEntity['@fiori.draft.enabled']) {
39
- continue
40
- }
41
-
42
- ref.expand = _getExpandSubCqn(model, target, [...targets, parentEntityName], false)
43
- result.push(ref)
44
- } else if (!isRoot && !element.isAssociation) {
45
- result.push(ref)
46
- }
47
- }
48
-
49
- return result
50
- }
51
-
52
- const _getDraftAdminRef = () => {
53
- return {
54
- ref: ['DraftAdministrativeData'],
55
- expand: [{ ref: ['DraftUUID'] }, { ref: ['InProcessByUser'] }]
56
- }
57
- }
58
-
59
- const _removeIsActiveEntityRecursively = resultSet => {
60
- resultSet.forEach(result => {
61
- delete result.IsActiveEntity
62
- Object.values(result).forEach(val => {
63
- if (Array.isArray(val)) {
64
- _removeIsActiveEntityRecursively(val)
65
- }
66
- })
67
- })
68
- }
69
-
70
- const _draftCompositionTree = async (service, req) => {
71
- let draftData, activeData, adminData
72
-
73
- const expanded = _getExpandSubCqn(service.model.definitions, ensureNoDraftsSuffix(req.target.name), [])
74
-
75
- const cqnDraft = _getRootCQN(req, false)
76
- cqnDraft.SELECT.columns.push(_getDraftAdminRef())
77
- cqnDraft.SELECT.columns.push(...expanded)
78
-
79
- const cqnActive = _getRootCQN(req, true)
80
- cqnActive.SELECT.columns.push(...expanded)
81
-
82
- const dbtx = cds.tx(req)
83
-
84
- const results = await Promise.all([dbtx.run(cqnDraft), dbtx.run(cqnActive)])
85
-
86
- if (results[0].length === 1) {
87
- _removeIsActiveEntityRecursively(results[0])
88
-
89
- adminData = results[0][0].DraftAdministrativeData
90
- delete results[0][0].DraftAdministrativeData
91
- draftData = results[0][0]
92
- }
93
-
94
- if (results[1].length === 1) {
95
- _removeIsActiveEntityRecursively(results[1])
96
- activeData = results[1][0]
97
- }
98
-
99
- return { draftData, activeData, adminData }
100
- }
101
-
102
- /**
103
- * Generic Handler for draftActivate requests.
104
- * In case of success it triggers an 'UPDATE' or 'CREATE' event.
105
- *
106
- * @param req
107
- */
108
- const fioriGenericActivate = async function (req, next) {
109
- if (!req.target._isDraftEnabled) return next()
110
-
111
- if (
112
- isActiveEntityRequested(req.query.SELECT.from.ref[0].where || []) ||
113
- req.query.SELECT.from.ref.length > 2 ||
114
- !isDraftRootEntity(this.model.definitions, ensureNoDraftsSuffix(req.target.name))
115
- ) {
116
- req.reject(400, 'Action "draftActivate" can only be called on the root draft entity')
117
- }
118
-
119
- if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
120
-
121
- const { draftData, activeData, adminData } = await _draftCompositionTree(this, req)
122
- if (!draftData) req.reject(404)
123
-
124
- if (adminData.InProcessByUser !== req.user.id) {
125
- req.reject(403, 'DRAFT_LOCKED_BY_ANOTHER_USER', [adminData.InProcessByUser])
126
- }
127
-
128
- // create or update
129
- let query, event
130
- if (activeData) {
131
- readAndDeleteKeywords(['IsActiveEntity'], req.query.SELECT.from.ref[0].where)
132
- event = 'UPDATE'
133
- // REVISIT: setting data should be part of ql
134
- query = UPDATE(req.target).where(req.query.SELECT.from.ref[0].where)
135
- query.UPDATE.data = draftData
136
- } else {
137
- event = 'CREATE'
138
- query = INSERT.into(req.target).entries(draftData)
139
- }
140
-
141
- // REVISIT: _draftMetadata
142
- const request = new cds.Request({ event, query, data: draftData, _draftMetadata: adminData })
143
-
144
- // REVISIT: should not be necessary
145
- request._ = Object.assign(request._, req._)
146
- request._.params = req.params
147
- request._.query = req.query
148
-
149
- // use finally to preserve r.messages in success or error case
150
- let result
151
- try {
152
- result = await this.dispatch(request)
153
- } finally {
154
- // REVISIT: should not be necessary
155
- if (request.messages) for (const m of request.messages) req.info(m)
156
- }
157
-
158
- // delete draft data
159
- const deleteDraftAdminCqn = getDeleteDraftAdminCqn(adminData.DraftUUID)
160
- const draftTablesToDeleteFrom = [req.target.name + '_drafts']
161
- for (const [entity] of getCompositionTargets(req.target, this).entries()) {
162
- draftTablesToDeleteFrom.push(entity + '_drafts')
163
- }
164
-
165
- await cds.db.tx(req).run([
166
- deleteDraftAdminCqn,
167
- ...draftTablesToDeleteFrom.map(dt => {
168
- const d = DELETE.from(dt).where({ DraftAdministrativeData_DraftUUID: adminData.DraftUUID })
169
- d._suppressDeepDelete = true // hidden flag to tell db layer that no deep delete is required
170
- return d
171
- })
172
- ])
173
-
174
- if (event === 'CREATE') {
175
- // REVISIT: we need to use okra API here because it must be set in the batched request
176
- // status code must be set in handler to allow overriding for FE V2
177
- // REVISIT: needs reworking for new adapter, especially re $batch
178
- if (req._?.odataRes) {
179
- req._?.odataRes?.setStatusCode(201, { overwrite: true })
180
- } else if (req.http?.res) {
181
- req.http.res.status(201)
182
- }
183
- }
184
-
185
- return result
186
- }
187
-
188
- module.exports = cds.service.impl(function (srv) {
189
- srv.on('draftActivate', '*', fioriGenericActivate)
190
- })
@@ -1,201 +0,0 @@
1
- const cds = require('../../cds')
2
-
3
- const { SELECT } = cds.ql
4
-
5
- const { isNavigationToMany } = require('../utils/req')
6
- const { getKeysCondition, removeIsActiveEntityRecursively } = require('../utils/where')
7
- const { ensureNoDraftsSuffix, ensureDraftsSuffix, draftIsLocked } = require('../utils/handler')
8
-
9
- const { DRAFT_COLUMNS_ADMIN_MAP } = require('../../common/constants/draft')
10
- const { deepCopyArray } = require('../../common/utils/copy')
11
- const DRAFT_COLUMNS_ADMIN = Object.keys(DRAFT_COLUMNS_ADMIN_MAP)
12
- const PREFIX_DRAFT_COLUMNS = DRAFT_COLUMNS_ADMIN.map(col => ({ ref: ['DRAFT_DraftAdministrativeData', col] }))
13
-
14
- /**
15
- * Provide information about the parent entity, i.e. the entity that has the to-many composition element.
16
- * Limitation: only works for one key (besides IsActiveEntity)
17
- *
18
- * @param service
19
- * @param req
20
- * @returns {object}
21
- * @private
22
- */
23
-
24
- const _validateDraft = (req, draftResult, isBoundAction) => {
25
- if (!draftResult || draftResult.length === 0) req.reject(404)
26
-
27
- const draftAdminData = draftResult[0]
28
-
29
- // the same user that locked the entity can always delete/update it
30
- if (draftAdminData.InProcessByUser === req.user.id) return
31
-
32
- // proceed with the delete/update action only if it was initiated by a different
33
- // user than the one who locked the entity and the configured drafts cancellation
34
- // timeout timer has expired
35
- if (draftIsLocked(draftAdminData.LastChangeDateTime)) {
36
- req.reject(403, 'DRAFT_LOCKED_BY_ANOTHER_USER', [draftAdminData.CreatedByUser])
37
- }
38
-
39
- // At this point, the request user ID isn't the owner of the draft.
40
- if (isBoundAction) req.reject(403)
41
- }
42
-
43
- const _addDraftDataToContext = (req, result) => {
44
- if (!result || result.length === 0) return
45
- if (req.rejected) return
46
- if (!req._draftMetadata) req._draftMetadata = {}
47
-
48
- DRAFT_COLUMNS_ADMIN.forEach(column => {
49
- if (column in result[0]) req._draftMetadata[column] = result[0][column]
50
- })
51
-
52
- req.data.DraftAdministrativeData_DraftUUID = result[0].DraftUUID
53
- }
54
-
55
- const _getSelectDraftDataCqn = (entityName, where) => {
56
- return SELECT.from(ensureDraftsSuffix(entityName), PREFIX_DRAFT_COLUMNS)
57
- .join('DRAFT.DraftAdministrativeData')
58
- .on('DraftAdministrativeData_DraftUUID =', { ref: ['DRAFT.DraftAdministrativeData', 'DraftUUID'] })
59
- .where(where)
60
- }
61
-
62
- const _getRoot = req => {
63
- if (!req.query) return
64
-
65
- const refObj = req.query.SELECT?.from || req.query.UPDATE?.entity || req.query.INSERT?.into || req.query.DELETE?.from
66
- const ref0 = refObj.ref[0]
67
-
68
- const root = {
69
- entityName: ensureDraftsSuffix(ref0.id),
70
- where: removeIsActiveEntityRecursively(deepCopyArray(ref0.where))
71
- }
72
-
73
- for (const item of ref0.where) {
74
- if (item.ref && item.ref[item.ref.length - 1] === 'IsActiveEntity') {
75
- const index = ref0.where.indexOf(item)
76
- root.IsActiveEntity = ref0.where[index + 2].val
77
- break
78
- }
79
- }
80
-
81
- return root
82
- }
83
-
84
- const _getDraftDataFromExistingDraft = async (req, root, isBoundAction) => {
85
- if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
86
- if (!root) return []
87
- if (root?.IsActiveEntity === false) {
88
- const query = _getSelectDraftDataCqn(root.entityName, root.where)
89
- const result = await cds.tx(req).run(query)
90
- return result
91
- }
92
-
93
- // do not expect validate draft ownership for action call on active instances
94
- if (isBoundAction) return []
95
-
96
- const rootWhere = getKeysCondition(req)
97
- const query = _getSelectDraftDataCqn(ensureNoDraftsSuffix(req.target.name), rootWhere)
98
- const result = await cds.tx(req).run(query)
99
- return result
100
- }
101
-
102
- const _addDraftDataFromExistingDraft = async req => {
103
- const root = _getRoot(req)
104
- const result = await _getDraftDataFromExistingDraft(req, root)
105
-
106
- if (!root) return []
107
- if (root.IsActiveEntity === false) {
108
- _validateDraft(req, result)
109
- _addDraftDataToContext(req, result)
110
- return result
111
- }
112
-
113
- if (result && result.length > 0) _validateDraft(req, result)
114
- _addDraftDataToContext(req, result)
115
- return result
116
- }
117
-
118
- /**
119
- * Generic Handler for before NEW requests.
120
- */
121
- const _new = async function (req) {
122
- if (!req.target._isDraftEnabled) return
123
-
124
- if (isNavigationToMany(req, this.model)) {
125
- const result = await _addDraftDataFromExistingDraft(req)
126
-
127
- // in order to fix corner case where active subitems are created in draft case
128
- if (result.length === 0) req.reject(404)
129
-
130
- return
131
- }
132
-
133
- req._draftMetadata = req._draftMetadata || {}
134
- req.data.DraftAdministrativeData_DraftUUID = cds.utils.uuid()
135
- req._draftMetadata.DraftUUID = req.data.DraftAdministrativeData_DraftUUID
136
- }
137
-
138
- /**
139
- * Generic Handler for before PATCH and UPDATE requests.
140
- */
141
- const _patch = async function (req) {
142
- if (!req.target._isDraftEnabled) return
143
-
144
- const result = await _addDraftDataFromExistingDraft(req)
145
-
146
- // no result means that the draft does not exist
147
- if (result.length === 0) req.reject(404)
148
- }
149
-
150
- /**
151
- * Generic Handler for before DELETE and CANCEL requests.
152
- */
153
- const _cancel = async function (req) {
154
- if (!req.target._isDraftEnabled) return
155
-
156
- await _addDraftDataFromExistingDraft(req)
157
- }
158
-
159
- const _validateDraftBoundAction = async function (req) {
160
- const isBoundAction = true
161
- const result = await _getDraftDataFromExistingDraft(req, _getRoot(req), isBoundAction)
162
- if (result && result.length > 0) _validateDraft(req, result, isBoundAction)
163
- }
164
-
165
- const _allowEntityCollectionOnAction = action => {
166
- return (
167
- action['@cds.odata.bindingparameter.collection'] ||
168
- (action.params && Object.values(action.params).some(e => e?.items?.type === '$self'))
169
- )
170
- }
171
-
172
- const _registerBoundActionHandlers = function (entities) {
173
- for (let entity of entities) {
174
- if (!entity.actions) return
175
-
176
- const boundActions = Object.values(entity.actions).filter(
177
- action =>
178
- action.kind === 'action' &&
179
- action.name !== 'draftPrepare' &&
180
- action.name !== 'draftEdit' &&
181
- action.name !== 'draftActivate' &&
182
- !_allowEntityCollectionOnAction(action)
183
- )
184
-
185
- for (const action of boundActions) {
186
- this.before(action.name, entity.name, req => _validateDraftBoundAction(req))
187
- }
188
- }
189
- }
190
-
191
- _new._initial = true
192
- _patch._initial = true
193
- _cancel._initial = true
194
-
195
- module.exports = cds.service.impl(function (srv) {
196
- srv.before('NEW', '*', _new)
197
- srv.before('PATCH', '*', _patch)
198
- srv.before('CANCEL', '*', _cancel)
199
- const entities = Object.values(srv.entities || {}).filter(entity => entity._isDraftEnabled)
200
- _registerBoundActionHandlers.call(srv, entities)
201
- })
@@ -1,19 +0,0 @@
1
- const cds = require('../../cds')
2
- const { deleteDraft } = require('../utils/delete')
3
-
4
- /**
5
- * Generic Handler for CANCEL requests.
6
- * In case of success it returns an empty object.
7
- * If the entry to be deleted does not exist, it rejects with error to return a 404.
8
- *
9
- * @param req
10
- */
11
- const fioriGenericCancel = function (req, next) {
12
- if (!req.target._isDraftEnabled) return next()
13
-
14
- return deleteDraft(req, this)
15
- }
16
-
17
- module.exports = cds.service.impl(function (srv) {
18
- srv.on('CANCEL', '*', fioriGenericCancel)
19
- })