@sap/cds 5.5.3 → 5.6.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 (227) hide show
  1. package/CHANGELOG.md +134 -1
  2. package/apis/services.d.ts +27 -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/index.js +1 -1
  12. package/lib/compile/to/sql.js +22 -2
  13. package/lib/connect/bindings.js +2 -1
  14. package/lib/connect/index.js +1 -1
  15. package/lib/core/infer.js +1 -1
  16. package/lib/core/reflect.js +3 -1
  17. package/lib/env/index.js +175 -41
  18. package/lib/env/requires.js +24 -3
  19. package/lib/i18n/localize.js +33 -5
  20. package/lib/index.js +7 -6
  21. package/lib/log/format/kibana.js +6 -2
  22. package/lib/ql/DELETE.js +1 -1
  23. package/lib/ql/INSERT.js +1 -1
  24. package/lib/ql/Query.js +13 -10
  25. package/lib/ql/SELECT.js +15 -8
  26. package/lib/ql/UPDATE.js +1 -1
  27. package/lib/ql/Whereable.js +5 -0
  28. package/lib/req/context.js +87 -37
  29. package/lib/req/{impl.js → request.js} +1 -1
  30. package/lib/req/{res.js → response.js} +0 -0
  31. package/lib/serve/Service-api.js +1 -1
  32. package/lib/serve/Service-dispatch.js +12 -2
  33. package/lib/serve/Service-handlers.js +21 -7
  34. package/lib/serve/Service-methods.js +1 -1
  35. package/lib/serve/Transaction.js +7 -6
  36. package/lib/serve/index.js +1 -1
  37. package/lib/utils/axios.js +7 -0
  38. package/lib/utils/data.js +1 -1
  39. package/libx/_runtime/audit/Service.js +18 -18
  40. package/libx/_runtime/audit/generic/personal/access.js +1 -1
  41. package/libx/_runtime/audit/generic/personal/modification.js +3 -2
  42. package/libx/_runtime/audit/generic/personal/utils.js +23 -63
  43. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +6 -0
  44. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +37 -35
  45. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
  46. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +3 -1
  47. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +5 -5
  48. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +1 -1
  49. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +13 -7
  50. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +84 -34
  51. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +10 -4
  52. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +9 -3
  53. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +8 -6
  54. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +1 -3
  55. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +13 -11
  56. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +11 -95
  57. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/ResourcePathParser.js +17 -11
  58. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +2 -1
  59. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +6 -2
  60. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +3 -34
  61. package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +3 -3
  62. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +48 -18
  63. package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +10 -5
  64. package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +1 -1
  65. package/libx/_runtime/cds-services/adapter/rest/handlers/update.js +1 -1
  66. package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +1 -3
  67. package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +9 -2
  68. package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +20 -21
  69. package/libx/_runtime/cds-services/services/utils/columns.js +6 -1
  70. package/libx/_runtime/cds-services/services/utils/compareJson.js +1 -8
  71. package/libx/_runtime/cds-services/services/utils/differ.js +7 -26
  72. package/libx/_runtime/cds-services/services/utils/handlerUtils.js +2 -4
  73. package/libx/_runtime/cds-services/util/assert.js +29 -13
  74. package/libx/_runtime/cds.js +2 -1
  75. package/libx/_runtime/common/aspects/Association.js +72 -0
  76. package/libx/_runtime/common/aspects/any.js +8 -45
  77. package/libx/_runtime/common/aspects/entity.js +0 -1
  78. package/libx/_runtime/common/aspects/relation.js +40 -0
  79. package/libx/_runtime/common/aspects/utils.js +73 -1
  80. package/libx/_runtime/common/auth/strategies/utils/uaa.js +10 -14
  81. package/libx/_runtime/common/composition/data.js +3 -2
  82. package/libx/_runtime/common/composition/delete.js +3 -1
  83. package/libx/_runtime/common/composition/tree.js +23 -18
  84. package/libx/_runtime/common/composition/utils.js +34 -8
  85. package/libx/_runtime/common/error/frontend.js +6 -1
  86. package/libx/_runtime/common/generic/auth.js +15 -13
  87. package/libx/_runtime/common/generic/crud.js +2 -2
  88. package/libx/_runtime/common/generic/etag.js +11 -8
  89. package/libx/_runtime/common/generic/input.js +3 -3
  90. package/libx/_runtime/common/generic/paging.js +9 -5
  91. package/libx/_runtime/common/generic/put.js +3 -2
  92. package/libx/_runtime/common/generic/sorting.js +3 -3
  93. package/libx/_runtime/common/generic/temporal.js +3 -3
  94. package/libx/_runtime/common/toggles/alpha.js +1 -1
  95. package/libx/_runtime/common/utils/cqn.js +20 -1
  96. package/libx/_runtime/common/utils/cqn2cqn4sql.js +125 -139
  97. package/libx/_runtime/common/utils/csn.js +50 -52
  98. package/libx/_runtime/common/utils/foreignKeyPropagations.js +41 -176
  99. package/libx/_runtime/common/utils/generateOnCond.js +40 -70
  100. package/libx/_runtime/common/utils/{enrichWithKeysFromWhere.js → keys.js} +29 -28
  101. package/libx/_runtime/common/utils/postProcessing.js +3 -0
  102. package/libx/_runtime/common/utils/propagateForeignKeys.js +84 -0
  103. package/libx/_runtime/common/utils/resolveStructured.js +1 -1
  104. package/libx/_runtime/common/utils/resolveView.js +19 -9
  105. package/libx/_runtime/common/utils/rewriteAsterisks.js +94 -0
  106. package/libx/_runtime/common/utils/search2cqn4sql.js +9 -8
  107. package/libx/_runtime/common/utils/template.js +54 -46
  108. package/libx/_runtime/db/Service.js +9 -2
  109. package/libx/_runtime/db/expand/expandCQNToJoin.js +10 -24
  110. package/libx/_runtime/db/expand/rawToExpanded.js +2 -1
  111. package/libx/_runtime/db/generic/create.js +1 -0
  112. package/libx/_runtime/db/generic/input.js +7 -11
  113. package/libx/_runtime/db/generic/integrity.js +2 -2
  114. package/libx/_runtime/db/generic/rewrite.js +2 -5
  115. package/libx/_runtime/db/generic/update.js +1 -0
  116. package/libx/_runtime/db/query/read.js +10 -5
  117. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +6 -0
  118. package/libx/_runtime/db/sql-builder/SelectBuilder.js +7 -2
  119. package/libx/_runtime/db/sql-builder/annotations.js +1 -0
  120. package/libx/_runtime/db/utils/columns.js +14 -43
  121. package/libx/_runtime/db/utils/deep.js +5 -7
  122. package/libx/_runtime/fiori/generic/activate.js +3 -2
  123. package/libx/_runtime/fiori/generic/before.js +2 -2
  124. package/libx/_runtime/fiori/generic/cancel.js +3 -2
  125. package/libx/_runtime/fiori/generic/delete.js +3 -2
  126. package/libx/_runtime/fiori/generic/edit.js +2 -2
  127. package/libx/_runtime/fiori/generic/new.js +2 -2
  128. package/libx/_runtime/fiori/generic/patch.js +2 -2
  129. package/libx/_runtime/fiori/generic/prepare.js +2 -2
  130. package/libx/_runtime/fiori/generic/read.js +17 -63
  131. package/libx/_runtime/fiori/generic/readOverDraft.js +4 -4
  132. package/libx/_runtime/fiori/uiflex/extensibility/index.cds +15 -0
  133. package/libx/_runtime/fiori/uiflex/extensibility/index.js +148 -0
  134. package/libx/_runtime/fiori/uiflex/handler/transformREAD.js +119 -0
  135. package/libx/_runtime/fiori/uiflex/handler/transformRESULT.js +43 -0
  136. package/libx/_runtime/fiori/uiflex/handler/transformWRITE.js +62 -0
  137. package/libx/_runtime/fiori/uiflex/index.js +35 -0
  138. package/libx/_runtime/fiori/uiflex/utils.js +78 -0
  139. package/libx/_runtime/fiori/utils/handler.js +3 -13
  140. package/libx/_runtime/fiori/utils/where.js +6 -1
  141. package/libx/_runtime/hana/Service.js +5 -2
  142. package/libx/_runtime/hana/execute.js +1 -1
  143. package/libx/_runtime/hana/pool.js +12 -11
  144. package/libx/_runtime/hana/search2cqn4sql.js +34 -43
  145. package/libx/_runtime/hana/searchToContains.js +3 -3
  146. package/libx/_runtime/index.js +5 -2
  147. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +1 -1
  148. package/libx/_runtime/messaging/common-utils/AMQPClient.js +16 -3
  149. package/libx/_runtime/messaging/common-utils/connections.js +11 -14
  150. package/libx/_runtime/messaging/common-utils/naming-conventions.js +1 -1
  151. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -1
  152. package/libx/_runtime/messaging/enterprise-messaging.js +1 -1
  153. package/libx/_runtime/messaging/message-queuing.js +18 -0
  154. package/libx/_runtime/remote/Service.js +14 -2
  155. package/libx/_runtime/remote/utils/client-types.d.ts +7 -0
  156. package/libx/_runtime/remote/utils/client.js +117 -23
  157. package/libx/_runtime/sqlite/Service.js +4 -3
  158. package/libx/_runtime/sqlite/convertAssocToOneManaged.js +1 -3
  159. package/libx/_runtime/sqlite/execute.js +1 -1
  160. package/libx/gql/GraphQLAdapter.js +33 -0
  161. package/libx/gql/constants/adapter.js +69 -0
  162. package/libx/gql/constants/cds.js +18 -0
  163. package/libx/gql/constants/graphql.js +33 -0
  164. package/libx/gql/resolvers/crud/create.js +15 -0
  165. package/libx/gql/resolvers/crud/delete.js +24 -0
  166. package/libx/gql/resolvers/crud/index.js +6 -0
  167. package/libx/gql/resolvers/crud/read.js +25 -0
  168. package/libx/gql/resolvers/crud/update.js +31 -0
  169. package/libx/gql/resolvers/crud/utils/index.js +36 -0
  170. package/libx/gql/resolvers/field.js +5 -0
  171. package/libx/gql/resolvers/index.js +7 -0
  172. package/libx/gql/resolvers/mutation.js +23 -0
  173. package/libx/gql/resolvers/parse/ast/enrich.js +51 -0
  174. package/libx/gql/resolvers/parse/ast/fragment.js +11 -0
  175. package/libx/gql/resolvers/parse/ast/fromObject.js +39 -0
  176. package/libx/gql/resolvers/parse/ast/index.js +3 -0
  177. package/libx/gql/resolvers/parse/ast/meta.js +4 -0
  178. package/libx/gql/resolvers/parse/ast/variable.js +7 -0
  179. package/libx/gql/resolvers/parse/ast2cqn/columns.js +42 -0
  180. package/libx/gql/resolvers/parse/ast2cqn/entries.js +31 -0
  181. package/libx/gql/resolvers/parse/ast2cqn/index.js +8 -0
  182. package/libx/gql/resolvers/parse/ast2cqn/limit.js +6 -0
  183. package/libx/gql/resolvers/parse/ast2cqn/orderBy.js +24 -0
  184. package/libx/gql/resolvers/parse/ast2cqn/utils/index.js +3 -0
  185. package/libx/gql/resolvers/parse/ast2cqn/where.js +70 -0
  186. package/libx/gql/resolvers/parse/utils/index.js +8 -0
  187. package/libx/gql/resolvers/query.js +13 -0
  188. package/libx/gql/resolvers/root.js +34 -0
  189. package/libx/gql/schema/generate.js +18 -0
  190. package/libx/gql/schema/index.js +5 -0
  191. package/libx/gql/schema/mutation.js +76 -0
  192. package/libx/gql/schema/query.js +108 -0
  193. package/libx/gql/schema/typeDefMap.js +45 -0
  194. package/libx/gql/schema/utils/index.js +54 -0
  195. package/libx/gql/utils/index.js +12 -0
  196. package/libx/{_runtime/odata/cqn2odata.js → odata/cqn2odata/index.js} +39 -100
  197. package/libx/odata/index.js +80 -0
  198. package/libx/odata/odata2cqn/afterburner.js +170 -0
  199. package/libx/{_runtime/odata/odata2cqn.pegjs → odata/odata2cqn/grammar.pegjs} +102 -123
  200. package/libx/odata/odata2cqn/index.js +3 -0
  201. package/libx/odata/odata2cqn/parser.js +1 -0
  202. package/libx/odata/utils/index.js +64 -0
  203. package/libx/rest/RestAdapter.js +101 -0
  204. package/libx/rest/RestRequest.js +30 -0
  205. package/libx/rest/index.js +3 -0
  206. package/libx/rest/middleware/auth.js +22 -0
  207. package/libx/rest/middleware/content.js +15 -0
  208. package/libx/rest/middleware/create.js +40 -0
  209. package/libx/rest/middleware/delete.js +20 -0
  210. package/libx/rest/middleware/error.js +56 -0
  211. package/libx/rest/middleware/operation.js +39 -0
  212. package/libx/rest/middleware/parse.js +90 -0
  213. package/libx/rest/middleware/read.js +29 -0
  214. package/libx/rest/middleware/update.js +42 -0
  215. package/libx/rest/utils/data.js +65 -0
  216. package/package.json +4 -1
  217. package/server.js +42 -29
  218. package/lib/req/cls.js +0 -39
  219. package/libx/_runtime/cds-services/services/utils/diff.js +0 -53
  220. package/libx/_runtime/cds-services/util/auditlog.js +0 -247
  221. package/libx/_runtime/cds-services/util/xsenv.js +0 -51
  222. package/libx/_runtime/common/utils/backlinks.js +0 -83
  223. package/libx/_runtime/common/utils/rewriteAsterisk.js +0 -72
  224. package/libx/_runtime/odata/index.js +0 -55
  225. package/libx/_runtime/odata/odata2cqn.js +0 -1
  226. package/libx/_runtime/odata/readToCqn.js +0 -129
  227. package/libx/_runtime/remote/cqn2odata/index.js +0 -2
