@sap/cds 8.8.3 → 8.9.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 (68) hide show
  1. package/CHANGELOG.md +40 -4
  2. package/_i18n/i18n_en_US_saptrc.properties +3 -0
  3. package/bin/colors.js +2 -0
  4. package/bin/test.js +103 -75
  5. package/eslint.config.mjs +16 -4
  6. package/lib/compile/for/lean_drafts.js +4 -0
  7. package/lib/compile/parse.js +26 -6
  8. package/lib/env/cds-env.js +3 -1
  9. package/lib/env/cds-requires.js +0 -3
  10. package/lib/env/schemas/cds-rc.js +11 -0
  11. package/lib/log/format/aspects/cls.js +2 -1
  12. package/lib/log/format/json.js +1 -1
  13. package/lib/plugins.js +2 -3
  14. package/lib/ql/SELECT.js +2 -1
  15. package/lib/ql/cds-ql.js +2 -0
  16. package/lib/ql/cds.ql-predicates.js +6 -4
  17. package/lib/ql/resolve.js +46 -0
  18. package/lib/req/validate.js +1 -0
  19. package/lib/srv/bindings.js +64 -43
  20. package/lib/srv/cds-connect.js +1 -1
  21. package/lib/srv/cds-serve.js +2 -2
  22. package/lib/srv/middlewares/auth/ias-auth.js +2 -0
  23. package/lib/srv/protocols/http.js +2 -2
  24. package/lib/srv/protocols/index.js +1 -1
  25. package/lib/srv/protocols/odata-v4.js +0 -1
  26. package/lib/srv/srv-tx.js +1 -1
  27. package/lib/test/cds-test.js +3 -4
  28. package/lib/utils/cds-utils.js +19 -19
  29. package/lib/utils/colors.js +46 -45
  30. package/lib/utils/csv-reader.js +5 -5
  31. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +1 -2
  32. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +1 -1
  33. package/libx/_runtime/common/Service.js +4 -2
  34. package/libx/_runtime/common/composition/data.js +1 -2
  35. package/libx/_runtime/common/composition/tree.js +6 -4
  36. package/libx/_runtime/common/generic/sorting.js +6 -2
  37. package/libx/_runtime/common/utils/cqn2cqn4sql.js +6 -7
  38. package/libx/_runtime/common/utils/differ.js +1 -1
  39. package/libx/_runtime/common/utils/draft.js +1 -1
  40. package/libx/_runtime/common/utils/foreignKeyPropagations.js +6 -2
  41. package/libx/_runtime/common/utils/keys.js +13 -84
  42. package/libx/_runtime/common/utils/propagateForeignKeys.js +4 -3
  43. package/libx/_runtime/common/utils/resolveView.js +96 -102
  44. package/libx/_runtime/common/utils/rewriteAsterisks.js +1 -1
  45. package/libx/_runtime/common/utils/stream.js +2 -3
  46. package/libx/_runtime/db/utils/columns.js +1 -1
  47. package/libx/_runtime/fiori/lean-draft.js +11 -7
  48. package/libx/_runtime/messaging/kafka.js +3 -4
  49. package/libx/_runtime/remote/Service.js +13 -5
  50. package/libx/_runtime/remote/utils/client.js +1 -0
  51. package/libx/_runtime/ucl/Service.js +135 -126
  52. package/libx/common/utils/path.js +34 -22
  53. package/libx/odata/middleware/create.js +2 -0
  54. package/libx/odata/middleware/operation.js +8 -2
  55. package/libx/odata/middleware/parse.js +1 -1
  56. package/libx/odata/middleware/stream.js +1 -2
  57. package/libx/odata/middleware/update.js +2 -0
  58. package/libx/odata/parse/afterburner.js +17 -9
  59. package/libx/odata/parse/cqn2odata.js +3 -1
  60. package/libx/odata/parse/grammar.peggy +21 -19
  61. package/libx/odata/parse/parser.js +1 -1
  62. package/libx/odata/utils/metadata.js +8 -2
  63. package/libx/odata/utils/odataBind.js +36 -0
  64. package/libx/outbox/index.js +1 -0
  65. package/libx/rest/middleware/operation.js +9 -8
  66. package/libx/rest/middleware/parse.js +1 -0
  67. package/package.json +3 -3
  68. package/lib/i18n/resources.js +0 -150
@@ -1,7 +1,6 @@
1
1
  const cds = require('../../cds')
