@sap/cds 7.8.1 → 7.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 (137) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/_i18n/i18n_ar.properties +3 -0
  3. package/_i18n/i18n_cs.properties +3 -0
  4. package/_i18n/i18n_da.properties +3 -0
  5. package/_i18n/i18n_es_MX.properties +3 -0
  6. package/_i18n/i18n_fi.properties +3 -0
  7. package/_i18n/i18n_hu.properties +6 -0
  8. package/_i18n/i18n_ko.properties +3 -0
  9. package/_i18n/i18n_ms.properties +3 -0
  10. package/_i18n/i18n_nl.properties +3 -0
  11. package/_i18n/i18n_no.properties +3 -0
  12. package/_i18n/i18n_ro.properties +3 -0
  13. package/_i18n/i18n_sv.properties +3 -0
  14. package/_i18n/i18n_th.properties +3 -0
  15. package/_i18n/i18n_tr.properties +6 -0
  16. package/_i18n/i18n_zh_TW.properties +3 -0
  17. package/bin/serve.js +5 -5
  18. package/lib/auth/basic-auth.js +1 -1
  19. package/lib/compile/cdsc.js +33 -6
  20. package/lib/compile/etc/_localized.js +14 -7
  21. package/lib/compile/for/lean_drafts.js +9 -0
  22. package/lib/compile/to/edm-files.js +116 -0
  23. package/lib/compile/to/edm.js +8 -1
  24. package/lib/compile/to/hdbtabledata.js +3 -3
  25. package/lib/compile/to/sql.js +4 -2
  26. package/lib/compile/to/yaml.js +22 -21
  27. package/lib/dbs/cds-deploy.js +5 -6
  28. package/lib/env/cds-env.js +7 -0
  29. package/lib/env/cds-requires.js +20 -1
  30. package/lib/env/defaults.js +21 -5
  31. package/lib/env/schemas/cds-package.js +1 -1
  32. package/lib/env/schemas/cds-rc.js +85 -4
  33. package/lib/index.js +1 -1
  34. package/lib/linked/classes.js +2 -2
  35. package/lib/linked/entities.js +10 -0
  36. package/lib/linked/models.js +1 -1
  37. package/lib/plugins.js +1 -1
  38. package/lib/ql/INSERT.js +17 -3
  39. package/lib/ql/Query.js +4 -0
  40. package/lib/ql/infer.js +1 -1
  41. package/lib/req/request.js +1 -1
  42. package/lib/srv/cds-serve.js +1 -0
  43. package/lib/srv/middlewares/cds-context.js +1 -1
  44. package/lib/srv/protocols/odata-v4.js +5 -6
  45. package/lib/srv/srv-models.js +9 -2
  46. package/lib/utils/cds-test.js +2 -0
  47. package/lib/utils/cds-utils.js +9 -4
  48. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
  49. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -1
  50. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -1
  51. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +1 -1
  52. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +3 -6
  53. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +22 -10
  54. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +4 -4
  55. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +4 -3
  56. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +1 -1
  57. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +4 -1
  58. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -1
  59. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +38 -1
  60. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +2 -2
  61. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +32 -21
  62. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +1 -1
  63. package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +1 -2
  64. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +0 -10
  65. package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +3 -1
  66. package/libx/_runtime/cds-services/services/utils/compareJson.js +2 -274
  67. package/libx/_runtime/{cds-services/services → common}/Service.js +39 -29
  68. package/libx/_runtime/common/generic/auth/autoexpose.js +41 -0
  69. package/libx/_runtime/common/generic/auth/index.js +2 -0
  70. package/libx/_runtime/common/generic/auth/readOnly.js +0 -11
  71. package/libx/_runtime/common/generic/auth/restrict.js +6 -5
  72. package/libx/_runtime/common/generic/auth/utils.js +1 -1
  73. package/libx/_runtime/common/generic/crud.js +5 -8
  74. package/libx/_runtime/common/generic/etag.js +8 -6
  75. package/libx/_runtime/common/generic/sorting.js +2 -2
  76. package/libx/_runtime/common/i18n/messages.properties +1 -0
  77. package/libx/_runtime/{cds-services/services → common}/utils/columns.js +4 -4
  78. package/libx/_runtime/common/utils/compareJson.js +274 -0
  79. package/libx/_runtime/common/utils/cqn2cqn4sql.js +1 -1
  80. package/libx/_runtime/{cds-services/services → common}/utils/differ.js +8 -8
  81. package/libx/_runtime/common/utils/ensureIEEE754.js +29 -0
  82. package/libx/_runtime/common/utils/{postProcessing.js → postProcess.js} +1 -3
  83. package/libx/_runtime/common/utils/resolveView.js +0 -16
  84. package/libx/_runtime/common/utils/rewriteAsterisks.js +1 -1
  85. package/libx/_runtime/common/utils/search2cqn4sql.js +1 -1
  86. package/libx/_runtime/common/utils/streamProp.js +9 -2
  87. package/libx/_runtime/common/utils/ucsn.js +1 -1
  88. package/libx/_runtime/db/expand/expandCQNToJoin.js +5 -3
  89. package/libx/_runtime/db/generic/rewrite.js +7 -13
  90. package/libx/_runtime/fiori/generic/activate.js +1 -1
  91. package/libx/_runtime/fiori/generic/edit.js +1 -1
  92. package/libx/_runtime/fiori/generic/prepare.js +1 -1
  93. package/libx/_runtime/fiori/lean-draft.js +151 -46
  94. package/libx/_runtime/fiori/utils/handler.js +1 -1
  95. package/libx/_runtime/hana/execute.js +6 -2
  96. package/libx/_runtime/hana/search2cqn4sql.js +1 -1
  97. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +1 -2
  98. package/libx/_runtime/messaging/event-broker.js +212 -0
  99. package/libx/_runtime/remote/Service.js +9 -32
  100. package/libx/_runtime/remote/utils/client.js +13 -21
  101. package/libx/_runtime/sqlite/convertAssocToOneManaged.js +7 -1
  102. package/libx/_runtime/sqlite/execute.js +8 -3
  103. package/libx/_runtime/ucl/Service.js +259 -0
  104. package/libx/common/assert/index.js +6 -11
  105. package/libx/common/assert/validation.js +6 -1
  106. package/libx/odata/index.js +47 -25
  107. package/libx/odata/middleware/batch.js +8 -7
  108. package/libx/odata/middleware/create.js +42 -16
  109. package/libx/odata/middleware/delete.js +18 -11
  110. package/libx/odata/middleware/metadata.js +15 -14
  111. package/libx/odata/middleware/operation.js +30 -40
  112. package/libx/odata/middleware/parse.js +2 -3
  113. package/libx/odata/middleware/read.js +59 -52
  114. package/libx/odata/middleware/service-document.js +7 -7
  115. package/libx/odata/middleware/stream.js +26 -24
  116. package/libx/odata/middleware/update.js +53 -92
  117. package/libx/odata/parse/afterburner.js +45 -47
  118. package/libx/odata/parse/grammar.peggy +3 -3
  119. package/libx/odata/parse/multipartToJson.js +10 -22
  120. package/libx/odata/parse/parser.js +1 -1
  121. package/libx/odata/utils/etag.js +13 -0
  122. package/libx/odata/utils/handler.js +120 -0
  123. package/libx/odata/utils/index.js +15 -2
  124. package/libx/odata/utils/metaInfo.js +410 -0
  125. package/libx/odata/utils/path.js +5 -2
  126. package/libx/odata/utils/readAfterWrite.js +23 -0
  127. package/libx/odata/utils/result.js +4 -5
  128. package/libx/rest/RestAdapter.js +4 -13
  129. package/libx/rest/middleware/parse.js +40 -7
  130. package/package.json +1 -1
  131. package/server.js +2 -1
  132. package/libx/_runtime/cds-services/util/dataProcessUtils.js +0 -93
  133. package/libx/_runtime/common/utils/thenable.js +0 -51
  134. package/libx/_runtime/rest/service.js +0 -2
  135. package/libx/odata/parse/parseToCqn.js +0 -39
  136. package/libx/rest/middleware/input.js +0 -54
  137. package/libx/rest/middleware/payload.js +0 -13
