@sap/cds 5.5.5 → 5.6.3

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 (207) hide show
  1. package/CHANGELOG.md +139 -1
  2. package/apis/services.d.ts +31 -1
  3. package/app/index.js +22 -11
  4. package/bin/build/buildTaskFactory.js +1 -1
  5. package/bin/build/provider/buildTaskProviderInternal.js +1 -1
  6. package/bin/build/provider/fiori/index.js +1 -1
  7. package/bin/build/provider/hana/2migration.js +8 -7
  8. package/bin/build/provider/java-cf/index.js +1 -1
  9. package/bin/deploy/to-hana/hana.js +1 -17
  10. package/common.cds +8 -0
  11. package/lib/compile/to/sql.js +22 -2
  12. package/lib/connect/bindings.js +2 -1
  13. package/lib/core/reflect.js +4 -1
  14. package/lib/env/index.js +180 -42
  15. package/lib/env/requires.js +16 -1
  16. package/lib/i18n/localize.js +33 -5
  17. package/lib/index.js +3 -3
  18. package/lib/log/format/kibana.js +6 -2
  19. package/lib/ql/Query.js +1 -0
  20. package/lib/ql/SELECT.js +15 -8
  21. package/lib/ql/Whereable.js +5 -0
  22. package/lib/req/context.js +13 -5
  23. package/lib/serve/Service-dispatch.js +8 -1
  24. package/lib/utils/axios.js +7 -0
  25. package/lib/utils/data.js +1 -1
  26. package/lib/utils/tests.js +1 -1
  27. package/libx/_runtime/audit/Service.js +18 -18
  28. package/libx/_runtime/audit/generic/personal/access.js +1 -1
  29. package/libx/_runtime/audit/generic/personal/modification.js +3 -2
  30. package/libx/_runtime/audit/generic/personal/utils.js +23 -63
  31. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +4 -0
  32. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +37 -35
  33. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +3 -1
  34. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +5 -5
  35. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +13 -7
  36. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +84 -34
  37. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +12 -4
  38. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +9 -3
  39. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +8 -6
  40. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +1 -3
  41. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +13 -11
  42. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +11 -95
  43. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/ResourcePathParser.js +17 -11
  44. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +2 -1
  45. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/SetResponseHeadersCommand.js +1 -0
  46. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +6 -2
  47. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +3 -34
  48. package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +3 -3
  49. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +64 -31
  50. package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +10 -5
  51. package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +1 -1
  52. package/libx/_runtime/cds-services/adapter/rest/handlers/update.js +1 -1
  53. package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +1 -3
  54. package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +20 -21
  55. package/libx/_runtime/cds-services/services/utils/columns.js +6 -1
  56. package/libx/_runtime/cds-services/services/utils/compareJson.js +1 -8
  57. package/libx/_runtime/cds-services/services/utils/differ.js +7 -26
  58. package/libx/_runtime/cds-services/services/utils/handlerUtils.js +2 -4
  59. package/libx/_runtime/cds-services/util/assert.js +29 -13
  60. package/libx/_runtime/cds.js +2 -1
  61. package/libx/_runtime/common/aspects/Association.js +72 -0
  62. package/libx/_runtime/common/aspects/any.js +8 -45
  63. package/libx/_runtime/common/aspects/entity.js +0 -1
  64. package/libx/_runtime/common/aspects/relation.js +40 -0
  65. package/libx/_runtime/common/aspects/utils.js +73 -1
  66. package/libx/_runtime/common/auth/strategies/utils/uaa.js +10 -14
  67. package/libx/_runtime/common/composition/data.js +3 -2
  68. package/libx/_runtime/common/composition/delete.js +3 -1
  69. package/libx/_runtime/common/composition/tree.js +23 -18
  70. package/libx/_runtime/common/composition/update.js +9 -1
  71. package/libx/_runtime/common/composition/utils.js +34 -8
  72. package/libx/_runtime/common/error/frontend.js +6 -1
  73. package/libx/_runtime/common/generic/auth.js +5 -9
  74. package/libx/_runtime/common/generic/crud.js +2 -2
  75. package/libx/_runtime/common/generic/etag.js +11 -8
  76. package/libx/_runtime/common/generic/input.js +3 -3
  77. package/libx/_runtime/common/generic/paging.js +9 -5
  78. package/libx/_runtime/common/generic/put.js +3 -2
  79. package/libx/_runtime/common/generic/sorting.js +3 -3
  80. package/libx/_runtime/common/generic/temporal.js +3 -3
  81. package/libx/_runtime/common/utils/cqn.js +20 -1
  82. package/libx/_runtime/common/utils/cqn2cqn4sql.js +125 -139
  83. package/libx/_runtime/common/utils/csn.js +50 -52
  84. package/libx/_runtime/common/utils/foreignKeyPropagations.js +41 -176
  85. package/libx/_runtime/common/utils/generateOnCond.js +40 -70
  86. package/libx/_runtime/common/utils/{enrichWithKeysFromWhere.js → keys.js} +29 -28
  87. package/libx/_runtime/common/utils/postProcessing.js +3 -0
  88. package/libx/_runtime/common/utils/propagateForeignKeys.js +84 -0
  89. package/libx/_runtime/common/utils/resolveStructured.js +1 -1
  90. package/libx/_runtime/common/utils/resolveView.js +7 -5
  91. package/libx/_runtime/common/utils/rewriteAsterisks.js +94 -0
  92. package/libx/_runtime/common/utils/search2cqn4sql.js +9 -8
  93. package/libx/_runtime/common/utils/template.js +54 -46
  94. package/libx/_runtime/db/Service.js +9 -2
  95. package/libx/_runtime/db/expand/expandCQNToJoin.js +54 -33
  96. package/libx/_runtime/db/expand/rawToExpanded.js +2 -1
  97. package/libx/_runtime/db/generic/arrayed.js +13 -28
  98. package/libx/_runtime/db/generic/create.js +1 -0
  99. package/libx/_runtime/db/generic/input.js +7 -11
  100. package/libx/_runtime/db/generic/integrity.js +2 -2
  101. package/libx/_runtime/db/generic/rewrite.js +2 -5
  102. package/libx/_runtime/db/generic/update.js +1 -0
  103. package/libx/_runtime/db/query/read.js +9 -4
  104. package/libx/_runtime/db/sql-builder/SelectBuilder.js +7 -2
  105. package/libx/_runtime/db/sql-builder/annotations.js +1 -0
  106. package/libx/_runtime/db/utils/columns.js +14 -43
  107. package/libx/_runtime/fiori/generic/activate.js +3 -2
  108. package/libx/_runtime/fiori/generic/before.js +2 -2
  109. package/libx/_runtime/fiori/generic/cancel.js +3 -2
  110. package/libx/_runtime/fiori/generic/delete.js +3 -2
  111. package/libx/_runtime/fiori/generic/edit.js +3 -3
  112. package/libx/_runtime/fiori/generic/new.js +2 -2
  113. package/libx/_runtime/fiori/generic/patch.js +2 -2
  114. package/libx/_runtime/fiori/generic/prepare.js +2 -2
  115. package/libx/_runtime/fiori/generic/read.js +45 -63
  116. package/libx/_runtime/fiori/generic/readOverDraft.js +4 -4
  117. package/libx/_runtime/fiori/uiflex/extensibility/index.cds +15 -0
  118. package/libx/_runtime/fiori/uiflex/extensibility/index.js +148 -0
  119. package/libx/_runtime/fiori/uiflex/handler/transformREAD.js +119 -0
  120. package/libx/_runtime/fiori/uiflex/handler/transformRESULT.js +43 -0
  121. package/libx/_runtime/fiori/uiflex/handler/transformWRITE.js +62 -0
  122. package/libx/_runtime/fiori/uiflex/index.js +35 -0
  123. package/libx/_runtime/fiori/uiflex/utils.js +78 -0
  124. package/libx/_runtime/fiori/utils/handler.js +3 -13
  125. package/libx/_runtime/fiori/utils/where.js +6 -1
  126. package/libx/_runtime/hana/pool.js +12 -11
  127. package/libx/_runtime/hana/search2cqn4sql.js +34 -43
  128. package/libx/_runtime/hana/searchToContains.js +3 -3
  129. package/libx/_runtime/index.js +5 -2
  130. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +1 -1
  131. package/libx/_runtime/messaging/common-utils/AMQPClient.js +16 -3
  132. package/libx/_runtime/messaging/common-utils/connections.js +11 -14
  133. package/libx/_runtime/messaging/common-utils/naming-conventions.js +1 -1
  134. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -1
  135. package/libx/_runtime/messaging/message-queuing.js +18 -0
  136. package/libx/_runtime/remote/Service.js +20 -4
  137. package/libx/_runtime/remote/utils/client-types.d.ts +7 -0
  138. package/libx/_runtime/remote/utils/client.js +117 -23
  139. package/libx/_runtime/sqlite/Service.js +2 -2
  140. package/libx/_runtime/sqlite/convertAssocToOneManaged.js +1 -3
  141. package/libx/gql/GraphQLAdapter.js +33 -0
  142. package/libx/gql/constants/adapter.js +69 -0
  143. package/libx/gql/constants/cds.js +18 -0
  144. package/libx/gql/constants/graphql.js +33 -0
  145. package/libx/gql/resolvers/crud/create.js +15 -0
  146. package/libx/gql/resolvers/crud/delete.js +24 -0
  147. package/libx/gql/resolvers/crud/index.js +6 -0
  148. package/libx/gql/resolvers/crud/read.js +25 -0
  149. package/libx/gql/resolvers/crud/update.js +31 -0
  150. package/libx/gql/resolvers/crud/utils/index.js +36 -0
  151. package/libx/gql/resolvers/field.js +5 -0
  152. package/libx/gql/resolvers/index.js +7 -0
  153. package/libx/gql/resolvers/mutation.js +23 -0
  154. package/libx/gql/resolvers/parse/ast/enrich.js +51 -0
  155. package/libx/gql/resolvers/parse/ast/fragment.js +11 -0
  156. package/libx/gql/resolvers/parse/ast/fromObject.js +39 -0
  157. package/libx/gql/resolvers/parse/ast/index.js +3 -0
  158. package/libx/gql/resolvers/parse/ast/meta.js +4 -0
  159. package/libx/gql/resolvers/parse/ast/variable.js +7 -0
  160. package/libx/gql/resolvers/parse/ast2cqn/columns.js +42 -0
  161. package/libx/gql/resolvers/parse/ast2cqn/entries.js +31 -0
  162. package/libx/gql/resolvers/parse/ast2cqn/index.js +8 -0
  163. package/libx/gql/resolvers/parse/ast2cqn/limit.js +6 -0
  164. package/libx/gql/resolvers/parse/ast2cqn/orderBy.js +24 -0
  165. package/libx/gql/resolvers/parse/ast2cqn/utils/index.js +3 -0
  166. package/libx/gql/resolvers/parse/ast2cqn/where.js +70 -0
  167. package/libx/gql/resolvers/parse/utils/index.js +8 -0
  168. package/libx/gql/resolvers/query.js +13 -0
  169. package/libx/gql/resolvers/root.js +34 -0
  170. package/libx/gql/schema/generate.js +18 -0
  171. package/libx/gql/schema/index.js +5 -0
  172. package/libx/gql/schema/mutation.js +76 -0
  173. package/libx/gql/schema/query.js +108 -0
  174. package/libx/gql/schema/typeDefMap.js +45 -0
  175. package/libx/gql/schema/utils/index.js +54 -0
  176. package/libx/gql/utils/index.js +12 -0
  177. package/libx/{_runtime/odata/cqn2odata.js → odata/cqn2odata/index.js} +39 -100
  178. package/libx/odata/index.js +80 -0
  179. package/libx/odata/odata2cqn/afterburner.js +170 -0
  180. package/libx/{_runtime/odata/odata2cqn.pegjs → odata/odata2cqn/grammar.pegjs} +102 -123
  181. package/libx/odata/odata2cqn/index.js +3 -0
  182. package/libx/odata/odata2cqn/parser.js +1 -0
  183. package/libx/odata/utils/index.js +64 -0
  184. package/libx/rest/RestAdapter.js +101 -0
  185. package/libx/rest/RestRequest.js +30 -0
  186. package/libx/rest/index.js +3 -0
  187. package/libx/rest/middleware/auth.js +22 -0
  188. package/libx/rest/middleware/content.js +15 -0
  189. package/libx/rest/middleware/create.js +40 -0
  190. package/libx/rest/middleware/delete.js +20 -0
  191. package/libx/rest/middleware/error.js +56 -0
  192. package/libx/rest/middleware/operation.js +39 -0
  193. package/libx/rest/middleware/parse.js +90 -0
  194. package/libx/rest/middleware/read.js +29 -0
  195. package/libx/rest/middleware/update.js +42 -0
  196. package/libx/rest/utils/data.js +65 -0
  197. package/package.json +4 -1
  198. package/server.js +29 -7
  199. package/libx/_runtime/cds-services/services/utils/diff.js +0 -53
  200. package/libx/_runtime/cds-services/util/auditlog.js +0 -247
  201. package/libx/_runtime/cds-services/util/xsenv.js +0 -51
  202. package/libx/_runtime/common/utils/backlinks.js +0 -83
  203. package/libx/_runtime/common/utils/rewriteAsterisk.js +0 -72
  204. package/libx/_runtime/odata/index.js +0 -55
  205. package/libx/_runtime/odata/odata2cqn.js +0 -1
  206. package/libx/_runtime/odata/readToCqn.js +0 -129
  207. package/libx/_runtime/remote/cqn2odata/index.js +0 -2
