@sap/cds 6.3.1 → 6.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/apis/cds.d.ts +1 -1
  3. package/apis/core.d.ts +118 -90
  4. package/apis/cqn.d.ts +11 -2
  5. package/apis/internal/inference.d.ts +7 -2
  6. package/apis/ql.d.ts +45 -11
  7. package/apis/serve.d.ts +8 -1
  8. package/apis/services.d.ts +303 -305
  9. package/bin/build/buildTaskEngine.js +28 -36
  10. package/bin/build/buildTaskFactory.js +32 -81
  11. package/bin/build/buildTaskHandler.js +3 -2
  12. package/bin/build/buildTaskProvider.js +2 -2
  13. package/bin/build/buildTaskProviderFactory.js +5 -14
  14. package/bin/build/constants.js +0 -1
  15. package/bin/build/provider/buildTaskHandlerEdmx.js +7 -6
  16. package/bin/build/provider/buildTaskHandlerFeatureToggles.js +6 -5
  17. package/bin/build/provider/buildTaskHandlerInternal.js +9 -30
  18. package/bin/build/provider/buildTaskProviderInternal.js +70 -58
  19. package/bin/build/provider/fiori/index.js +6 -5
  20. package/bin/build/provider/hana/2migration.js +20 -3
  21. package/bin/build/provider/hana/2tabledata.js +1 -0
  22. package/bin/build/provider/hana/index.js +40 -17
  23. package/bin/build/provider/java/index.js +10 -10
  24. package/bin/build/provider/mtx/index.js +25 -16
  25. package/bin/build/provider/mtx/resourcesTarBuilder.js +22 -27
  26. package/bin/build/provider/mtx-extension/index.js +3 -2
  27. package/bin/build/provider/mtx-sidecar/index.js +16 -15
  28. package/bin/build/provider/nodejs/index.js +14 -56
  29. package/bin/build/util.js +56 -16
  30. package/bin/deploy/to-hana/cfUtil.js +4 -1
  31. package/bin/deploy/to-hana/gitUtil.js +1 -1
  32. package/bin/deploy/to-hana/hana.js +45 -38
  33. package/bin/deploy/to-hana/hdiDeployUtil.js +8 -9
  34. package/bin/deploy/to-hana/mtaUtil.js +13 -14
  35. package/bin/mtx/in-cds.js +3 -1
  36. package/bin/serve.js +1 -1
  37. package/bin/version.js +2 -1
  38. package/lib/compile/cds-compile.js +1 -0
  39. package/lib/compile/cdsc.js +1 -0
  40. package/lib/compile/etc/_localized.js +2 -2
  41. package/lib/compile/for/lean_drafts.js +83 -0
  42. package/lib/compile/for/nodejs.js +1 -0
  43. package/lib/compile/minify.js +2 -1
  44. package/lib/compile/parse.js +2 -1
  45. package/lib/compile/to/gql.js +1 -1
  46. package/lib/compile/to/sql.js +11 -1
  47. package/lib/core/entities.js +1 -1
  48. package/lib/core/index.js +8 -9
  49. package/lib/core/infer.js +1 -0
  50. package/lib/dbs/cds-deploy.js +97 -41
  51. package/lib/env/cds-env.js +9 -10
  52. package/lib/env/cds-requires.js +8 -2
  53. package/lib/env/defaults.js +0 -4
  54. package/lib/env/schemas/cds-rc.json +38 -0
  55. package/lib/ql/SELECT.js +10 -4
  56. package/lib/srv/bindings.js +1 -1
  57. package/lib/srv/factory.js +1 -1
  58. package/lib/srv/protocols/index.js +3 -1
  59. package/lib/srv/srv-methods.js +1 -1
  60. package/lib/utils/cds-utils.js +11 -0
  61. package/lib/utils/inflect.js +13 -12
  62. package/lib/utils/tar.js +53 -10
  63. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +2 -2
  64. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -1
  65. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -1
  66. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +1 -1
  67. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +1 -15
  68. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +1 -1
  69. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +1 -1
  70. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/errors/UriSyntaxError.js +1 -1
  71. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +6 -1
  72. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/utils/BufferedWriter.js +1 -1
  73. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/ConditionalRequestValidator.js +0 -12
  74. package/libx/_runtime/cds-services/adapter/odata-v4/utils/oDataConfiguration.js +1 -7
  75. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +4 -0
  76. package/libx/_runtime/cds-services/services/Service.js +23 -1
  77. package/libx/_runtime/cds-services/util/assert.js +0 -41
  78. package/libx/_runtime/common/composition/data.js +5 -1
  79. package/libx/_runtime/common/generic/auth/utils.js +3 -3
  80. package/libx/_runtime/common/generic/input.js +4 -24
  81. package/libx/_runtime/common/generic/paging.js +3 -3
  82. package/libx/_runtime/common/utils/csn.js +21 -15
  83. package/libx/_runtime/common/utils/draft.js +2 -1
  84. package/libx/_runtime/common/utils/resolveView.js +25 -4
  85. package/libx/_runtime/common/utils/rewriteAsterisks.js +3 -1
  86. package/libx/_runtime/common/utils/rowUUIDGenerator.js +21 -0
  87. package/libx/_runtime/common/utils/templateProcessor.js +12 -15
  88. package/libx/_runtime/common/utils/templateProcessorPathSerializer.js +23 -0
  89. package/libx/_runtime/db/expand/expandCQNToJoin.js +29 -12
  90. package/libx/_runtime/db/generic/input.js +7 -13
  91. package/libx/_runtime/db/sql-builder/UpsertBuilder.js +47 -0
  92. package/libx/_runtime/db/sql-builder/index.js +2 -0
  93. package/libx/_runtime/db/sql-builder/sqlFactory.js +9 -0
  94. package/libx/_runtime/db/utils/columns.js +4 -2
  95. package/libx/_runtime/fiori/generic/read.js +1 -12
  96. package/libx/_runtime/fiori/lean-draft.js +657 -0
  97. package/libx/_runtime/fiori/utils/handler.js +1 -1
  98. package/libx/_runtime/hana/pool.js +16 -1
  99. package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +2 -1
  100. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +1 -1
  101. package/libx/_runtime/messaging/enterprise-messaging.js +2 -3
  102. package/libx/_runtime/messaging/outbox/utils.js +109 -70
  103. package/libx/_runtime/messaging/service.js +16 -7
  104. package/libx/_runtime/remote/Service.js +15 -2
  105. package/libx/_runtime/remote/utils/client.js +41 -11
  106. package/libx/_runtime/sqlite/Service.js +3 -0
  107. package/libx/_runtime/sqlite/convertDraftAdminPathExpression.js +56 -0
  108. package/libx/_runtime/sqlite/customBuilder/CustomUpsertBuilder.js +59 -0
  109. package/libx/_runtime/sqlite/customBuilder/index.js +5 -0
  110. package/libx/_runtime/sqlite/execute.js +1 -1
  111. package/libx/_runtime/types/api.js +2 -2
  112. package/libx/rest/RestAdapter.js +15 -13
  113. package/package.json +1 -1
  114. package/server.js +1 -0
