@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,25 +1,37 @@
1
1
  const cds = require('../../cds')
2
2
 
3
3
  const { getAllKeys } = require('../../cds-services/adapter/odata-v4/odata-to-cqn/utils')
4
- const { getOnCond } = require('../../common/utils/generateOnCond')
5
4
 
6
5
  const { getNavigationIfStruct } = require('../../common/utils/structured')
7
6
  const { ensureNoDraftsSuffix, ensureDraftsSuffix, ensureUnlocalized } = require('../../common/utils/draft')
8
7
  const { filterKeys } = require('../../fiori/utils/handler')
8
+ const { isAsteriskColumn } = require('../../common/utils/rewriteAsterisks')
9
9
 
10
10
  // Symbols are used to add extra information in response structure
11
11
  const GET_KEY_VALUE = Symbol.for('sap.cds.getKeyValue')
12
12
  const TO_MANY = Symbol.for('sap.cds.toMany')
13
13
  const TO_ACTIVE = Symbol.for('sap.cds.toActive')
14
-
15
14
  const SKIP_MAPPING = Symbol.for('sap.cds.skipMapping')
16
15
  const IDENTIFIER = Symbol.for('sap.cds.identifier')
17
16
  const IS_ACTIVE = Symbol.for('sap.cds.isActive')
18
17
  const IS_UNION_DRAFT = Symbol.for('sap.cds.isUnionDraft')
18
+
19
19
  const { DRAFT_COLUMNS } = require('../../common/constants/draft')
20
20
 
21
21
  const { getCQNUnionFrom } = require('../../common/utils/union')
22
22
 