@@ -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 = {
@@ -1,8 +1,23 @@
1
- const { getOnCondElements } = require('./backlinks')
1
+ const { where2obj } = require('./cqn')
2
+
3
+ function _getOnCondElements(onCond, onCondElements = []) {
4
+ const andIndex = onCond.indexOf('and')
5
+ const entityKey = onCond[2].ref && onCond[2].ref.join('.')
6
+ const entityVal = onCond[2].val
7
+ const targetKey = onCond[0].ref && onCond[0].ref.join('.')
8
+ const targetVal = onCond[0].val
9
+ onCondElements.push({ entityKey, targetKey, entityVal, targetVal })
10
+
11
+ if (andIndex !== -1) {
12
+ _getOnCondElements(onCond.slice(andIndex + 1), onCondElements)
13
+ }
14
+ return onCondElements
15
+ }
2
16
 
3
17
  function _modifyWhereWithNavigations(where, newWhere, targetKeyElement, keyName) {
4
18
  if (where) {
5
- const whereCopy = JSON.parse(JSON.stringify(where)) // copy where else query will be modified
19
+ // copy where else query will be modified
20
+ const whereCopy = JSON.parse(JSON.stringify(where))
6
21
  if (newWhere.length > 0) newWhere.push('and')
7
22
  newWhere.push(...whereCopy)
8
23
  }
@@ -14,7 +29,7 @@ function _modifyWhereWithNavigations(where, newWhere, targetKeyElement, keyName)
14
29
  })