@@ -1,29 +1,11 @@
1
1
  const cds = require('../../cds')
2
2
 
3
3
  const { ensureNoDraftsSuffix } = require('./draft')
4
- const { getFlatArray } = require('../../db/utils/deep')
5
4
 
6
5
  const getEtagElement = entity => {
7
6
  return Object.values(entity.elements).find(element => element['@odata.etag'])
8
7
  }
9
8
 
10
- const _isBacklink = (assoc, parent, target) => {
11
- const comps = Object.values(target.associations || {})
12
- .filter(assoc => assoc._isCompositionEffective)
13
- .filter(assoc => assoc.target === parent.name)
14
- if (comps.length === 0) return false
15
-
16
- let backlink = false
17
- for (const comp of comps) {
18
- const on = comp.on.find(ele => typeof ele === 'object' && ele.ref[0] !== '$self')
19
- if (on.ref.length === 2 && on.ref[on.ref.length - 1] === assoc.name) {
20
- backlink = true
21
- break
22
- }
23
- }
24
- return backlink
25
- }
26
-
27
9
  const _isDependent = (assoc, parent, target) => {
28
10
  return (
29
11
  assoc._isAssociationStrict &&
@@ -33,7 +15,7 @@ const _isDependent = (assoc, parent, target) => {
33
15
  assoc['@assert.integrity'] !== false &&
34
16
  parent['@assert.integrity'] !== false &&
35
17
  (!parent._service || parent._service['@assert.integrity'] !== false) &&
36
- !_isBacklink(assoc, parent, target)
18
+ !assoc._isCompositionBacklink
37
19
  )
38
20
  }
39
21
 
@@ -65,18 +47,14 @@ const getDependents = (entity, model) => {
65
47
  return entity.set('__dependents', dependents)
66
48
  }
67
49
 
68
- const _getUps = (entity, model, previous = []) => {
50
+ const _getUps = (entity, model) => {
69
51
  const ups = []
70
52
  for (const def of Object.values(model.definitions)) {
71
53
  if (def.kind !== 'entity') continue
72
54
  if (!def.associations) continue
73
- for (const assoc of Object.values(def.associations)) {
74
- if (assoc.target !== entity.name) continue
75
- ups.push({
76
- entity: assoc.parent,
77
- assoc: assoc,
78
- previous
79
- })
55
+ for (const element of Object.values(def.associations)) {
56
+ if (element.target !== entity.name || element._isBacklink) continue
57
+ ups.push(element)
80
58
  }
81
59
  }
82
60
  return ups
@@ -86,31 +64,35 @@ const _ifDataSubject = (entity, role) => {
86
64
  return entity['@PersonalData.EntitySemantics'] === 'DataSubject' && entity['@PersonalData.DataSubjectRole'] === role
87
65
  }
88
66
 
89
- const getDataSubject = (entity, model, role) => {
90
- const hash = '__dataSubject4' + role
91
- if (entity.own(hash)) return entity[hash]
92
-
93
- if (_ifDataSubject(entity, role)) return entity.set(hash, { entity: entity, assoc: [], previous: [] })
94
-
95
- let dataSubject
96
- let ups = _getUps(entity, model)
97
- while (!dataSubject) {
98
- for (const { entity, assoc, previous } of ups) {
99
- if (_ifDataSubject(entity, role)) dataSubject = { entity, assoc, previous }
67
+ const _getDataSubjectUp = (role, model, element, first = element) => {
68
+ const upElements = _getUps(element.parent, model)
69
+ for (const element of upElements) {
70
+ if (_ifDataSubject(element.parent, role)) {
71
+ return { element: first, up: { element }, entity: element.parent }
72
+ }
73
+ // dfs is a must here
74
+ const dataSubject = _getDataSubjectUp(role, model, element, first)
75
+ if (dataSubject) {
76
+ dataSubject.up = { element, up: dataSubject.up }
77
+ return dataSubject
100
78
  }
101
- if (dataSubject) break
102
- ups = getFlatArray([
103
- ...ups.map(up => [..._getUps(up.entity, model, up.previous.concat({ entity: up.entity, assoc: up.assoc }))])
104
- ])
105
79
  }
80
+ }
106
81
 
107
- return entity.set(hash, dataSubject)
82
+ const getDataSubject = (entity, model, role, element) => {
83
+ const hash = '__dataSubject4' + role
84
+ if (entity.own(hash)) return entity[hash]
85
+ if (_ifDataSubject(element.parent, role)) return entity.set(hash, { element, entity: element.parent })
86
+ return entity.set(hash, _getDataSubjectUp(role, model, element))
108
87
  }
109
88
 
110
- const _findRootEntity = (entities, edmName) => {
89
+ const _resolve = (name, model, namespace) =>
90
+ model.entities(namespace)[name] || model.definitions[`${namespace}.${name}`]
91
+
92
+ const _findRootEntity = (model, edmName, namespace) => {
111
93
  const parts = edmName.split('_')
112
94
  let csnName = parts.shift()
113
- let target = entities[csnName]
95
+ let target = _resolve(csnName, model, namespace)
114
96
  const len = parts.length
115
97
  // try to find a correct entity "greedy" and count leftovers (x4 case below)
116
98
  // e.g. we have 2 entities: 'C_root_' and dependant 'C_root_.kid_'
@@ -124,10 +106,10 @@ const _findRootEntity = (entities, edmName) => {
124
106
  * if target in entities connect with .
125
107
  * if target not in entities connect with _
126
108
  */
127
- csnName = `${csnName}${entities[csnName] ? '.' : '_'}${parts[i]}`
109
+ csnName = `${csnName}${_resolve(csnName, model, namespace) ? '.' : '_'}${parts[i]}`
128
110
  ++acc
129
- if (entities[csnName]) {
130
- target = entities[csnName]
111
+ if (_resolve(csnName, model, namespace)) {
112
+ target = _resolve(csnName, model, namespace)
131
113
  left -= acc
132
114
  acc = 0
133
115
  }
@@ -145,12 +127,13 @@ const findCsnTargetFor = (edmName, model, namespace) => {
145
127
 
146
128
  if (mapping[edmName]) return mapping[edmName]
147
129
 
148
- const entities = model.entities(namespace)
149
130
  // simple cases
150
- let target = entities[edmName] || entities[edmName.replace(/_/g, '.')]
131
+ let target = _resolve(edmName, model, namespace) || _resolve(edmName.replace(/_/g, '.'), model, namespace)
132
+
133
+ // hard cases
151
134
  if (!target) {
152
135
  // probably, a combination of '_' and '.', resolving
153
- const finding = _findRootEntity(entities, edmName)
136
+ const finding = _findRootEntity(model, edmName, namespace)
154
137
  target = finding.target
155
138
  // something left in navigation path => x4 navigation
156
139
  // resolving within found entity
@@ -203,11 +186,26 @@ const isRootEntity = (definitions, entityName) => {
203
186
  return true
204
187
  }
205
188
 
189
+ function alias2ref(service, edm) {
190
+ const defs = edm[service.definition.name]
191
+ for (const each of Object.values(service.entities)) {
192
+ const def = defs[each.name.replace(service.definition.name + '.', '').replace(/\./g, '_')]
193
+ if (!def || !def.$Key || def.$Key.every(ele => typeof ele === 'string')) continue
194
+ each._alias2ref = {}
195
+ for (const mapping of def.$Key.filter(ele => typeof ele !== 'string')) {
196
+ for (const [key, value] of Object.entries(mapping)) {
197
+ each._alias2ref[key] = value.split('/')
198
+ }
199
+ }
200
+ }
201
+ }
202
+
206
203
  module.exports = {
207
204
  getEtagElement,
208
205
  findCsnTargetFor,
209
206
  getElementDeep,
210
207
  getDependents,
211
208
  isRootEntity,
212
- getDataSubject
209
+ getDataSubject,
210
+ alias2ref
213
211
  }
@@ -1,93 +1,6 @@
1
- const cds = require('../../cds')
2
-
3
- const _generateParentField = (foreignKeyPropagations, row) => {
4
- if (
5
- foreignKeyPropagations.parentFieldName &&
6
- !row[foreignKeyPropagations.parentFieldName] &&
7
- foreignKeyPropagations.autoGenerate
8
- ) {
9
- row[foreignKeyPropagations.parentFieldName] = cds.utils.uuid()
10
- }
11
- }
12
-
13
- const _generateChildField = (foreignKeyPropagations, childRow) => {
14
- if (foreignKeyPropagations.deep) {
15
- const deep = foreignKeyPropagations.deep
16
- _generateChildField(deep.propagation, childRow[deep.targetName])
17
- } else if (childRow && !childRow[foreignKeyPropagations.childFieldName] && foreignKeyPropagations.autoGenerate) {
18
- childRow[foreignKeyPropagations.childFieldName] = cds.utils.uuid()
19
- }
20
- }
21
-
22
- const _getNestedVal = (row, prefix) => {
23
- let val = row
24
- const splitted = prefix.split('_')
25
- let k = ''
26
-
27
- while (splitted.length > 0) {
28
- k += splitted.shift()
29
- if (k in val) {
30
- val = val[k]
31
- k = ''
32
- } else {
33
- k += '_'
34
- }
35
- }
36
-
37
- return val
38
- }
39
-
40
- const _propagateToChid = (foreignKeyPropagation, row, childRow) => {
41
- const { parentFieldName, childFieldName, prefix, parentFieldValue } = foreignKeyPropagation
42
-
43
- if (parentFieldName) {
44
- if (prefix) {
45
- const nested = _getNestedVal(row, prefix)
46
- childRow[childFieldName] = nested[parentFieldName]
47
- } else {
48
- childRow[childFieldName] = row[parentFieldName]
49
- }
50
- } else if (parentFieldValue !== undefined) {
51
- childRow[childFieldName] = parentFieldValue
52
- }
53
- }
54
-
55
- const _propagateToParent = (foreignKeyPropagations, childRow, row) => {
56
- if (foreignKeyPropagations.deep) {
57
- const deep = foreignKeyPropagations.deep
58
- _propagateToParent(deep.propagation, childRow[deep.targetName], childRow)
59
- }
60
-
61
- if (childRow && Object.prototype.hasOwnProperty.call(childRow, foreignKeyPropagations.childFieldName)) {
62
- row[foreignKeyPropagations.parentFieldName] = childRow[foreignKeyPropagations.childFieldName]
63
- }
64
- }
65
-
66
- const propagateForeignKeys = (tKey, row, foreignKeyPropagations, opts = {}) => {
67
- const childRows = Array.isArray(row[tKey]) ? row[tKey] : [row[tKey]]
68
-
69
- for (const childRow of childRows) {
70
- if (!childRow) return
71
-
72
- for (const foreignKeyPropagation of foreignKeyPropagations) {
73
- if (foreignKeyPropagation.fillChild) {
74
- _generateParentField(foreignKeyPropagation, row)
75
- if (opts.onlyWriteCompositionEffective && !foreignKeyPropagations._element._isCompositionEffective) {
76
- delete row[tKey]
77
- } else {
78
- _propagateToChid(foreignKeyPropagation, row, childRow)
79
- }
80
- } else {
81
- _generateChildField(foreignKeyPropagation, childRow)
82
- _propagateToParent(foreignKeyPropagation, childRow, row)
83
- }
84
- }
85
- }
86
- }
87
-
88
- const _getSubOns = on => {
1
+ const _getSubOns = element => {
89
2
  // this only works for on conds with `and`, once we support `or` this needs to be adjusted
90
- const newOn = on.filter(e => e !== '(' && e !== ')')
3
+ const newOn = element.on ? element.on.filter(e => e !== '(' && e !== ')') : []
91
4
  const subOns = []
92
5
  let currArr = []
93
6
 
@@ -108,8 +21,6 @@ const _getSubOns = on => {
108
21
  return subOns
109
22
  }
110
23
 
111
- const _autoGenerate = el => el && el.type === 'cds.UUID' && el.key
112
-
113
24
  const _parentFieldsFromSimpleOnCond = (element, subOn) => {
114
25
  const idxChildField = subOn.findIndex(o => o.ref && o.ref[0] === element.name)
115
26
  if (idxChildField === -1 || subOn[1] !== '=') return
@@ -124,26 +35,20 @@ const _parentFieldsFromSimpleOnCond = (element, subOn) => {
124
35
  if (idxChildInParent > -1) parentRef.splice(idxChildInParent, 1)
125
36
  parentRef = [parentRef.join('_')]
126
37
  }
38
+ const parentElement = parentRef && element.parent.elements[parentRef[0]]
127
39
 
128
40
  if (!childElement) {
129
41
  // update on view with key in parent
130
-
131
- return [
132
- {
133
- fillChild: false,
134
- childFieldName,
135
- parentFieldName: parentRef[0],
136
- autoGenerate: _autoGenerate(element.parent.elements[parentRef[0]])
137
- }
138
- ]
42
+ return [{ fillChild: false, parentElement, childElement }]
139
43
  }
140
44
 
141
- if (!childElement.on && parentRef && parentRef.length === 1) {
142
- return _foreignKeyPropagationsFromToManyOn(element, parentRef, childFieldName)
45
+ if (!childElement.on) {
46
+ if (element._isSelfManaged) return _foreignKeyPropagationsFromToManyOn(element, childFieldName)
47
+ if (parentElement) return [{ fillChild: true, parentElement, childElement }]
143
48
  }
144
49
 
145
50
  if ('val' in subOn[idxParentField]) {
146
- return _foreignKeyPropagationsFromStaticOn(childFieldName, idxParentField, subOn)
51
+ return [{ fillChild: true, parentFieldValue: subOn[idxParentField].val, childElement }]
147
52
  }
148
53
 
149
54
  if (childElement._isAssociationStrict && childElement.on) {
@@ -151,40 +56,19 @@ const _parentFieldsFromSimpleOnCond = (element, subOn) => {
151
56
  }
152
57
  }
153
58
 
154
- const _foreignKeyPropagationsFromStaticOn = (childFieldName, idxParentField, subOn) => {
155
- if (childFieldName) {
156
- return [{ fillChild: true, parentFieldValue: subOn[idxParentField].val, childFieldName }]
157
- }
158
- }
159
-
160
- const _foreignKeyPropagationsFromToManyOn = (element, parentRef, childFieldName) => {
161
- const parentFieldName = parentRef[0]
162
- if (parentFieldName === '$self') {
163
- const foreignKeys = _foreignKeysForTarget(element, childFieldName)
164
-
165
- // REVISIT foreignKeys is empty if we have deep operations where a sub element is annotated with persistence skip
166
- if (foreignKeys && foreignKeys.length) {
167
- const parentKeys = _parentKeys(element)
168
-
169
- return resolvedKeys(parentKeys, foreignKeys, true)
170
- }
171
-
172
- return []
59
+ const _foreignKeyPropagationsFromToManyOn = (element, childFieldName) => {
60
+ const foreignKeys = _foreignKeysForTarget(element, childFieldName)
61
+ // REVISIT foreignKeys is empty if we have deep operations where a sub element is annotated with persistence skip
62
+ if (foreignKeys && foreignKeys.length) {
63
+ const parentKeys = _parentKeys(element)
64
+ return _resolvedKeys(parentKeys, foreignKeys, true)
173
65
  }
174
-
175
- return [
176
- {
177
- fillChild: true,
178
- childFieldName,
179
- parentFieldName,
180
- autoGenerate: _autoGenerate(element.parent.elements[parentFieldName])
181
- }
182
- ]
66
+ return []
183
67
  }
184
68
 
185
69
  const _foreignKeyPropagationsFromCustomBacklink = (element, childElement) => {
186
70
  const foreignKeyPropagations = []
187
- const subOns = _getSubOns(childElement.on)
71
+ const subOns = _getSubOns(childElement)
188
72
 
189
73
  for (const subOn of subOns) {
190
74
  if (subOn[1] === '=') {
@@ -192,20 +76,26 @@ const _foreignKeyPropagationsFromCustomBacklink = (element, childElement) => {
192
76
  const otherFieldIdx = parentFieldIdx === 0 ? 2 : 0
193
77
  const otherField = subOn[otherFieldIdx]
194
78
 
195
- if (otherField.ref && otherField.ref.length === 1) {
79
+ if (parentFieldIdx === -1 && subOn[otherFieldIdx === 0 ? 2 : 0].val !== undefined) {
80
+ const parentField = subOn[otherFieldIdx === 0 ? 2 : 0]
81
+ foreignKeyPropagations.push({
82
+ fillChild: false,
83
+ parentFieldValue: parentField.val,
84
+ childElement: element._target.elements[otherField.ref[0]]
85
+ })
86
+ } else if (otherField.ref && otherField.ref.length === 1) {
196
87
  const parentFieldName = subOn[parentFieldIdx].ref[1]
197
88
  foreignKeyPropagations.push({
198
89
  fillChild: true,
199
- parentFieldName,
200
- childFieldName: otherField.ref[0],
201
- autoGenerate: _autoGenerate(element.parent.elements[parentFieldName])
90
+ parentElement: element.parent.elements[parentFieldName],
91
+ childElement: element._target.elements[otherField.ref[0]]
202
92
  })
203
93
  } else if (otherField.val !== undefined) {
204
94
  const parentFieldName = subOn[parentFieldIdx] && subOn[parentFieldIdx].ref[1]
205
95
  const parentField = subOn[otherFieldIdx === 2 ? 0 : 2]
206
96
  foreignKeyPropagations.push({
207
97
  fillChild: true,
208
- parentFieldName,
98
+ parentElement: element.parent.elements[parentFieldName],
209
99
  parentFieldValue: parentField.val,
210
100
  childFieldValue: otherField.val
211
101
  })
@@ -216,8 +106,8 @@ const _foreignKeyPropagationsFromCustomBacklink = (element, childElement) => {
216
106
  return foreignKeyPropagations
217
107
  }
218
108
 
219
- const _foreignKeyPropagationsFromOn = (element, on) => {
220
- const subOns = _getSubOns(on)
109
+ const _foreignKeyPropagationsFromOn = element => {
110
+ const subOns = _getSubOns(element)
221
111
  const foreignKeyPropagations = []
222
112
 
223
113
  for (const subOn of subOns) {
@@ -265,10 +155,9 @@ const _resolve4struct = (others, struct, fkps, fillChild, childIsStruct, i) => {
265
155
 
266
156
  // push propagation
267
157
  fkps.push({
268
- childFieldName: childIsStruct ? current.name : other.name,
269
- parentFieldName: childIsStruct ? other.name : current.name,
158
+ childElement: childIsStruct ? current : other,
159
+ parentElement: childIsStruct ? other : current,
270
160
  fillChild,
271
- autoGenerate: _autoGenerate(other),
272
161
  prefix,
273
162
  deep: !fillChild && _resolveTargetForeignKey(childIsStruct ? current : other)
274
163
  })
@@ -285,11 +174,11 @@ const _resolveTargetForeignKey = targetKey => {
285
174
  const targetName = targetKey['@odata.foreignKey4']
286
175
  if (!targetName) return
287
176
  const _foreignKeyProps = foreignKeyPropagations(targetKey.parent.elements[targetName])
288
- const propagation = _foreignKeyProps.find(_fkp => targetKey.name === _fkp.parentFieldName)
177
+ const propagation = _foreignKeyProps.find(_fkp => _fkp.parentElement && targetKey.name === _fkp.parentElement.name)
289
178
  return { targetName, propagation }
290
179
  }
291
180
 
292
- const resolvedKeys = (foreignKeys, targetKeys, fillChild) => {
181
+ const _resolvedKeys = (foreignKeys, targetKeys, fillChild) => {
293
182
  const foreignKeyPropagations = []
294
183
 
295
184
  for (let i = 0; i < foreignKeys.length; i++) {
@@ -302,9 +191,8 @@ const resolvedKeys = (foreignKeys, targetKeys, fillChild) => {
302
191
  } else {
303
192
  foreignKeyPropagations.push({
304
193
  fillChild,
305
- childFieldName: tk.name,
306
- autoGenerate: _autoGenerate(tk),
307
- parentFieldName: fk.name,
194
+ parentElement: fk,
195
+ childElement: tk,
308
196
  // needed only for child -> parent propagation since template loops in other direction
309
197
  deep: !fillChild && _resolveTargetForeignKey(tk)
310
198
  })
@@ -314,32 +202,23 @@ const resolvedKeys = (foreignKeys, targetKeys, fillChild) => {
314
202
  return foreignKeyPropagations
315
203
  }
316
204
 
317
- const _foreignKeyPropagations = element => {
205
+ const foreignKeyPropagations = element => {
318
206
  if (element.is2many && element.on) {
319
- return _foreignKeyPropagationsFromOn(element, element.on)
207
+ return _foreignKeyPropagationsFromOn(element)
320
208
  }
321
-
322
209
  if (element.is2one) {
323
210
  if (!element.on) {
324
211
  const foreignKeys = _foreignKeys(element)
325
-
326
212
  if (foreignKeys) {
327
213
  const targetKeys = _targetKeys(element)
328
- return resolvedKeys(foreignKeys, targetKeys)
214
+ return _resolvedKeys(foreignKeys, targetKeys)
329
215
  }
330
-
331
- return []
332
216
  } else {
333
217
  // It's a link through a backlink
334
- return _foreignKeyPropagationsFromOn(element, element.on)
218
+ return _foreignKeyPropagationsFromOn(element)
335
219
  }
336
220
  }
337
- }
338
-
339
- const foreignKeyPropagations = element => {
340
- const foreignKeyProps = _foreignKeyPropagations(element)
341
- if (foreignKeyProps) foreignKeyProps._element = element
342
- return foreignKeyProps
221
+ return []
343
222
  }
344
223
 
345
224
  const _foreignKeys = csnElement => {
@@ -365,20 +244,6 @@ const _parentKeys = csnElement => {
365
244
  )
366
245
  }
367
246
 
368
- const _structPrefix = csnElement => {
369
- if (csnElement.parent && csnElement.parent._isStructured) {
370
- const prefix = _structPrefix(csnElement.parent)
371
- if (!prefix) return csnElement.parent.name + '_'
372
- return prefix + csnElement.parent.name + '_'
373
- }
374
-
375
- return ''
376
- }
377
-
378
247
  module.exports = {
379
- foreignKeyPropagations,
380
- propagateForeignKeys,
381
- _foreignKeys,
382
- _targetKeys,
383
- _structPrefix
248
+ foreignKeyPropagations
384
249
  }
@@ -1,26 +1,23 @@
1
- const { foreignKeyPropagations } = require('./foreignKeyPropagations')
2
- const { _structPrefix } = require('./foreignKeyPropagations')
3
-
4
1
  const _toRef = (alias, column) => {
5
2
  if (Array.isArray(column)) column = column.join('_')
6
3
  return { ref: alias ? [alias, column] : [column] }
7
4
  }
8
5
 
9
- const _adaptOnConditionElements = (onCond, associationName, selectAlias, joinAlias) => {
6
+ const _adaptRefs = (onCond, path, { select, join }) => {
10
7
  const adaptedOnCondition = onCond.map(el => {
11
8
  const ref = el.ref
12
9
 
13
10
  if (ref) {
14
- if (ref[0] === associationName.join('_') && ref[1]) {
15
- return _toRef(selectAlias, ref.slice(1))
11
+ if (ref[0] === path.join('_') && ref[1]) {
12
+ return _toRef(select, ref.slice(1))
16
13
  }
17
14
 
18
15
  // no alias for special $user of canonical localized association
19
- if (ref[0] === '$user' && associationName[0] === 'localized') {
16
+ if (ref[0] === '$user' && path[0] === 'localized') {
20
17
  return _toRef(undefined, ref.slice(0))
21
18
  }
22
19
 
23
- return _toRef(joinAlias, ref.slice(0))
20
+ return _toRef(join, ref.slice(0))
24
21
  }
25
22
 
26
23
  return el
@@ -29,8 +26,7 @@ const _adaptOnConditionElements = (onCond, associationName, selectAlias, joinAli
29
26
  return adaptedOnCondition
30
27
  }
31
28
 
32
- const _args = (csnElement, options) => {
33
- const { associationNames, csn, aliases } = options
29
+ const _args = (csnElement, path, aliases) => {
34
30
  const onCond = csnElement.on
35
31
 
36
32
  if (!onCond || !onCond.length) {
@@ -41,86 +37,60 @@ const _args = (csnElement, options) => {
41
37
  return onCond
42
38
  }
43
39
 
44
- if ((onCond[0].ref && onCond[0].ref[0] === '$self') || (onCond[2].ref && onCond[2].ref[0] === '$self')) {
45
- const target =
46
- onCond[0].ref[0] === '$self' ? onCond[2].ref[onCond[2].ref.length - 1] : onCond[0].ref[onCond[0].ref.length - 1]
47
-
48
- const oc = getOnCondNew(csn.definitions[csnElement.target].elements[target], {
49
- associationNames: target,
50
- csn,
51
- // revert join and select aliases because of backlink
52
- aliases: { select: aliases.join, join: aliases.select }
53
- })
54
-
55
- if (onCond.some(ele => ele === 'and')) {
56
- const and = _adaptOnConditionElements(
57
- onCond.slice(onCond.findIndex(ele => ele === 'and') + 1),
58
- associationNames,
59
- aliases.select,
60
- aliases.join
61
- )
62
- oc.push('and', ...and)
63
- }
40
+ if (!csnElement._isSelfManaged) return _adaptRefs(onCond, path, aliases)
41
+
42
+ // revert join and select aliases because of backlink
43
+ const oc = _newOnConditions(csnElement._backlink, [csnElement._backlink.name], {
44
+ select: aliases.join,
45
+ join: aliases.select
46
+ })
64
47
 
65
- return oc
48
+ if (onCond.some(e => e === 'and')) {
49
+ // managed with ON-conditions must contain `$self`, which we replace with `oc`
50
+ const onCondWithouSelf = _adaptRefs(_onCondWithout$self(onCond), path, aliases)
51
+ oc.push('and', ...onCondWithouSelf)
66
52
  }
67
53
 
68
- return _adaptOnConditionElements(onCond, associationNames, aliases.select, aliases.join)
54
+ return oc
69
55
  }
70
56
 
71
- const _foreignToOn = (csnElement, options) => {
72
- const { aliases } = options
73
- const on = []
74
- const foreignKeys = foreignKeyPropagations(csnElement)
57
+ const _isSelfRef = e => e && e.ref && e.ref[0] === '$self'
75
58
 
76
- // for external services, there might be no foreign keys
77
- if (!foreignKeys) return on
59
+ const _onCondWithout$self = onCond => {
60
+ const onCondWithoutSelf = [...onCond]
61
+ const selfIndex = onCondWithoutSelf.findIndex((e, i, on) => {
62
+ if (e === 'and') return _isSelfRef(on[i + 1]) || _isSelfRef(on[i + 3])
63
+ return on[i + 1] === '=' && (_isSelfRef(e) || _isSelfRef(on[i + 2]))
64
+ })
65
+ onCondWithoutSelf.splice(selfIndex, 4)
66
+ return onCondWithoutSelf
67
+ }
78
68
 
79
- for (const key of foreignKeys) {
69
+ const _foreignToOn = (csnElement, path, { select, join }) => {
70
+ // this is only for 2one managed w/o ON-conditions i.e. no static values are possible
71
+ const on = []
72
+ for (const key of csnElement._foreignKeys) {
80
73
  if (on.length !== 0) {
81
74
  on.push('and')
82
75
  }
83
-
84
- // TODO static values possible here?
85
-
86
- const ref1 = _toRef(aliases.select, key.prefix ? `${key.prefix}_${key.childFieldName}` : key.childFieldName)
87
- const structPrefix = _structPrefix(csnElement)
88
- const ref2 = _toRef(aliases.join, `${structPrefix}${key.parentFieldName}`)
76
+ const ref1 = _toRef(select, key.prefix ? `${key.prefix}_${key.childElement.name}` : key.childElement.name)
77
+ const structPrefix = path.length > 1 ? path.slice(0, -1) : []
78
+ const ref2 = _toRef(join, [...structPrefix, key.parentElement.name])
89
79
  on.push(ref1, '=', ref2)
90
80
  }
91
-
92
81
  return on
93
82
  }
94
83
 
95
- /**
96
- * Gets the `ON` conditions in CQN format based on the CSN.
97
- *
98
- * @param {object} csnElement
99
- * @param {import('../../types/api').ONConditionOptions} [options]
100
- * @returns {Array}
101
- * @private
102
- */
103
- function getOnCondNew(csnElement, options) {
104
- const defaultOptions = {
105
- aliases: {},
106
- resolveView: true
107
- }
108
- options = { ...defaultOptions, ...options }
109
- const { associationNames } = options
110
-
111
- if (!Array.isArray(associationNames)) {
112
- options.associationNames = [associationNames]
113
- }
114
-
84
+ const _newOnConditions = (csnElement, path, aliases) => {
115
85
  if (csnElement.keys) {
116
- return _foreignToOn(csnElement, options)
86
+ return _foreignToOn(csnElement, path, aliases)
117
87
  }
118
88
 
119
- return _args(csnElement, options)
89
+ return _args(csnElement, path, aliases)
120
90
  }
121
91
 
122
- const getOnCond = (...args) => {
123
- return ['(', ...getOnCondNew(...args), ')']
92
+ const getOnCond = (csnElement, path = [], aliases = { select: '', join: '' }) => {
93
+ return ['(', ..._newOnConditions(csnElement, path, aliases), ')']
124
94
  }
125
95
 
126
96
  module.exports = {