@sap/cds 6.4.1 → 6.6.0

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 (139) hide show
  1. package/CHANGELOG.md +79 -6
  2. package/README.md +5 -0
  3. package/apis/cqn.d.ts +14 -3
  4. package/apis/ql.d.ts +8 -8
  5. package/apis/services.d.ts +37 -65
  6. package/apis/test.d.ts +7 -0
  7. package/bin/build/buildTaskEngine.js +9 -14
  8. package/bin/build/buildTaskFactory.js +1 -1
  9. package/bin/build/buildTaskHandler.js +3 -14
  10. package/bin/build/index.js +8 -2
  11. package/bin/build/provider/buildTaskProviderInternal.js +18 -13
  12. package/bin/build/provider/fiori/index.js +5 -10
  13. package/bin/build/provider/hana/2migration.js +11 -2
  14. package/bin/build/provider/hana/index.js +17 -14
  15. package/bin/build/provider/hana/template/.hdiconfig-hanacloud +137 -0
  16. package/bin/build/provider/hana/template/package.json +3 -0
  17. package/bin/build/provider/mtx/resourcesTarBuilder.js +12 -3
  18. package/bin/build/provider/mtx-extension/index.js +57 -37
  19. package/bin/build/provider/mtx-sidecar/index.js +1 -1
  20. package/bin/build/util.js +18 -1
  21. package/bin/cds.js +1 -5
  22. package/bin/deploy/to-hana/hana.js +10 -3
  23. package/bin/serve.js +36 -20
  24. package/common.cds +7 -0
  25. package/lib/auth/jwt-auth.js +8 -7
  26. package/lib/compile/for/lean_drafts.js +55 -6
  27. package/lib/compile/minify.js +3 -3
  28. package/lib/dbs/cds-deploy.js +18 -17
  29. package/lib/env/cds-requires.js +1 -1
  30. package/lib/env/defaults.js +5 -1
  31. package/lib/env/schemas/cds-rc.json +74 -3
  32. package/lib/index.js +4 -2
  33. package/lib/lazy.js +6 -8
  34. package/lib/log/cds-error.js +2 -2
  35. package/lib/ql/Whereable.js +22 -11
  36. package/lib/ql/cds-ql.js +1 -1
  37. package/lib/req/cds-context.js +3 -3
  38. package/lib/req/response.js +8 -3
  39. package/lib/req/user.js +12 -2
  40. package/lib/srv/bindings.js +1 -2
  41. package/lib/srv/cds-serve.js +2 -1
  42. package/lib/srv/middlewares/trace.js +31 -15
  43. package/lib/srv/protocols/odata-v2-proxy.js +8 -8
  44. package/lib/srv/srv-handlers.js +26 -7
  45. package/lib/srv/srv-methods.js +2 -2
  46. package/lib/srv/srv-models.js +8 -3
  47. package/lib/utils/cds-test.js +7 -5
  48. package/lib/utils/cds-utils.js +3 -1
  49. package/lib/utils/tar.js +6 -3
  50. package/libx/_runtime/auth/strategies/JWT.js +1 -0
  51. package/libx/_runtime/auth/strategies/ias-auth.js +3 -2
  52. package/libx/_runtime/auth/strategies/mock.js +12 -1
  53. package/libx/_runtime/auth/strategies/xssecUtils.js +7 -8
  54. package/libx/_runtime/auth/strategies/xsuaa.js +1 -0
  55. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +6 -2
  56. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -2
  57. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +26 -1
  58. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +8 -0
  59. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +1 -1
  60. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +11 -2
  61. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/PrimitiveValueDecoder.js +8 -8
  62. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/ValueConverter.js +1 -1
  63. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/validator/ValueValidator.js +14 -14
  64. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +1 -0
  65. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ResourceJsonSerializer.js +3 -0
  66. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/utils/UriHelper.js +2 -1
  67. package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +3 -2
  68. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +7 -0
  69. package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +0 -3
  70. package/libx/_runtime/cds-services/services/Service.js +11 -19
  71. package/libx/_runtime/cds-services/services/utils/columns.js +42 -40
  72. package/libx/_runtime/cds-services/util/assert.js +7 -1
  73. package/libx/_runtime/common/code-ext/WorkerReq.js +81 -0
  74. package/libx/_runtime/common/code-ext/config.js +13 -0
  75. package/libx/_runtime/common/code-ext/execute.js +113 -0
  76. package/libx/_runtime/common/code-ext/handlers.js +49 -0
  77. package/libx/_runtime/common/code-ext/worker.js +40 -0
  78. package/libx/_runtime/common/code-ext/workerQuery.js +45 -0
  79. package/libx/_runtime/common/code-ext/workerQueryExecutor.js +36 -0
  80. package/libx/_runtime/common/composition/data.js +5 -2
  81. package/libx/_runtime/common/composition/tree.js +2 -0
  82. package/libx/_runtime/common/generic/auth/restrict.js +1 -1
  83. package/libx/_runtime/common/generic/crud.js +4 -0
  84. package/libx/_runtime/common/generic/etag.js +3 -1
  85. package/libx/_runtime/common/generic/input.js +12 -14
  86. package/libx/_runtime/common/i18n/index.js +1 -1
  87. package/libx/_runtime/common/utils/cqn2cqn4sql.js +47 -22
  88. package/libx/_runtime/common/utils/path.js +5 -26
  89. package/libx/_runtime/common/utils/search2cqn4sql.js +16 -9
  90. package/libx/_runtime/common/utils/templateProcessorPathSerializer.js +19 -13
  91. package/libx/_runtime/db/data-conversion/post-processing.js +1 -1
  92. package/libx/_runtime/db/expand/expandCQNToJoin.js +7 -4
  93. package/libx/_runtime/db/expand/rawToExpanded.js +3 -2
  94. package/libx/_runtime/db/generic/input.js +2 -2
  95. package/libx/_runtime/db/generic/integrity.js +1 -0
  96. package/libx/_runtime/db/generic/virtual.js +1 -0
  97. package/libx/_runtime/db/query/read.js +3 -2
  98. package/libx/_runtime/db/utils/localized.js +1 -1
  99. package/libx/_runtime/fiori/generic/activate.js +7 -1
  100. package/libx/_runtime/fiori/generic/before.js +9 -1
  101. package/libx/_runtime/fiori/generic/edit.js +8 -1
  102. package/libx/_runtime/fiori/generic/new.js +2 -0
  103. package/libx/_runtime/fiori/generic/patch.js +2 -0
  104. package/libx/_runtime/fiori/generic/prepare.js +2 -0
  105. package/libx/_runtime/fiori/generic/read.js +16 -5
  106. package/libx/_runtime/fiori/generic/readOverDraft.js +2 -0
  107. package/libx/_runtime/fiori/lean-draft.js +505 -241
  108. package/libx/_runtime/fiori/utils/delete.js +2 -0
  109. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +5 -5
  110. package/libx/_runtime/hana/pool.js +1 -1
  111. package/libx/_runtime/hana/search2cqn4sql.js +51 -51
  112. package/libx/_runtime/messaging/Outbox.js +1 -1
  113. package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +1 -0
  114. package/libx/_runtime/messaging/enterprise-messaging.js +2 -6
  115. package/libx/_runtime/messaging/file-based.js +1 -2
  116. package/libx/_runtime/messaging/outbox/OutboxRunner.js +1 -1
  117. package/libx/_runtime/messaging/outbox/utils.js +1 -1
  118. package/libx/_runtime/messaging/service.js +0 -1
  119. package/libx/_runtime/remote/Service.js +1 -0
  120. package/libx/_runtime/sqlite/convertDraftAdminPathExpression.js +19 -3
  121. package/libx/_runtime/sqlite/customBuilder/CustomExpressionBuilder.js +0 -18
  122. package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +0 -18
  123. package/libx/_runtime/sqlite/customBuilder/CustomSelectBuilder.js +0 -24
  124. package/libx/_runtime/sqlite/customBuilder/CustomUpsertBuilder.js +2 -1
  125. package/libx/_runtime/sqlite/customBuilder/index.js +47 -32
  126. package/libx/odata/afterburner.js +23 -8
  127. package/libx/odata/cqn2odata.js +1 -1
  128. package/libx/odata/grammar.pegjs +3 -4
  129. package/libx/odata/index.js +5 -1
  130. package/libx/odata/parseToCqn.js +3 -3
  131. package/libx/odata/parser.js +1 -1
  132. package/libx/odata/utils.js +58 -1
  133. package/libx/rest/middleware/parse.js +26 -4
  134. package/package.json +1 -1
  135. package/server.js +1 -1
  136. package/libx/_runtime/sqlite/customBuilder/CustomDeleteBuilder.js +0 -17
  137. package/libx/_runtime/sqlite/customBuilder/CustomReferenceBuilder.js +0 -11
  138. package/libx/_runtime/sqlite/customBuilder/CustomUpdateBuilder.js +0 -17
  139. /package/bin/build/provider/hana/template/{.hdiconfig → .hdiconfig-haas} +0 -0