2
2
  const { SELECT, INSERT, DELETE, UPDATE } = cds.ql
3
3
  const Query = require('../../../../lib/ql/cds.ql-Query')
4
- const { resolveView } = require('./resolveView')
5
4
  const { ensureNoDraftsSuffix, getDraftColumnsCQNForDraft, ensureDraftsSuffix } = require('./draft')
6
5
  const { flattenStructuredSelect, OPERATIONS_MAP } = require('./structured')
7
6
  const search2cqn4sql = require('./search2cqn4sql')
@@ -854,7 +853,7 @@ const _convertUpsert = (query, model) => {
854
853
  }
855
854
  Object.assign(upsert.UPSERT, { into: { ref: [resolvedIntoClause], as: query.UPSERT.into.as } })
856
855
 
857
- const resolved = resolveView(upsert, model, cds.db)
856
+ const resolved = cds.ql.resolve.resolve4db(upsert, cds.db)
858
857
  // required for deploying of extensions, not used anywhere else except UpsertBuilder
859
858
  resolved._target = resolved.UPSERT?._transitions?.[0].target || query._target
860
859
  // resolved._target = query._target
@@ -881,7 +880,7 @@ const _convertInsert = (query, model) => {
881
880
 
882
881
  const target = model.definitions[resolvedIntoClause]
883
882
  if (!target) return insert
884
- return resolveView(insert, model, cds.db)
883
+ return cds.ql.resolve.resolve4db(insert, cds.db)
885
884
  }
886
885
 
