@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,13 +1,9 @@
1
1
  const cds = require('../../cds')
2
2
 
3
- const { isSelfManaged, isBacklink } = require('../utils/backlinks')
4
-
5
3
  const { ensureNoDraftsSuffix } = require('../utils/draft')
6
4
  const { isRootEntity } = require('../utils/csn')
7
5
  const { getTransition, getDBTable } = require('../utils/resolveView')
8
6
 
9
- const { foreignKeyPropagations } = require('../utils/foreignKeyPropagations')
10
-
11
7
  const getError = require('../../common/error')
12
8
 
13
9
  /*
@@ -15,21 +11,25 @@ const getError = require('../../common/error')
15
11
  */
16
12
 
17
13
  const _foreignKeysToLinks = (element, inverse) =>
18
- foreignKeyPropagations(element).map(e => {
14
+ element._foreignKeys.map(e => {
19
15
  e = inverse
20
16
  ? {
21
- childFieldName: e.parentFieldName,
22
- parentFieldName: e.childFieldName,
17
+ childElement: e.parentElement,
18
+ parentElement: e.childElement,
23
19
  childFieldValue: e.parentFieldValue,
24
20
  parentFieldValue: e.childFieldValue,
25
21
  prefix: e.prefix
26
22
  }
27
23
  : e
28
- const link = {
29
- entityKey:
30
- e.prefix && !e.parentFieldName.includes(e.prefix) ? e.prefix + '_' + e.parentFieldName : e.parentFieldName,
31
- targetKey: e.prefix && !e.childFieldName.includes(e.prefix) ? e.prefix + '_' + e.childFieldName : e.childFieldName
32
- }
24
+ const link = {}
25
+ if (e.parentElement)
26
+ link.entityKey =
27
+ e.prefix && !e.parentElement.name.includes(e.prefix)
28
+ ? `${e.prefix}_${e.parentElement.name}`
29
+ : e.parentElement.name
30
+ if (e.childElement)
31
+ link.targetKey =
32
+ e.prefix && !e.childElement.name.includes(e.prefix) ? `${e.prefix}_${e.childElement.name}` : e.childElement.name
33
33
  if (e.parentFieldValue !== undefined) link.entityVal = e.parentFieldValue
34
34
  if (e.childFieldValue !== undefined) link.targetVal = e.childFieldValue
35
35
  return link
@@ -57,7 +57,7 @@ const _resolvedElement = (element, service) => {
57
57
  const _navigationExistsInCompositionMap = (element, compositionMap) =>
58
58
  compositionMap.has(element.target) && element._isCompositionEffective
59
59
 
60
- const _isUnManaged = element => element.on && !isSelfManaged(element)
60
+ const _isUnManaged = element => element.on && !element._isSelfManaged
61
61
 
62
62
  const _isNonRecursiveNavigation = (element, rootEntityName) =>
63
63
  rootEntityName !== element.target && element._isCompositionEffective
@@ -106,7 +106,7 @@ const _getCompositionTreeRec = ({
106
106
  backLinks: [],
107
107
  customBackLinks: []
108
108
  })
109
- if (!isSelfManaged(element)) {
109
+ if (!element._isSelfManaged) {
110
110
  const backLinks = _foreignKeysToLinks(element, true) || []
111
111
  if (element.is2many) {
112
112
  compositionElement.customBackLinks.push(...backLinks)
@@ -118,7 +118,11 @@ const _getCompositionTreeRec = ({
118
118
  for (const backLinkName in targetEntity.elements) {
119
119
  const _backLink = targetEntity.elements[backLinkName]
120
120
  if (!_backLink._isAssociationEffective) continue
121
- if (isBacklink(_backLink, definitions[compositionElement.target], false, element.name)) {
121
+ if (
122
+ _backLink._isCompositionBacklink &&
123
+ _backLink.target === compositionElement.target &&
124
+ _backLink._anchor.name === element.name
125
+ ) {
122
126
  const backLinks = _foreignKeysToLinks(_backLink) || []
123
127
  if (_isUnManaged(element)) {
124
128
  compositionElement.customBackLinks.push(...backLinks)
@@ -131,7 +135,7 @@ const _getCompositionTreeRec = ({
131
135
  compositionTree.compositionElements.push(compositionElement)
132
136
  } else if (_isNonRecursiveNavigation(element, rootEntityName)) {
133
137
  const subObject = _createSubElement(element, definitions)
134
- if (!isSelfManaged(element)) {
138
+ if (!element._isSelfManaged) {
135
139
  const backLinks = _foreignKeysToLinks(element, true) || []
136
140
  if (element.is2many) {
137
141
  subObject.customBackLinks.push(...backLinks)
@@ -151,7 +155,8 @@ const _getCompositionTreeRec = ({
151
155
  })
152
156
  } else if (
153
157
  element._isAssociationEffective &&
154
- isBacklink(element, definitions[compositionTree.target], false, compositionTree.name) &&
158
+ element._isCompositionBacklink &&
159
+ element.target === compositionTree.target &&
155
160
  compositionMap.has(element.target)
156
161
  ) {
157
162
  const backLinks = _foreignKeysToLinks(element) || []
@@ -218,7 +223,7 @@ const _cacheCompositionParentsOfOne = ({ definitions }) => {
218
223
  if (!parent.kind === 'entity' || !parent.elements) continue
219
224
  for (const elementName in parent.elements) {
220
225
  const element = parent.elements[elementName]
221
- if (element._isCompositionEffective && element.is2one && !isSelfManaged(element)) {
226
+ if (element._isCompositionEffective && element.is2one && !element._isSelfManaged) {
222
227
  const targetName = element.target
223
228
  const target = definitions[targetName]
224
229
  if (!target) continue
@@ -1,5 +1,3 @@
1
- const { isSelfManaged } = require('../utils/backlinks')
2
-
3
1
  const { ensureNoDraftsSuffix, ensureDraftsSuffix } = require('../utils/draft')
4
2
 
5
3
  const addDraftSuffix = (draft, name) => {
@@ -24,6 +22,7 @@ const keyElements = entity => {
24
22
 
25
23
  const key = (entity, data) => {
26
24
  return keyElements(entity).reduce((result, element) => {
25
+ if (element.name === 'IsActiveEntity' && !Object.prototype.hasOwnProperty.call(data, element.name)) return result
27
26
  result[element.name] = data[element.name]
28
27
  return result
29
28
  }, {})
@@ -33,10 +32,6 @@ const val = element => (element && element.val) || element
33
32
 
34
33
  const array = x => (Array.isArray(x) ? x : [x])
35
34
 
36
- const isManaged = element => {
37
- return isSelfManaged(element) || !element.on
38
- }
39
-
40
35
  const isCompOrAssoc = (entity, k, onlyToOne) => {
41
36
  return (
42
37
  entity.elements &&
@@ -60,6 +55,37 @@ const cleanDeepData = (entity, data, onlyToOne = false) => {
60
55
  })
61
56
  }
62
57
 
58
+ const _getBacklinkNameFromOnCond = element => {
59
+ if (element.on && element.on.length === 3 && element.on[0].ref && element.on[2].ref) {
60
+ if (element.on[0].ref[0] === '$self') {
61
+ return element.on[2].ref[element.on[2].ref.length - 1]
62
+ } else if (element.on[2].ref[0] === '$self') {
63
+ return element.on[0].ref[element.on[0].ref.length - 1]
64
+ }
65
+ }
66
+ }
67
+
68
+ const isBacklink = (element, parent, checkContained, backLinkName) => {
69
+ if (!element._isAssociationStrict) return false
70
+ if (!parent || !(element.keys || element.on)) return false
71
+ if (element.target !== parent.name) return false
72
+
73
+ const _isBackLink = parentElement =>
74
+ (!checkContained || parentElement._isContained) && _getBacklinkNameFromOnCond(parentElement) === element.name
75
+
76
+ if (backLinkName) {
77
+ const parentElement = parent.elements[backLinkName]
78
+ return parentElement.isAssociation && _isBackLink(parentElement)
79
+ }
80
+ for (const parentElementName in parent.elements) {
81
+ const parentElement = parent.elements[parentElementName]
82
+ if (!parentElement.isAssociation) continue
83
+ if (_isBackLink(parentElement)) return true
84
+ }
85
+
86
+ return false
87
+ }
88
+
63
89
  module.exports = {
64
90
  addDraftSuffix,
65
91
  whereKey,
@@ -67,7 +93,7 @@ module.exports = {
67
93
  key,
68
94
  val,
69
95
  array,
70
- isManaged,
71
96
  isCompOrAssoc,
72
- cleanDeepData
97
+ cleanDeepData,
98
+ isBacklink
73
99
  }
@@ -22,6 +22,8 @@ const {
22
22
  MAX_SEVERITY
23
23
  } = require('./constants')
24
24
 
25
+ const SKIP_SANITIZATION = '@cds.skip_sanitization'
26
+
25
27
  const _getFiltered = err => {
26
28
  const error = {}
27
29
 
@@ -103,10 +105,13 @@ const normalizeError = (err, req) => {
103
105
  // make sure it's a number
104
106
  statusCode = statusCode ? Number(statusCode) : 500
105
107
 
106
- if (statusCode >= 500 && process.env.NODE_ENV === 'production') {
108
+ // REVISIT: make === 500 in cds^6
109
+ // error[SKIP_SANITIZATION] is not an official API!!!
110
+ if (statusCode >= 500 && process.env.NODE_ENV === 'production' && !error[SKIP_SANITIZATION]) {
107
111
  // > return sanitized error to client
108
112
  return { error: { code: `${statusCode}`, message: i18n(statusCode, locale) }, statusCode }
109
113
  }
114
+ delete error[SKIP_SANITIZATION]
110
115
 
111
116
  // no top level null codes
112
117
  if (error.code === 'null') {
@@ -6,7 +6,7 @@ const cds = require('../../cds')
6
6
  const { SELECT } = cds.ql
7
7
 
8
8
  const { getRequiresAsArray } = require('../utils/auth')
9
- const cqn2cqn4sql = require('../utils/cqn2cqn4sql')
9
+ const { cqn2cqn4sql } = require('../utils/cqn2cqn4sql')
10
10
  const { isActiveEntityRequested, removeIsActiveEntityRecursively } = require('../../fiori/utils/where')
11
11
  const { ensureDraftsSuffix } = require('../../fiori/utils/handler')
12
12
 
@@ -204,9 +204,10 @@ const _findTableName = (ref, aliases) => {
204
204
 
205
205
  const _getTableForColumn = (col, aliases, model) => {
206
206
  for (let i = 0; i < aliases.length; i++) {
207
- const alias = aliases[i]
207
+ const index = aliases.length - i - 1
208
+ const alias = aliases[index]
208
209
  if (Object.keys(model.definitions[alias].elements).includes(col)) {
209
- return { index: i, table: alias.replace(/\./g, '_') }
210
+ return { index, table: alias.replace(/\./g, '_') }
210
211
  }
211
212
  }
212
213
 
@@ -549,13 +550,18 @@ const _addNormalizedRestrict = (restrict, restricts, definition, definitions) =>
549
550
  if (where) {
550
551
  // operate on a copy
551
552
  let _where = where
552
- const paths = where.match(/ (\w*)\.(\w*)/g) || []
553
+ // find all path expressions in order to normalize shorthand (i.e., inject "[exists ...]")
554
+ const paths = (where.match(/ (\w\.*)*/g) || []).filter(m => m.match(/\./) && m !== ' ')
553
555
  for (let i = 0; i < paths.length; i++) {
554
556
  const parts = paths[i].trim().split('.')
555
557
  let current = definition
556
558
  while (parts.length) {
557
559
  current = current.elements[parts.shift()]
558
- if (current.is2many) _where = _where.replace(current.name + '.', current.name + '[exists ') + ']'
560
+ if (current.isAssociation && _where.includes(current.name + '.')) {
561
+ const matches = _where.match(new RegExp(`(${current.name}).(.*)]`))
562
+ _where = _where.replace(`${matches[1]}.`, `${current.name}[exists `)
563
+ _where = _where.replace(matches[2], `${matches[2]}]`)
564
+ }
559
565
  if (current.target) current = definitions[current.target]
560
566
  }
561
567
  }
@@ -882,19 +888,15 @@ const _secureDependentEntities = srv => {
882
888
  }
883
889
  }
884
890
 
885
- module.exports = function () {
886
- /*
887
- * @restrict, @requires, @readonly, @insertonly, and @Capabilities for entities
888
- */
891
+ module.exports = cds.service.impl(function () {
892
+ // @restrict, @requires, @readonly, @insertonly, and @Capabilities for entities
889
893
  _secureDependentEntities(this)
890
894
  for (const k in this.entities) {
891
895
  const entity = this.entities[k]
892
896
  if (!_authDependsOnParents(entity)) _registerAuthHandlers(entity, this)
893
897
  }
894
898
 
895
- /*
896
- * @restrict and @requires for operations
897
- */
899
+ // @restrict and @requires for operations
898
900
  for (const k in this.operations) {
899
901
  const operation = this.operations[k]
900
902
 
@@ -904,4 +906,4 @@ module.exports = function () {
904
906
  // @restrict
905
907
  _registerOperationRestrictHandlers(operation, this)
906
908
  }
907
- }
909
+ })
@@ -67,7 +67,7 @@ const _updateReqData = (req, that) => {
67
67
  }
68
68
  }
69
69
 
70
- module.exports = function () {
70
+ module.exports = cds.service.impl(function () {
71
71
  this.on(['CREATE', 'READ', 'UPDATE', 'DELETE'], '*', async function (req) {
72
72
  if (typeof req.query !== 'string' && req.target && req.target._hasPersistenceSkip) {
73
73
  req.reject(501, 'PERSISTENCE_SKIP_NO_GENERIC_CRUD', [req.target.name])
@@ -128,4 +128,4 @@ module.exports = function () {
128
128
 
129
129
  return req.data
130
130
  })
131
- }
131
+ })
@@ -2,9 +2,9 @@ const cds = require('../../cds')
2
2
  const { SELECT } = cds.ql
3
3
 
4
4
  // REVISIT: draft should not be handled here, e.g., target.name should be adjusted before
5
- const { isActiveEntityRequested, removeIsActiveEntityRecursively } = require('../../fiori/utils/where')
5
+ const { isActiveEntityRequested } = require('../../fiori/utils/where')
6
6
  const { ensureDraftsSuffix } = require('../../fiori/utils/handler')
7
- const cqn2cqn4sql = require('../../common/utils/cqn2cqn4sql')
7
+ const { cqn2cqn4sql } = require('../../common/utils/cqn2cqn4sql')
8
8
  const ODataRequest = require('../../cds-services/adapter/odata-v4/ODataRequest')
9
9
 
10
10
  const C_U_ = {
@@ -22,10 +22,11 @@ const getSelectCQN = (query, target, model) => {
22
22
  } else {
23
23
  requestTarget = query.DELETE.from
24
24
  }
25
+
26
+ const targetName = isActiveEntityRequested(requestTarget.ref[0].where) ? target.name : ensureDraftsSuffix(target.name)
25
27
  const cqn = cqn2cqn4sql(SELECT.from(requestTarget), model)
26
- cqn.SELECT.from.ref[0] = isActiveEntityRequested(cqn.SELECT.where) ? target.name : ensureDraftsSuffix(target.name)
27
28
  cqn.columns([target._etag])
28
- cqn.SELECT.where = removeIsActiveEntityRecursively(cqn.SELECT.where)
29
+ cqn.SELECT.from.ref[0] = targetName
29
30
 
30
31
  return cqn
31
32
  }
@@ -60,11 +61,12 @@ const _handler = async function (req) {
60
61
  }
61
62
  }
62
63
 
63
- /*
64
+ /**
64
65
  * handler registration
66
+ *
65
67
  */
66
68
  /* istanbul ignore next */
67
- module.exports = function () {
69
+ module.exports = cds.service.impl(function () {
68
70
  _handler._initial = true
69
71
 
70
72
  for (const k in this.entities) {
@@ -75,8 +77,9 @@ module.exports = function () {
75
77
  continue
76
78
  }
77
79
 
78
- // Handler for CREATE is registered for backwards compatiblity w.r.t. ETag generation
80
+ // handler for CREATE is registered for backwards compatibility w.r.t. ETag generation
79
81
  let events = ['CREATE', 'READ', 'UPDATE', 'DELETE']
82
+
80
83
  // if odata and fiori is separated, this will not be needed in the odata version
81
84
  if (entity._isDraftEnabled) {
82
85
  events = ['READ', 'NEW', 'DELETE', 'PATCH', 'EDIT', 'CANCEL']
@@ -88,4 +91,4 @@ module.exports = function () {
88
91
  this.before(action, entity, _handler)
89
92
  }
90
93
  }
91
- }
94
+ })
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  const cds = require('../../cds')
11
- const enrichDataWithKeysFromWhere = require('../utils/enrichWithKeysFromWhere')
11
+ const { enrichDataWithKeysFromWhere } = require('../utils/keys')
12
12
  const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
13
13
  const { checkInputConstraints, checkIfAssocDeep } = require('../../cds-services/util/assert')
14
14
  const getTemplate = require('../utils/template')
@@ -295,7 +295,7 @@ function _actionFunctionHandler(req) {
295
295
  _handler._initial = true
296
296
  _actionFunctionHandler._initial = true
297
297
 
298
- module.exports = function () {
298
+ module.exports = cds.service.impl(function () {
299
299
  this.before(['CREATE', 'UPDATE', 'NEW', 'PATCH'], '*', _handler)
300
300
 
301
301
  const operationNames = []
@@ -318,4 +318,4 @@ module.exports = function () {
318
318
  this.before(boundOps, entity.name, _actionFunctionHandler)
319
319
  }
320
320
  }
321
- }
321
+ })
@@ -1,3 +1,4 @@
1
+ const cds = require('../../cds')
1
2
  const { getDefaultPageSize } = require('../utils/page')
2
3
 
3
4
  const _handler = function (req) {
@@ -5,15 +6,18 @@ const _handler = function (req) {
5
6
  if (!req._.req) return
6
7
 
7
8
  // target === null if view with parameters
8
- if (!req.target || !req.query.SELECT || req.query.SELECT.limit || req.query.SELECT.one) return
9
+ if (!req.target || !req.query.SELECT || req.query.SELECT.one) return
9
10
 
10
- req.query.limit(...[getDefaultPageSize(req.target), 0])
11
+ let { rows, offset } = req.query.SELECT.limit || {}
12
+ rows = rows && 'val' in rows ? rows.val : getDefaultPageSize(req.target)
13
+ offset = offset && 'val' in offset ? offset.val : 0
14
+ req.query.limit(...[rows, offset])
11
15
  }
12
16
 
13
- /*
17
+ /**
14
18
  * handler registration
15
19
  */
16
- module.exports = function () {
20
+ module.exports = cds.service.impl(function () {
17
21
  _handler._initial = true
18
22
  this.before('READ', '*', _handler)
19
- }
23
+ })
@@ -5,6 +5,7 @@
5
5
  * and is neither key nor read-only (e.g., managed, virtual, etc.)
6
6
  */
7
7
 
8
+ const cds = require('../../cds')
8
9
  const getTemplate = require('../utils/template')
9
10
  const templateProcessor = require('../utils/templateProcessor')
10
11
  const { getDataFromCQN, setDataFromCQN } = require('../utils/data')
@@ -90,6 +91,6 @@ function _handler(req) {
90
91
 
91
92
  _handler._initial = true
92
93
 
93
- module.exports = function () {
94
+ module.exports = cds.service.impl(function () {
94
95
  this.before(['UPDATE'], '*', _handler)
95
- }
96
+ })
@@ -85,13 +85,13 @@ const _handler = function (req) {
85
85
  }
86
86
  }
87
87
 
88
- /*
88
+ /**
89
89
  * handler registration
90
90
  */
91
- module.exports = function () {
91
+ module.exports = cds.service.impl(function () {
92
92
  _handler._initial = true
93
93
  this.before('READ', '*', _handler)
94
- }
94
+ })
95
95
 
96
96
  // REVISIT: remove (currently needed for test)
97
97
  module.exports.handler = _handler
@@ -71,11 +71,11 @@ const _handler = function (req) {
71
71
  }
72
72
  }
73
73
 
74
- /*
74
+ /**
75
75
  * handler registration
76
76
  */
77
- module.exports = function () {
77
+ module.exports = cds.service.impl(function () {
78
78
  _handler._initial = true
79
79
  // always run to allow interaction with temporal data in custom handlers
80
80
  this.before('*', _handler)
81
- }
81
+ })
@@ -8,7 +8,7 @@ module.exports = cds => {
8
8
  // attach _getHash helper to mtx
9
9
  if (!cds.mtx._getHash) {
10
10
  cds.mtx._getHash = req => {
11
- let hash = req.user.tenant
11
+ let hash = req.tenant
12
12
  if (req.features) hash += ':' + Object.keys(req.features).join(';')
13
13
  return hash
14
14
  }
@@ -16,7 +16,26 @@ const getEntityNameFromUpdateCQN = cqn => {
16
16
  return (cqn.UPDATE.entity.ref && cqn.UPDATE.entity.ref[0]) || cqn.UPDATE.entity.name || cqn.UPDATE.entity
17
17
  }
18
18
 
19
+ // scope: simple wheres à la "[{ ref: ['foo'] }, '=', { val: 'bar' }, 'and', ... ]"
20
+ function where2obj(where, target = null) {
21
+ const data = {}
22
+ for (let i = 0; i < where.length; i++) {
23
+ const whereEl = where[i]
24
+ const colName = whereEl.ref && whereEl.ref[whereEl.ref.length - 1]
25
+ // optional validation if target is passed
26
+ if (target) {
27
+ const colEl = target.elements[colName]
28
+ if (!colEl || !colEl.key) continue
29
+ }
30
+ const opWhere = where[i + 1]
31
+ const valWhere = where[i + 2]
32
+ if (opWhere === '=' && valWhere && 'val' in valWhere) data[colName] = valWhere.val
33
+ }
34
+ return data
35
+ }
36
+
19
37
  module.exports = {
20
38
  getEntityNameFromDeleteCQN,
21
- getEntityNameFromUpdateCQN
39
+ getEntityNameFromUpdateCQN,
40
+ where2obj
22
41
  }