@@ -11,7 +11,7 @@ const _redirectXpr = (xpr, localize) => {
11
11
  }
12
12
 
13
13
  if (ele.SELECT) {
14
- redirect(ele.SELECT, localize)
14
+ if (!ele._suppressLocalization) redirect(ele.SELECT, localize)
15
15
  }
16
16
  })
17
17
  }
@@ -112,9 +112,11 @@ const fioriGenericActivate = async function (req) {
112
112
  req.query.SELECT.from.ref.length > 2 ||
113
113
  !isDraftRootEntity(this.model.definitions, ensureNoDraftsSuffix(req.target.name))
114
114
  ) {
115
- req.reject(400)
115
+ req.reject(400, 'Action "draftActivate" can only be called on the root draft entity')
116
116
  }
117
117
 
118
+ if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
119
+
118
120
  const { draftData, activeData, adminData } = await _draftCompositionTree(this, req)
119
121
 
120
122
  if (!draftData) req.reject(404)
@@ -174,6 +176,10 @@ const fioriGenericActivate = async function (req) {
174
176
  })
175
177
  ])
176
178
 
179
+ // REVISIT: we need to use okra API here because it must be set in the batched request
180
+ // status code must be set in handler to allow overriding for FE V2
181
+ req?._?.odataRes.setStatusCode(201)
182
+
177
183
  return result