887
886
  function _modifyNavigationInWhere(whereClause, target) {
@@ -913,7 +912,7 @@ const _plainDelete = (cqn, model) => {
913
912
 
914
913
  if (!target) return cqn
915
914
 
916
- return resolveView(cqn, model, cds.db)
915
+ return cds.ql.resolve.resolve4db(cqn, cds.db)
917
916
  }
918
917
 
919
918
  const _convertDelete = (query, model, options) => {
@@ -946,7 +945,7 @@ const _convertDelete = (query, model, options) => {
946
945
 
947
946
  if (!targetEntity) return deleet
948
947
 
949
- return resolveView(deleet, model, cds.db)
948
+ return cds.ql.resolve.resolve4db(deleet, cds.db)
950
949
  }
951
950
 
952
951
  function _plainUpdate(cqn, model) {
@@ -958,7 +957,7 @@ function _plainUpdate(cqn, model) {
958
957
  }
959
958
 
960
959
  if (!target) return cqn
961
- return resolveView(cqn, model, cds.db)
960
+ return cds.ql.resolve.resolve4db(cqn, cds.db)
962
961
  }
963
962
 
964
963
  const _convertUpdate = (query, model, options) => {
@@ -996,7 +995,7 @@ const _convertUpdate = (query, model, options) => {
996
995
 
997
996
  if (!targetEntity) return update
998
997
 
999
- return resolveView(update, model, cds.db)
998
+ return cds.ql.resolve.resolve4db(update, cds.db)
1000
999
  }
1001
1000
 
1002
1001
  /**
@@ -67,7 +67,7 @@ module.exports = class Differ {
67
67
  const combinedData = providedData || Object.assign({}, req.query.UPDATE.data || {}, req.query.UPDATE.with || {})
68
68
  enrichDataWithKeysFromWhere(combinedData, req, this._srv)
69
69
  const lastTransition = newQuery.UPDATE._transitions[newQuery.UPDATE._transitions.length - 1]
70
- const revertedPersistent = revertData(req._.partialPersistentState, lastTransition, this._srv)
70
+ const revertedPersistent = revertData(req._.partialPersistentState, lastTransition, cds.db)
71
71
  const diff = compareJson(combinedData, revertedPersistent, req.target, { ignoreDraftColumns: true })
72
72
  return diff
73
73
  }
@@ -1,4 +1,4 @@
1
- const cds = require('../../cds')
1
+ const cds = require('../../../../lib')
2
2
  const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
3
3
 
4
4
  const _4sqlite = cds.env.i18n && Array.isArray(cds.env.i18n.for_sqlite) ? cds.env.i18n.for_sqlite : []
@@ -68,8 +68,12 @@ const _parentFieldsFromSimpleOnCond = (element, subOn) => {
68
68
  }
69
69
 
70
70
  if (!childElement.on) {
71
- if (element._isSelfManaged) return _foreignKeyPropagationsFromToManyOn(element, childFieldName)
72
- if (parentElement) return [{ fillChild: true, parentElement, childElement }]
71
+ const propagations = []
72
+ if ('val' in subOn[idxParentField])
73
+ propagations.push({ fillChild: true, parentFieldValue: subOn[idxParentField].val, childElement })
74
+ if (element._isSelfManaged)
75
+ return [...propagations, ..._foreignKeyPropagationsFromToManyOn(element, childFieldName)]
76
+ if (parentElement) return [...propagations, { fillChild: true, parentElement, childElement }]
73
77
  }
74
78
 
75
79
  if ('val' in subOn[idxParentField]) {
@@ -1,35 +1,6 @@
1
1
  const { where2obj } = require('./cqn')
2
2
  const { deepCopy } = require('./copy')
3
- const { getOnCond } = require('./generateOnCond')
4
-
5
- function _getOnCondElements(onCond, onCondElements = []) {
6
- const andIndex = onCond.indexOf('and')
7
-
8
- const ref0 = onCond[0].xpr ? onCond[0].xpr[0].ref : onCond[0].ref
9
- const ref1 = onCond[0].xpr ? onCond[0].xpr[2].ref : onCond[2].ref
10
- const val0 = onCond[0].xpr ? onCond[0].xpr[0].val : onCond[0].val
11
- const val1 = onCond[0].xpr ? onCond[0].xpr[2].val : onCond[2].val
12
-
13
- let entityRef, targetRef, entityVal
14
- if (ref0?.[0] === 'target') {
15
- targetRef = ref0
16
- entityRef = ref1
17
- entityVal = val1
18
- } else if (ref1?.[0] === 'target') {
19
- targetRef = ref1
20
- entityRef = ref0
21
- entityVal = val0
22
- }
23
-
24
- const entityKey = entityRef && entityRef.slice(1).join('.')
25
- const targetKey = targetRef && targetRef.slice(1).join('.')
26
- onCondElements.push({ entityKey, targetKey, entityVal })
27
-
28
- if (andIndex !== -1) {
29
- _getOnCondElements(onCond.slice(andIndex + 1), onCondElements)
30
- }
31
- return onCondElements
32
- }
3
+ const { foreignKeyPropagations } = require('./foreignKeyPropagations')
33
4
 
34
5
  function _mergeWhere(base, additional) {
35
6
  if (additional?.length) {
@@ -56,16 +27,20 @@ function _buildWhereForNavigations(ref, newWhere, model, target) {
56
27
 
57
28
  if (!navigationElement || !navigationElement.on) return
58
29
 
59
- const onCond = getOnCond(navigationElement, [navigationElement.name], { select: 'source', join: 'target' })
60
- const nextKeys = _getOnCondElements(onCond[0].xpr)
30
+ const nextKeys = foreignKeyPropagations(navigationElement)
61
31
 
62
32
  // only add where once in _modifyWhereWithNavigations
63
33
  let whereAdded = false
64
34
  for (const key of nextKeys) {
65
- const targetKeyElement = navigationElement._target.elements[key.entityKey]
35
+ const targetKeyElement = navigationElement._target.elements[key.childElement.name]
66
36
 
67
37
  if (targetKeyElement && (targetKeyElement.isAssociation || targetKeyElement._foreignKey4)) {
68
- _modifyWhereWithNavigations(!whereAdded && currentRef.where, newWhere, key.entityKey, key.targetKey)
38
+ _modifyWhereWithNavigations(
39
+ !whereAdded && currentRef.where,
40
+ newWhere,
41
+ key.childElement.name,
42
+ key.parentElement.name
43
+ )
69
44
  whereAdded = true
70
45
  }
71
46
  }
@@ -76,6 +51,9 @@ function _buildWhereForNavigations(ref, newWhere, model, target) {
76
51
  function _renameOnUp(newWhere, entityKey, targetKey) {
77
52
  let renamed = false
78
53
  newWhere.forEach(element => {
54
+ if (element.xpr && element.xpr.length) {
55
+ renamed = _renameOnUp(element.xpr, entityKey, targetKey) || renamed
56
+ }
79
57
  if (element.ref && element.ref[0] === targetKey) {
80
58
  element.ref = [entityKey]
81
59
  renamed = true
@@ -84,54 +62,6 @@ function _renameOnUp(newWhere, entityKey, targetKey) {
84
62
  return renamed
85
63
  }
86
64
 
87
- function calculateWhereForNavigationsFromRefPath(ref, newWhere, target) {
88
- const currentRef = ref[0]
89
- const nextRef = ref[1]
90
-
91
- if (nextRef) {
92
- const csnEntity = target
93
- const navigationElement = csnEntity && csnEntity.elements[nextRef.id || nextRef]
94
-
95
- if (!navigationElement || !navigationElement.on) return
96
-
97
- const onCond = target._relations[nextRef.id || nextRef].join('target', 'source')
98
- const nextKeys = _getOnCondElements(onCond[0].xpr)
99
-
100
- const seg_keys = where2obj(currentRef.where ?? [])
101
- for (const key of nextKeys) {
102
- if (navigationElement.is2one && csnEntity.elements[key.entityKey] && !navigationElement._isSelfManaged) {
103
- // foreign key in root
104
- continue
105
- }
106
-
107
- const targetKeyElement = navigationElement._target.elements[key.targetKey]
108
-
109
- if (targetKeyElement && key.targetKey && key.entityKey) {
110
- if (newWhere.length) {
111
- if (_renameOnUp(newWhere, key.targetKey, key.entityKey)) {
112
- continue
113
- }
114
- newWhere.push('and')
115
- }
116
- newWhere.push({ ref: [key.targetKey] }, '=', { val: seg_keys[key.entityKey] })
117
- } else if (targetKeyElement && key.targetKey && key.entityVal !== undefined) {
118
- if (newWhere.length) newWhere.push('and')
119
- newWhere.push({ ref: [key.targetKey] }, '=', { val: key.entityVal })
120
- }
121
- }
122
- calculateWhereForNavigationsFromRefPath(ref.slice(1), newWhere, navigationElement._target)
123
- }
124
- }
125
-
126
- function getKeysForNavigationFromRefPath(ref, target) {
127
- const where = []
128
- calculateWhereForNavigationsFromRefPath(ref, where, target)
129
- if (where.length) {
130
- return where2obj(where)
131
- }
132
- return {}
133
- }
134
-
135
65
  function _getWhereFromInsert(query, model) {
136
66
  const where = []
137
67
  if (query.INSERT.into.ref && query.INSERT.into.ref.length > 1) {
@@ -172,6 +102,5 @@ function enrichDataWithKeysFromWhere(data, { query, target }, { model }) {
172
102
 
173
103
  module.exports = {
174
104
  where2obj,
175
- enrichDataWithKeysFromWhere,
176
- getKeysForNavigationFromRefPath
105
+ enrichDataWithKeysFromWhere
177
106
  }
@@ -5,6 +5,7 @@ const { prefixForStruct } = require('../../common/utils/csn')
5
5
  const _autoGenerate = e => e && e.isUUID && e.key
6
6
 
7
7
  const _set = (row, value, element, enumerable) => {
8
+ if (value === undefined) return // only if properly propagated/generated
8
9
  if (!element.parent.elements[element.name]) return // only when in model
9
10
  if (!enumerable && element.foreignKeySource) {
10
11
  // only for foreign keys
@@ -86,7 +87,7 @@ module.exports = (
86
87
  row,
87
88
  foreignKeyPropagations,
88
89
  isCompositionEffective,
89
- { deleteAssocs = false, enumerable = true } = {}
90
+ { deleteAssocs = false, enumerable = true, generateKeys = true } = {}
90
91
  ) => {
91
92
  if (!row || !(tKey in row)) return
92
93
  if (row[tKey] === null) {
@@ -109,11 +110,11 @@ module.exports = (
109
110
  // propagate or generate in parent
110
111
  const pk = foreignKeyPropagation.parentElement && foreignKeyPropagation.parentElement.name
111
112
  if (pk && !(pk in row)) _propagateToParent(foreignKeyPropagation, childRow, row, enumerable)
112
- if (!(pk in row)) _generateParentField(foreignKeyPropagation, row, enumerable)
113
+ if (!(pk in row) && generateKeys) _generateParentField(foreignKeyPropagation, row, enumerable, generateKeys)
113
114
 
114
115
  if (isCompositionEffective) _propagateToChild(foreignKeyPropagation, row, childRow, enumerable)
115
116
  } else {
116
- if (isCompositionEffective) _generateChildField(foreignKeyPropagation, childRow, enumerable)
117
+ if (isCompositionEffective && generateKeys) _generateChildField(foreignKeyPropagation, childRow, enumerable)
117
118
  _propagateToParent(foreignKeyPropagation, childRow, row, enumerable)
118
119
  }
119
120
  }