@sap/cds 5.6.4 → 5.7.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 (176) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/_i18n/i18n_fr.properties +4 -4
  3. package/apis/cds.d.ts +7 -10
  4. package/apis/connect.d.ts +3 -3
  5. package/apis/core.d.ts +2 -4
  6. package/apis/models.d.ts +2 -3
  7. package/apis/ql.d.ts +0 -1
  8. package/apis/services.d.ts +3 -3
  9. package/bin/build/buildTaskFactory.js +16 -10
  10. package/bin/build/buildTaskProviderFactory.js +3 -3
  11. package/bin/build/constants.js +2 -1
  12. package/bin/build/provider/buildTaskProviderInternal.js +14 -14
  13. package/bin/build/provider/hana/2migration.js +2 -3
  14. package/bin/build/provider/hana/index.js +34 -0
  15. package/bin/build/provider/hana/migrationtable.js +90 -22
  16. package/bin/build/provider/hana/template/undeploy.json +5 -0
  17. package/bin/build/provider/node-cf/index.js +9 -2
  18. package/bin/serve.js +16 -18
  19. package/lib/compile/cdsc.js +15 -5
  20. package/lib/compile/etc/_localized.js +4 -4
  21. package/lib/compile/extend.js +8 -0
  22. package/lib/compile/index.js +3 -1
  23. package/lib/compile/minify.js +61 -0
  24. package/lib/compile/resolve.js +4 -1
  25. package/lib/compile/to/gql.js +9 -0
  26. package/lib/compile/to/sql.js +26 -30
  27. package/lib/connect/index.js +1 -1
  28. package/lib/core/entities.js +0 -3
  29. package/lib/core/infer.js +1 -0
  30. package/lib/core/reflect.js +0 -34
  31. package/lib/deploy.js +25 -17
  32. package/lib/env/defaults.js +3 -1
  33. package/lib/env/index.js +8 -3
  34. package/lib/env/presets.js +38 -0
  35. package/lib/env/requires.js +16 -11
  36. package/lib/index.js +13 -11
  37. package/lib/log/format/kibana.js +3 -1
  38. package/lib/log/index.js +2 -2
  39. package/lib/req/cds-context.js +79 -0
  40. package/lib/req/context.js +5 -77
  41. package/lib/req/request.js +1 -1
  42. package/lib/serve/Service-api.js +8 -4
  43. package/lib/serve/Service-dispatch.js +0 -7
  44. package/lib/serve/Service-methods.js +6 -8
  45. package/lib/serve/Transaction.js +35 -30
  46. package/lib/serve/adapters.js +1 -4
  47. package/lib/utils/axios.js +1 -1
  48. package/libx/_runtime/audit/Service.js +44 -20
  49. package/libx/_runtime/audit/generic/personal/access.js +16 -11
  50. package/libx/_runtime/audit/generic/personal/modification.js +5 -5
  51. package/libx/_runtime/audit/generic/personal/utils.js +46 -37
  52. package/libx/_runtime/{common/auth → auth}/index.js +21 -7
  53. package/libx/_runtime/{common/auth → auth}/strategies/JWT.js +2 -2
  54. package/libx/_runtime/{common/auth → auth}/strategies/basic.js +2 -2
  55. package/libx/_runtime/{common/auth → auth}/strategies/dummy.js +1 -1
  56. package/libx/_runtime/{common/auth → auth}/strategies/mock.js +2 -2
  57. package/libx/_runtime/{common/auth → auth}/strategies/utils/uaa.js +1 -1
  58. package/libx/_runtime/{common/auth → auth}/strategies/utils/xssec.js +0 -0
  59. package/libx/_runtime/{common/auth → auth}/strategies/xsuaa.js +2 -2
  60. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +7 -2
  61. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +0 -7
  62. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +0 -8
  63. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +3 -4
  64. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +6 -7
  65. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +3 -4
  66. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +2 -11
  67. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +16 -6
  68. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +26 -65
  69. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +0 -7
  70. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +3 -66
  71. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +24 -0
  72. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +2 -2
  73. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +13 -10
  74. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/ConditionalRequestControlCommand.js +0 -7
  75. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/ConditionalRequestValidator.js +0 -8
  76. package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +54 -76
  77. package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +0 -7
  78. package/libx/_runtime/cds-services/adapter/rest/handlers/create.js +3 -6
  79. package/libx/_runtime/cds-services/adapter/rest/handlers/delete.js +3 -6
  80. package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +3 -6
  81. package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +3 -6
  82. package/libx/_runtime/cds-services/adapter/rest/handlers/update.js +3 -6
  83. package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +8 -4
  84. package/libx/_runtime/cds-services/services/Service.js +0 -6
  85. package/libx/_runtime/cds-services/services/utils/columns.js +10 -3
  86. package/libx/_runtime/cds-services/services/utils/compareJson.js +3 -6
  87. package/libx/_runtime/cds-services/services/utils/differ.js +4 -1
  88. package/libx/_runtime/cds-services/services/utils/handlerUtils.js +1 -41
  89. package/libx/_runtime/cds-services/util/assert.js +1 -262
  90. package/libx/_runtime/cds.js +6 -9
  91. package/libx/_runtime/common/aspects/entity.js +1 -1
  92. package/libx/_runtime/common/composition/delete.js +4 -2
  93. package/libx/_runtime/common/composition/update.js +22 -38
  94. package/libx/_runtime/common/composition/utils.js +3 -7
  95. package/libx/_runtime/common/error/standardError.js +11 -0
  96. package/libx/_runtime/common/generic/auth.js +61 -30
  97. package/libx/_runtime/common/generic/crud.js +11 -23
  98. package/libx/_runtime/common/generic/input.js +20 -0
  99. package/libx/_runtime/common/generic/put.js +4 -10
  100. package/libx/_runtime/common/generic/sorting.js +12 -30
  101. package/libx/_runtime/common/perf/index.js +24 -0
  102. package/libx/_runtime/common/utils/cqn.js +58 -1
  103. package/libx/_runtime/common/utils/cqn2cqn4sql.js +289 -114
  104. package/libx/_runtime/common/utils/csn.js +38 -56
  105. package/libx/_runtime/common/utils/entityFromCqn.js +6 -6
  106. package/libx/_runtime/common/utils/resolveView.js +4 -5
  107. package/libx/_runtime/common/utils/rewriteAsterisks.js +46 -5
  108. package/libx/_runtime/common/utils/search2cqn4sql.js +21 -9
  109. package/libx/_runtime/common/utils/structured.js +35 -25
  110. package/libx/_runtime/db/Service.js +0 -6
  111. package/libx/_runtime/db/expand/expand-v2.js +130 -0
  112. package/libx/_runtime/db/expand/expandCQNToJoin.js +38 -52
  113. package/libx/_runtime/db/expand/index.js +3 -1
  114. package/libx/_runtime/db/generic/input.js +52 -10
  115. package/libx/_runtime/db/generic/integrity.js +367 -26
  116. package/libx/_runtime/db/generic/virtual.js +51 -13
  117. package/libx/_runtime/db/query/update.js +9 -3
  118. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +8 -9
  119. package/libx/_runtime/{common → db}/utils/propagateForeignKeys.js +11 -14
  120. package/libx/_runtime/fiori/generic/activate.js +1 -0
  121. package/libx/_runtime/fiori/generic/before.js +2 -1
  122. package/libx/_runtime/fiori/generic/edit.js +1 -0
  123. package/libx/_runtime/fiori/generic/patch.js +1 -1
  124. package/libx/_runtime/fiori/generic/read.js +123 -57
  125. package/libx/_runtime/fiori/uiflex/index.js +1 -1
  126. package/libx/_runtime/fiori/uiflex/{extensibility/index.js → service.js} +3 -3
  127. package/libx/_runtime/fiori/utils/delete.js +7 -1
  128. package/libx/_runtime/hana/Service.js +1 -8
  129. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +5 -14
  130. package/libx/_runtime/hana/execute.js +10 -4
  131. package/libx/_runtime/hana/pool.js +55 -45
  132. package/libx/_runtime/hana/search.js +7 -6
  133. package/libx/_runtime/hana/search2cqn4sql.js +8 -5
  134. package/libx/_runtime/hana/searchToContains.js +3 -1
  135. package/libx/_runtime/index.js +5 -5
  136. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +3 -3
  137. package/libx/_runtime/messaging/Outbox.js +53 -0
  138. package/libx/_runtime/messaging/common-utils/AMQPClient.js +17 -10
  139. package/libx/_runtime/messaging/common-utils/connections.js +14 -9
  140. package/libx/_runtime/messaging/common-utils/waitingTime.js +2 -0
  141. package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -3
  142. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -2
  143. package/libx/_runtime/messaging/enterprise-messaging.js +21 -15
  144. package/libx/_runtime/messaging/file-based.js +5 -5
  145. package/libx/_runtime/messaging/message-queuing.js +2 -3
  146. package/libx/_runtime/messaging/outbox/OutboxRunner.js +75 -0
  147. package/libx/_runtime/messaging/outbox/utils.js +192 -0
  148. package/libx/_runtime/messaging/service.js +16 -30
  149. package/libx/_runtime/remote/Service.js +15 -0
  150. package/libx/_runtime/remote/utils/client.js +15 -3
  151. package/libx/_runtime/remote/utils/{dataConversion.js → data.js} +12 -2
  152. package/libx/_runtime/sqlite/Service.js +7 -10
  153. package/libx/_runtime/sqlite/customBuilder/CustomExpressionBuilder.js +19 -0
  154. package/libx/_runtime/sqlite/execute.js +18 -12
  155. package/libx/_runtime/types/api.js +2 -1
  156. package/libx/odata/{odata2cqn/afterburner.js → afterburner.js} +19 -15
  157. package/libx/odata/{cqn2odata/index.js → cqn2odata.js} +1 -1
  158. package/libx/odata/{odata2cqn/grammar.pegjs → grammar.pegjs} +171 -130
  159. package/libx/odata/index.js +16 -14
  160. package/libx/odata/parser.js +1 -0
  161. package/libx/odata/utils.js +57 -0
  162. package/libx/rest/RestAdapter.js +2 -6
  163. package/libx/rest/utils/data.js +1 -6
  164. package/package.json +4 -3
  165. package/server.js +4 -5
  166. package/srv/audit-log.cds +87 -0
  167. package/{libx/_runtime/fiori/uiflex/extensibility/index.cds → srv/flex.cds} +0 -0
  168. package/srv/flex.js +1 -0
  169. package/srv/outbox.cds +11 -0
  170. package/srv/outbox.js +0 -0
  171. package/libx/_runtime/cds-services/adapter/perf/performance.js +0 -104
  172. package/libx/_runtime/cds-services/adapter/perf/performanceMeasurement.js +0 -33
  173. package/libx/odata/odata2cqn/index.js +0 -3
  174. package/libx/odata/odata2cqn/parser.js +0 -1
  175. package/libx/odata/readme.md +0 -1
  176. package/libx/odata/utils/index.js +0 -64
