@sap/cds 5.6.1 → 5.7.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 (184) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/_i18n/i18n_fr.properties +4 -4
  3. package/apis/cds.d.ts +7 -10
  4. package/apis/connect.d.ts +3 -3
  5. package/apis/core.d.ts +2 -4
  6. package/apis/models.d.ts +2 -3
  7. package/apis/ql.d.ts +0 -1
  8. package/apis/services.d.ts +7 -3
  9. package/bin/build/buildTaskFactory.js +16 -10
  10. package/bin/build/buildTaskProviderFactory.js +3 -3
  11. package/bin/build/constants.js +2 -1
  12. package/bin/build/provider/buildTaskProviderInternal.js +14 -14
  13. package/bin/build/provider/hana/2migration.js +2 -3
  14. package/bin/build/provider/hana/index.js +34 -0
  15. package/bin/build/provider/hana/migrationtable.js +90 -22
  16. package/bin/build/provider/hana/template/undeploy.json +5 -0
  17. package/bin/build/provider/node-cf/index.js +9 -2
  18. package/bin/serve.js +16 -18
  19. package/lib/compile/cdsc.js +15 -5
  20. package/lib/compile/etc/_localized.js +4 -4
  21. package/lib/compile/extend.js +8 -0
  22. package/lib/compile/index.js +3 -1
  23. package/lib/compile/minify.js +61 -0
  24. package/lib/compile/resolve.js +4 -1
  25. package/lib/compile/to/gql.js +9 -0
  26. package/lib/compile/to/sql.js +26 -30
  27. package/lib/connect/index.js +1 -1
  28. package/lib/core/entities.js +0 -3
  29. package/lib/core/infer.js +1 -0
  30. package/lib/core/reflect.js +1 -34
  31. package/lib/deploy.js +25 -17
  32. package/lib/env/defaults.js +3 -1
  33. package/lib/env/index.js +13 -4
  34. package/lib/env/presets.js +38 -0
  35. package/lib/env/requires.js +16 -11
  36. package/lib/index.js +13 -11
  37. package/lib/log/format/kibana.js +4 -2
  38. package/lib/log/index.js +2 -2
  39. package/lib/ql/Whereable.js +1 -0
  40. package/lib/req/cds-context.js +79 -0
  41. package/lib/req/context.js +5 -77
  42. package/lib/req/request.js +1 -1
  43. package/lib/serve/Service-api.js +8 -4
  44. package/lib/serve/Service-dispatch.js +0 -7
  45. package/lib/serve/Service-methods.js +6 -8
  46. package/lib/serve/Transaction.js +35 -30
  47. package/lib/serve/adapters.js +1 -4
  48. package/lib/utils/axios.js +1 -1
  49. package/libx/_runtime/audit/Service.js +44 -20
  50. package/libx/_runtime/audit/generic/personal/access.js +16 -11
  51. package/libx/_runtime/audit/generic/personal/modification.js +5 -5
  52. package/libx/_runtime/audit/generic/personal/utils.js +46 -37
  53. package/libx/_runtime/{common/auth → auth}/index.js +21 -7
  54. package/libx/_runtime/{common/auth → auth}/strategies/JWT.js +2 -2
  55. package/libx/_runtime/{common/auth → auth}/strategies/basic.js +2 -2
  56. package/libx/_runtime/{common/auth → auth}/strategies/dummy.js +1 -1
  57. package/libx/_runtime/{common/auth → auth}/strategies/mock.js +2 -2
  58. package/libx/_runtime/{common/auth → auth}/strategies/utils/uaa.js +1 -1
  59. package/libx/_runtime/{common/auth → auth}/strategies/utils/xssec.js +0 -0
  60. package/libx/_runtime/{common/auth → auth}/strategies/xsuaa.js +2 -2
  61. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +7 -2
  62. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +0 -7
  63. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +0 -8
  64. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +3 -4
  65. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +6 -7
  66. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +3 -4
  67. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +2 -11
  68. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +16 -6
  69. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +26 -65
  70. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +0 -7
  71. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +3 -66
  72. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +26 -0
  73. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +5 -5
  74. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +2 -2
  75. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +13 -10
  76. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/ConditionalRequestControlCommand.js +0 -7
  77. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/SetResponseHeadersCommand.js +1 -0
  78. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/ConditionalRequestValidator.js +0 -8
  79. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +18 -15
  80. package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +54 -76
  81. package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +0 -7
  82. package/libx/_runtime/cds-services/adapter/rest/handlers/create.js +3 -6
  83. package/libx/_runtime/cds-services/adapter/rest/handlers/delete.js +3 -6
  84. package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +3 -6
  85. package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +3 -6
  86. package/libx/_runtime/cds-services/adapter/rest/handlers/update.js +3 -6
  87. package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +2 -2
  88. package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +8 -4
  89. package/libx/_runtime/cds-services/services/Service.js +0 -6
  90. package/libx/_runtime/cds-services/services/utils/columns.js +10 -3
  91. package/libx/_runtime/cds-services/services/utils/compareJson.js +4 -7
  92. package/libx/_runtime/cds-services/services/utils/differ.js +4 -1
  93. package/libx/_runtime/cds-services/services/utils/handlerUtils.js +1 -41
  94. package/libx/_runtime/cds-services/util/assert.js +1 -262
  95. package/libx/_runtime/cds.js +6 -9
  96. package/libx/_runtime/common/aspects/entity.js +1 -1
  97. package/libx/_runtime/common/composition/delete.js +4 -2
  98. package/libx/_runtime/common/composition/update.js +27 -35
  99. package/libx/_runtime/common/composition/utils.js +3 -7
  100. package/libx/_runtime/common/error/standardError.js +11 -0
  101. package/libx/_runtime/common/generic/auth.js +61 -30
  102. package/libx/_runtime/common/generic/crud.js +11 -23
  103. package/libx/_runtime/common/generic/input.js +20 -0
  104. package/libx/_runtime/common/generic/paging.js +2 -2
  105. package/libx/_runtime/common/generic/put.js +4 -10
  106. package/libx/_runtime/common/generic/sorting.js +12 -30
  107. package/libx/_runtime/common/perf/index.js +24 -0
  108. package/libx/_runtime/common/utils/cqn.js +58 -1
  109. package/libx/_runtime/common/utils/cqn2cqn4sql.js +289 -114
  110. package/libx/_runtime/common/utils/csn.js +38 -56
  111. package/libx/_runtime/common/utils/entityFromCqn.js +6 -6
  112. package/libx/_runtime/common/utils/resolveView.js +4 -5
  113. package/libx/_runtime/common/utils/rewriteAsterisks.js +46 -5
  114. package/libx/_runtime/common/utils/search2cqn4sql.js +21 -9
  115. package/libx/_runtime/common/utils/structured.js +35 -25
  116. package/libx/_runtime/db/Service.js +0 -6
  117. package/libx/_runtime/db/expand/expand-v2.js +130 -0
  118. package/libx/_runtime/db/expand/expandCQNToJoin.js +82 -61
  119. package/libx/_runtime/db/expand/index.js +3 -1
  120. package/libx/_runtime/db/generic/arrayed.js +14 -27
  121. package/libx/_runtime/db/generic/input.js +52 -10
  122. package/libx/_runtime/db/generic/integrity.js +367 -26
  123. package/libx/_runtime/db/generic/virtual.js +51 -13
  124. package/libx/_runtime/db/query/update.js +9 -3
  125. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +8 -9
  126. package/libx/_runtime/{common → db}/utils/propagateForeignKeys.js +11 -14
  127. package/libx/_runtime/fiori/generic/activate.js +1 -0
  128. package/libx/_runtime/fiori/generic/before.js +2 -1
  129. package/libx/_runtime/fiori/generic/edit.js +2 -1
  130. package/libx/_runtime/fiori/generic/patch.js +1 -1
  131. package/libx/_runtime/fiori/generic/read.js +151 -57
  132. package/libx/_runtime/fiori/uiflex/handler/transformRESULT.js +0 -4
  133. package/libx/_runtime/fiori/uiflex/index.js +1 -1
  134. package/libx/_runtime/fiori/uiflex/{extensibility/index.js → service.js} +6 -4
  135. package/libx/_runtime/fiori/utils/delete.js +7 -1
  136. package/libx/_runtime/hana/Service.js +1 -8
  137. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +5 -14
  138. package/libx/_runtime/hana/execute.js +10 -4
  139. package/libx/_runtime/hana/pool.js +55 -45
  140. package/libx/_runtime/hana/search.js +7 -6
  141. package/libx/_runtime/hana/search2cqn4sql.js +8 -5
  142. package/libx/_runtime/hana/searchToContains.js +3 -1
  143. package/libx/_runtime/index.js +5 -5
  144. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +3 -3
  145. package/libx/_runtime/messaging/Outbox.js +53 -0
  146. package/libx/_runtime/messaging/common-utils/AMQPClient.js +17 -10
  147. package/libx/_runtime/messaging/common-utils/connections.js +14 -9
  148. package/libx/_runtime/messaging/common-utils/waitingTime.js +2 -0
  149. package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -3
  150. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -2
  151. package/libx/_runtime/messaging/enterprise-messaging.js +21 -15
  152. package/libx/_runtime/messaging/file-based.js +5 -5
  153. package/libx/_runtime/messaging/message-queuing.js +2 -3
  154. package/libx/_runtime/messaging/outbox/OutboxRunner.js +75 -0
  155. package/libx/_runtime/messaging/outbox/utils.js +192 -0
  156. package/libx/_runtime/messaging/service.js +16 -30
  157. package/libx/_runtime/remote/Service.js +21 -2
  158. package/libx/_runtime/remote/utils/client.js +15 -3
  159. package/libx/_runtime/remote/utils/{dataConversion.js → data.js} +12 -2
  160. package/libx/_runtime/sqlite/Service.js +7 -10
  161. package/libx/_runtime/sqlite/customBuilder/CustomExpressionBuilder.js +19 -0
  162. package/libx/_runtime/sqlite/execute.js +18 -12
  163. package/libx/_runtime/types/api.js +2 -1
  164. package/libx/odata/{odata2cqn/afterburner.js → afterburner.js} +28 -16
  165. package/libx/odata/{cqn2odata/index.js → cqn2odata.js} +1 -1
  166. package/libx/odata/{odata2cqn/grammar.pegjs → grammar.pegjs} +182 -118
  167. package/libx/odata/index.js +18 -15
  168. package/libx/odata/parser.js +1 -0
  169. package/libx/odata/utils.js +57 -0
  170. package/libx/rest/RestAdapter.js +2 -6
  171. package/libx/rest/utils/data.js +1 -6
  172. package/package.json +4 -3
  173. package/server.js +13 -10
  174. package/srv/audit-log.cds +87 -0
  175. package/{libx/_runtime/fiori/uiflex/extensibility/index.cds → srv/flex.cds} +0 -0
  176. package/srv/flex.js +1 -0
  177. package/srv/outbox.cds +11 -0
  178. package/srv/outbox.js +0 -0
  179. package/libx/_runtime/cds-services/adapter/perf/performance.js +0 -104
  180. package/libx/_runtime/cds-services/adapter/perf/performanceMeasurement.js +0 -33
  181. package/libx/odata/odata2cqn/index.js +0 -3
  182. package/libx/odata/odata2cqn/parser.js +0 -1
  183. package/libx/odata/readme.md +0 -1
  184. package/libx/odata/utils/index.js +0 -64