@@ -138,6 +138,7 @@ class JoinCQNFromExpanded {
138
138
  this._addImplicitOrderBy(readToOneCQN, entity, tableAlias)
139
139
  const givenColumns = readToOneCQN.columns
140
140
  readToOneCQN.columns = []
141
+ if (entity['@cds.localized'] === false) defaultLanguage = true
141
142
  this._expandedToFlat({ entity, givenColumns, readToOneCQN, tableAlias, toManyTree, defaultLanguage })
142
143
  } else {
143
144
  const table = unionTable || this._getRef(SELECT).table
@@ -150,6 +151,7 @@ class JoinCQNFromExpanded {
150
151
 
151
152
  const givenColumns = readToOneCQN.columns
152
153
  readToOneCQN.columns = []
154
+ if (entity['@cds.localized'] === false) defaultLanguage = true
153
155
  this._expandedToFlat({ entity, givenColumns, readToOneCQN, tableAlias, toManyTree, defaultLanguage })
154
156
  }
155
157
 
@@ -491,6 +493,7 @@ class JoinCQNFromExpanded {
491
493
  * @param {boolean} arg.defaultLanguage - Use default language for localized fields
492
494
  * @private
493
495
  */
496
+ // eslint-disable-next-line complexity
494
497
  _expandedToFlat({ entity, givenColumns, readToOneCQN, tableAlias, toManyTree, defaultLanguage }) {
495
498
  const toManyColumns = []
496
499
  const mappings = this._getMappingObject(toManyTree)
@@ -518,7 +521,8 @@ class JoinCQNFromExpanded {
518
521
  entity._isDraftEnabled &&
519
522
  navTarget._isAssociationStrict &&
520
523
  !navTarget['@odata.draft.enclosed'] &&
521
- navTarget.name !== 'DraftAdministrativeData'
524
+ navTarget.name !== 'DraftAdministrativeData' &&
525
+ !entity.elements[navProp]._isCompositionBacklink
522
526
  ) {
523
527
  mappings[navProp] = { [TO_ACTIVE]: true }
524
528
  }
@@ -660,7 +664,10 @@ class JoinCQNFromExpanded {
660
664
  const activeTableRequired =
661
665
  readToOneCQN[IS_UNION_DRAFT] || // > REVISIT: blocks expanding comp2one
662
666
  readToOneCQN[IS_ACTIVE] ||
663
- (element && element._isAssociationStrict && !element['@odata.draft.enclosed']) ||
667
+ (element &&
668
+ element._isAssociationStrict &&
669
+ !element['@odata.draft.enclosed'] &&
670
+ !element._isCompositionBacklink) ||
664
671
  !this._csn.definitions[target]._isDraftEnabled
665
672
 
666
673
  const colTarget = target && ensureUnlocalized(target)
@@ -688,7 +695,7 @@ class JoinCQNFromExpanded {
688
695
 
689
696
  const expandedEntity = this._getEntityForTable(target)
690
697
  if (readToOneCQN[IS_UNION_DRAFT] && expandedEntity.drafts) {
691
- const cols = column.expand.filter(c => !c.expand && !(c.ref[0] in DRAFT_COLUMNS_MAP)).map(c => c.ref[0])
698
+ const cols = column.expand.filter(c => c.ref && !c.expand && !(c.ref[0] in DRAFT_COLUMNS_MAP)).map(c => c.ref[0])
692
699
  const ks = Object.keys(expandedEntity.keys).filter(
693
700
  c => !expandedEntity.keys[c].isAssociation && !(c in DRAFT_COLUMNS_MAP)
694
701
  )
@@ -968,6 +975,23 @@ class JoinCQNFromExpanded {
968
975
  return column
969
976
  }
970
977
 
978
+ if (Array.isArray(column.xpr)) {
979
+ return this._buildNewAliasColumn(
980
+ {
981
+ ...column,
982
+ xpr: column.xpr.map(x => {
983
+ if (x.ref) {
984
+ const res = this._buildNewAliasColumn(x, entity, tableAlias, mappings)
985
+ delete res.as
986
+ return res
987
+ } else return x
988
+ })
989
+ },
990
+ entity,
991
+ tableAlias,
992
+ mappings
993
+ )
994
+ }
971
995
  return this._buildNewAliasColumn(column, entity, tableAlias, mappings)
972
996
  }
973
997
 
@@ -1146,7 +1170,8 @@ class JoinCQNFromExpanded {
1146
1170
  readToOneCQN[IS_ACTIVE] ||
1147
1171
  (element._isAssociationStrict && !element['@odata.draft.enclosed']) ||
1148
1172
  !this._csn.definitions[colTarget]._isDraftEnabled
1149
- const ref = this._getJoinRef(entity.elements, colRef[0], expandActive, defaultLanguageThis)
1173
+
1174
+ const ref = this._refFromRefByExpand(column.ref[0], colTarget, defaultLanguageThis, expandActive)
1150
1175
  const tableAlias = this._createAlias(toManyTree.concat(colRef).join(':'))
1151
1176
  const on = entity._relations[colRef[0]].join(tableAlias, 'filterExpand')
1152
1177
  const filterExpand = this._getFilterExpandCQN(readToOneCQN, on, parentAlias, entity.keys)
@@ -1239,14 +1264,6 @@ class JoinCQNFromExpanded {
1239
1264
  return cqn
1240
1265
  }
1241
1266
 
1242
- _getJoinRef(elements, column, isActive, defaultLanguage) {
1243
- const assoc = elements[column]
1244
- if (typeof isActive !== 'boolean' || isActive || !assoc.isComposition) {
1245
- return defaultLanguage ? ensureUnlocalized(assoc.target) : assoc.target
1246
- }
1247
- return assoc.target + '_drafts'
1248
- }
1249
-
1250
1267
  _isPathExpressionToOne(ref, entity) {
1251
1268
  const ref0 = ref[0]
1252
1269
  const el = entity.elements[ref0]
@@ -18,8 +18,6 @@ const propagateForeignKeys = require('../../common/utils/propagateForeignKeys')
18
18
  const getTemplate = require('../../common/utils/template')
19
19
  const templateProcessor = require('../../common/utils/templateProcessor')
20
20
 
21
- const { checkIfAssocDeep } = require('../../cds-services/util/assert')
22
-
23
21
  const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
24
22
 
25
23
  const _isManaged = (category, event) =>
@@ -85,7 +83,12 @@ const _processCategory = (req, category, { row, key, element }) => {
85
83
  if (k.isAssociation || k.name === 'IsActiveEntity') continue
86
84
 
87
85
  if (!k.isUUID && !(k.name in val)) {
88
- req.error(400, 'ASSERT_NOT_NULL', key + '[' + k + ']', [key + '[' + k + ']'])
86
+ req.error({
87
+ code: 'MUST_NOT_BE_NULL',
88
+ message: 'Value is required',
89
+ target: key + '[' + k + ']',
90
+ args: [key + '[' + k + ']']
91
+ })
89
92
  return
90
93
  }
91
94
  }
@@ -93,7 +96,7 @@ const _processCategory = (req, category, { row, key, element }) => {
93
96
 
94
97
  // not null without default (for better error message)
95
98
  if (category === '!default' && val == null && req.event === 'CREATE') {
96
- req.error(400, 'ASSERT_NOT_NULL', key, [key])
99
+ req.error({ code: 'MUST_NOT_BE_NULL', message: 'Value is required', target: key, args: [key] })
97
100
  return
98
101
  }
99
102
 
@@ -101,11 +104,6 @@ const _processCategory = (req, category, { row, key, element }) => {
101
104
  if (category === 'uuid' && !val && req.event === 'CREATE') {
102
105
  row[key] = cds.utils.uuid()
103
106
  }
104
-
105
- // check for forbidden deep operations for association
106
- if (category === 'associationEffective' && (req.event === 'CREATE' || req.event === 'UPDATE')) {
107
- checkIfAssocDeep(element, val, req)
108
- }
109
107
  }
110
108
 
111
109
  const _processorFn = req => elementInfo => {
@@ -152,10 +150,6 @@ const _pickCRUD = element => {
152
150
  categories.push({ category: '@cds.on.update', args: element['@cds.on.update'] })
153
151
  }
154
152
 
155
- if (element._isAssociationStrict && !element._target._hasPersistenceSkip) {
156
- categories.push('associationEffective')
157
- }
158
-
159
153
  // REVISIT: element._foreignKeys.length seems to be a very broad check
160
154
  if (element.isAssociation && element._foreignKeys.length) {
161
155
  categories.push({ category: 'propagateForeignKeys' })
@@ -0,0 +1,47 @@
1
+ const InsertBuilder = require('./InsertBuilder')
2
+ const getAnnotatedColumns = require('./annotations')
3
+
4
+ class UpsertBuilder extends InsertBuilder {
5
+ constructor(obj, options, csn) {
6
+ super(obj, options, csn)
7
+ }
8
+
9
+ // REVISIT: We need to copy over the implementation for annotation handling
10
+ build() {
11
+ this._outputObj = {
12
+ sql: ['UPSERT'],
13
+ values: []
14
+ }
15
+ this._obj = { INSERT: this._obj.UPSERT, _target: this._obj._target }
16
+
17
+ const entityName = this._into()
18
+
19
+ this._columnIndexesToDelete = []
20
+ const annotatedColumns = getAnnotatedColumns(entityName, this._csn)
21
+ // hack: treat update annotations as insert because of sql builder impl
22
+ if (annotatedColumns) {
23
+ annotatedColumns.insertAnnotatedColumns = annotatedColumns.updateAnnotatedColumns
24
+ }
25
+
26
+ if (this._obj.INSERT.columns) {
27
+ this._removeAlreadyExistingInsertAnnotatedColumnsFromMap(annotatedColumns)
28
+ this._columns(annotatedColumns)
29
+ }
30
+
31
+ if (this._obj.INSERT.values || this._obj.INSERT.rows) {
32
+ if (annotatedColumns && !this._obj.INSERT.columns) {
33
+ // if columns not provided get indexes from csn
34
+ this._getAnnotatedColumnIndexes(annotatedColumns)
35
+ }
36
+
37
+ this._values(annotatedColumns)
38
+ } else if (this._obj.INSERT.entries && this._obj.INSERT.entries.length !== 0) {
39
+ this._entries(annotatedColumns)
40
+ }
41
+
42
+ this._outputObj.sql = this._outputObj.sql.join(' ') + ' WITH PRIMARY KEY'
43
+ return this._outputObj
44
+ }
45
+ }
46
+
47
+ module.exports = UpsertBuilder
@@ -3,6 +3,7 @@ const DropBuilder = require('./DropBuilder')
3
3
  const SelectBuilder = require('./SelectBuilder')
4
4
  const InsertBuilder = require('./InsertBuilder')
5
5
  const UpdateBuilder = require('./UpdateBuilder')
6
+ const UpsertBuilder = require('./UpsertBuilder')
6
7
  const DeleteBuilder = require('./DeleteBuilder')
7
8
  const ExpressionBuilder = require('./ExpressionBuilder')
8
9
  const ReferenceBuilder = require('./ReferenceBuilder')
@@ -18,6 +19,7 @@ module.exports = {
18
19
  SelectBuilder,
19
20
  InsertBuilder,
20
21
  UpdateBuilder,
22
+ UpsertBuilder,
21
23
  DeleteBuilder,
22
24
  ExpressionBuilder,
23
25
  ReferenceBuilder,
@@ -2,6 +2,7 @@ const DeleteBuilder = require('./DeleteBuilder')
2
2
  const InsertBuilder = require('./InsertBuilder')
3
3
  const SelectBuilder = require('./SelectBuilder')
4
4
  const UpdateBuilder = require('./UpdateBuilder')
5
+ const UpsertBuilder = require('./UpsertBuilder')
5
6
  const CreateBuilder = require('./CreateBuilder')
6
7
  const DropBuilder = require('./DropBuilder')
7
8
 
@@ -16,6 +17,10 @@ const _getCustomBuilderIfExists = (options, type) => {
16
17
  return options.customBuilder.UpdateBuilder
17
18
  }
18
19
 
20
+ case 'UPSERT': {
21
+ return options.customBuilder.UpsertBuilder
22
+ }
23
+
19
24
  case 'DELETE': {
20
25
  return options.customBuilder.DeleteBuilder
21
26
  }
@@ -77,6 +82,10 @@ const build = (cqn, options, csn) => {
77
82
  return build(_getCustomBuilderIfExists(options, 'UPDATE') || UpdateBuilder)
78
83
  }
79
84
 
85
+ if (cqn.UPSERT) {
86
+ return build(_getCustomBuilderIfExists(options, 'UPSERT') || UpsertBuilder)
87
+ }
88
+
80
89
  if (cqn.DELETE) {
81
90
  return build(_getCustomBuilderIfExists(options, 'DELETE') || DeleteBuilder)
82
91
  }
@@ -16,12 +16,14 @@ const getColumns = (entity, { _4db, onlyKeys } = { _4db: true, onlyKeys: false }
16
16
  if (!(entity && entity.elements)) return []
17
17
  const columnNames = []
18
18
  // REVISIT!!!
19
- const elements = Object.getPrototypeOf(entity.elements) || entity.elements
19
+ const elements = cds.env.features.lean_draft
20
+ ? entity.elements
21
+ : Object.getPrototypeOf(entity.elements) || entity.elements
20
22
  for (const elementName in elements) {
21
23
  const element = elements[elementName]
22
24
  if (onlyKeys && !element.key) continue
23
25
  if (element.isAssociation) continue
24
- if (_4db && entity._isDraftEnabled && elementName in DRAFT_COLUMNS_MAP) continue
26
+ if (!cds.env.features.lean_draft && _4db && entity._isDraftEnabled && elementName in DRAFT_COLUMNS_MAP) continue
25
27
  if ((cds.env.effective.odata.structs || cds.env.features.ucsn_struct_conversion) && element.elements) {
26
28
  columnNames.push(...resolveStructured({ element, structProperties: [] }, false))
27
29
  continue
@@ -714,16 +714,6 @@ const _getSiblingScenario = (req, columns, model, siblingIndex, nav) => {
714
714
  const _getSiblingQueryFromWhere = (query, queryIndex, parentQuery) => {
715
715
  if (query.SELECT && query.SELECT.where && queryIndex > 0) {
716
716
  for (let i = 0; i < query.SELECT.where.length; i++) {
717
- if (query.SELECT.where[i].xpr && queryIndex > 0) {
718
- const sibilingQueryFromWhere = _getSiblingQueryFromWhere(
719
- { SELECT: { where: query.SELECT.where[i].xpr } },
720
- queryIndex - 1,
721
- query
722
- )
723
-
724
- if (sibilingQueryFromWhere) return sibilingQueryFromWhere
725
- }
726
-
727
717
  if (query.SELECT.where[i] === 'exists' && queryIndex > 0) {
728
718
  return _getSiblingQueryFromWhere(query.SELECT.where[i + 1], queryIndex - 1, query)
729
719
  }
@@ -757,7 +747,6 @@ const _replaceWhereExists = (query, _siblingIndex, siblingCQN) => {
757
747
 
758
748
  const indexExists = query.SELECT.where.indexOf('exists')
759
749
  if (indexExists > -1) {
760
- if (_siblingIndex > 0) return _replaceWhereExists(query.SELECT.where[indexExists + 1], _siblingIndex - 1)
761
750
  query.SELECT.where.splice(indexExists + 1, 1, siblingCQN)
762
751
  }
763
752
  }
@@ -1019,7 +1008,7 @@ const _generateCQN = (req, columns, from, model) => {
1019
1008
  let siblingIndex = nav.indexOf('SiblingEntity')
1020
1009
 
1021
1010
  // it can also be a property access (new parser), then we must shift it
1022
- if (siblingIndex === 1 && req.target.elements[nav[0]]) {
1011
+ if (siblingIndex > 0 && req.target.elements[nav[0]] && !req.target.elements[nav[0]].isAssociation) {
1023
1012
  nav.shift()
1024
1013
  siblingIndex = siblingIndex - 1
1025
1014
  }