23
+ function getCqnCopy(readToOneCQN) {
24
+ const readToOneCQNCopy = JSON.parse(JSON.stringify(readToOneCQN))
25
+ if (readToOneCQN[GET_KEY_VALUE] !== undefined) readToOneCQNCopy[GET_KEY_VALUE] = readToOneCQN[GET_KEY_VALUE]
26
+ if (readToOneCQN[TO_MANY] !== undefined) readToOneCQNCopy[TO_MANY] = readToOneCQN[TO_MANY]
27
+ if (readToOneCQN[TO_ACTIVE] !== undefined) readToOneCQNCopy[TO_ACTIVE] = readToOneCQN[TO_ACTIVE]
28
+ if (readToOneCQN[SKIP_MAPPING] !== undefined) readToOneCQNCopy[SKIP_MAPPING] = readToOneCQN[SKIP_MAPPING]
29
+ if (readToOneCQN[IDENTIFIER] !== undefined) readToOneCQNCopy[IDENTIFIER] = readToOneCQN[IDENTIFIER]
30
+ if (readToOneCQN[IS_ACTIVE] !== undefined) readToOneCQNCopy[IS_ACTIVE] = readToOneCQN[IS_ACTIVE]
31
+ if (readToOneCQN[IS_UNION_DRAFT] !== undefined) readToOneCQNCopy[IS_UNION_DRAFT] = readToOneCQN[IS_UNION_DRAFT]
32
+ return readToOneCQNCopy
33
+ }
34
+
23
35
  class JoinCQNFromExpanded {
24
36
  constructor(cqn, csn, locale) {
25
37
  this._SELECT = Object.assign({}, cqn.SELECT)
@@ -91,16 +103,16 @@ class JoinCQNFromExpanded {
91
103
  * @private
92
104
  */
93
105
  _createJoinCQNFromExpanded(SELECT, toManyTree, defaultLanguage) {
94
- const unionArgs = SELECT.from.args
95
- const isJoinOfTwoUnions = unionArgs && unionArgs.every(a => a.SELECT)
106
+ const joinArgs = SELECT.from.args
107
+ const isJoinOfTwoSelects = joinArgs && joinArgs.every(a => a.SELECT)
96
108
 
97
109
  const unionTableRef = this._getUnionTable(SELECT)
98
110
  const unionTable = unionTableRef && unionTableRef.table
99
111
  const tableAlias = this._getTableAlias(SELECT, toManyTree, unionTable)
100
112
 
101
- const readToOneCQN = this._getReadToOneCQN(SELECT, isJoinOfTwoUnions ? 'filterExpand' : tableAlias)
113
+ const readToOneCQN = this._getReadToOneCQN(SELECT, isJoinOfTwoSelects ? 'filterExpand' : tableAlias)
102
114
 
103
- if (isJoinOfTwoUnions) {
115
+ if (isJoinOfTwoSelects) {
104
116
  // mappings
105
117
  const mappings = this._getMappingObject(toManyTree)
106
118
  const prefix = `${tableAlias}_`
@@ -110,7 +122,7 @@ class JoinCQNFromExpanded {
110
122
  mappings[c.as.replace(prefix, '')] = c.as
111
123
  })
112
124
  // expand to one
113
- const entity = this._csn.definitions[unionArgs[0].SELECT.from.SET.args[1].SELECT.from.ref[0]]
125
+ const entity = this._csn.definitions[joinArgs[0].SELECT.from.SET.args[1].SELECT.from.ref[0]]
114
126
  const givenColumns = readToOneCQN.columns
115
127
  readToOneCQN.columns = []
116
128
  this._expandedToFlat({ entity, givenColumns, readToOneCQN, tableAlias, toManyTree, defaultLanguage })
@@ -128,6 +140,9 @@ class JoinCQNFromExpanded {
128
140
  this._expandedToFlat({ entity, givenColumns, readToOneCQN, tableAlias, toManyTree, defaultLanguage })
129
141
  }
130
142
 
143
+ // brute force hack
144
+ readToOneCQN.columns = readToOneCQN.columns.filter(c => c.as !== 'filterExpand_IsActiveEntity')
145
+
131
146
  // Add at start, so that the deepest level is post processed first
132
147
  this.queries.push({
133
148
  SELECT: readToOneCQN,
@@ -480,6 +495,8 @@ class JoinCQNFromExpanded {
480
495
  const toManyColumns = []
481
496
  const mappings = this._getMappingObject(toManyTree)
482
497
 
498
+ const readToOneCQNCopy = getCqnCopy(readToOneCQN)
499
+
483
500
  for (const column of givenColumns) {
484
501
  let navigation
485
502
  if (column.expand) {
@@ -523,7 +540,16 @@ class JoinCQNFromExpanded {
523
540
  }
524
541
 
525
542
  // only as second step handle expand to many, or else keys might still be unknown
526
- this._toMany({ entity, readToOneCQN, tableAlias, toManyColumns, toManyTree, mappings, defaultLanguage })
543
+ this._toMany({
544
+ entity,
545
+ readToOneCQN,
546
+ tableAlias,
547
+ toManyColumns,
548
+ toManyTree,
549
+ mappings,
550
+ defaultLanguage,
551
+ readToOneCQNCopy
552
+ })
527
553
  }
528
554
 
529
555
  adjustOrderBy(orderBy, mappings, column, tableAlias) {
@@ -822,23 +848,12 @@ class JoinCQNFromExpanded {
822
848
  // No sub select
823
849
  const subSelectColumns = this._getSubSelectColumns(readToOneCQN)
824
850
 
825
- const navigation = getNavigationIfStruct(entity, tableAlias === columns[0] ? columns.slice(1) : columns)
826
- const onConditionOptions = {
827
- associationNames: columns,
828
- csn: this._csn,
829
- aliases: {
830
- select: tableAlias,
831
- join: parentAlias
832
- },
833
- resolveView: false
834
- }
835
-
836
851
  if (subSelectColumns.length === 0) {
837
- return getOnCond(navigation, onConditionOptions)
852
+ return entity._relations[tableAlias === columns[0] ? columns.slice(1) : columns].join(tableAlias, parentAlias)
838
853
  }
839
854
 
840
855
  const aliases = this._getAliases(subSelectColumns)
841
- const on = getOnCond(navigation, onConditionOptions)
856
+ const on = entity._relations[tableAlias === columns[0] ? columns.slice(1) : columns].join(tableAlias, parentAlias)
842
857
 
843
858
  for (const element of on) {
844
859
  if (element.ref && aliases[element.ref[0]] && aliases[element.ref[0]][element.ref[1]]) {
@@ -968,7 +983,16 @@ class JoinCQNFromExpanded {
968
983
  return DRAFT_COLUMNS.includes(ref[0])
969
984
  }
970
985
 
971
- _toMany({ entity, readToOneCQN, tableAlias, toManyColumns, toManyTree, mappings, defaultLanguage }) {
986
+ _toMany({
987
+ entity,
988
+ readToOneCQN,
989
+ tableAlias,
990
+ toManyColumns,
991
+ toManyTree,
992
+ mappings,
993
+ defaultLanguage,
994
+ readToOneCQNCopy
995
+ }) {
972
996
  if (toManyColumns.length === 0) {
973
997
  return
974
998
  }
@@ -979,7 +1003,7 @@ class JoinCQNFromExpanded {
979
1003
  const select = this._buildExpandedCQN({
980
1004
  column,
981
1005
  entity,
982
- readToOneCQN,
1006
+ readToOneCQN: readToOneCQNCopy,
983
1007
  toManyTree,
984
1008
  mappings,
985
1009
  parentAlias,
@@ -1069,15 +1093,7 @@ class JoinCQNFromExpanded {
1069
1093
  !this._csn.definitions[colTarget]._isDraftEnabled
1070
1094
  const ref = this._getJoinRef(entity.elements, colRef[0], expandActive, defaultLanguageThis)
1071
1095
  const tableAlias = this._createAlias(toManyTree.concat(colRef).join(':'))
1072
- const onConditionOptions = {
1073
- associationNames: colRef[0],
1074
- csn: this._csn,
1075
- aliases: {
1076
- select: tableAlias,
1077
- join: 'filterExpand'
1078
- }
1079
- }
1080
- const on = getOnCond(element, onConditionOptions)
1096
+ const on = entity._relations[colRef[0]].join(tableAlias, 'filterExpand')
1081
1097
  const filterExpand = this._getFilterExpandCQN(readToOneCQN, on, parentAlias, entity.keys)
1082
1098
  const expandedEntity = this._csn.definitions[colTarget]
1083
1099
  const joinColumns = this._getJoinColumnsFromOnAddToMapping(mappings[colRef[0]], parentAlias, on, entity)
@@ -1529,8 +1545,13 @@ class JoinCQNFromExpanded {
1529
1545
  }
1530
1546
 
1531
1547
  _isNotIncludedIn(columns) {
1548
+ if (columns.some(column => isAsteriskColumn(column))) return _ => false
1532
1549
  return entry =>
1533
- !columns.some(column => (column.ref && column.ref[1] === entry) || ('val' in column && column.as === entry))
1550
+ !columns.some(
1551
+ column =>
1552
+ (typeof column === 'object' && column.ref && column.ref[1] === entry) ||
1553
+ ('val' in column && column.as === entry)
1554
+ )
1534
1555
  }
1535
1556
 
1536
1557
  /**
@@ -95,7 +95,7 @@ class RawToExpanded {
95
95
 
96
96
  // the expanded items may include the actives of the deleted drafts -> filter out
97
97
  if (rootIsActiveEntity !== null) {
98
- if (mapping[TO_ACTIVE]) expandedItems = expandedItems.filter(ele => ele.IsActiveEntity === true)
98
+ if (mapping[TO_ACTIVE]) expandedItems = expandedItems.filter(ele => ele.IsActiveEntity !== false)
99
99
  else expandedItems = expandedItems.filter(ele => ele.IsActiveEntity === rootIsActiveEntity)
100
100
  }
101
101
 
@@ -119,6 +119,7 @@ class RawToExpanded {
119
119
  row[key] = parsed || null
120
120
  } else if (rootIsActiveEntity !== null) {
121
121
  if (mapping[TO_ACTIVE]) row[key] = parsed && parsed.IsActiveEntity !== false ? parsed : null
122
+ else if (rootIsActiveEntity) row[key] = parsed && parsed.IsActiveEntity !== false ? parsed : null
122
123
  else row[key] = parsed && parsed.IsActiveEntity === rootIsActiveEntity ? parsed : null
123
124
  }
124
125
  } else {
@@ -1,28 +1,13 @@
1
- // REVISIT: use templating mechanism (resp. results.metadata, once available) to make more efficient
2
-
3
1
  const { getEntityFromCQN } = require('../../common/utils/entityFromCqn')
2
+ const getTemplate = require('../../common/utils/template')
3
+ const templateProcessor = require('../../common/utils/templateProcessor')
4
4
 
5
- const _toArray = (result, elements) => {
6
- // REVISIT: This is a very expensive loop in loop...
7
- // and 100% overhead the results don't contain arrayed elements,
8
- // and 100% of all currently existing stakeholder projects don't.
9
- // but that's not that easy to fix -> see comment about results.metadata below
10
- for (const row of result) {
11
- for (const column in row) {
12
- if (elements[column] === undefined || row[column] === undefined) continue
5
+ const _pick = element => {
6
+ if (element.kind === 'element' && element.items) return 'arrayed'
7
+ }
13
8
 
14
- // .items marks arrayed element
15
- if (elements[column].items) {
16
- row[column] = JSON.parse(row[column])
17
- } else if (elements[column].is2many) {
18
- _toArray(row[column], elements[column]._target.elements)
19
- } else if (elements[column].is2one) {
20
- _toArray([row[column]], elements[column]._target.elements)
21
- } else if (elements[column].elements) {
22
- _toArray([row[column]], elements[column].elements)
23
- }
24
- }
25
- }
9
+ const _processFn = ({ row, key, plain }) => {
10
+ if (plain === 'arrayed' && row && row[key]) row[key] = JSON.parse(row[key])
26
11
  }
27
12
 
28
13
  /**
@@ -35,12 +20,12 @@ const _toArray = (result, elements) => {
35
20
  module.exports = function (result, req) {
36
21
  if (!this.model) return
37
22
 
38
- if (!Array.isArray(result)) result = [result]
39
-
40
- // REVISIT: We need results.metadata to make that more efficient
41
- // results.metadata ~= cds.infer(req.query).metadata
42
- // REVISIT: No entity for sets/unions outside of common draft scenarios
43
23
  const entity = getEntityFromCQN(req, this)
44
24
  if (!entity) return
45
- if (entity) _toArray(result, entity.elements)
25
+
26
+ const template = getTemplate('db-arrayed', this, entity, { pick: _pick })
27
+ if (template.elements.size === 0) return
28
+
29
+ for (const row of Array.isArray(result) ? result : [result])
30
+ templateProcessor({ processFn: _processFn, row, template })
46
31
  }
@@ -31,6 +31,7 @@ module.exports = async function (req) {
31
31
  // If entry is available, reject event
32
32
  // REVISIT: db specifics
33
33
  if (err.message.match(/unique constraint/i)) {
34
+ err.originalMessage = err.message
34
35
  err.message = 'ENTITY_ALREADY_EXISTS'
35
36
  err.code = 400
36
37
  }
@@ -13,8 +13,8 @@ const cds = require('../../cds')
13
13
 
14
14
  const normalizeTimeData = require('../utils/normalizeTimeData')
15
15
 
16
- const enrichDataWithKeysFromWhere = require('../../common/utils/enrichWithKeysFromWhere')
17
- const { foreignKeyPropagations, propagateForeignKeys } = require('../../common/utils/foreignKeyPropagations')
16
+ const { enrichDataWithKeysFromWhere } = require('../../common/utils/keys')
17
+ const { propagateForeignKeys } = require('../../common/utils/propagateForeignKeys')
18
18
  const getTemplate = require('../../common/utils/template')
19
19
  const templateProcessor = require('../../common/utils/templateProcessor')
20
20
 
@@ -25,13 +25,13 @@ const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
25
25
  const _isManaged = (category, event) =>
26
26
  (category === '@cds.on.insert' && event === 'CREATE') || (category === '@cds.on.update' && event === 'UPDATE')
27
27
 
28
- const _processComplexCategory = ({ row, key, val, category, req }) => {
28
+ const _processComplexCategory = ({ row, key, val, category, req, element }) => {
29
29
  const categoryArgs = category.args
30
30
  category = category.category
31
31
 
32
32
  // propagate keys
33
33
  if (category === 'propagateForeignKeys') {
34
- propagateForeignKeys(key, row, categoryArgs.foreignKeyPropagations, { onlyWriteCompositionEffective: true })
34
+ propagateForeignKeys(key, row, element._foreignKeys, element._isCompositionEffective)
35
35
  return
36
36
  }
37
37
 
@@ -63,7 +63,7 @@ const _processComplexCategory = ({ row, key, val, category, req }) => {
63
63
  const _processCategory = ({ category, row, key, element, val, req }) => {
64
64
  // use args only inside this if (sonar type error warning)
65
65
  if (typeof category === 'object') {
66
- _processComplexCategory({ category, row, key, val, req })
66
+ _processComplexCategory({ category, row, key, val, req, element })
67
67
  return
68
68
  }
69
69
 
@@ -136,12 +136,8 @@ const _pick = element => {
136
136
  categories.push('associationEffective')
137
137
  }
138
138
 
139
- if (element.isAssociation) {
140
- const _foreignKeyPropagations = foreignKeyPropagations(element)
141
-
142
- if (_foreignKeyPropagations) {
143
- categories.push({ category: 'propagateForeignKeys', args: { foreignKeyPropagations: _foreignKeyPropagations } })
144
- }
139
+ if (element.isAssociation && element._foreignKeys.length) {
140
+ categories.push({ category: 'propagateForeignKeys' })
145
141
  }
146
142
 
147
143
  // generate uuid
@@ -1,7 +1,7 @@
1
1
  const cds = require('../../cds')
2
2
  const { SELECT } = cds.ql
3
3
 
4
- const cqn2cqn4sql = require('../../common/utils/cqn2cqn4sql')
4
+ const { cqn2cqn4sql } = require('../../common/utils/cqn2cqn4sql')
5
5
  const { getDependents } = require('../../common/utils/csn')
6
6
 
7
7
  const CRUD = {
@@ -54,7 +54,7 @@ async function beforeDelete(req) {
54
54
  select = select.where(req.query.DELETE.where)
55
55
  }
56
56
 
57
- select = cqn2cqn4sql(select, this.model)
57
+ select = cqn2cqn4sql(select, this.model, { service: this })
58
58
 
59
59
  req._beforeDeleteData = await this._read(this.model, this.dbc, select, req.context || req)
60
60
  }
@@ -1,6 +1,5 @@
1
- const cqn2cqn4sql = require('../../common/utils/cqn2cqn4sql')
1
+ const { cqn2cqn4sql } = require('../../common/utils/cqn2cqn4sql')
2
2
  const generateAliases = require('../utils/generateAliases')
3
- const rewriteAsterisk = require('../../common/utils/rewriteAsterisk')
4
3
  const { restoreLink } = require('../../common/utils/resolveView')
5
4
 
6
5
  const _isLinked = req => {
@@ -21,13 +20,11 @@ function handler(req) {
21
20
  const streaming = req.query._streaming
22
21
  const validationQuery = req.query._validationQuery
23
22
 
24
- rewriteAsterisk(req)
25
-
26
23
  // for restore link to req.data
27
24
  const linked = _isLinked(req)
28
25
 
29
26
  // convert to sql cqn
30
- req.query = cqn2cqn4sql(req.query, this.model)
27
+ req.query = cqn2cqn4sql(req.query, this.model, { service: this })
31
28
 
32
29
  // REVISIT: should not be necessary
33
30
  // restore link to req.data
@@ -75,6 +75,7 @@ module.exports = async function (req) {
75
75
  } catch (err) {
76
76
  // REVISIT: db specifics
77
77
  if (err.message.match(/unique constraint/i)) {
78
+ err.originalMessage = err.message
78
79
  err.message = 'UNIQUE_CONSTRAINT_VIOLATION'
79
80
  err.code = 400
80
81
  }
@@ -10,8 +10,8 @@ function _arrayWithCount(a, count) {
10
10
  }
11
11
 
12
12
  function _createCountQuery(query) {
13
- const _query = JSON.parse(JSON.stringify(query)) // REVISIT: Use query.clone() insteadconst _query = JSON.parse(JSON.stringify(query))
14
- _query.SELECT.columns = [{ func: 'count', args: [{ val: 1 }], as: '_counted_' }]
13
+ const _query = JSON.parse(JSON.stringify(query)) // REVISIT: Use query.clone() instead
14
+ _query.SELECT.columns = [{ func: 'count', args: [{ val: 1 }], as: '$count' }]
15
15
  delete _query.SELECT.groupBy
16
16
  delete _query.SELECT.limit
17
17
  delete _query.SELECT.orderBy // not necessary to keep that
@@ -25,6 +25,11 @@ function _createCountQuery(query) {
25
25
  return _query
26
26
  }
27
27
 
28
+ const countValue = countResult => {
29
+ if (countResult._counted_ != null) return countResult._counted_
30
+ if (countResult.$count != null) return countResult.$count
31
+ }
32
+
28
33
  const read = (executeSelectCQN, executeStreamCQN) => (model, dbc, query, req) => {
29
34
  const { user, locale, timestamp } = req
30
35
  const isoTs = timestampToISO(timestamp)
@@ -45,11 +50,11 @@ const read = (executeSelectCQN, executeStreamCQN) => (model, dbc, query, req) =>
45
50
  const countResultPromise = executeSelectCQN(model, dbc, countQuery, user, locale, isoTs)
46
51
  if (query.SELECT.limit.rows && query.SELECT.limit.rows.val === 0) {
47
52
  // We don't need to perform our result query
48
- return countResultPromise.then(countResult => _arrayWithCount([], countResult[0]._counted_))
53
+ return countResultPromise.then(countResult => _arrayWithCount([], countValue(countResult[0])))
49
54
  } else {
50
55
  const resultPromise = executeSelectCQN(model, dbc, query, user, locale, isoTs)
51
56
  return Promise.all([countResultPromise, resultPromise]).then(([countResult, result]) =>
52
- _arrayWithCount(result, countResult[0]._counted_)
57
+ _arrayWithCount(result, countValue(countResult[0]))
53
58
  )
54
59
  }
55
60
  } else {
@@ -390,8 +390,13 @@ class SelectBuilder extends BaseBuilder {
390
390
  }
391
391
  }
392
392
  }
393
-
394
- const { sql, values } = new this.ReferenceBuilder(element, this._options, this._csn).build()
393
+ const { sql, values } = new this.ReferenceBuilder(
394
+ Object.assign({}, element, {
395
+ sort: Object.prototype.hasOwnProperty.call(element, 'sort') ? element.sort : true
396
+ }),
397
+ this._options,
398
+ this._csn
399
+ ).build()
395
400
  sqls.push(sql)
396
401
  this._outputObj.values.push(...values)
397
402
  }
@@ -1,4 +1,5 @@
1
1
  const getColumns = require('../utils/columns')
2
+ // REVISIT: Remove support for @odata.on... with @sap/cds ^6
2
3
  const ANNOTATIONS = ['@cds.on.insert', '@odata.on.insert', '@cds.on.update', '@odata.on.update']
3
4
  const { ensureNoDraftsSuffix } = require('../../common/utils/draft')
4
5
 
@@ -3,17 +3,6 @@ const resolveStructured = require('../../common/utils/resolveStructured')
3
3
 
4
4
  const { DRAFT_COLUMNS } = require('../../common/constants/draft')
5
5
 
6
- const _filterAssociationAndComposition = (entity, columnName) => {
7
- return entity.elements[columnName]
8
- ? entity.elements[columnName].is2one !== true && entity.elements[columnName].is2many !== true
9
- : true
10
- }
11
- const _filterDraft = (entity, columnName) => {
12
- return DRAFT_COLUMNS.includes(columnName) !== true && _filterAssociationAndComposition(entity, columnName)
13
- }
14
-
15
- const _mapNameToValue = (entity, array) => array.map(key => entity.elements[key] || { name: key })
16
-
17
6
  /**
18
7
  * This method gets all columns for an entity.
19
8
  * It includes the generated foreign keys from managed associations, structured elements and complex and custom types.
@@ -22,42 +11,24 @@ const _mapNameToValue = (entity, array) => array.map(key => entity.elements[key]
22
11
  * @param entity - the csn entity
23
12
  * @returns {Array} - array of columns
24
13
  */
25
- const getColumns = entity => {
14
+ const getColumns = (entity, { db, onlyKeys } = { db: true, onlyKeys: false }) => {
26
15
  // REVISIT is this correct or just a problem that occurs because of new structure we do not deal with yet?
27
- if (!entity.elements) return []
28
-
29
- let columnNames
16
+ if (!(entity && entity.elements)) return []
17
+ const columnNames = []
30
18
  // REVISIT!!!
31
- const proto = Object.getPrototypeOf(entity.elements)
32
- if (proto) columnNames = Object.keys(proto)
33
- else columnNames = Object.keys(entity.elements)
34
-
35
- if (cds.env.effective.odata.structs) {
36
- const toBeDeleted = []
37
- for (const column of columnNames) {
38
- const element = entity.elements[column]
39
- if (element && element.elements) {
40
- toBeDeleted.push(column)
41
- columnNames.push(
42
- ...resolveStructured({ structName: element.name, structProperties: [] }, element.elements, false)
43
- )
44
- }
19
+ const elements = Object.getPrototypeOf(entity.elements) || entity.elements
20
+ for (const elementName in elements) {
21
+ const element = elements[elementName]
22
+ if (onlyKeys && !element.key) continue
23
+ if (element.isAssociation) continue
24
+ if (db && entity._isDraftEnabled && DRAFT_COLUMNS.includes(elementName)) continue
25
+ if (cds.env.effective.odata.structs && element.elements) {
26
+ columnNames.push(...resolveStructured({ structName: elementName, structProperties: [] }, element.elements, false))
27
+ continue
45
28
  }
46
-
47
- columnNames = columnNames.filter(col => !toBeDeleted.includes(col))
29
+ columnNames.push(elementName)
48
30
  }
49
-
50
- if (entity && entity._isDraftEnabled) {
51
- return _mapNameToValue(
52
- entity,
53
- columnNames.filter(key => _filterDraft(entity, key))
54
- )
55
- }
56
-
57
- return _mapNameToValue(
58
- entity,
59
- columnNames.filter(key => _filterAssociationAndComposition(entity, key))
60
- )
31
+ return columnNames.map(name => elements[name] || { name })
61
32
  }
62
33
 
63
34
  module.exports = getColumns
@@ -93,6 +93,7 @@ const _draftCompositionTree = async (service, req) => {
93
93
  }
94
94
 
95
95
  if (results[1].length === 1) {
96
+ _removeIsActiveEntityRecursively(results[1])
96
97
  activeData = results[1][0]
97
98
  }
98
99
 
@@ -175,8 +176,8 @@ const _handler = async function (req) {
175
176
  return result
176
177
  }
177
178
 
178
- module.exports = function () {
179
+ module.exports = cds.service.impl(function () {
179
180
  for (const entity of Object.values(this.entities).filter(e => e._isDraftEnabled)) {
180
181
  this.on('draftActivate', entity, _handler)
181
182
  }
182
- }
183
+ })
@@ -186,7 +186,7 @@ const _deleteCancel = async function (req) {
186
186
  await _addDraftDataFromExistingDraft(req, this)
187
187
  }
188
188
 
189
- module.exports = function () {
189
+ module.exports = cds.service.impl(function () {
190
190
  _new._initial = true
191
191
  _patchUpdate._initial = true
192
192
  _deleteCancel._initial = true
@@ -196,4 +196,4 @@ module.exports = function () {
196
196
  this.before(['PATCH', 'UPDATE'], entity, _patchUpdate)
197
197
  this.before(['DELETE', 'CANCEL'], entity, _deleteCancel)
198
198
  }
199
- }
199
+ })
@@ -1,3 +1,4 @@
1
+ const cds = require('../../cds')
1
2
  const { deleteDraft } = require('../utils/delete')
2
3
 
3
4
  /**
@@ -11,8 +12,8 @@ const _handler = function (req) {
11
12
  return deleteDraft(req, this)
12
13
  }
13
14
 
14
- module.exports = function () {
15
+ module.exports = cds.service.impl(function () {
15
16
  for (const entity of Object.values(this.entities).filter(e => e._isDraftEnabled)) {
16
17
  this.on('CANCEL', entity, _handler)
17
18
  }
18
- }
19
+ })
@@ -1,3 +1,4 @@
1
+ const cds = require('../../cds')
1
2
  const { deleteDraft } = require('../utils/delete')
2
3
 
3
4
  /**
@@ -13,8 +14,8 @@ const _handler = function (req) {
13
14
  return deleteDraft(req, this, true)
14
15
  }
15
16
 
16
- module.exports = function () {
17
+ module.exports = cds.service.impl(function () {
17
18
  for (const entity of Object.values(this.entities).filter(e => e._isDraftEnabled)) {
18
19
  this.on('DELETE', entity, _handler)
19
20
  }
20
- }
21
+ })
@@ -106,7 +106,7 @@ const _handler = async function (req) {
106
106
  // Only allows one active entity to be processed at a time, locking out other
107
107
  // users who need to edit the same record simultaneously.
108
108
  // .forUpdate(): lock the record, a wait of 0 is equivalent to no wait
109
- const lockRecordCQN = SELECT.from(lockTargetEntity).where(lockWhere).forUpdate({ wait: 0 })
109
+ const lockRecordCQN = SELECT.from(lockTargetEntity, [1]).where(lockWhere).forUpdate({ wait: 0 })
110
110
 
111
111
  const columnNames = getColumns(req.target, { onlyNames: true, filterVirtual: true })
112
112
  const rootCQN = SELECT.from(req.target, columnNames).where(rootWhere)
@@ -172,8 +172,8 @@ const _handler = async function (req) {
172
172
  return Object.assign({}, results[0][0], { HasDraftEntity: false, HasActiveEntity: true, IsActiveEntity: false })
173
173
  }
174
174
 
175
- module.exports = function () {
175
+ module.exports = cds.service.impl(function () {
176
176
  for (const entity of Object.values(this.entities).filter(e => e._isDraftEnabled)) {
177
177
  this.on('EDIT', entity, _handler)
178
178
  }
179
- }
179
+ })
@@ -96,8 +96,8 @@ const _handler = async function (req, next) {
96
96
  return result[0]
97
97
  }
98
98
 
99
- module.exports = function () {
99
+ module.exports = cds.service.impl(function () {
100
100
  for (const entity of Object.values(this.entities).filter(e => e._isDraftEnabled)) {
101
101
  this.on('NEW', entity, _handler)
102
102
  }
103
- }
103
+ })
@@ -91,8 +91,8 @@ const _handler = async function (req) {
91
91
  return result[0]
92
92
  }
93
93
 
94
- module.exports = function () {
94
+ module.exports = cds.service.impl(function () {
95
95
  for (const entity of Object.values(this.entities).filter(e => e._isDraftEnabled)) {
96
96
  this.on('PATCH', entity, _handler)
97
97
  }
98
- }
98
+ })
@@ -50,8 +50,8 @@ const _handler = async function (req) {
50
50
  return result[0]
51
51
  }
52
52
 
53
- module.exports = function () {
53
+ module.exports = cds.service.impl(function () {
54
54
  for (const entity of Object.values(this.entities).filter(e => e._isDraftEnabled)) {
55
55
  this.on('draftPrepare', entity, _handler)
56
56
  }
57
- }
57
+ })