@sap/cds 6.1.2 → 6.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/CHANGELOG.md +92 -8
  2. package/apis/cds.d.ts +18 -6
  3. package/apis/connect.d.ts +1 -1
  4. package/apis/cqn.d.ts +1 -1
  5. package/apis/log.d.ts +23 -5
  6. package/apis/ql.d.ts +128 -61
  7. package/apis/services.d.ts +11 -0
  8. package/apis/test.d.ts +61 -0
  9. package/apis/utils.d.ts +15 -0
  10. package/app/fiori/preview.js +1 -0
  11. package/bin/build/buildTaskEngine.js +70 -22
  12. package/bin/build/buildTaskFactory.js +18 -11
  13. package/bin/build/buildTaskHandler.js +1 -1
  14. package/bin/build/buildTaskProviderFactory.js +3 -13
  15. package/bin/build/constants.js +0 -1
  16. package/bin/build/index.js +14 -6
  17. package/bin/build/provider/buildTaskHandlerEdmx.js +2 -3
  18. package/bin/build/provider/buildTaskHandlerFeatureToggles.js +2 -2
  19. package/bin/build/provider/buildTaskHandlerInternal.js +3 -6
  20. package/bin/build/provider/buildTaskProviderInternal.js +51 -39
  21. package/bin/build/provider/fiori/index.js +3 -3
  22. package/bin/build/provider/hana/2migration.js +1 -1
  23. package/bin/build/provider/hana/index.js +34 -27
  24. package/bin/build/provider/java/index.js +6 -7
  25. package/bin/build/provider/mtx/index.js +20 -18
  26. package/bin/build/provider/mtx/resourcesTarBuilder.js +8 -11
  27. package/bin/build/provider/mtx-sidecar/index.js +13 -17
  28. package/bin/build/provider/nodejs/index.js +8 -7
  29. package/bin/build/util.js +22 -4
  30. package/bin/cds.js +8 -4
  31. package/bin/deploy/to-hana/cfUtil.js +53 -18
  32. package/bin/mtx/in-cds.js +1 -0
  33. package/bin/serve.js +37 -30
  34. package/lib/auth/basic-auth.js +33 -0
  35. package/lib/auth/dummy-auth.js +7 -0
  36. package/lib/auth/ias-auth.js +2 -0
  37. package/lib/auth/index.js +31 -0
  38. package/lib/auth/jwt-auth.js +3 -0
  39. package/lib/auth/mocked-users.js +72 -0
  40. package/lib/auth/passport-basic.js +12 -0
  41. package/lib/auth/passport-digest.js +14 -0
  42. package/lib/auth/xsuaa-auth.js +3 -0
  43. package/lib/compile/cds-compile.js +3 -3
  44. package/lib/compile/to/cdl.js +5 -1
  45. package/lib/compile/to/edm.js +8 -0
  46. package/lib/compile/to/gql.js +1 -0
  47. package/lib/compile/to/json.js +30 -5
  48. package/lib/compile/to/sql.js +3 -1
  49. package/lib/core/index.js +5 -1
  50. package/lib/dbs/cds-deploy.js +36 -6
  51. package/lib/env/cds-env.js +15 -5
  52. package/lib/env/cds-requires.js +51 -58
  53. package/lib/env/defaults.js +1 -0
  54. package/lib/env/schemas/cds-package.json +4 -0
  55. package/lib/env/schemas/cds-rc.json +63 -77
  56. package/lib/i18n/localize.js +16 -5
  57. package/lib/index.js +9 -4
  58. package/lib/log/cds-error.js +4 -6
  59. package/lib/log/cds-log.js +89 -53
  60. package/lib/log/service/index.js +1 -0
  61. package/lib/ql/CREATE.js +2 -5
  62. package/lib/ql/DELETE.js +1 -1
  63. package/lib/ql/DROP.js +1 -3
  64. package/lib/ql/INSERT.js +3 -3
  65. package/lib/ql/Query.js +10 -23
  66. package/lib/ql/SELECT.js +1 -2
  67. package/lib/ql/UPDATE.js +2 -2
  68. package/lib/ql/Whereable.js +7 -15
  69. package/lib/ql/cds-ql.js +9 -3
  70. package/lib/req/cds-context.js +11 -3
  71. package/lib/req/context.js +29 -23
  72. package/lib/req/locale.js +9 -5
  73. package/lib/req/request.js +1 -0
  74. package/lib/req/user.js +2 -1
  75. package/lib/srv/cds-connect.js +1 -1
  76. package/lib/srv/cds-serve.js +21 -14
  77. package/lib/srv/middlewares/cds-context.js +29 -0
  78. package/lib/srv/middlewares/ctx-model.js +24 -0
  79. package/lib/srv/middlewares/errors.js +9 -0
  80. package/lib/srv/middlewares/index.js +22 -0
  81. package/lib/srv/middlewares/sap-statistics.js +13 -0
  82. package/lib/srv/middlewares/trace.js +102 -0
  83. package/lib/srv/protocols/_legacy.js +42 -0
  84. package/lib/srv/protocols/graphql.js +39 -0
  85. package/lib/srv/protocols/hcql.js +37 -0
  86. package/lib/srv/protocols/index.js +86 -0
  87. package/lib/srv/protocols/odata-v2-proxy.js +3767 -0
  88. package/lib/srv/protocols/odata-v2.js +26 -0
  89. package/lib/srv/protocols/odata-v4.js +16 -0
  90. package/lib/srv/protocols/rest.js +13 -0
  91. package/lib/srv/srv-api.js +5 -0
  92. package/lib/srv/srv-models.js +4 -6
  93. package/lib/utils/axios.js +3 -2
  94. package/lib/utils/cds-test.js +27 -21
  95. package/lib/utils/cds-utils.js +19 -20
  96. package/lib/utils/tar.js +175 -0
  97. package/libx/_runtime/audit/generic/personal/utils.js +18 -7
  98. package/libx/_runtime/audit/utils/v2.js +1 -0
  99. package/libx/_runtime/auth/index.js +4 -0
  100. package/libx/_runtime/auth/strategies/ias-auth.js +76 -0
  101. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +8 -3
  102. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +15 -4
  103. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -1
  104. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/orderByToCQN.js +1 -1
  105. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +1 -1
  106. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/ResourcePathParser.js +9 -0
  107. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriInfo.js +5 -1
  108. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +12 -0
  109. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/utils/BufferedWriter.js +6 -2
  110. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/RequestValidator.js +47 -7
  111. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
  112. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +0 -2
  113. package/libx/_runtime/cds-services/services/utils/compareJson.js +3 -1
  114. package/libx/_runtime/cds-services/util/assert.js +7 -0
  115. package/libx/_runtime/common/aspects/relation.js +1 -1
  116. package/libx/_runtime/common/composition/data.js +61 -15
  117. package/libx/_runtime/common/composition/delete.js +0 -1
  118. package/libx/_runtime/common/composition/insert.js +0 -1
  119. package/libx/_runtime/common/composition/tree.js +4 -10
  120. package/libx/_runtime/common/composition/update.js +44 -21
  121. package/libx/_runtime/common/generic/auth/capabilities.js +8 -10
  122. package/libx/_runtime/common/generic/crud.js +1 -2
  123. package/libx/_runtime/common/generic/etag.js +4 -4
  124. package/libx/_runtime/common/generic/input.js +21 -6
  125. package/libx/_runtime/common/generic/paging.js +3 -3
  126. package/libx/_runtime/common/generic/put.js +7 -4
  127. package/libx/_runtime/common/generic/sorting.js +4 -4
  128. package/libx/_runtime/common/generic/temporal.js +3 -6
  129. package/libx/_runtime/common/i18n/messages.properties +0 -7
  130. package/libx/_runtime/common/utils/cqn2cqn4sql.js +11 -6
  131. package/libx/_runtime/common/utils/csn.js +0 -28
  132. package/libx/_runtime/common/utils/draft.js +8 -1
  133. package/libx/_runtime/common/utils/path.js +7 -1
  134. package/libx/_runtime/common/utils/propagateForeignKeys.js +122 -0
  135. package/libx/_runtime/common/utils/resolveView.js +2 -3
  136. package/libx/_runtime/common/utils/template.js +2 -3
  137. package/libx/_runtime/db/data-conversion/post-processing.js +3 -44
  138. package/libx/_runtime/db/generic/input.js +6 -6
  139. package/libx/_runtime/db/sql-builder/dataTypes.js +4 -0
  140. package/libx/_runtime/fiori/generic/activate.js +2 -2
  141. package/libx/_runtime/fiori/generic/before.js +40 -72
  142. package/libx/_runtime/fiori/generic/cancel.js +2 -2
  143. package/libx/_runtime/fiori/generic/delete.js +2 -2
  144. package/libx/_runtime/fiori/generic/edit.js +2 -2
  145. package/libx/_runtime/fiori/generic/new.js +3 -5
  146. package/libx/_runtime/fiori/generic/patch.js +49 -43
  147. package/libx/_runtime/fiori/generic/prepare.js +2 -2
  148. package/libx/_runtime/fiori/generic/read.js +27 -37
  149. package/libx/_runtime/fiori/utils/where.js +4 -2
  150. package/libx/_runtime/hana/Service.js +1 -3
  151. package/libx/_runtime/hana/conversion.js +3 -0
  152. package/libx/_runtime/hana/driver.js +33 -3
  153. package/libx/_runtime/hana/dynatrace.js +1 -0
  154. package/libx/_runtime/hana/search2Contains.js +12 -1
  155. package/libx/_runtime/hana/search2cqn4sql.js +10 -27
  156. package/libx/_runtime/hana/streaming.js +1 -0
  157. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +4 -2
  158. package/libx/_runtime/messaging/common-utils/AMQPClient.js +1 -0
  159. package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +5 -2
  160. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -0
  161. package/libx/_runtime/messaging/enterprise-messaging.js +62 -3
  162. package/libx/_runtime/messaging/outbox/utils.js +1 -1
  163. package/libx/_runtime/messaging/redis-messaging.js +1 -0
  164. package/libx/_runtime/remote/Service.js +2 -2
  165. package/libx/_runtime/remote/utils/client.js +35 -11
  166. package/libx/_runtime/remote/utils/data.js +7 -2
  167. package/libx/_runtime/sqlite/Service.js +18 -7
  168. package/libx/_runtime/sqlite/conversion.js +3 -0
  169. package/libx/_runtime/sqlite/convertAssocToOneManaged.js +3 -3
  170. package/libx/_runtime/sqlite/localized.js +8 -8
  171. package/libx/odata/afterburner.js +39 -7
  172. package/libx/odata/cqn2odata.js +6 -3
  173. package/libx/odata/grammar.pegjs +66 -18
  174. package/libx/odata/index.js +3 -2
  175. package/libx/odata/parser.js +1 -1
  176. package/libx/odata/utils.js +2 -0
  177. package/libx/rest/RestAdapter.js +62 -43
  178. package/libx/rest/middleware/input.js +2 -3
  179. package/libx/rest/middleware/parse.js +2 -1
  180. package/libx/rest/middleware/update.js +1 -1
  181. package/package.json +2 -2
  182. package/server.js +5 -4
  183. package/srv/mtx.cds +1 -1
  184. package/srv/mtx.js +4 -24
  185. package/lib/srv/adapters.js +0 -85
  186. package/lib/utils/resources/index.js +0 -48
  187. package/lib/utils/resources/tar.js +0 -49
  188. package/lib/utils/resources/utils.js +0 -11
  189. package/libx/_runtime/db/utils/propagateForeignKeys.js +0 -93
  190. package/libx/_runtime/extensibility/activate.js +0 -69
  191. package/libx/_runtime/extensibility/add.js +0 -50
  192. package/libx/_runtime/extensibility/addExtension.js +0 -72
  193. package/libx/_runtime/extensibility/defaults.js +0 -34
  194. package/libx/_runtime/extensibility/handler/transformREAD.js +0 -121
  195. package/libx/_runtime/extensibility/handler/transformRESULT.js +0 -51
  196. package/libx/_runtime/extensibility/handler/transformWRITE.js +0 -64
  197. package/libx/_runtime/extensibility/linter/allowlist_checker.js +0 -373
  198. package/libx/_runtime/extensibility/linter/annotations_checker.js +0 -113
  199. package/libx/_runtime/extensibility/linter/checker_base.js +0 -20
  200. package/libx/_runtime/extensibility/linter/namespace_checker.js +0 -180
  201. package/libx/_runtime/extensibility/linter.js +0 -32
  202. package/libx/_runtime/extensibility/push.js +0 -118
  203. package/libx/_runtime/extensibility/service.js +0 -38
  204. package/libx/_runtime/extensibility/token.js +0 -57
  205. package/libx/_runtime/extensibility/utils.js +0 -131
  206. package/libx/_runtime/extensibility/validation.js +0 -50
  207. package/libx/_runtime/extensibility/views.js +0 -12
  208. package/srv/extensibility-service.cds +0 -59
  209. package/srv/extensibility-service.js +0 -1
  210. package/srv/extensions.cds +0 -8
  211. package/srv/model-provider.cds +0 -61
  212. package/srv/model-provider.js +0 -143