@@ -1,9 +1,7 @@
1
1
  const cds = require('../../../cds')
2
-
3
2
  const { SELECT } = cds.ql
4
3
 
5
- const { checkReferenceIntegrity } = require('../../util/assert')
6
- const { processDeep, processDeepAsync } = require('../../util/dataProcessUtils')
4
+ const { processDeep } = require('../../util/dataProcessUtils')
7
5
 
8
6
  const { DRAFT_COLUMNS } = require('../../../common/constants/draft')
9
7
 
@@ -145,43 +143,6 @@ const flattenDeepToOneAssociations = (req, csn) => {
145
143
  )
146
144
  }
147
145
 
148
- const checkIntegrityWrapper = (req, csn, run) => async (data, entity) => {
149
- const errors = await checkReferenceIntegrity(entity, data, req, csn, run)
150
- if (errors && errors.length !== 0) {
151
- for (const err of errors) {
152
- req.error(err)
153
- }
154
- }
155
- }
156
-
157
- const _isUncheckableInsert = query => {
158
- return query.INSERT && (query.INSERT.rows || query.INSERT.values || query.INSERT.as)
159
- }
160
-
161
- // REVISIT: lower to db layer, where it's used
162
- const checkIntegrityUtil = async (req, csn, run) => {
163
- if (!run) return
164
-
165
- // REVISIT
166
- if (typeof req.query === 'string' || req.target._unresolved) return
167
-
168
- // FIXME: doesn't work for uncheckable inserts
169
- if (_isUncheckableInsert(req.query)) return
170
-
171
- // REVISIT: integrity check needs context.data
172
- if (Object.keys(req.data).length === 0) {
173
- // REVISIT: We may need to double-check re req.data being undefined or empty
174
- if (req.query.DELETE) {
175
- req.data = req._beforeDeleteData || {}
176
- } else if (req.context && req.context.data && Object.keys(req.context.data).length > 0) {
177
- req.data = req.context.data
178
- }
179
- }
180
- if (Object.keys(req.data).length === 0) return
181
-
182
- await processDeepAsync(checkIntegrityWrapper(req, csn, run), req.data, req.target, false, true)
183
- }
184
-
185
146
  /*
186
147
  * merge CQNs
187
148
  */