15
30
  }
16
31
 
17
- const _buildWhereForNavigations = (ref, newWhere, model, target) => {
32
+ function _buildWhereForNavigations(ref, newWhere, model, target) {
18
33
  const currentRef = ref[0]
19
34
  const nextRef = ref[1]
20
35
 
@@ -24,7 +39,7 @@ const _buildWhereForNavigations = (ref, newWhere, model, target) => {
24
39
 
25
40
  if (!navigationElement || !navigationElement.on) return
26
41
 
27
- const nextKeys = getOnCondElements(navigationElement.on)
42
+ const nextKeys = _getOnCondElements(navigationElement.on)
28
43
  for (const key of nextKeys) {
29
44
  const keyName = key.targetKey.replace(navigationElement.name + '.', '')
30
45
  const targetKeyElement = navigationElement._target.elements[keyName]
@@ -55,37 +70,23 @@ function _getWhereFromUpdate(query, target, model) {
55
70
  return query.UPDATE.where
56
71
  }
57
72
 
58
- function _addKeysFromWhereToData(where, target, data) {
59
- const whereLength = where.length
60
- for (let i = 0; i < whereLength; i++) {
61
- const whereEl = where[i]
62
- const colName = whereEl.ref && whereEl.ref[whereEl.ref.length - 1]
63
- const colEl = colName && target.elements[colName]
64
- if (colEl && colEl.key) {
65
- const opWhere = where[i + 1]
66
- const valWhere = where[i + 2]
67
- if (opWhere === '=' && valWhere && 'val' in valWhere) {
68
- data[colName] = valWhere.val
69
- }
70
- }
71
- }
72
- }
73
-
74
73
  // params: data, req, service/tx
75
- module.exports = (data, { query, target }, { model }) => {
74
+ function enrichDataWithKeysFromWhere(data, { query, target }, { model }) {
76
75
  if (query.INSERT) {
77
76
  const where = _getWhereFromInsert(query, target, model)
78
77
  if (!where || !where.length) return
79
-
80
78
  if (!Array.isArray(data)) data = [data]
81
-
82
- for (const d of data) {
83
- _addKeysFromWhereToData(where, target, d)
84
- }
79
+ for (const d of data) Object.assign(d, where2obj(where, target))
85
80
  } else if (query.UPDATE) {
86
81
  const where = _getWhereFromUpdate(query, target, model)
87
82
  if (!where || !where.length) return
88
- if (!data) data = query.UPDATE.data = {} // REVISIT: We should not expect data to be present always!
89
- _addKeysFromWhereToData(where, target, data)
83
+ // REVISIT: We should not expect data to be present always!
84
+ if (!data) data = query.UPDATE.data = {}
85
+ Object.assign(data, where2obj(where, target))
90
86
  }
91
87
  }
88
+
89
+ module.exports = {
90
+ where2obj,
91
+ enrichDataWithKeysFromWhere
92
+ }
@@ -64,6 +64,9 @@ const handleAliasInResult = (columns, result) => {
64
64
  // REVISIT: todo renaming for deep operations
65
65
  const postProcess = (query, result, service, onlySelectAliases = false) => {
66
66
  if (query.SELECT) {
67
+ if (query.SELECT.columns && query.SELECT.columns.find(col => col.func === 'count' && col.as === '$count')) {
68
+ return [{ $count: result }]
69
+ }
67
70
  handleAliasInResult(query.SELECT.columns, result)
68
71
 
69
72
  if (!onlySelectAliases) {
@@ -0,0 +1,84 @@
1
+ const cds = require('../../cds')
2
+
3
+ const _generateParentField = ({ parentElement }, row) => {
4
+ if (_autoGenerate(parentElement) && !row[parentElement.name]) {
5
+ row[parentElement.name] = cds.utils.uuid()
6
+ }
7
+ }
8
+
9
+ const _generateChildField = ({ deep, childElement }, childRow) => {
10
+ if (deep) {
11
+ _generateChildField(deep.propagation, childRow[deep.targetName])
12
+ } else if (_autoGenerate(childElement) && childRow && !childRow[childElement.name]) {
13
+ childRow[childElement.name] = cds.utils.uuid()
14
+ }
15
+ }
16
+
17
+ const _autoGenerate = e => e && e.type === 'cds.UUID' && e.key
18
+
19
+ const _getNestedVal = (row, prefix) => {
20
+ let val = row
21
+ const splitted = prefix.split('_')
22
+ let k = ''
23
+
24
+ while (splitted.length > 0) {
25
+ k += splitted.shift()
26
+ if (k in val) {
27
+ val = val[k]
28
+ k = ''
29
+ } else {
30
+ k += '_'
31
+ }
32
+ }
33
+
34
+ return val
35
+ }
36
+
37
+ const _propagateToChid = ({ parentElement, childElement, prefix, parentFieldValue }, row, childRow) => {
38
+ if (!childElement) return
39
+ if (parentElement) {
40
+ if (prefix) {
41
+ const nested = _getNestedVal(row, prefix)
42
+ childRow[childElement.name] = nested[parentElement.name]
43
+ } else {
44
+ childRow[childElement.name] = row[parentElement.name]
45
+ }
46
+ } else if (parentFieldValue !== undefined) {
47
+ childRow[childElement.name] = parentFieldValue
48
+ }
49
+ }
50
+
51
+ const _propagateToParent = ({ parentElement, childElement, deep }, childRow, row) => {
52
+ if (deep) {
53
+ _propagateToParent(deep.propagation, childRow[deep.targetName], childRow)
54
+ }
55
+ if (parentElement && childElement && childRow && Object.prototype.hasOwnProperty.call(childRow, childElement.name)) {
56
+ row[parentElement.name] = childRow[childElement.name]
57
+ }
58
+ }
59
+
60
+ const propagateForeignKeys = (tKey, row, foreignKeyPropagations, isCompositionEffective) => {
61
+ const childRows = Array.isArray(row[tKey]) ? row[tKey] : [row[tKey]]
62
+
63
+ for (const childRow of childRows) {
64
+ if (!childRow) return
65
+
66
+ for (const foreignKeyPropagation of foreignKeyPropagations) {
67
+ if (foreignKeyPropagation.fillChild) {
68
+ _generateParentField(foreignKeyPropagation, row)
69
+ if (!isCompositionEffective) {
70
+ delete row[tKey]
71
+ } else {
72
+ _propagateToChid(foreignKeyPropagation, row, childRow)
73
+ }
74
+ } else {
75
+ _generateChildField(foreignKeyPropagation, childRow)
76
+ _propagateToParent(foreignKeyPropagation, childRow, row)
77
+ }
78
+ }
79
+ }
80
+ }
81
+
82
+ module.exports = {
83
+ propagateForeignKeys
84
+ }
@@ -22,7 +22,7 @@ const _flattenProps = (subElement, structName, structProperties, structElement,
22
22
 
23
23
  const _resolveStructured = ({ structName, structProperties }, subElements, asRef = true) => {
24
24
  if (!subElements) {
25
- return
25
+ return []
26
26
  }
27
27
 
28
28
  // only add from structProperties