178
184
  }
179
185
 
@@ -78,6 +78,7 @@ const _getRoot = req => {
78
78
  }
79
79
 
80
80
  const _getDraftDataFromExistingDraft = async (req, root, isBoundAction) => {
81
+ if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
81
82
  if (!root) return []
82
83
  if (root?.IsActiveEntity === false) {
83
84
  const query = _getSelectDraftDataCqn(root.entityName, root.where)
@@ -155,6 +156,13 @@ const _validateDraftBoundAction = async function (req) {
155
156
  if (result && result.length > 0) _validateDraft(req, result, isBoundAction)
156
157
  }
157
158
 
159
+ const _allowEntityCollectionOnAction = action => {
160
+ return (
161
+ action['@cds.odata.bindingparameter.collection'] ||
162
+ (action.params && Object.values(action.params).some(e => e?.items?.type === '$self'))
163
+ )
164
+ }
165
+
158
166
  const _registerBoundActionHandlers = function (entityName, actions) {
159
167
  if (!actions) return
160
168
 
@@ -164,7 +172,7 @@ const _registerBoundActionHandlers = function (entityName, actions) {
164
172
  action.name !== 'draftPrepare' &&
165
173
  action.name !== 'draftEdit' &&
166
174
  action.name !== 'draftActivate' &&
167
- !action['@cds.odata.bindingparameter.collection']
175
+ !_allowEntityCollectionOnAction(action)
168
176
  )
169
177
 
170
178
  for (const action of boundActions) {
@@ -70,9 +70,11 @@ const _select = async (lockRecordCQN, draftExistsCQN, selectCQNs, req, dbtx) =>
70
70
  */
71
71
  const fioriGenericEdit = async function (req) {
72
72
  if (!isActiveEntityRequested(req.query.SELECT.where || [])) {
73
- req.reject(400)
73
+ req.reject(400, 'Action "draftEdit" can only be called on the active entity')
74
74
  }
75
75
 
76
+ if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
77
+
76
78
  const { definitions } = this.model
77
79
 
78
80
  // TODO replace with generic where filter
@@ -156,6 +158,11 @@ const fioriGenericEdit = async function (req) {
156
158
  }
157
159
 
158
160
  await Promise.all(insertCQNs.map(CQN => dbtx.run(CQN)))
161
+
162
+ // REVISIT: we need to use okra API here because it must be set in the batched request
163
+ // status code must be set in handler to allow overriding for FE V2
164
+ req?._?.odataRes.setStatusCode(201)
165
+
159
166
  return results[0][0]
160
167
  }
161
168
 
@@ -54,6 +54,8 @@ const fioriGenericNew = async function (req, next) {
54
54
  return onDraftActivate(req, next)
55
55
  }
56
56
 
57
+ if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
58
+
57
59
  const navigationToMany = isNavigationToMany(req)
58
60
 
59
61
  const adminDataCQN = navigationToMany
@@ -61,6 +61,8 @@ const _joinDraftAdministrativeData = (selectResolved, target) => {
61
61
  const fioriGenericPatch = async function (req) {
62
62
  if (req.data.IsActiveEntity === true) req.reject(400, 'Patch can only be applied to a draft entity')
63
63
 
64
+ if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
65
+
64
66
  const dbtx = cds.tx(req)
65
67
 
66
68
  const selectResolved = cqn2cqn4sql(_getSelectCQN(req), this.model)
@@ -16,6 +16,8 @@ const fioriGenericPrepare = async function (req) {
16
16
  req.reject(400, 'Action "draftPrepare" can only be called on a draft entity')
17
17
  }
18
18
 
19
+ if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
20
+
19
21
  const target = ensureDraftsSuffix(req.target.name)
20
22
  const columns = getColumns(this.model.definitions[ensureNoDraftsSuffix(req.target.name)], {
21
23
  keysOnly: true,
@@ -18,6 +18,7 @@ const {
18
18
  } = require('../utils/handler')
19
19
  const { deleteCondition, readAndDeleteKeywords, removeIsActiveEntityRecursively } = require('../utils/where')
20
20
  const { adaptStreamCQN } = require('../../cds-services/adapter/odata-v4/utils/stream')
21
+ const getError = require('../../common/error')
21
22
 
22
23
  const _findSubselect = where => {
23
24
  return where.find((e, i) => {
@@ -37,15 +38,16 @@ const _findRootSubSelectFor = query => {
37
38
 
38
39
  // append where with clauses from @restrict
39
40
  const _getWhereWithAppendedDraftRestrictions = (where = [], req) => {
41
+ const restrictions = []
40
42
  if (req.query._draftRestrictions) {
41
43
  for (const each of req.query._draftRestrictions) {
42
44
  const xpr = each._xpr
43
45
  if (each.target.name === ensureUnlocalized(req.target.name)) {
44
- if (where.length) where.push('and')
45
46
  // restriction might contain or clause -> use xpr for grouping
46
- xpr.includes('or') ? where.push({ xpr }) : where.push(...xpr)
47
+ if (restrictions.length) restrictions.push('or')
48
+ xpr.includes('or') ? restrictions.push({ xpr }) : restrictions.push(...xpr)
47
49
  } else {
48
- // > restriction inherited from parent via autoexposure
50
+ // restriction inherited from parent via autoexposure
49
51
  // find inner most sub select if available and append restriction to where clause
50
52
  const rootSubSelect = _findRootSubSelectFor({ SELECT: { where } })
51
53
  if (rootSubSelect && rootSubSelect.SELECT.from) {
@@ -62,6 +64,10 @@ const _getWhereWithAppendedDraftRestrictions = (where = [], req) => {
62
64
  }
63
65
  }
64
66
 
67
+ if (restrictions.length) {
68
+ if (where.length) where.push('and', { xpr: restrictions })
69
+ else where.push(...restrictions)
70
+ }
65
71
  return where
66
72
  }
67
73
 
@@ -1177,7 +1183,7 @@ const _getOriginalColumns = req => {
1177
1183
  const originalColumns = {}
1178
1184
  // expanded columns are handled generically in db
1179
1185
  for (const c of req.query.SELECT.columns) {
1180
- originalColumns[c.ref ? c.ref[c.ref.length - 1] : c.as || c] = true
1186
+ originalColumns[c.as || (c.ref && c.ref[c.ref.length - 1]) || c] = true
1181
1187
  }
1182
1188
 
1183
1189
  return originalColumns
@@ -1221,6 +1227,9 @@ const _postProcess = (result, req, cqnScenario, deleteLastChangeDateTime) => {
1221
1227
  }
1222
1228
  }
1223
1229
 
1230
+ if (result.HasActiveEntity === null) result.HasActiveEntity = false
1231
+ if (result.HasDraftEntity === null) result.HasDraftEntity = false
1232
+ if (result.IsActiveEntity === null) result.IsActiveEntity = false
1224
1233
  return result
1225
1234
  }
1226
1235
 
@@ -1261,6 +1270,8 @@ const _adaptColumns4readAfterWrite = (req, cqnScenario, query4sql) => {
1261
1270
  * @param req
1262
1271
  */
1263
1272
  const fioriGenericRead = async function (req) {
1273
+ if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
1274
+
1264
1275
  const query = req.query
1265
1276
  const originalFrom = _copyCQNPartial(query.SELECT.from)
1266
1277
 
@@ -1293,7 +1304,7 @@ const fioriGenericRead = async function (req) {
1293
1304
  cqnScenario = _generateCQN(reqClone, nonDraftColumns, originalFrom, this.model)
1294
1305
  }
1295
1306
 
1296
- if (!cqnScenario) req.reject(400)
1307
+ if (!cqnScenario) throw getError(501)
1297
1308
 
1298
1309
  // ensure base columns for calculation are selected in draft admin expand
1299
1310
  _adaptDraftAdminExpand(cqnScenario.cqn)
@@ -109,6 +109,8 @@ const _shouldReadOverDraft = (req, definitions) => {
109
109
  * @returns {Promise<Array>}
110
110
  */
111
111
  const _readOverDraftHandler = async function (req, next) {
112
+ if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
113
+
112
114
  const definitions = this.model.definitions
113
115
 
114
116
  // determine whether the request is handled here (read over draft handler),