@@ -1,5 +1,4 @@
1
1
  const { fs } = require('@sap/cds-foss')
2
-
3
2
  class MigrationTableParser {
4
3
  constructor() {
5
4
  }
@@ -212,17 +211,17 @@ class MigrationTableModel {
212
211
  * into the current branch representing the extension.
213
212
  * <code>extension.merge(base, targetTable)</code>
214
213
  *
215
- * A---B---E---F extension
216
- * /
217
- * A---B---C base
214
+ * A---B---E---F extension X---Y extension
215
+ * / /
216
+ * A---B---C base A---B base
218
217
  *
219
218
  * || upgrade extension with base - migration version C is inserted into extension, its version number is updated,
220
219
  * || its version number is rebased on the version number of the extension
221
220
  * \/
222
221
  *
223
- * A---B---E---F---C extension
224
- * /
225
- * A---B---C base
222
+ * A---B---E---F---C extension A---B---X---Y extension
223
+ * / /
224
+ * A---B---C base A---B base
226
225
  *
227
226
  * @param {MigrationTableModel} base The new base migration table version from which all missing migration versions
228
227
  * will be taken and merged into the new migration table result.
@@ -230,17 +229,18 @@ class MigrationTableModel {
230
229
  * retrieved from the migration table file created by cds build based on the final CDS model version.
231
230
  * @returns {MigrationTableModel} The merge result containg missing migrations from base.
232
231
  */
233
- merge(base, targetTable) {
234
- let idxMaster = base.migrations.entries.findIndex(baseMigration => {
235
- return this.migrations.entries.some(extensionMigration => this._compareMigrations(extensionMigration, baseMigration))
236
- })
232
+ merge(base, targetTable, addBaseVersionAnnotation) {
233
+ let idxBase = this._findCommonBaseMigrationIdx(base)
237
234
  const mergeResult = new MigrationTableModel(targetTable, this.migrations.clone())
238
- // insert all migration versions of master
239
- for (let idx = idxMaster === - 1 ? base.migrations.entries.length - 1 : idxMaster - 1; idx >= 0; idx--) {
235
+
236
+ // insert all migration versions since the histories diverged
237
+ for (let idx = idxBase >= 0 ? idxBase - 1 : base.migrations.entries.length - 1, versionNumber = this.versionNumber + 1; idx >= 0; idx--, versionNumber++) {
240
238
  const migration = base.migrations.entries[idx].clone();
239
+ if (addBaseVersionAnnotation) {
240
+ migration._addBaseMigrationVersion(migration.versionNumber)
241
+ }
241
242
  // update migration version number and insert at beginning
242
- migration.versionNumber = this.versionNumber + 1
243
- mergeResult.table.versionNumber = this.versionNumber + 1
243
+ migration.versionNumber = mergeResult.table.versionNumber = versionNumber
244
244
  mergeResult.migrations.entries.splice(0, 0, migration)
245
245
  }
246
246
  return mergeResult
@@ -250,13 +250,63 @@ class MigrationTableModel {
250
250
  return new MigrationTableModel(this.table.clone(), this.migrations.clone())
251
251
  }
252
252
 
253
- // migrations are identical if the underlying changesets are identical
254
- _compareMigrations(migration1, migration2) {
255
- return this._compactStr(migration1.changeset) === this._compactStr(migration2.changeset)
253
+ /**
254
+ * Returns the index of the common base migration version (since the time their histories diverged)
255
+ * or -1 if there is no common base. The index points to the base migration table model.
256
+ * @param {MigrationTableModel} base The new base migration table version.
257
+ * @returns The index of the common base migration, -1 if there is no common base.
258
+ */
259
+ _findCommonBaseMigrationIdx(base) {
260
+ // get the index based on the annotation '-- migration-base=<version number>'
261
+ const extMigration = this.migrations.entries.find(extMigration => extMigration._getBaseMigrationVersion() !== -1)
262
+ // get the index based on DDL equality
263
+ const idxBase = base.migrations.entries.findIndex(baseMigration => {
264
+ return this.migrations.entries.some(extMigration => this._compareMigrations(baseMigration, extMigration))
265
+ })
266
+ if (!extMigration && idxBase === -1) {
267
+ // no common base version - all good
268
+ return -1
269
+ }
270
+ if (extMigration && idxBase !== -1 && extMigration._getBaseMigrationVersion() === base.migrations.entries[idxBase].versionNumber) {
271
+ // both indices refer to the same base version - all good
272
+ return idxBase
273
+ }
274
+ // differences encountered, cause might be:
275
+ // 1. older versions may not add 'migration-base' annotations
276
+ // 2. the same DDL statements might exist in different migrations
277
+ if (!extMigration && idxBase !== -1) {
278
+ // if migration-base annotations do not exist use equality of DDL statements
279
+ // older versions did not add 'migration-base' annotations - most probably all good
280
+ return idxBase
281
+ }
282
+ if (extMigration) {
283
+ // favor 'migration-base' annotation over DDL statement equality
284
+ const baseVersion = extMigration._getBaseMigrationVersion()
285
+ const idxBase = base.migrations.entries.findIndex(baseMigration => baseMigration.versionNumber === baseVersion)
286
+ if (idxBase > -1) {
287
+ return idxBase
288
+ }
289
+ }
290
+ return idxBase
291
+ }
292
+
293
+ /**
294
+ * Migrations are identical if the corresponding DDL statements are identical,
295
+ * ignoring order and ignoring comments.
296
+ */
297
+ _compareMigrations(m1, m2) {
298
+ if (m1.ddl.length !== m2.ddl.length) {
299
+ return false
300
+ }
301
+ const mDdl2 = m2.ddl.map(entry => this._compactLine(entry))
302
+ return m1.ddl.some(entry => mDdl2.includes(this._compactLine(entry)))
256
303
  }
257
304
 
258
- _compactStr(array) {
259
- return JSON.stringify(array.map(change => change.replace(/ /g, '')))
305
+ /**
306
+ * Delete comments and return only those lines representing DDL statements.
307
+ */
308
+ _compactLine(line) {
309
+ return line.replace(/ /g, '')
260
310
  }
261
311
  }
262
312
 
@@ -349,7 +399,6 @@ class Migration {
349
399
  this._lines = lines
350
400
  }
351
401
  this._versionNumber = MigrationTableParser._parseVersionNumber(this.lines[0])
352
- this._changeset = this._lines.filter(line => !MigrationTableParser._isMigrationMarker(line))
353
402
  }
354
403
 
355
404
  /**
@@ -378,7 +427,7 @@ class Migration {
378
427
  * Returns the changeset containing the DDL statements of this migration.
379
428
  */
380
429
  get changeset() {
381
- return this._changeset
430
+ return this._lines.filter(line => !MigrationTableParser._isMigrationMarker(line))
382
431
  }
383
432
 
384
433
  /**
@@ -389,6 +438,25 @@ class Migration {
389
438
  return this.changeset.filter(line => MigrationTableParser._isDDL(line))
390
439
  }
391
440
 
441
+ _addBaseMigrationVersion(versionNumber) {
442
+ this._addComment(1, `-- migration-base=${versionNumber}`)
443
+ }
444
+
445
+ _getBaseMigrationVersion() {
446
+ let versionNumber = -1
447
+ if (this._lines.length > 1) {
448
+ const match = this._lines[1].match(/(^\s*-- migration-base=)(\d+)\s*$/)
449
+ if (match && match.length === 3) {
450
+ versionNumber = parseInt(match[2])
451
+ }
452
+ }
453
+ return versionNumber
454
+ }
455
+
456
+ _addComment(start, comment) {
457
+ this._lines.splice(start, 0, comment)
458
+ }
459
+
392
460
  /**
393
461
  * Returns the string representation of this migration.
394
462
  */
@@ -0,0 +1,5 @@
1
+ [
2
+ "src/gen/**/*.hdbview",
3
+ "src/gen/**/*.hdbindex",
4
+ "src/gen/**/*.hdbconstraint"
5
+ ]
@@ -4,7 +4,10 @@ const BuildTaskHandlerEdmx = require('../buildTaskHandlerEdmx')
4
4
  const { getHanaDbModuleDescriptor, getServiceModuleDescriptor } = require('../../mtaUtil')
5
5
  const { BuildError } = require('../../util')
6
6
  const { BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY, ODATA_VERSION, ODATA_VERSION_V2,
7
- BUILD_TASK_HANA, FOLDER_GEN, BUILD_NODEJS_EDMX_GENERAION, EDMX_GENERATION, SKIP_PACKAGE_JSON_GENERATION, SKIP_MANIFEST_GENERATION, CONTENT_EDMX, CONTENT_MANIFEST, CONTENT_PACKAGE_JSON } = require('../../constants')
7
+ BUILD_TASK_HANA, FOLDER_GEN, BUILD_NODEJS_EDMX_GENERAION, EDMX_GENERATION,
8
+ SKIP_PACKAGE_JSON_GENERATION, SKIP_MANIFEST_GENERATION,
9
+ CONTENT_EDMX, CONTENT_MANIFEST, CONTENT_PACKAGE_JSON, CONTENT_PACKAGELOCK_JSON }
10
+ = require('../../constants')
8
11
  const { WARNING } = require('../../buildTaskHandler')
9
12
 
10
13
  const FILE_NAME_MANIFEST_YML = "manifest.yml"
@@ -16,6 +19,7 @@ class NodeCfModuleBuilder extends BuildTaskHandlerEdmx {
16
19
  this.task.options[CONTENT_EDMX] = this.hasBuildOption(CONTENT_EDMX, true) || this.hasCdsEnvOption(BUILD_NODEJS_EDMX_GENERAION, true) || this.hasBuildOption(EDMX_GENERATION, true) ? true : false
17
20
  this.task.options[CONTENT_MANIFEST] = !this.hasBuildOption(CONTENT_MANIFEST, false) && !this.hasBuildOption(SKIP_MANIFEST_GENERATION, true) ? true : false
18
21
  this.task.options[CONTENT_PACKAGE_JSON] = !this.hasBuildOption(CONTENT_PACKAGE_JSON, false) && !this.hasBuildOption(SKIP_PACKAGE_JSON_GENERATION, true) ? true : false
22
+ this.task.options[CONTENT_PACKAGELOCK_JSON] = !this.hasBuildOption(CONTENT_PACKAGELOCK_JSON, false) ? true : false
19
23
 
20
24
  if (this.task.options.compileDest) {
21
25
  throw new BuildError("Option not supported - compileDest")
@@ -66,7 +70,7 @@ class NodeCfModuleBuilder extends BuildTaskHandlerEdmx {
66
70
  }
67
71
 
68
72
  async _copyNativeContent(src, dest) {
69
- const regex = RegExp('\\.cds$|package\\.json|manifest\\.y.?ml')
73
+ const regex = RegExp('\\.cds$|package\\.json|package-lock\\.json|manifest\\.y.?ml')
70
74
 
71
75
  await super.copyNativeContent(src, dest, (entry) => {
72
76
  if (fs.statSync(entry).isDirectory()) {
@@ -92,6 +96,9 @@ class NodeCfModuleBuilder extends BuildTaskHandlerEdmx {
92
96
  packageJsonCopy = true
93
97
  return true
94
98
  }
99
+ if (/package-lock\.json$/.test(entry) && this.hasBuildOption(CONTENT_PACKAGELOCK_JSON, true)) {
100
+ return true
101
+ }
95
102
  return false
96
103
  })
97
104
 
package/bin/serve.js CHANGED
@@ -124,7 +124,7 @@ module.exports = Object.assign ( serve, {
124
124
 
125
125
 
126
126
  const cds = require('../lib'), { exists, isfile, local, path } = cds.utils
127
- let log = console.log // provisional logger, see _prepareLogging
127
+ let log = console.log // provisional logger, see _prepare_logging
128
128
 
129
129
  /**
130
130
  * The main function which dispatches into the respective usage variants.
@@ -139,7 +139,7 @@ async function serve (all=[], o={}) { // NOSONAR
139
139
  else if (o.service) { o.from = pms ? pms.split(',') : '*'}
140
140
  else if (o.from) { o.service = pms }
141
141
  else if (exists(pms)) { o.service ='all', o.from = all }
142
- else { o.service = pms, o.from = '*' }
142
+ else { o.service = pms||'all', o.from = '*' }
143
143
 
144
144
  // IMPORTANT: never load any @sap/cds modules before the chdir above happened!
145
145
  // handle --watch and --project
@@ -151,16 +151,16 @@ async function serve (all=[], o={}) { // NOSONAR
151
151
  const {features} = cds.env
152
152
  {
153
153
  // handle --with-mocks resp. --mocked
154
- if (!features.no_mocking) o.mocked = _with_mocks (o)
154
+ if (features.with_mocks) o.mocked = _with_mocks(o)
155
155
 
156
156
  // handle --in-memory resp. --in-memory?
157
- if (features.in_memory_db) o.in_memory = _in_memory (o)
157
+ if (features.in_memory_db) o.in_memory = _in_memory(o)
158
158
 
159
159
  // load service bindings when mocking or asked to
160
160
  if (features.mocked_bindings && o.mocked || o['with-bindings']) await cds.service.bindings
161
161
 
162
162
  // live reload, in cooperation with cds watch
163
- if (features.live_reload) require('../app/etc/livereload')
163
+ if (features.live_reload) require('../app/etc/livereload')
164
164
 
165
165
  // add dev helper for Fiori URLs
166
166
  if (features.fiori_routes) require('../app/fiori/routes')
@@ -203,38 +203,37 @@ function _prepare_logging () { // NOSONAR
203
203
  // change `log` function to cds.log
204
204
  const LOG = cds.log('serve', { prefix:'cds' })
205
205
  log = LOG._info && LOG.info
206
+ if (!log) return
206
207
 
207
- const _timer = log && `[cds] - launched at ${new Date().toLocaleString()}, in`
208
- _timer && console.time (_timer)
208
+ const _timer = `[cds] - launched at ${new Date().toLocaleString()}, in`
209
+ console.time (_timer)
209
210
 
210
211
  // print information when model is loaded
211
212
  cds.on ('loaded', (model)=>{
212
- log && log (`model loaded from ${model.$sources.length} file(s):\n\x1b[2m`)
213
- for (let each of model.$sources) log && console.log (' ', local(each))
214
- log && console.log ('\x1b[0m')
213
+ log (`model loaded from ${model.$sources.length} file(s):\n\x1b[2m`)
214
+ for (let each of model.$sources) console.log (' ', local(each))
215
+ console.log ('\x1b[0m')
215
216
  })
216
217
 
217
218
  // print information about each connected service
218
219
  cds.on ('connect', ({name,kind,options:{use,credentials}})=>{
219
- log && log (`connect to ${name} > ${use||kind}`, credentials ? _redacted(credentials) : '')
220
+ log (`connect to ${name} > ${use||kind}`, credentials ? _redacted(credentials) : '')
220
221
  })
221
222
 
222
223
  // print information about each provided service
223
224
  cds.on ('serving', (srv) => {
224
225
  const details = { at: srv.path }
225
226
  if (srv._source) details.impl = local(srv._source)
226
- log && log (`${srv.mocked ? 'mocking' : 'serving'} ${srv.name}`, details)
227
+ log (`${srv.mocked ? 'mocking' : 'serving'} ${srv.name}`, details)
227
228
  })
228
229
 
229
230
  // print info when we are finally on air
230
231
  cds.once ('listening', ({url})=>{
231
- log && console.log ()
232
- log && log ('server listening on',{url})
232
+ console.log ()
233
+ log ('server listening on',{url})
233
234
  _timer && console.timeEnd (_timer)
234
- if (process.stdin.isTTY) log && log (`[ terminate with ^C ]\n`)
235
+ if (process.stdin.isTTY) log (`[ terminate with ^C ]\n`)
235
236
  })
236
-
237
- return cds
238
237
  }
239
238
 
240
239
 
@@ -275,7 +274,6 @@ function _in_memory (o) {
275
274
 
276
275
  /** handles --with-mocks option */
277
276
  function _with_mocks (o) {
278
- if (process.env.NODE_ENV === 'production') return
279
277
  if (o.mocked || (o.mocked = o['with-mocks'])) {
280
278
  cds.on ('loaded', model => cds.deploy.include_external_entities_in(model))
281
279
  const mocks = cds.env.features.test_mocks && isfile ('test/mocked.js')
@@ -1,9 +1,19 @@
1
- const {cdsc,odata,sql,hana} = require ('../index').env
1
+ const {cdsc,odata,sql,hana,features} = require ('../index').env
2
+ const constraints = {
3
+ assertIntegrity: features.assert_integrity,
4
+ assertIntegrityType: features.assert_integrity_type
5
+ }
6
+ // REVISIT: remove with compiler ^2.11
7
+ const { assertIntegrity: ai, assertIntegrityType: ait } = constraints
8
+ if ((typeof ai === 'string' && ai.match(/individual/i)) || (ait && ait.match(/db/i))) {
9
+ cdsc.beta = cdsc.beta || {}
10
+ cdsc.beta.foreignKeyConstraints = true
11
+ }
2
12
  const compile = require ('@sap/cds-compiler')
3
13
  const _4cdsc = Symbol('_4cdsc')
4
14
 
5
15
  /**
6
- * Returns a copy of the given options object, which all mappings applied and
16
+ * Returns a copy of the given options object, with all mappings applied and
7
17
  * finally overridden with entries from cds.env.cdsc. That is, the equivalent
8
18
  * of {...o, ...[...mappings], ...cds.env.cdsc }.
9
19
  * @type <T> (src:T,...mappings:{}[]) => T
@@ -11,7 +21,7 @@ const _4cdsc = Symbol('_4cdsc')
11
21
  function _options4 (src, ...mappings) {
12
22
  if (src[_4cdsc]) return src //> already prepared for cdsc
13
23
  // Create a derivate of given src options object
14
- const dst = Object.defineProperty({__proto__:src},_4cdsc,{value:true}) // NOTE: {__proto__:src} not possible due to compiler obviously cloning options
24
+ const dst = Object.defineProperty({__proto__:src, ...src}, _4cdsc,{value:true}) // NOTE: {__proto__:src} alone doesn't suffice, due to compiler obviously cloning options; {...src} doesn't suffice as non-enumerables from .effective.odata would get lost
15
25
  // Apply mappings in order of appearance -> latter ones override formers
16
26
  for (let map of mappings) for (let k in map) {
17
27
  let v = dst[k]; if (v === undefined) continue
@@ -61,10 +71,10 @@ const _options = {for: Object.assign (_options4, {
61
71
  },
62
72
 
63
73
  sql(o,_env) {
64
- return _options4 ({ ..._env||sql, ...o }, {
74
+ return _options4 ({ ...constraints, ..._env||sql, ...o }, {
65
75
  sql_mapping : 'names', //> legacy
66
76
  sqlDialect : 'dialect', //> legacy
67
- sqlMapping : 'names',
77
+ sqlMapping : 'names',
68
78
  dialect : 'sqlDialect',
69
79
  names : (o,v) => v !== 'plain' ? o.sqlMapping = v : undefined,
70
80
  })
@@ -42,13 +42,13 @@ function unfold_csn (m) { // NOSONAR
42
42
  // only do that once per model
43
43
  if (!m || m[_been_here]) return m
44
44
  // eslint-disable-next-line no-console
45
- DEBUG && console.trace ('unfolding csn...')
45
+ DEBUG && DEBUG ('unfolding csn...')
46
46
  const pass2 = []
47
47
 
48
48
  const _locales = _on_sqlite && _locales_4sql.sqlite
49
49
 
50
50
  // Pass 1 - add localized.<locale> entities and views
51
- for (let each in m.definitions) {
51
+ for (let each in cds.linked(m).definitions) {
52
52
  const d = m.definitions [each]
53
53
  // Add <entry>_texts proxies for all <entry>.texts entities
54
54
  if (_texts_entries !== false && each.endsWith('.texts')) {
@@ -99,5 +99,5 @@ function unfold_csn (m) { // NOSONAR
99
99
 
100
100
 
101
101
  // feature-toggled exports
102
- module.exports = Object.assign ( unfold_csn, { unfold_ddl })
103
- if (!env.features.localized) module.exports = Object.assign ( x=>x, { unfold_ddl: x=>x })
102
+ module.exports = { unfold_csn, unfold_ddl }
103
+ if (!env.features.localized) Object.assign (module.exports, { unfold_csn: x=>x, unfold_ddl: x=>x })
@@ -0,0 +1,8 @@
1
+ const { extend } = require ('../lazy')
2
+ const compile = require ('./index')
3
+
4
+ module.exports = o => o.definitions ? { with(...exts) {
5
+ const all = { 'base.csn': JSON.stringify(o) }
6
+ exts.forEach ((x,i)=> all[i+'.csn'] = JSON.stringify(x))
7
+ return compile (all)
8
+ }} : extend(o)
@@ -16,6 +16,7 @@ const compile = module.exports = Object.assign (cds_compile, {
16
16
  to: lazified ({
17
17
  csn: cds_compile,
18
18
  cdl: require('./to/cdl'),
19
+ gql: require('./to/gql'),
19
20
  yml: require('./to/yaml'),
20
21
  yaml: require('./to/yaml'),
21
22
  json: require('./to/json'),
@@ -26,6 +27,7 @@ const compile = module.exports = Object.assign (cds_compile, {
26
27
  serviceinfo: require('./to/srvinfo'), //> REVISIT: move to CLI
27
28
  }),
28
29
 
30
+ _localized: require('./etc/_localized'),
29
31
  })
30
32
 
31
33
 
@@ -53,7 +55,7 @@ function cds_compile (model, options, _flavor) {
53
55
  }
54
56
  else return _fluent (_finalize (cdsc.compileSources(model,o))) //> compile CDL sources
55
57
  function _finalize (csn) {
56
- if (o.min) csn = cds.linked(csn).minified()
58
+ if (o.min) csn = cds.minify(csn)
57
59
  // REVISIT: experimental implementation to detect external APIs
58
60
  for (let each in csn.definitions) {
59
61
  const d = csn.definitions[each]
@@ -0,0 +1,61 @@
1
+ module.exports = function cds_minify (csn, _roots = global.cds.env.features.skip_unused) {
2
+ if (_roots === false) return csn
3
+ if (csn[_minified]) return csn; else Object.defineProperty (csn,_minified,{value:true})
4
+ const all = csn.definitions, reached = new Set
5
+ if (_roots === 'services') {
6
+ for (let n in all) if (all[n].kind === 'service') _visit_service(n)
7
+ } else if (typeof _roots === 'string') {
8
+ _visit_service(_roots)
9
+ } else for (let n in all) {
10
+ let d = all[n]
11
+ if (d.kind === 'service') _visit_service(n)
12
+ else if (d.kind === 'entity') {
13
+ if (d['@cds.persistence.skip'] === 'if-unused') continue
14
+ if (n.endsWith('.texts')) {
15
+ let e = all[n.slice(0,-6)]
16
+ if (e && e['@cds.persistence.skip'] === 'if-unused') continue
17
+ }
18
+ _visit(d)
19
+ }
20
+ }
21
+ function _visit_service (service) {
22
+ reached.add (all[service])
23
+ for (let e in all) if (e.startsWith(service+'.')) _visit(all[e])
24
+ }
25
+ function _visit_query (q) {
26
+ if (q.SELECT) return _visit_query (q.SELECT)
27
+ if (q.SET) return q.SET.args.forEach (_visit_query)
28
+ if (q.from) {
29
+ if (q.from.join) return q.from.args.forEach (_visit)
30
+ else return _visit (q.from)
31
+ }
32
+ }
33
+ function _visit (d) {
34
+ if (typeof d === 'string') {
35
+ if (d.startsWith('cds.')) return
36
+ else d = all[d]
37
+ } else if (d.ref) return d.ref.reduce((p,n) => {
38
+ let d = p.elements[n.id || n] // > n.id -> view with parameters
39
+ _visit(d)
40
+ return d
41
+ },{elements:all})
42
+ if (reached.has(d)) return; else reached.add(d)
43
+ if (d.includes) d.includes.forEach(i => _visit(all[i])) // Note: with delete d.includes, redirects in AFC broke
44
+ if (d.projection) _visit_query (d.projection)
45
+ if (d.query) _visit_query (d.query)
46
+ if (d.type) _visit (d.type)
47
+ if (d.target) _visit (d.target)
48
+ if (d.targetAspect) _visit (d.targetAspect)
49
+ if (d.items) _visit (d.items)
50
+ if (d.returns) _visit (d.returns)
51
+ for (let e in d.elements) _visit (d.elements[e])
52
+ for (let a in d.actions) _visit (d.actions[a])
53
+ for (let p in d.params) _visit (d.params[p])
54
+ }
55
+ const minified = Object.create (csn.__proto__, Object.getOwnPropertyDescriptors(csn))
56
+ const less = minified.definitions = {}
57
+ for (let n in all) if (reached.has(all[n])) less[n] = all[n]
58
+ return minified
59
+ }
60
+
61
+ const _minified = Symbol('minified')
@@ -23,9 +23,12 @@ module.exports = exports = function cds_resolve (model, o={}) { // NOSONAR
23
23
 
24
24
  const cwd = o.root || global.cds && global.cds.root, local = resolve (cwd,model)
25
25
  const context = _paths(cwd), {cached} = context
26
- const id = model.startsWith('.') ? local : model
26
+ let id = model.startsWith('.') ? local : model
27
27
  if (id in cached && !o.skipModelCache) return cached[id]
28
28
 
29
+ // expand @sap/cds by cds.home
30
+ if (id.startsWith('@sap/cds')) id = global.cds.home +'/'+ id.slice(8)
31
+
29
32
  // fetch file with .cds/.csn suffix as is
30
33
  if (/\.(csn|cds)$/.test(id)) try {
31
34
  return cached[id] = _resolved ([ _resolve (id,context) ])
@@ -0,0 +1,9 @@
1
+ const { generate } = require ('../../../libx/gql/schema')
2
+ const cds = require ('../..')
3
+
4
+ function cds_compile_to_gql (csn) {
5
+ const m = cds.linked(csn)
6
+ return generate (m.services.map(s => ({ name:s.name, model:m })))
7
+ }
8
+
9
+ module.exports = cds_compile_to_gql
@@ -1,47 +1,25 @@
1
- const cds = require ('../..'), minified = csn => cds.linked(csn).minified()
1
+ const cds = require ('../..'), { unfold_ddl: cds_localized } = cds.compile._localized
2
2
  const cdsc = require ('../cdsc')
3
- const {unfold_ddl} = cds.alpha_localized
4
- const EXT_BACK_PACK = 'extensions__'
5
3
 
6
- function cds_compile_to_sql_ (csn,_o) {
4
+
5
+ function cds_compile_to_sql (csn,_o) {
6
+ csn = _extended(cds.minify(csn))
7
7
  const o = cdsc._options.for.sql(_o) //> used twice below...
8
- const all = cdsc.to.sql (minified(csn),o)
9
- const sql = unfold_ddl (all.map (each => each
10
- .replace(/^-- .+\n/,'') //> strip comments
11
- ), csn, o)
8
+ const all = cdsc.to.sql(csn,o) .map (each => each.replace(/^-- .+\n/,'')) //> strip comments
9
+ const sql = cds_localized(all, csn, o)
12
10
  if (o.as === 'str') return `\n${sql.join('\n\n')}\n`
13
11
  return sql
14
12
  }
15
13
 
16
- function cds_compile_to_sql (csn,_o) {
17
- const defs = cds.linked(csn).definitions
18
- for (let each in defs) {
19
- const d = defs[each], q = d.query
20
- // q may have SET instead of SELECT
21
- if (q && q.SELECT && q.SELECT.columns && _is_extensible(d)) _add_extensions2 (q.SELECT.columns)
22
- }
23
- function _is_extensible (d) {
24
- if(!d || !d.elements) return false
25
- if (EXT_BACK_PACK in d.elements) return true
26
- else return _is_extensible (d.__proto__)
27
- }
28
- function _add_extensions2 (cols) {
29
- if (cols.some(({ref}) => ref && ref[0] === EXT_BACK_PACK)) return
30
- cols.push({ref:[EXT_BACK_PACK]})
31
- }
32
- const ddl = cds_compile_to_sql_ (csn,_o)
33
- return ddl
34
- }
35
-
36
14
 
37
15
  function cds_compile_to_hdbtable (csn,o) {
38
- const all = cdsc.to.hdi (minified(csn),o)
16
+ const all = cdsc.to.hdi (cds.minify(csn),o)
39
17
  return _2many(all)
40
18
  }
41
19
 
42
20
 
43
21
  function cds_compile_to_hdbcds (csn,o) {
44
- const all = cdsc.to.hdbcds (minified(csn),o)
22
+ const all = cdsc.to.hdbcds (cds.minify(csn),o)
45
23
  const constructFileName = (fileName) => {
46
24
  const identifier = fileName.split('.');
47
25
  const suffix = identifier.pop();
@@ -61,3 +39,21 @@ module.exports = Object.assign (cds_compile_to_sql, {
61
39
  hdbcds: cds_compile_to_hdbcds,
62
40
  hdbtable: cds_compile_to_hdbtable,
63
41
  })
42
+
43
+
44
+
45
+ /////////////////////////////////////////////////////////////////////////////
46
+ // UI Flex - read extensions__ to views, when ext fields are read
47
+ const _extended = (csn) => {
48
+ const defs = cds.linked(csn).definitions
49
+ for (let each in defs) {
50
+ const d = defs[each], q = d.query // TODO: q may have SET instead of SELECT
51
+ if (q && q.SELECT && q.SELECT.columns && _is_extensible(d)) {
52
+ if (!q.SELECT.columns.some(({ref}) => ref && ref[0] === _extensions)) q.SELECT.columns.push({ref:[_extensions]})
53
+ }
54
+ }
55
+ return csn
56
+ }
57
+ const _is_extensible = d => _extensions in d.elements || d.__proto__.elements && _is_extensible (d.__proto__)
58
+ const _extensions = 'extensions__'
59
+ /////////////////////////////////////////////////////////////////////////////
@@ -38,7 +38,7 @@ connect.to = async (datasource, options) => {
38
38
  // check if required service definition exists
39
39
  const required = cds.requires[datasource]
40
40
  if (required && required.model && datasource !== 'db' && !m.definitions[required.service||datasource]) {
41
- LOG.error (`No service definition found for '${required.service || datasource}', as required by 'cds.requires.${datasource}':`, required)
41
+ LOG._error && LOG.error(`No service definition found for '${required.service || datasource}', as required by 'cds.requires.${datasource}':`, required)
42
42
  throw new Error (`No service definition found for '${required.service || datasource}'`)
43
43
  }
44
44
  // construct new service instance
@@ -11,9 +11,6 @@ class entity extends struct {
11
11
  get compositions() {
12
12
  return this.own('_compositions') || this.set('_compositions', this._elements (e => e instanceof Composition))
13
13
  }
14
- get texts() {
15
- return this.own('_texts') || this.set('_texts', {__proto__:this, name: this.name + '.texts' })
16
- }
17
14
  get drafts() {
18
15
  return this.own('_drafts') || this.set('_drafts',
19
16
  this.elements.HasDraftEntity && { name: this.name + '_drafts', keys: this.keys }
package/lib/core/infer.js CHANGED
@@ -14,6 +14,7 @@ module.exports = (q,defs) => {
14
14
 
15
15
  const _resolve = (from, defs) => {
16
16
  if (!from || from.name) return from
17
+ while (from.SELECT) from = from.SELECT.from
17
18
  if (from.join || from.set) return //_unresolved()
18
19
  if (from.ref) {
19
20
  if (from.ref.length === 1) {