@@ -11,6 +11,7 @@ const cds = require('../../cds')
11
11
  const LOG = cds.log('app')
12
12
  const { enrichDataWithKeysFromWhere } = require('../utils/keys')
13
13
  const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
14
+ const propagateForeignKeys = require('../utils/propagateForeignKeys')
14
15
  const { checkInputConstraints, assertTargets } = require('../../cds-services/util/assert')
15
16
  const getTemplate = require('../utils/template')
16
17
  const templateProcessor = require('../utils/templateProcessor')
@@ -126,6 +127,11 @@ const _processCategory = (req, category, value, elementInfo, assertMap) => {
126
127
  const { row, key, element, isRoot } = elementInfo
127
128
  category = _getSimpleCategory(category)
128
129
 
130
+ if (category === 'propagateForeignKeys') {
131
+ propagateForeignKeys(key, row, element._foreignKeys, element.isComposition, { enumerable: false })
132
+ return
133
+ }
134
+
129
135
  // remember mandatory
130
136
  if (category === 'mandatory') {
131
137
  value.mandatory = true
@@ -147,7 +153,7 @@ const _processCategory = (req, category, value, elementInfo, assertMap) => {
147
153
  }
148
154
 
149
155
  // generate UUIDs
150
- if (category === 'uuid' && !value.val && (event !== 'UPDATE' || !isRoot)) {
156
+ if (category === 'uuid' && !value.val && ((event !== 'UPDATE' && event !== 'PATCH') || !isRoot)) {
151
157
  value.val = row[key] = cds.utils.uuid()
152
158
  }
153
159
 
@@ -186,6 +192,11 @@ const _pick = element => {
186
192
  // collect actions to apply
187
193
  const categories = []
188
194
 
195
+ // REVISIT: element._foreignKeys.length seems to be a very broad check
196
+ if (element.isAssociation && element._foreignKeys.length) {
197
+ categories.push({ category: 'propagateForeignKeys' })
198
+ }
199
+
189
200
  if (element['@assert.range'] || element['@assert.enum'] || element['@assert.format']) {
190
201
  categories.push('assert')
191
202
  }
@@ -237,11 +248,14 @@ const _getBoundActionBindingParameter = req => {
237
248
  return (actions && actions[action] && actions[action]['@cds.odata.bindingparameter.name']) || 'in'
238
249
  }
239
250
 
240
- async function _handler(req) {
251
+ async function commonGenericInput(req) {
241
252
  if (!req.query) return // FIXME: the code below expects req.query to be defined
242
253
  if (!req.target) return
243
254
 
244
- const template = getTemplate('app-input', this, req.target, { pick: _pick })
255
+ const template = getTemplate('app-input', this, req.target, {
256
+ pick: _pick,
257
+ ignore: element => element._isAssociationStrict
258
+ })
245
259
  if (template.elements.size === 0) return
246
260
 
247
261
  const errors = []
@@ -301,7 +315,8 @@ const _processActionFunctionRow = (row, param, key, errors, event, service) => {
301
315
 
302
316
  // structured
303
317
  const template = getTemplate('app-input-operation', service, param, {
304
- pick: _pick
318
+ pick: _pick,
319
+ ignore: element => element._isAssociationStrict
305
320
  })
306
321
 
307
322
  if (template && template.elements.size) {
@@ -371,11 +386,11 @@ function _actionFunctionHandler(req) {
371
386
  _callError(req, errors)
372
387
  }
373
388
 
374
- _handler._initial = true
389
+ commonGenericInput._initial = true
375
390
  _actionFunctionHandler._initial = true
376
391
 
377
392
  module.exports = cds.service.impl(function () {
378
- this.before(['CREATE', 'UPDATE', 'NEW', 'PATCH'], '*', _handler)
393
+ this.before(['CREATE', 'UPDATE', 'NEW', 'PATCH'], '*', commonGenericInput)
379
394
  const operationNames = []
380
395
 
381
396
  for (const operation of this.operations) {
@@ -1,7 +1,7 @@
1
1
  const cds = require('../../cds')
2
2
  const { getDefaultPageSize, getMaxPageSize } = require('../utils/page')
3
3
 
4
- const _handler = function (req) {
4
+ const commonGenericPaging = function (req) {
5
5
  // only if http request
6
6
  if (!req._.req) return
7
7
 
@@ -24,6 +24,6 @@ const _addPaging = function (query, target) {
24
24
  * handler registration
25
25
  */
26
26
  module.exports = cds.service.impl(function () {
27
- _handler._initial = true
28
- this.before('READ', '*', _handler)
27
+ commonGenericPaging._initial = true
28
+ this.before('READ', '*', commonGenericPaging)
29
29
  })
@@ -55,7 +55,7 @@ const _pick = element => {
55
55
  }
56
56
  }
57
57
 
58
- function _handler(req) {
58
+ function commonGenericPut(req) {
59
59
  if (req.method !== 'PUT') return
60
60
  if (!req.query) return // FIXME: the code below expects req.query to be defined
61
61
  if (!req.target) return
@@ -64,7 +64,10 @@ function _handler(req) {
64
64
  const { elements } = req.target
65
65
  for (const k in req.data) if (k in elements && elements[k]['@Core.MediaType']) return
66
66
 
67
- const template = getTemplate('app-put', this, req.target, { pick: _pick })
67
+ const template = getTemplate('app-put', this, req.target, {
68
+ pick: _pick,
69
+ ignore: element => element._isAssociationStrict
70
+ })
68
71
  if (template.elements.size === 0) return
69
72
 
70
73
  // REVISIT: req.data should point into req.query
@@ -84,8 +87,8 @@ function _handler(req) {
84
87
  setDataFromCQN(req)
85
88
  }
86
89
 
87
- _handler._initial = true
90
+ commonGenericPut._initial = true
88
91
 
89
92
  module.exports = cds.service.impl(function () {
90
- this.before(['UPDATE'], '*', _handler)
93
+ this.before(['UPDATE'], '*', commonGenericPut)
91
94
  })
@@ -43,7 +43,7 @@ const _getStaticOrders = req => {
43
43
  *
44
44
  * @param req
45
45
  */
46
- const _handler = function (req) {
46
+ const commonGenericSorting = function (req) {
47
47
  if (!req.query || !req.query.SELECT || req.query.SELECT.one) return
48
48
 
49
49
  let select = req.query.SELECT
@@ -77,9 +77,9 @@ const _handler = function (req) {
77
77
  * handler registration
78
78
  */
79
79
  module.exports = cds.service.impl(function () {
80
- _handler._initial = true
81
- this.before('READ', '*', _handler)
80
+ commonGenericSorting._initial = true
81
+ this.before('READ', '*', commonGenericSorting)
82
82
  })
83
83
 
84
84
  // REVISIT: remove (currently needed for test)
85
- module.exports.handler = _handler
85
+ module.exports.handler = commonGenericSorting
@@ -1,5 +1,4 @@
1
1
  const cds = require('../../cds')
2
- const LOG = cds.log('app')
3
2
 
4
3
  const _getDateFromQueryOptions = str => {
5
4
  if (str) {
@@ -11,8 +10,6 @@ const _getDateFromQueryOptions = str => {
11
10
 
12
11
  const _isDate = dateStr => !dateStr.includes(':')
13
12
  const _isTimestamp = dateStr => dateStr.includes('.')
14
- const _isWarningRequired = (warning, queryOptions) =>
15
- !warning && queryOptions && (queryOptions['sap-valid-from'] || queryOptions['sap-valid-to'])
16
13
  const _isAsOfNow = queryOptions =>
17
14
  !queryOptions || (!queryOptions['sap-valid-at'] && !queryOptions['sap-valid-to'] && !queryOptions['sap-valid-from'])
18
15
 
@@ -41,7 +38,7 @@ const _getTimeDelta = (target, queryOption) => {
41
38
  *
42
39
  * @param req
43
40
  */
44
- const _handler = function (req) {
41
+ const commonGenericTemporal = function (req) {
45
42
  // REVISIT: public API for query options
46
43
  const { _queryOptions } = req
47
44
 
@@ -70,7 +67,7 @@ const _handler = function (req) {
70
67
  * handler registration
71
68
  */
72
69
  module.exports = cds.service.impl(function () {
73
- _handler._initial = true
70
+ commonGenericTemporal._initial = true
74
71
  // always run to allow interaction with temporal data in custom handlers
75
- this.before('*', _handler)
72
+ this.before('*', commonGenericTemporal)
76
73
  })
@@ -49,10 +49,6 @@ ASSERT_REFERENCE_INTEGRITY=Reference integrity is violated for association "{0}"
49
49
  ASSERT_TARGET="Value doesn't exist"
50
50
  ASSERT_DEEP_ASSOCIATION=It is not allowed to modify sub documents in {0} Association "{1}"
51
51
 
52
- # persistence
53
- PERSISTENCE_SKIP_NO_GENERIC_CRUD=Entity "{0}" is annotated with "@sap.persistence.skip" and cannot be served generically.
54
- NON_WRITABLE_VIEW={0} on views with join and/or union is not supported
55
-
56
52
  # db
57
53
  NO_DATABASE_CONNECTION=No database connection
58
54
  ENTITY_ALREADY_EXISTS=Entity already exists
@@ -74,9 +70,6 @@ ENTITY_IS_NOT_CRUD=Entity "{0}" is not {1}
74
70
  ENTITY_IS_NOT_CRUD_VIA_NAVIGATION=Entity "{0}" is not {1} via navigation "{2}"
75
71
  ENTITY_IS_AUTOEXPOSED=Entity "{0}" is not explicitly exposed as part of the service
76
72
  EXPAND_IS_RESTRICTED=Navigation property "{0}" is not allowed for expand operation
77
- EXPAND_COUNT_UNSUPPORTED="/$count" is not supported for expand operation
78
- ORDERBY_LAMBDA_UNSUPPORTED="$orderby" does not support lambda
79
- EXPAND_APPLY_UNSUPPORTED="$apply" is not supported for expand operation
80
73
 
81
74
  # rest protocol adapter
82
75
  INVALID_RESOURCE="{0}" is not a valid resource
@@ -5,7 +5,7 @@ const { SELECT, INSERT, DELETE, UPDATE } = cds.ql
5
5
  const Query = require('../../../../lib/ql/Query')
6
6
 
7
7
  const { resolveView } = require('./resolveView')
8
- const { ensureNoDraftsSuffix, getDraftColumnsCQNForDraft } = require('./draft')
8
+ const { ensureNoDraftsSuffix, getDraftColumnsCQNForDraft, ensureDraftsSuffix } = require('./draft')
9
9
  const { flattenStructuredSelect, OPERATIONS_MAP } = require('./structured')
10
10
  const search2cqn4sql = require('./search2cqn4sql')
11
11
  const { getEntityNameFromCQN } = require('./entityFromCqn')
@@ -43,11 +43,11 @@ const convertPathExpressionToWhere = (fromClause, model, options) => {
43
43
  return { target, alias, where, cardinality, args }
44
44
  }
45
45
 
46
- let previousSelect, previousEntityName, previousTableAlias, structParent, previousArgs
46
+ let previousSelect, previousEntityName, previousTableAlias, structParent, previousArgs, draft
47
47
  let prefix = []
48
48
  let columns
49
49
  for (let i = 0; i < fromClause.ref.length; i++) {
50
- const entity = structParent || model.definitions[previousEntityName]
50
+ const entity = structParent || (previousEntityName && model.definitions[ensureNoDraftsSuffix(previousEntityName)])
51
51
  const element = _elementFromRef(fromClause.ref[i], entity)
52
52
 
53
53
  if (element && element._isStructured) {
@@ -55,6 +55,7 @@ const convertPathExpressionToWhere = (fromClause, model, options) => {
55
55
  structParent = element
56
56
  continue
57
57
  } else if (element && element.isAssociation) {
58
+ if (element._isAssociationStrict && !element['@odata.draft.enclosed']) draft = false
58
59
  _modifyNavigationInWhere(fromClause.ref[i].where, element._target)
59
60
  } else if (element && previousSelect && i === fromClause.ref.length - 1) {
60
61
  columns = [{ ref: [...prefix, element.name] }]
@@ -62,8 +63,10 @@ const convertPathExpressionToWhere = (fromClause, model, options) => {
62
63
  continue
63
64
  }
64
65
 
65
- const currentEntityName = _getEntityName(fromClause, entity, i)
66
+ const entityName = _getEntityName(fromClause, entity, i)
67
+ const currentEntityName = draft ? entityName && ensureDraftsSuffix(entityName) : entityName
66
68
  if (!currentEntityName) continue
69
+ if (!draft && currentEntityName.endsWith('_drafts')) draft = true
67
70
  const tableAlias = `T${i}`
68
71
  const currentSelect = SELECT.from(`${currentEntityName} as ${tableAlias}`)
69
72
 
@@ -81,7 +84,10 @@ const convertPathExpressionToWhere = (fromClause, model, options) => {
81
84
  if (previousSelect) {
82
85
  const navigation = _getTargetFromRef(fromClause.ref[i])
83
86
  previousSelect.where(
84
- model.definitions[previousEntityName]._relations[[...prefix, navigation]].join(tableAlias, previousTableAlias)
87
+ model.definitions[ensureNoDraftsSuffix(previousEntityName)]._relations[[...prefix, navigation]].join(
88
+ tableAlias,
89
+ previousTableAlias
90
+ )
85
91
  )
86
92
  _convertSelect(previousSelect, model, options)
87
93
  currentSelect.where('exists', previousSelect)
@@ -613,7 +619,6 @@ const _convertPathExpression = (query, model, options = {}) => {
613
619
  for (const whereEl of query.SELECT.where || []) {
614
620
  if (typeof whereEl === 'object' && whereEl.SELECT) _convertPathExpression(whereEl, model)
615
621
  }
616
-
617
622
  const conversion = convertPathExpressionToWhere(query.SELECT.from, model, options)
618
623
  if (!conversion) return
619
624
  const { target, alias, where, cardinality, columns, args } = conversion
@@ -1,8 +1,6 @@
1
1
  const cds = require('../../cds')
2
2
  const resolveStructured = require('../../common/utils/resolveStructured')
3
3
 
4
- const { ensureNoDraftsSuffix } = require('./draft')
5
-
6
4
  const getEtagElement = entity => {
7
5
  return Object.values(entity.elements).find(element => element['@odata.etag'])
8
6
  }
@@ -165,31 +163,6 @@ const getElementDeep = (entity, ref) => {
165
163
  return current
166
164
  }
167
165
 
168
- const isRootEntity = (definitions, entityName) => {
169
- const entity = definitions[entityName]
170
- if (!entity) return false
171
-
172
- // TODO: There can be unmanaged relations to some parent -> not detected by the following code
173
- const associationElements = Object.keys(entity.elements)
174
- .map(key => entity.elements[key])
175
- .filter(element => element._isAssociationStrict)
176
-
177
- for (const { target } of associationElements) {
178
- const parentEntity = definitions[target]
179
- for (const parentElementName in parentEntity.elements) {
180
- const parentElement = parentEntity.elements[parentElementName]
181
- if (
182
- parentElement.isComposition &&
183
- parentElement.target === entityName &&
184
- !(parentElement.parent && ensureNoDraftsSuffix(parentElement.parent.name) === entityName)
185
- ) {
186
- return false
187
- }
188
- }
189
- }
190
- return true
191
- }
192
-
193
166
  const _setAlias2ref = entity => {
194
167
  const _ref2alias = {}
195
168
  const _alias2ref = {}
@@ -270,7 +243,6 @@ module.exports = {
270
243
  getEtagElement,
271
244
  findCsnTargetFor,
272
245
  getElementDeep,
273
- isRootEntity,
274
246
  getDataSubject,
275
247
  alias2ref,
276
248
  getComp2oneParents,
@@ -1,4 +1,5 @@
1
1
  const cds = require('../../cds')
2
+ const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
2
3
 
3
4
  const _4sqlite = cds.env.i18n && Array.isArray(cds.env.i18n.for_sqlite) ? cds.env.i18n.for_sqlite : []
4
5
  // compiler reserves 'localized' and raises a corresponding exception if used in models
@@ -65,10 +66,16 @@ const getDraftColumnsCQNForDraft = () => {
65
66
  ]
66
67
  }
67
68
 
69
+ const filterNonDraftColumns = columns =>
70
+ columns.filter(
71
+ col => (col.ref && !(col.ref[col.ref.length - 1] in DRAFT_COLUMNS_MAP)) || (!col.ref && !(col in DRAFT_COLUMNS_MAP))
72
+ )
73
+
68
74
  module.exports = {
69
75
  ensureUnlocalized,
70
76
  ensureDraftsSuffix,
71
77
  ensureNoDraftsSuffix,
72
78
  getDraftColumnsCQNForActive,
73
- getDraftColumnsCQNForDraft
79
+ getDraftColumnsCQNForDraft,
80
+ filterNonDraftColumns
74
81
  }
@@ -1,4 +1,5 @@
1
1
  const cds = require('../../cds')
2
+ const { ensureNoDraftsSuffix } = require('./draft')
2
3
 
3
4
  /*
4
5
  * returns path like <service>.<entity>:<prop1>.<prop2> for ref = [{ id: '<service>.<entity>' }, '<prop1>', '<prop2>']
@@ -22,7 +23,12 @@ const getEntityFromPath = (path, def) => {
22
23
  path = typeof path === 'string' ? cds.parse.path(path) : path
23
24
  const segments = [...path.ref]
24
25
  while (segments.length) {
25
- const segment = segments.shift()
26
+ let segment = segments.shift()
27
+ if (segment.id && typeof segment.id === 'string') {
28
+ segment.id = ensureNoDraftsSuffix(segment.id)
29
+ } else if (typeof segment === 'string') {
30
+ segment = ensureNoDraftsSuffix(segment)
31
+ }
26
32
  current = current.elements[segment.id || segment]
27
33
  if (current && current.target) current = current._target
28
34
  }
@@ -0,0 +1,122 @@
1
+ const cds = require('../../cds')
2
+
3
+ const { prefixForStruct } = require('../../common/utils/csn')
4
+
5
+ const _autoGenerate = e => e && e.isUUID && e.key
6
+
7
+ const _set = (row, value, element, enumerable) => {
8
+ if (!element.parent.elements[element.name]) return // only when in model
9
+ if (!enumerable && element.foreignKeySource) {
10
+ // only for foreign keys
11
+ Object.defineProperty(row, element.name, {
12
+ get() {
13
+ return value
14
+ },
15
+ set(v) {
16
+ // Make sure that it becomes enumerable again if set manually afterwards
17
+ Object.defineProperty(row, element.name, { value: v, configurable: true, enumerable: true })
18
+ },
19
+ enumerable: false,
20
+ configurable: true
21
+ })
22
+ } else {
23
+ row[element.name] = value
24
+ }
25
+ }
26
+
27
+ const _generateParentField = ({ parentElement }, row, enumerable) => {
28
+ if (_autoGenerate(parentElement) && !row[parentElement.name]) {
29
+ _set(row, cds.utils.uuid(), parentElement, enumerable)
30
+ }
31
+ }
32
+
33
+ const _generateChildField = ({ deep, childElement }, childRow, enumerable) => {
34
+ if (deep) {
35
+ _generateChildField(deep.propagation, childRow[deep.targetName], enumerable)
36
+ } else if (_autoGenerate(childElement) && childRow && !childRow[childElement.name]) {
37
+ _set(childRow, cds.utils.uuid(), childElement, enumerable)
38
+ }
39
+ }
40
+
41
+ const _getNestedVal = (row, prefix) => {
42
+ let val = row
43
+ const splitted = prefix.split('_')
44
+ splitted.pop() // remove last `_`
45
+ let k = ''
46
+
47
+ while (splitted.length > 0) {
48
+ k += splitted.shift()
49
+ if (k in val) {
50
+ val = val[k]
51
+ k = ''
52
+ } else {
53
+ k += '_'
54
+ }
55
+ }
56
+
57
+ return val
58
+ }
59
+
60
+ const _propagateToChild = ({ parentElement, childElement, parentFieldValue }, row, childRow, enumerable) => {
61
+ if (!childElement || !childElement.parent.elements[childElement.name]) return
62
+ if (parentElement) {
63
+ const prefix = prefixForStruct(parentElement)
64
+ if (prefix) {
65
+ const nested = _getNestedVal(row, prefix)
66
+ _set(childRow, nested[parentElement.name], childElement, enumerable)
67
+ } else {
68
+ _set(childRow, row[parentElement.name], childElement, enumerable)
69
+ }
70
+ } else if (parentFieldValue !== undefined) {
71
+ _set(childRow, parentFieldValue, childElement, enumerable)
72
+ }
73
+ }
74
+
75
+ const _propagateToParent = ({ parentElement, childElement, deep }, childRow, row, enumerable) => {
76
+ if (deep) {
77
+ _propagateToParent(deep.propagation, childRow[deep.targetName], childRow, enumerable)
78
+ }
79
+ if (parentElement && childElement && childRow && childElement.name in childRow) {
80
+ _set(row, childRow[childElement.name], parentElement, enumerable)
81
+ }
82
+ }
83
+
84
+ module.exports = (
85
+ tKey,
86
+ row,
87
+ foreignKeyPropagations,
88
+ isCompositionEffective,
89
+ { deleteAssocs = false, enumerable = true } = {}
90
+ ) => {
91
+ if (!row || !(tKey in row)) return
92
+ if (row[tKey] === null) {
93
+ for (const foreignKeyPropagation of foreignKeyPropagations) {
94
+ if (!foreignKeyPropagation.fillChild) {
95
+ _set(row, null, foreignKeyPropagation.parentElement, enumerable)
96
+ }
97
+ }
98
+ if (deleteAssocs && !isCompositionEffective) delete row[tKey]
99
+ return
100
+ }
101
+
102
+ const childRows = Array.isArray(row[tKey]) ? row[tKey] : [row[tKey]]
103
+
104
+ for (const childRow of childRows) {
105
+ if (!childRow) return
106
+
107
+ for (const foreignKeyPropagation of foreignKeyPropagations) {
108
+ if (foreignKeyPropagation.fillChild) {
109
+ // propagate or generate in parent
110
+ const pk = foreignKeyPropagation.parentElement && foreignKeyPropagation.parentElement.name
111
+ if (pk && !(pk in row)) _propagateToParent(foreignKeyPropagation, childRow, row, enumerable)
112
+ if (!(pk in row)) _generateParentField(foreignKeyPropagation, row, enumerable)
113
+
114
+ if (isCompositionEffective) _propagateToChild(foreignKeyPropagation, row, childRow, enumerable)
115
+ } else {
116
+ _generateChildField(foreignKeyPropagation, childRow, enumerable)
117
+ _propagateToParent(foreignKeyPropagation, childRow, row, enumerable)
118
+ }
119
+ }
120
+ }
121
+ if (deleteAssocs && !isCompositionEffective) delete row[tKey]
122
+ }
@@ -501,9 +501,8 @@ const _checkForForbiddenViews = queryTarget => {
501
501
  if (!select.from || select.from.join || select.from.length > 1) {
502
502
  throw getError({
503
503
  code: 501,
504
- message: 'NON_WRITABLE_VIEW',
505
- target: queryTarget.name,
506
- args: [_event || 'INSERT|UPDATE|DELETE']
504
+ message: `${_event || 'INSERT|UPDATE|DELETE'} on views with join and/or union is not supported`,
505
+ target: queryTarget.name
507
506
  })
508
507
  }
509
508
  if (select.where) {
@@ -67,7 +67,7 @@ const _getNextTarget = (model, element, currentPath = []) => {
67
67
  * @param {object} targetEntity The target entity which needs to be traversed
68
68
  * @param {object} callbacks
69
69
  * @param {function} callbacks.pick Callback function to pick elements. If it returns a truthy value, the element will be picked. The returned value is part of the template.
70
- * @param {function} callbacks.ignore Callback function to ignore elements. If it returns a truthy value, the element will be ignored.
70
+ * @param {function} callbacks.ignore Callback function to ignore the target of an element. If it returns a truthy value, the element's target will be ignored.
71
71
  * @param {object} [parent=null] The parent entity
72
72
  * @param {Map} [_entityMap] This parameter is an implementation side-effect — don't use it
73
73
  * @param {array} [targetPath=[]]
@@ -92,8 +92,6 @@ function _getTemplate(model, cache, targetEntity, callbacks, parent = null, _ent
92
92
 
93
93
  for (const elementName in elements) {
94
94
  const element = elements[elementName]
95
- if (ignore && ignore(element, targetEntity, parent)) continue
96
-
97
95
  _pick(pick, element, targetEntity, parent, templateElements, elementName)
98
96
 
99
97
  if (element.items) {
@@ -101,6 +99,7 @@ function _getTemplate(model, cache, targetEntity, callbacks, parent = null, _ent
101
99
  }
102
100
 
103
101
  const { nextTargetName, nextTarget } = _getNextTarget(model, element, currentPath)
102
+ if (ignore && ignore(element)) continue
104
103
  const nextTargetCached = _entityMap.get(nextTargetName)
105
104
 
106
105
  if (nextTargetCached) {
@@ -3,55 +3,14 @@ const { ensureNoDraftsSuffix } = require('../../common/utils/draft')
3
3
  const { proxifyIfFlattened } = require('../../../common/utils/ucsn')
4
4
  const cds = require('../../../../lib')
5
5
 
6
- const _refs = (refs, as) => {
7
- const arr = []
8
- for (const element of refs) {
9
- // multiple join are nested, so we need to find all the table names in there as well
10
- if (Object.prototype.hasOwnProperty.call(element, 'join')) {
11
- arr.push(..._extractRefs(element))
12
- // Likely a union
13
- } else if (Object.prototype.hasOwnProperty.call(element, 'SELECT')) {
14
- arr.push(..._extractRefs(element.SELECT.from, element.as))
15
- } else {
16
- arr.push(..._extractRefs(element, as))
17
- }
18
- }
19
-
20
- return arr
21
- }
22
-
23
- const _getActiveFromUnion = refs => {
24
- if (refs.length !== 2) return
25
- const [maybeDraft, maybeActive] = refs
26
- if (ensureNoDraftsSuffix(maybeDraft.ref[0]) === maybeActive.ref[0]) return maybeActive
27
- if (ensureNoDraftsSuffix(maybeActive.ref[0]) === maybeDraft.ref[0]) return maybeDraft
28
- }
29
-
30
- const _extractRefs = (from, as) => {
31
- if (from.SELECT) {
32
- return _extractRefs(from.SELECT.from, as || from.SELECT.as)
33
- }
34
- if (Object.prototype.hasOwnProperty.call(from, 'join')) {
35
- // cqn with join in from
36
- return _refs(from.args)
37
- }
38
- if (Object.prototype.hasOwnProperty.call(from, 'SET')) {
39
- let refs = _refs(from.SET.args).filter(a => !a.as || a.as !== 'filterAdmin')
40
- refs = _getActiveFromUnion(refs) ? [_getActiveFromUnion(refs)] : refs
41
- if (as) return refs.map(({ ref }) => ({ as, ref }))
42
- return refs
43
- }
44
- if (!from.ref) return []
45
- const ref = { ref: [...from.ref] }
46
- if (as || from.as) ref.as = as || from.as
47
- return [ref]
48
- }
49
-
50
6
  const _getCastFunction = ({ type }) => {
51
7
  switch (type) {
52
8
  case 'cds.Boolean':
53
9
  return Boolean
54
10
  case 'cds.Integer':
11
+ case 'cds.UInt8':
12
+ case 'cds.Int16':
13
+ case 'cds.Int32':
55
14
  return Number
56
15
  default:
57
16
  return String
@@ -14,7 +14,7 @@ const cds = require('../../cds')
14
14
  const normalizeTimeData = require('../utils/normalizeTimeData')
15
15
 
16
16
  const { enrichDataWithKeysFromWhere } = require('../../common/utils/keys')
17
- const propagateForeignKeys = require('../utils/propagateForeignKeys')
17
+ const propagateForeignKeys = require('../../common/utils/propagateForeignKeys')
18
18
  const getTemplate = require('../../common/utils/template')
19
19
  const templateProcessor = require('../../common/utils/templateProcessor')
20
20
 
@@ -32,8 +32,8 @@ const _processComplexCategory = ({ row, key, val, category, req, element }) => {
32
32
  category = category.category
33
33
 
34
34
  // propagate keys
35
- if (category === 'propagateForeignKeys' && key in row) {
36
- propagateForeignKeys(key, row, element._foreignKeys, element.isComposition)
35
+ if (category === 'propagateForeignKeys') {
36
+ propagateForeignKeys(key, row, element._foreignKeys, element.isComposition, { deleteAssocs: true })
37
37
  return
38
38
  }
39
39
 
@@ -197,7 +197,7 @@ const _pickDraft = element => {
197
197
  if (categories.length) return { categories }
198
198
  }
199
199
 
200
- function _handler(req) {
200
+ function dbGenericInput(req) {
201
201
  if (!this.model || typeof req.query === 'string' || !req.target) return
202
202
 
203
203
  // call with this for this.model
@@ -231,6 +231,6 @@ function _handler(req) {
231
231
  }
232
232
  }
233
233
 
234
- _handler._initial = true
234
+ dbGenericInput._initial = true
235
235
 
236
- module.exports = _handler
236
+ module.exports = dbGenericInput
@@ -3,7 +3,11 @@ const typeConversionMap = new Map()
3
3
  typeConversionMap.set('cds.UUID', { type: 'NVARCHAR', length: 36 })
4
4
  typeConversionMap.set('cds.Boolean', 'BOOLEAN')
5
5
  typeConversionMap.set('cds.Integer', 'INTEGER')
6
+ typeConversionMap.set('cds.UInt8', 'INTEGER')
7
+ typeConversionMap.set('cds.Int16', 'INTEGER')
8
+ typeConversionMap.set('cds.Int32', 'INTEGER')
6
9
  typeConversionMap.set('cds.Integer64', 'BIGINT')
10
+ typeConversionMap.set('cds.Int64', 'BIGINT')
7
11
  typeConversionMap.set('cds.Decimal', { type: 'DECIMAL' })
8
12
  typeConversionMap.set('cds.DecimalFloat', { type: 'DECIMAL' })
9
13
  typeConversionMap.set('cds.Double', 'DOUBLE')
@@ -106,7 +106,7 @@ const _draftCompositionTree = async (service, req) => {
106
106
  *
107
107
  * @param req
108
108
  */
109
- const _handler = async function (req) {
109
+ const fioriGenericActivate = async function (req) {
110
110
  if (
111
111
  isActiveEntityRequested(req.query.SELECT.from.ref[0].where || []) ||
112
112
  req.query.SELECT.from.ref.length > 2 ||
@@ -178,5 +178,5 @@ const _handler = async function (req) {
178
178
  }
179
179
 
180
180
  module.exports = cds.service.impl(function (srv, entity) {
181
- srv.on('draftActivate', entity, _handler)
181
+ srv.on('draftActivate', entity, fioriGenericActivate)
182
182
  })