@@ -235,6 +196,5 @@ const getDeepSelect = req => {
235
196
  module.exports = {
236
197
  getDeepSelect,
237
198
  allKeysAreProvided,
238
- checkIntegrityUtil,
239
199
  flattenDeepToOneAssociations
240
200
  }
@@ -1,10 +1,5 @@
1
- const cds = require('../../cds')
2
- const { all, resolve } = require('../../common/utils/thenable')
3
- const { getDependents } = require('../../common/utils/csn')
4
-
5
1
  // REVISIT: replace with cds.Request
6
2
  const getEntry = require('../../common/error/entry')
7
- const crypto = require('crypto')
8
3
 
9
4
  const ISO_DATE_PART1 =
10
5
  '[1-9]\\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)'
@@ -27,11 +22,8 @@ const ASSERT_FORMAT = 'ASSERT_FORMAT'
27
22
  const ASSERT_DATA_TYPE = 'ASSERT_DATA_TYPE'
28
23
  const ASSERT_ENUM = 'ASSERT_ENUM'
29
24
  const ASSERT_NOT_NULL = 'ASSERT_NOT_NULL'
30
- const ASSERT_REFERENCE_INTEGRITY = 'ASSERT_REFERENCE_INTEGRITY'
31
25
  const ASSERT_DEEP_ASSOCIATION = 'ASSERT_DEEP_ASSOCIATION'
32
26
 
33
- const ASSERT_INTEGRITY_ANNOTATION = '@assert.integrity'
34
-
35
27
  const _enumValues = element => {
36
28
  return Object.keys(element).map(enumKey => {
37
29
  const enum_ = element[enumKey]
@@ -178,6 +170,7 @@ const checkComplexType = ([key, value], elements, ignoreNonModelledData) => {
178
170
 
179
171
  const _checkStaticElementByKey = (definition, key, value, result, ignoreNonModelledData) => {
180
172
  const elementsOrParameters = definition.elements || definition.params
173
+ if (!elementsOrParameters) return result
181
174
  const elementOrParameter = elementsOrParameters[key]
182
175
 
183
176
  if (!elementOrParameter) {
@@ -309,259 +302,6 @@ const checkStatic = (definition, data, ignoreNonModelledData = false) => {
309
302
  }, [])
310
303
  }
311
304
 
312
- const _checkExistsWhere = (entity, whereList, run) => {
313
- const checks = whereList.map(where => {
314
- if (where.length === 0) {
315
- return true
316
- }
317
-
318
- const cqn = {
319
- SELECT: {
320
- columns: [{ val: 1, as: '_exists' }],
321
- from: { ref: [entity.name || entity] },
322
- where: where
323
- }
324
- }
325
-
326
- if (cds.context) {
327
- const hash = crypto.createHash('sha1').update(JSON.stringify(cqn)).digest('base64') // fastest hash
328
- if (!cds.context.__alreadyExecutedIntegrityChecks) cds.context.__alreadyExecutedIntegrityChecks = new Map()
329
- if (cds.context.__alreadyExecutedIntegrityChecks.has(hash)) {
330
- return cds.context.__alreadyExecutedIntegrityChecks.get(hash)
331
- } else {
332
- const promise = run(cqn).then(exists => {
333
- return exists.length !== 0
334
- })
335
- // we store the promise object in the map, it won't get executed twice when calling await Promise.all([promise, promise])
336
- cds.context.__alreadyExecutedIntegrityChecks.set(hash, promise)
337
- return promise
338
- }
339
- }
340
- return run(cqn).then(exists => {
341
- return exists.length !== 0
342
- })
343
- })
344
-
345
- return all(checks)
346
- }
347
-
348
- const _checkExists = (entity, data, req, run) => {
349
- if (!Array.isArray(data)) {
350
- return _checkExists(entity, [data], req, run).then(result => {
351
- return result[0]
352
- })
353
- }
354
-
355
- const where = data.map(row => {
356
- return Object.keys(entity.keys).reduce((where, name) => {
357
- if (row[name] !== undefined && row[name] !== null) {
358
- if (where.length > 0) {
359
- where.push('and')
360
- }
361
- where.push({ ref: [name] }, '=', { val: row[name] })
362
- }
363
-
364
- return where
365
- }, [])
366
- })
367
- return _checkExistsWhere(entity, where, run)
368
- }
369
-
370
- const _getFullForeignKeyName = (elementName, foreignKeyName) => `${elementName}_${foreignKeyName}`
371
-
372
- const _foreignKeyReducer = (key, foreignKeyName, row, element, ref) => {
373
- const fullForeignKeyName = _getFullForeignKeyName(element.name, foreignKeyName)
374
-
375
- if (ref.length > 1) {
376
- // ref includes assoc name, so we need to replace it by foreign key name
377
- const refWithFlatForeignKey = [...ref.slice(0, ref.length - 1), fullForeignKeyName]
378
- key[foreignKeyName] = _getDataFromRef(row, refWithFlatForeignKey)
379
- } else {
380
- key[foreignKeyName] = Object.prototype.hasOwnProperty.call(row, fullForeignKeyName) ? row[fullForeignKeyName] : null
381
- }
382
-
383
- return key
384
- }
385
-
386
- const _buildForeignKey = (element, row, ref) => {
387
- let foreignKey
388
-
389
- if (element.keys) {
390
- foreignKey = element.keys
391
- .map(obj => obj.ref[obj.ref.length - 1])
392
- .reduce((key, foreignKeyName) => {
393
- return _foreignKeyReducer(key, foreignKeyName, row, element, ref)
394
- }, {})
395
- }
396
-
397
- return foreignKey
398
- }
399
-
400
- const _getDataFromRef = (row, ref) => {
401
- if (row === undefined) return
402
-
403
- if (ref.length > 1) {
404
- return _getDataFromRef(row[ref[0]], ref.slice(1))
405
- }
406
-
407
- return row[ref[0]]
408
- }
409
-
410
- const _getElement = (entity, ref) => {
411
- if (ref.length > 1) {
412
- // structured
413
- return _getElement(entity.elements[ref[0]], ref.slice(1))
414
- }
415
-
416
- return entity.elements[ref[0]]
417
- }
418
-
419
- const _checkCreateUpdate = (result, ref, rootEntity, checks, data, req, run) => {
420
- const resolvedElement = _getElement(rootEntity, ref)
421
-
422
- return data.reduce((result, row) => {
423
- if (resolvedElement.on) return result
424
-
425
- const foreignKey = _buildForeignKey(resolvedElement, row, ref)
426
- if (foreignKey === undefined) return result
427
-
428
- checks.push(
429
- _checkExists(resolvedElement._target, foreignKey, req, run).then(exists => {
430
- if (!exists) {
431
- result.push(assertError(ASSERT_REFERENCE_INTEGRITY, resolvedElement, foreignKey))
432
- }
433
- })
434
- )
435
-
436
- return result
437
- }, result)
438
- }
439
-
440
- const _buildWhereDelete = (result, key, element, data) => {
441
- return data
442
- .map(d => {
443
- return Object.keys(d).reduce((result, name) => {
444
- if (key.ref[0] === name) {
445
- if (result.length > 0) {
446
- result.push('and')
447
- }
448
- result.push({ ref: [_getFullForeignKeyName(element.name, key.ref[0])] }, '=', { val: d[name] })
449
- }
450
-
451
- return result
452
- }, result)
453
- })
454
- .reduce((accumulatedWhere, currentWhere, i) => {
455
- if (i > 0) accumulatedWhere.push('or')
456
- accumulatedWhere.push(...currentWhere)
457
- return accumulatedWhere
458
- }, [])
459
- }
460
-
461
- const _checkDelete = (result, key, entity, checks, req, csn, run, data) => {
462
- const elements = csn.definitions[key].elements
463
- const source = csn.definitions[key].name
464
-
465
- const dependents = getDependents(req.target, csn) || []
466
- const sourceDependent = dependents.find(dep => dep.parent.name === source)
467
- if (!sourceDependent) return result
468
-
469
- return Object.keys(elements).reduce((result, assoc) => {
470
- if (!elements[assoc].target || !elements[assoc].keys) return result
471
-
472
- const targetDependent = dependents.find(dep => dep.target.name === elements[assoc].target)
473
- if (!targetDependent) return result
474
-
475
- const where = elements[assoc].keys.reduce((buildWhere, key) => {
476
- return _buildWhereDelete(buildWhere, key, elements[assoc], data)
477
- }, [])
478
- checks.push(
479
- _checkExistsWhere(source, [where], run).then(exists => {
480
- if (exists.includes(true)) {
481
- result.push(assertError(ASSERT_REFERENCE_INTEGRITY, elements[assoc], req.data))
482
- }
483
- })
484
- )
485
- return result
486
- }, result)
487
- }
488
-
489
- function _filterStructured(element, structuredAssocs, prefix) {
490
- const elements = element.elements
491
- for (const subElement in elements) {
492
- if (_filterAssocs(elements[subElement], structuredAssocs, prefix)) {
493
- structuredAssocs.push([...prefix, elements[subElement].name])
494
- }
495
- }
496
- }
497
-
498
- const _filterAssocs = (element, structuredAssocs, prefix = []) => {
499
- if (element.elements) {
500
- _filterStructured(element, structuredAssocs, [...prefix, element.name])
501
- }
502
-
503
- return (
504
- element._isAssociationStrict &&
505
- !element.virtual &&
506
- !element.abstract &&
507
- element[ASSERT_INTEGRITY_ANNOTATION] !== false &&
508
- !element['@odata.contained'] &&
509
- !element._target._hasPersistenceSkip
510
- )
511
- }
512
-
513
- // can be removed ones we switch to db integrity check
514
- const checkReferenceIntegrity = (entity, data, req, csn, run) => {
515
- const service = entity._service
516
- if (entity[ASSERT_INTEGRITY_ANNOTATION] === false || (service && service[ASSERT_INTEGRITY_ANNOTATION] === false)) {
517
- return
518
- }
519
-
520
- if (!Array.isArray(data)) data = [data]
521
-
522
- const checks = []
523
- let result
524
- if (req.event === 'CREATE' || req.event === 'UPDATE') {
525
- const structuredAssocRefs = []
526
- const associationRefs = Object.keys(entity.elements)
527
- .filter(elementName => _filterAssocs(entity.elements[elementName], structuredAssocRefs))
528
- .map(name => [name])
529
- result = [...associationRefs, ...structuredAssocRefs].reduce((createUpdateResult, ref) => {
530
- return _checkCreateUpdate(createUpdateResult, ref, entity, checks, data, req, run)
531
- }, [])
532
- }
533
- if (req.event === 'DELETE') {
534
- // we are only interested in table-level references not all derived ones on view levels
535
- // TODO: why?
536
- while (entity.query && entity.query._target) {
537
- entity = csn.definitions[entity.query._target.name]
538
- }
539
-
540
- result = Object.keys(csn.definitions)
541
- .filter(
542
- key =>
543
- !csn.definitions[key]['@cds.persistence.skip'] &&
544
- csn.definitions[key].elements !== undefined &&
545
- // skip check for events, aspects and localized tables
546
- csn.definitions[key].kind !== 'event' &&
547
- csn.definitions[key].kind !== 'aspect' &&
548
- csn.definitions[key].kind !== 'type' &&
549
- !csn.definitions[key].name.startsWith('localized.')
550
- )
551
- .reduce((deleteResult, key) => {
552
- return _checkDelete(deleteResult, key, entity, checks, req, csn, run, data)
553
- }, [])
554
- }
555
-
556
- if (checks.length) {
557
- return Promise.all(checks).then(() => {
558
- return result
559
- })
560
- }
561
-
562
- return resolve(result || [])
563
- }
564
-
565
305
  const checkKeys = (entity, data) => {
566
306
  if (!Array.isArray(data)) {
567
307
  return checkKeys(entity, [data])
@@ -583,7 +323,6 @@ module.exports = {
583
323
  checkStatic,
584
324
  checkInputConstraints,
585
325
  checkKeys,
586
- checkReferenceIntegrity,
587
326
  assertError,
588
327
  checkIfAssocDeep
589
328
  }
@@ -22,15 +22,12 @@ Object.defineProperty(cds, '_mtxEnabled', {
22
22
  * (lazy) feature flags
23
23
  */
24
24
  // referential integrity
25
- Object.defineProperty(cds.env.features, '_foreign_key_constraints', {
26
- get: () => cds.env.cdsc.beta && cds.env.cdsc.beta.foreignKeyConstraints,
27
- configurable: true
28
- })
29
- let assertIntegrity = cds.env.features.assert_integrity
30
- Object.defineProperty(cds.env.features, 'assert_integrity', {
31
- get: () => (assertIntegrity != null ? assertIntegrity : !cds.env.features._foreign_key_constraints),
32
- set: val => {
33
- assertIntegrity = val
25
+ // REVISIT: why is _db_foreign_key_constraints necessary?
26
+ Object.defineProperty(cds.env.features, '_db_foreign_key_constraints', {
27
+ get: () => {
28
+ const { assert_integrity: ai, assert_integrity_type: ait } = cds.env.features
29
+ if ((typeof ai === 'string' && ai.match(/individual/i)) || (ait && ait.match(/db/i))) return true
30
+ return false
34
31
  },
35
32
  configurable: true
36
33
  })
@@ -35,7 +35,7 @@ module.exports = class {
35
35
  '__isDraftEnabled',
36
36
  (this.associations && this.associations.DraftAdministrativeData) ||
37
37
  this.name.match(/\.DraftAdministrativeData$/) ||
38
- this.own('@odata.draft.enabled') // > case: entity not in service (tests only?)
38
+ (this.own('@odata.draft.enabled') && this.own('@Common.DraftRoot.ActivationAction'))
39
39
  )
40
40
  )
41
41
  }
@@ -140,7 +140,8 @@ const _addToCQNs = (cqns, subCQN, element, definitions, level) => {
140
140
  // Since `>2.5.2` compiler generates constraints for compositions of one like for annotations
141
141
  // Thus only single 2one case (`$self`-managed composition) has DELETE CASCADE
142
142
  // Here it's ignored to simplify i.e. handle all "2ones" in a same manner
143
- if (!cds.env.features._foreign_key_constraints || _is2oneComposition(element, definitions)) {
143
+ // REVISIT: why is _db_foreign_key_constraints necessary?
144
+ if (!cds.env.features._db_foreign_key_constraints || _is2oneComposition(element, definitions)) {
144
145
  cqns[level].push(subCQN)
145
146
  }
146
147
  }
@@ -251,7 +252,8 @@ const getDeepDeleteCQNs = (definitions, cqn) => {
251
252
  _recursivelyAliasRefs(parentWhere, 'ALIAS0', parentAlias)
252
253
  }
253
254
  const setNullUpdates = []
254
- if (cds.env.features._foreign_key_constraints && definitions[entityName].own('__oneCompositionParents')) {
255
+ // REVISIT: why is _db_foreign_key_constraints necessary?
256
+ if (cds.env.features._db_foreign_key_constraints && definitions[entityName].own('__oneCompositionParents')) {
255
257
  setNullUpdates.push(..._getSetNullParentForeignKeyCQNs(definitions, entityName, parentWhere, draft))
256
258
  }
257
259
  const subCascadeDeletes = _addSubCascadeDeleteCQN(definitions, compositionTree, parentWhere, 0, [], draft)
@@ -76,10 +76,13 @@ const _diffData = (newData, oldData, entity, newEntry, oldEntry, definitions) =>
76
76
  const oldVal = ctUtils.val(oldData[key])
77
77
 
78
78
  if (newVal !== undefined && newVal !== oldVal) {
79
+ if (!entity.elements[key]) continue
80
+
79
81
  if (entity.elements[key]._isStructured && Object.keys(newData[key]).length === 0) {
80
82
  // empty structured -> skip
81
83
  continue
82
84
  }
85
+
83
86
  result[key] = newData[key]
84
87
  continue
85
88
  }
@@ -135,36 +138,12 @@ function _addSubDeepUpdateCQNForUpdateInsert({
135
138
  return deepUpdateData
136
139
  }
137
140
 
138
- function _addSubDeepUpdateCQNCollectDelete(deleteCQNs, cqns, index) {
139
- deleteCQNs.forEach(deleteCQN => {
140
- if (
141
- !cqns.find((subCQNs, subIndex) => {
142
- if (subIndex > 0) {
143
- const deleteIndex = subCQNs.findIndex(cqn => {
144
- return cqn.DELETE && cqn.DELETE.from === deleteCQN.DELETE.from
145
- })
146
- if (deleteIndex > -1) {
147
- if (subIndex < index) {
148
- subCQNs.splice(deleteIndex, 1)
149
- } else {
150
- return true
151
- }
152
- }
153
- }
154
- return false
155
- })
156
- ) {
157
- cqns[index] = cqns[index] || []
158
- cqns[index].push(deleteCQN)
159
- }
160
- })
161
- }
162
-
163
141
  function _addSubDeepUpdateCQNCollect(definitions, cqns, updateCQNs, insertCQN, deleteCQN) {
164
142
  if (updateCQNs.length > 0) {
165
143
  cqns[0] = cqns[0] || []
166
144
  cqns[0].push(...updateCQNs)
167
145
  }
146
+
168
147
  if (insertCQN.INSERT.entries.length > 0) {
169
148
  cqns[0] = cqns[0] || []
170
149
  const deepInsertCQNs = getDeepInsertCQNs(definitions, insertCQN)
@@ -184,7 +163,10 @@ function _addSubDeepUpdateCQNCollect(definitions, cqns, updateCQNs, insertCQN, d
184
163
  cqns[0] = cqns[0] || []
185
164
  const deepDeleteCQNs = getDeepDeleteCQNs(definitions, deleteCQN)
186
165
  deepDeleteCQNs.forEach((deleteCQNs, index) => {
187
- _addSubDeepUpdateCQNCollectDelete(deleteCQNs, cqns, index)
166
+ deleteCQNs.forEach(el => {
167
+ cqns[index] = cqns[index] || []
168
+ cqns[index].push(el)
169
+ })
188
170
  })
189
171
  }
190
172
  }
@@ -200,18 +182,26 @@ const _addToData = (subData, entity, element, entry) => {
200
182
 
201
183
  function _addSubDeepUpdateCQNRecursion({ definitions, compositionTree, entity, data, selectData, cqns, draft }) {
202
184
  const selectDataByKey = _dataByKey(entity, selectData)
185
+
203
186
  for (const element of compositionTree.compositionElements) {
204
187
  const subData = []
205
188
  const selectSubData = []
189
+
206
190
  for (const entry of data) {
207
191
  if (element.name in entry) {
208
- _addToData(subData, entity, element, entry)
209
192
  const selectEntry = selectDataByKey.get(_serializedKey(entity, entry))
193
+
210
194
  if (selectEntry && element.name in selectEntry) {
195
+ if (selectEntry[element.name] === null && entry[element.name] === null) {
196
+ continue
197
+ }
211
198
  _addToData(selectSubData, entity, element, selectEntry)
212
199
  }
200
+
201
+ _addToData(subData, entity, element, entry)
213
202
  }
214
203
  }
204
+
215
205
  _addSubDeepUpdateCQN({
216
206
  definitions,
217
207
  compositionTree: element,
@@ -221,6 +211,7 @@ function _addSubDeepUpdateCQNRecursion({ definitions, compositionTree, entity, d
221
211
  draft
222
212
  })
223
213
  }
214
+
224
215
  return cqns
225
216
  }
226
217
 
@@ -245,11 +236,11 @@ const _addSubDeepUpdateCQN = ({ definitions, compositionTree, data, selectData,
245
236
  insertCQN,
246
237
  definitions
247
238
  })
239
+
248
240
  _addSubDeepUpdateCQNCollect(definitions, cqns, updateCQNs, insertCQN, deleteCQN)
249
241
 
250
- if (deepUpdateData.length === 0) {
251
- return Promise.resolve()
252
- }
242
+ if (deepUpdateData.length === 0) return Promise.resolve()
243
+
253
244
  return _addSubDeepUpdateCQNRecursion({
254
245
  definitions,
255
246
  compositionTree,
@@ -267,15 +258,16 @@ const _addSubDeepUpdateCQN = ({ definitions, compositionTree, data, selectData,
267
258
 
268
259
  const hasDeepUpdate = (definitions, cqn) => {
269
260
  if (cqn && cqn.UPDATE && cqn.UPDATE.entity && (cqn.UPDATE.data || cqn.UPDATE.with)) {
270
- const entityName =
271
- (cqn.UPDATE.entity.ref && cqn.UPDATE.entity.ref[0]) || cqn.UPDATE.entity.name || cqn.UPDATE.entity
261
+ const updateEntity = cqn.UPDATE.entity
262
+ const entityName = (updateEntity.ref && updateEntity.ref[0]) || updateEntity.name || updateEntity
272
263
  const entity = definitions && definitions[ensureNoDraftsSuffix(entityName)]
264
+
273
265
  if (entity) {
274
- return !!Object.keys(Object.assign({}, cqn.UPDATE.data || {}, cqn.UPDATE.with || {})).find(k => {
275
- return ctUtils.isCompOrAssoc(entity, k)
276
- })
266
+ const keys = Object.keys(Object.assign({}, cqn.UPDATE.data || {}, cqn.UPDATE.with || {}))
267
+ return !!keys.find(k => ctUtils.isCompOrAssoc(entity, k))
277
268
  }
278
269
  }
270
+
279
271
  return false
280
272
  }
281
273
 
@@ -32,13 +32,9 @@ const val = element => (element && element.val) || element
32
32
 
33
33
  const array = x => (Array.isArray(x) ? x : [x])
34
34
 
35
- const isCompOrAssoc = (entity, k, onlyToOne) => {
36
- return (
37
- entity.elements &&
38
- entity.elements[k] &&
39
- entity.elements[k].isAssociation &&
40
- ((onlyToOne && entity.elements[k].is2one) || !onlyToOne)
41
- )
35
+ const isCompOrAssoc = (entity, elementName, onlyToOne) => {
36
+ const element = entity.elements && entity.elements[elementName]
37
+ return !!(element && element.isAssociation && ((onlyToOne && element.is2one) || !onlyToOne))
42
38
  }
43
39
 
44
40
  const cleanDeepData = (entity, data, onlyToOne = false) => {
@@ -0,0 +1,11 @@
1
+ const isStandardError = err => {
2
+ return (
3
+ err instanceof TypeError ||
4
+ err instanceof ReferenceError ||
5
+ err instanceof SyntaxError ||
6
+ err instanceof RangeError ||
7
+ err instanceof URIError
8
+ )
9
+ }
10
+
11
+ module.exports = { isStandardError }