@@ -0,0 +1,274 @@
1
+ const { DRAFT_COLUMNS_MAP } = require('../constants/draft')
2
+
3
+ const _deepEqual = (val1, val2) => {
4
+ if (val1 && typeof val1 === 'object' && val2 && typeof val2 === 'object') {
5
+ for (const key in val1) {
6
+ if (!_deepEqual(val1[key], val2[key])) return false
7
+ }
8
+ return true
9
+ }
10
+ return val1 === val2
11
+ }
12
+
13
+ const _getCorrespondingEntryWithSameKeys = (source, entry, keys) => {
14
+ const idx = _getIdxCorrespondingEntryWithSameKeys(source, entry, keys)
15
+ return idx !== -1 ? source[idx] : undefined
16
+ }
17
+
18
+ const _getIdxCorrespondingEntryWithSameKeys = (source, entry, keys) =>
19
+ source.findIndex(sourceEntry => keys.every(key => _deepEqual(sourceEntry[key], entry[key])))
20
+
21
+ const _getKeysOfEntity = entity =>
22
+ Object.keys(entity.keys).filter(key => !(key in DRAFT_COLUMNS_MAP) && !entity.elements[key].isAssociation)
23
+
24
+ const _getCompositionsOfEntity = entity => Object.keys(entity.elements).filter(e => entity.elements[e].isComposition)
25
+
26
+ const _createToBeDeletedEntries = (oldEntry, entity, keys, compositions) => {
27
+ const toBeDeletedEntry = {
28
+ _op: 'delete'
29
+ }
30
+
31
+ for (const prop in oldEntry) {
32
+ if (prop in DRAFT_COLUMNS_MAP) {
33
+ continue
34
+ }
35
+ if (keys.includes(prop)) {
36
+ toBeDeletedEntry[prop] = oldEntry[prop]
37
+ } else if (compositions.includes(prop) && oldEntry[prop]) {
38
+ toBeDeletedEntry[prop] = entity.elements[prop].is2one
39
+ ? _createToBeDeletedEntries(
40
+ oldEntry[prop],
41
+ entity.elements[prop]._target,
42
+ _getKeysOfEntity(entity.elements[prop]._target),
43
+ _getCompositionsOfEntity(entity.elements[prop]._target)
44
+ )
45
+ : oldEntry[prop].map(entry =>
46
+ _createToBeDeletedEntries(
47
+ entry,
48
+ entity.elements[prop]._target,
49
+ _getKeysOfEntity(entity.elements[prop]._target),
50
+ _getCompositionsOfEntity(entity.elements[prop]._target)
51
+ )
52
+ )
53
+ } else {
54
+ toBeDeletedEntry._old = toBeDeletedEntry._old || {}
55
+ toBeDeletedEntry._old[prop] = oldEntry[prop]
56
+ }
57
+ }
58
+
59
+ return toBeDeletedEntry
60
+ }
61
+
62
+ const _hasOpDeep = (entry, element) => {
63
+ const entryArray = Array.isArray(entry) ? entry : [entry]
64
+ for (const entry_ of entryArray) {
65
+ if (entry_._op) return true
66
+
67
+ if (element && element.isComposition) {
68
+ const target = element._target
69
+ for (const prop in entry_) {
70
+ if (_hasOpDeep(entry_[prop], target.elements[prop])) {
71
+ return true
72
+ }
73
+ }
74
+ }
75
+ }
76
+
77
+ return false
78
+ }
79
+
80
+ const _addCompositionsToResult = (result, entity, prop, newValue, oldValue, opts) => {
81
+ /*
82
+ * REVISIT: the current impl results in {} instead of keeping null for compo to one.
83
+ * unfortunately, many follow-up errors occur (e.g., prop in null checks) if changed.
84
+ */
85
+ let composition
86
+ if (
87
+ newValue[prop] &&
88
+ typeof newValue[prop] === 'object' &&
89
+ !Array.isArray(newValue[prop]) &&
90
+ Object.keys(newValue[prop]).length === 0
91
+ ) {
92
+ composition = compareJsonDeep(entity.elements[prop]._target, undefined, oldValue && oldValue[prop], opts)
93
+ } else {
94
+ composition = compareJsonDeep(entity.elements[prop]._target, newValue[prop], oldValue && oldValue[prop], opts)
95
+ }
96
+ if (composition.some(c => _hasOpDeep(c, entity.elements[prop]))) {
97
+ result[prop] = entity.elements[prop].is2one ? composition[0] : composition
98
+ }
99
+ }
100
+
101
+ const _addPrimitiveValuesAndOperatorToResult = (result, prop, newValue, oldValue) => {
102
+ result[prop] = newValue[prop]
103
+
104
+ if (!result._op) {
105
+ result._op = oldValue ? 'update' : 'create'
106
+ }
107
+
108
+ if (result._op === 'update') {
109
+ result._old = result._old || {}
110
+ result._old[prop] = oldValue[prop]
111
+ }
112
+ }
113
+
114
+ const _addKeysToResult = (result, prop, newValue, oldValue) => {
115
+ result[prop] = newValue[prop]
116
+ if (!oldValue) {
117
+ result._op = 'create'
118
+ }
119
+ }
120
+
121
+ const _addToBeDeletedEntriesToResult = (results, entity, keys, newValues, oldValues) => {
122
+ // add to be deleted entries
123
+ for (const oldEntry of oldValues) {
124
+ const entry = _getCorrespondingEntryWithSameKeys(newValues, oldEntry, keys)
125
+
126
+ if (!entry) {
127
+ // prepare to be deleted (deep) entry without manipulating oldData
128
+ const toBeDeletedEntry = _createToBeDeletedEntries(oldEntry, entity, keys, _getCompositionsOfEntity(entity))
129
+ results.push(toBeDeletedEntry)
130
+ }
131
+ }
132
+ }
133
+
134
+ const _normalizeToArray = value => (Array.isArray(value) ? value : value === null ? [] : [value])
135
+
136
+ const _isUnManaged = element => {
137
+ return element.on && !element._isSelfManaged
138
+ }
139
+
140
+ const _skip = (entity, prop) => entity.elements[prop]._target._hasPersistenceSkip
141
+
142
+ const _skipToOne = (entity, prop) => {
143
+ return (
144
+ entity.elements[prop] && entity.elements[prop].is2one && _skip(entity, prop) && _isUnManaged(entity.elements[prop])
145
+ )
146
+ }
147
+
148
+ const _skipToMany = (entity, prop) => {
149
+ return entity.elements[prop] && entity.elements[prop].is2many && _skip(entity, prop)
150
+ }
151
+
152
+ // Returns all property names from the new entry and add missing managed elements
153
+ const _propertiesAndManaged = (newEntry, entity) => {
154
+ return [
155
+ ...Object.getOwnPropertyNames(newEntry),
156
+ ...Object.keys(entity.elements).filter(
157
+ elementName => newEntry[elementName] === undefined && entity.elements[elementName]['@cds.on.update']
158
+ )
159
+ ]
160
+ }
161
+
162
+ const _iteratePropsInNewEntry = (newEntry, keys, result, oldEntry, entity, opts) => {
163
+ // On app-service layer, generated foreign keys are not enumerable,
164
+ // include them here too.
165
+ for (const prop of _propertiesAndManaged(newEntry, entity)) {
166
+ if (keys.includes(prop)) {
167
+ _addKeysToResult(result, prop, newEntry, oldEntry)
168
+ continue
169
+ }
170
+
171
+ // if value did not change --> ignored
172
+ if (newEntry[prop] === (oldEntry && oldEntry[prop]) || (opts.ignoreDraftColumns && prop in DRAFT_COLUMNS_MAP)) {
173
+ continue
174
+ }
175
+
176
+ if (_skipToMany(entity, prop)) {
177
+ continue
178
+ }
179
+
180
+ if (_skipToOne(entity, prop)) {
181
+ continue
182
+ }
183
+
184
+ if (entity.elements[prop] && entity.elements[prop].isComposition) {
185
+ _addCompositionsToResult(result, entity, prop, newEntry, oldEntry, opts)
186
+ continue
187
+ }
188
+
189
+ _addPrimitiveValuesAndOperatorToResult(result, prop, newEntry, oldEntry)
190
+ }
191
+ }
192
+
193
+ const compareJsonDeep = (entity, newValue = [], oldValue = [], opts) => {
194
+ const resultsArray = []
195
+ const keys = _getKeysOfEntity(entity)
196
+
197
+ // normalize input
198
+ const newValues = _normalizeToArray(newValue)
199
+ const oldValues = _normalizeToArray(oldValue)
200
+
201
+ // add to be created and to be updated entries
202
+ for (const newEntry of newValues) {
203
+ const result = {}
204
+ const oldEntry = _getCorrespondingEntryWithSameKeys(oldValues, newEntry, keys)
205
+ _iteratePropsInNewEntry(newEntry, keys, result, oldEntry, entity, opts)
206
+ resultsArray.push(result)
207
+ }
208
+
209
+ _addToBeDeletedEntriesToResult(resultsArray, entity, keys, newValues, oldValues)
210
+
211
+ return resultsArray
212
+ }
213
+
214
+ /**
215
+ * Compares newValue with oldValues in a deep fashion.
216
+ * Output format is newValue with additional administrative properties.
217
+ * - "_op" provides info about the CRUD action to perform
218
+ * - "_old" provides info about the current DB state
219
+ *
220
+ * Unchanged values are not part of the result.
221
+ *
222
+ * Output format is:
223
+ * {
224
+ * _op: 'update',
225
+ * _old: { orderedAt: 'DE' },
226
+ * ID: 1,
227
+ * orderedAt: 'EN',
228
+ * items: [
229
+ * {
230
+ * _op: 'update',
231
+ * _old: { amount: 7 },
232
+ * ID: 7,
233
+ * amount: 8
234
+ * },
235
+ * {
236
+ * _op: 'create',
237
+ * ID: 8,
238
+ * amount: 8
239
+ * },
240
+ * {
241
+ * _op: 'delete',
242
+ * _old: {
243
+ * amount: 6
244
+ * },
245
+ * ID: 6
246
+ * }
247
+ * ]
248
+ * }
249
+ *
250
+ *
251
+ * If there is no change in an UPDATE, result is an object containing only the keys of the entity.
252
+ *
253
+ * @example
254
+ * compareJson(csnEntity, [{ID: 1, col1: 'A'}], [{ID: 1, col1: 'B'}])
255
+ *
256
+ * @param oldValue
257
+ * @param {object} entity
258
+ * @param {Array | object} newValue
259
+ * @param {Array} oldValues
260
+ *
261
+ * @returns {Array}
262
+ */
263
+ const compareJson = (newValue, oldValue, entity, opts = {}) => {
264
+ const options = Object.assign({ ignoreDraftColumns: false }, opts)
265
+ const result = compareJsonDeep(entity, newValue, oldValue, options)
266
+
267
+ // in case of batch insert, result is an array
268
+ // in all other cases it is an array with just one entry
269
+ return Array.isArray(newValue) ? result : result[0]
270
+ }
271
+
272
+ module.exports = {
273
+ compareJson
274
+ }
@@ -14,7 +14,7 @@ const { getEntityFromPath } = require('../../common/utils/path')
14
14
  const { removeIsActiveEntityRecursively } = require('../../fiori/utils/where')
15
15
  const { addRefToWhereIfNecessary } = require('../../../odata/parse/afterburner')
16
16
  const { addAliasToExpression, PARENT_ALIAS, FOREIGN_ALIAS } = require('../../db/utils/generateAliases')
17
- const { getColumns } = require('../../cds-services/services/utils/columns')
17
+ const { getColumns } = require('./columns')
18
18
 
19
19
  const _elementFromRef = (name, entity) => {
20
20
  if (!entity) return
@@ -1,15 +1,15 @@
1
- const cds = require('../../../cds')
1
+ const cds = require('../../cds')
2
2
  const { SELECT } = cds.ql
3
3
 
4
4
  const { compareJson } = require('./compareJson')
5
- const { selectDeepUpdateData } = require('../../../common/composition')
6
- const { ensureDraftsSuffix } = require('../../../fiori/utils/handler')
5
+ const { selectDeepUpdateData } = require('../composition')
6
+ const { ensureDraftsSuffix } = require('../../fiori/utils/handler')
7
7
 
8
- const { DRAFT_COLUMNS_MAP } = require('../../../common/constants/draft')
9
- const { cqn2cqn4sql, convertPathExpressionToWhere } = require('../../../common/utils/cqn2cqn4sql')
10
- const { revertData } = require('../../../common/utils/resolveView')
11
- const { removeIsActiveEntityRecursively } = require('../../../fiori/utils/where')
12
- const { enrichDataWithKeysFromWhere } = require('../../../common/utils/keys')
8
+ const { DRAFT_COLUMNS_MAP } = require('../constants/draft')
9
+ const { cqn2cqn4sql, convertPathExpressionToWhere } = require('./cqn2cqn4sql')
10
+ const { revertData } = require('./resolveView')
11
+ const { removeIsActiveEntityRecursively } = require('../../fiori/utils/where')
12
+ const { enrichDataWithKeysFromWhere } = require('./keys')
13
13
 
14
14
  module.exports = class Differ {
15
15
  constructor(srv) {
@@ -0,0 +1,29 @@
1
+ const getTemplate = require('./template')
2
+ const templateProcessor = require('./templateProcessor')
3
+
4
+ const _pick = element => {
5
+ return element._type in { 'cds.Decimal': 1, 'cds.Integer64': 1 }
6
+ }
7
+
8
+ const _processorFn = () => elementInfo => {
9
+ const { row, key } = elementInfo
10
+ if (typeof row?.[key] !== 'number') return
11
+ row[key] = `${row[key]}`
12
+ }
13
+
14
+ const ensureIEEE754 = (target, service, result) => {
15
+ const { model } = service
16
+ if (!target || !result || !model || !model.definitions[target.name]) return
17
+
18
+ const template = getTemplate('ensureIEEE754', service, target, { pick: _pick })
19
+ if (template.elements.size === 0) return
20
+
21
+ if (typeof result === 'object' && result != null) {
22
+ const processFn = _processorFn()
23
+ for (const row of Array.isArray(result) ? result : [result]) {
24
+ templateProcessor({ processFn, row, template })
25
+ }
26
+ }
27
+ }
28
+
29
+ module.exports = ensureIEEE754
@@ -89,6 +89,4 @@ const postProcess = (query, result, service, onlySelectAliases = false) => {
89
89
  return revertData(result, transition, service)
90
90
  }
91
91
 
92
- module.exports = {
93
- postProcess
94
- }
92
+ module.exports = postProcess
@@ -714,21 +714,6 @@ const resolveView = (query, model, service) => {
714
714
  return newQuery
715
715
  }
716
716
 
717
- /**
718
- * Restores the link of req.data and req.query in case req.query was overwritten.
719
- * Only applicable for UPDATEs and INSERTs.
720
- *
721
- * @param {*} req
722
- */
723
- const restoreLink = req => {
724
- if (req.query.INSERT?.entries) {
725
- if (Array.isArray(req.query.INSERT.entries)) req.data = req.query.INSERT.entries[0]
726
- else req.data = req.query.INSERT.entries
727
- } else if (req.query.UPDATE?.data) {
728
- req.data = req.query.UPDATE.data
729
- }
730
- }
731
-
732
717
  /**
733
718
  * Retrieves the actual query target by evaluating the created transitions.
734
719
  * @param {*} q - the resolved query
@@ -753,6 +738,5 @@ module.exports = {
753
738
  getDBTable,
754
739
  resolveView,
755
740
  getTransition,
756
- restoreLink,
757
741
  revertData
758
742
  }
@@ -42,7 +42,7 @@ const _resolveTarget = (ref, target) => {
42
42
  const element = target.elements[_ref]
43
43
  if (element) return element._target
44
44
 
45
- throw cds.error(`Navigation property '${_ref}' is not defined in '${target.name}'`, { code: 400 })
45
+ throw cds.error(`Navigation property "${_ref}" is not defined in ${target.name}`, { code: 400 })
46
46
  }
47
47
 
48
48
  const rewriteExpandAsterisk = (columns, target) => {
@@ -1,4 +1,4 @@
1
- const { computeColumnsToBeSearched } = require('../../../../libx/_runtime/cds-services/services/utils/columns')
1
+ const { computeColumnsToBeSearched } = require('./columns')
2
2
  const searchToLike = require('./searchToLike')
3
3
  const { getEntityNameFromCQN } = require('./entityFromCqn')
4
4
 
@@ -3,7 +3,9 @@ const { ensureNoDraftsSuffix, ensureUnlocalized } = require('../../fiori/utils/h
3
3
  const { isDuplicate } = require('./rewriteAsterisks')
4
4
 
5
5
  const _addColumn = (name, type, columns, url) => {
6
+ // we do not want to return these additional odata properties with new adapter
6
7
  if (cds.env.features.odata_new_adapter) return
8
+
7
9
  if (typeof type === 'object') {
8
10
  let mType = type['='].replaceAll(/\./g, '_')
9
11
  const ref = {
@@ -27,6 +29,7 @@ const _addColumn = (name, type, columns, url) => {
27
29
 
28
30
  const _addColumns = (target, columns) => {
29
31
  if (cds.env.features.odata_new_adapter) return
32
+
30
33
  for (const k in target.elements) {
31
34
  const el = target.elements[k]
32
35
  if (el['@Core.MediaType']) {
@@ -35,6 +38,7 @@ const _addColumns = (target, columns) => {
35
38
  }
36
39
  }
37
40
 
41
+ // eslint-disable-next-line complexity
38
42
  const handleStreamProperties = (target, columns, model) => {
39
43
  if (!target || !model || !columns) return
40
44
 
@@ -44,11 +48,14 @@ const handleStreamProperties = (target, columns, model) => {
44
48
  const name = col.ref && col.ref[col.ref.length - 1]
45
49
  const element = name && target.elements[name]
46
50
  const type = element && element.type
47
- const mediaType = element && element['@Core.MediaType']
51
+ const mediaType = element?.['@Core.MediaType']
52
+
53
+ // new adapter handles urls correctly and just returns them as they are, okra handles them wrongly due to wrong EDM generation by compiler
54
+ const ignoreMediaType = cds.env.features.odata_new_adapter && mediaType && element['@Core.IsURL']
48
55
 
49
56
  if (col === '*') {
50
57
  _addColumns(target, columns)
51
- } else if (col.ref && (type === 'cds.LargeBinary' || mediaType)) {
58
+ } else if (col.ref && (type === 'cds.LargeBinary' || (mediaType && !ignoreMediaType))) {
52
59
  if (mediaType) {
53
60
  _addColumn(name, mediaType, columns, element['@Core.IsURL'])
54
61
  columns.splice(index, 1)
@@ -75,7 +75,7 @@ const _cleanup = (row, definition, cleanupNull, cleanupStruct, errors, prefix =
75
75
  _cleanup(row[key], definition, cleanupNull, cleanupStruct, errors, [...prefix, key])
76
76
  } else {
77
77
  if (errors) {
78
- errors.push(getError(400, 'Property ' + key + ' does not exist in ' + definition.name))
78
+ errors.push(getError(400, `Property "${key}" does not exist in ${definition.name}`))
79
79
  }
80
80
  delete row[key]
81
81
  }
@@ -1024,7 +1024,7 @@ class JoinCQNFromExpanded {
1024
1024
  // Add table alias or name to handle cases, where joined tables have same column names
1025
1025
  if (this._isElement(column.ref, entity)) {
1026
1026
  const alias = tableAlias || ensureNoDraftsSuffix(entity.name)
1027
- aliasedElement.ref = alias ? [alias, column.ref[0]] : [column.ref[0]]
1027
+ aliasedElement.ref = alias ? [alias, ...column.ref] : [...column.ref]
1028
1028
  }
1029
1029
 
1030
1030
  if (skipMapping) return aliasedElement
@@ -1050,8 +1050,10 @@ class JoinCQNFromExpanded {
1050
1050
  }
1051
1051
 
1052
1052
  _isElement(ref, entity) {
1053
- if (!ref || ref.length !== 1) return false
1054
-
1053
+ if (!ref) return false
1054
+ if (ref.length > 1) {
1055
+ return entity.elements[ref[0]]?.isAssociation
1056
+ }
1055
1057
  // Normal element
1056
1058
  if (entity.elements[ref[0]]) return true
1057
1059
 
@@ -1,16 +1,13 @@
1
1
  const cds = require('../../cds')
2
2
  const { cqn2cqn4sql } = require('../../common/utils/cqn2cqn4sql')
3
3
  const { generateAliases } = require('../utils/generateAliases')
4
- const { restoreLink } = require('../../common/utils/resolveView')
5
4
 
6
- const _isLinked = req => {
5
+ const _restoreLink = req => {
7
6
  if (req.query.INSERT?.entries) {
8
- if (Array.isArray(req.query.INSERT.entries)) return req.data === req.query.INSERT.entries[0]
9
- return req.data === req.query.INSERT.entries
7
+ return (req.data = Array.isArray(req.query.INSERT.entries) ? req.query.INSERT.entries[0] : req.query.INSERT.entries)
10
8
  }
11
-
12
9
  if (req.query.UPDATE?.data) {
13
- return req.data === req.query.UPDATE.data
10
+ return (req.data = req.query.UPDATE.data)
14
11
  }
15
12
  }
16
13
 
@@ -26,19 +23,16 @@ function handler(req) {
26
23
  return
27
24
  }
28
25
 
29
- const streaming = cds.env.features.stream_compat && req.query._streaming
30
-
31
- // for restore link to req.data
32
- const linked = _isLinked(req)
26
+ const _streaming = cds.env.features.stream_compat && req.query._streaming
33
27
 
34
28
  // convert to sql cqn
35
29
  req.query = cqn2cqn4sql(req.query, this.model, { service: this })
36
30
 
37
- // REVISIT: should not be necessary
38
31
  // restore link to req.data
39
- if (linked) restoreLink(req)
32
+ _restoreLink(req)
33
+
34
+ if (_streaming) req.query._streaming = _streaming
40
35
 
41
- if (cds.env.features.stream_compat && streaming) req.query._streaming = streaming
42
36
  generateAliases(req.query)
43
37
  }
44
38
 
@@ -10,7 +10,7 @@ const {
10
10
  } = require('../utils/handler')
11
11
  const { readAndDeleteKeywords, isActiveEntityRequested, getKeyData } = require('../utils/where')
12
12
  const { isDraftRootEntity } = require('../../fiori/utils/csn')
13
- const { getColumns } = require('../../cds-services/services/utils/columns')
13
+ const { getColumns } = require('../../common/utils/columns')
14
14
  const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
15
15
 
16
16
  const _getRootCQN = (context, requestActiveData) => {
@@ -3,7 +3,7 @@ const getLockInfo = require('../utils/lockInfo')
3
3
  const { INSERT, SELECT, DELETE } = cds.ql
4
4
 
5
5
  const { getCompositionTree } = require('../../common/composition')
6
- const { getColumns } = require('../../cds-services/services/utils/columns')
6
+ const { getColumns } = require('../../common/utils/columns')
7
7
  const { draftIsLocked, ensureDraftsSuffix, ensureNoDraftsSuffix, getSubCQNs } = require('../utils/handler')
8
8
  const { isActiveEntityRequested } = require('../utils/where')
9
9
 
@@ -3,7 +3,7 @@ const { SELECT } = cds.ql
3
3
 
4
4
  const { isActiveEntityRequested } = require('../utils/where')
5
5
  const { ensureDraftsSuffix, ensureNoDraftsSuffix } = require('../utils/handler')
6
- const { getColumns } = require('../../cds-services/services/utils/columns')
6
+ const { getColumns } = require('../../common/utils/columns')
7
7
 
8
8
  /**
9
9
  * Generic Handler for PreparationAction requests.