@sap/cds 6.0.3 → 6.1.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 (131) hide show
  1. package/CHANGELOG.md +165 -18
  2. package/apis/cds.d.ts +11 -7
  3. package/apis/log.d.ts +46 -0
  4. package/apis/ql.d.ts +72 -15
  5. package/bin/build/buildTaskHandler.js +5 -2
  6. package/bin/build/constants.js +4 -1
  7. package/bin/build/provider/buildTaskHandlerEdmx.js +11 -39
  8. package/bin/build/provider/buildTaskHandlerFeatureToggles.js +14 -34
  9. package/bin/build/provider/buildTaskHandlerInternal.js +56 -4
  10. package/bin/build/provider/buildTaskProviderInternal.js +22 -14
  11. package/bin/build/provider/hana/index.js +12 -9
  12. package/bin/build/provider/java/index.js +18 -8
  13. package/bin/build/provider/mtx/index.js +7 -4
  14. package/bin/build/provider/mtx/resourcesTarBuilder.js +60 -35
  15. package/bin/build/provider/mtx-extension/index.js +57 -0
  16. package/bin/build/provider/mtx-sidecar/index.js +46 -18
  17. package/bin/build/provider/nodejs/index.js +34 -13
  18. package/bin/deploy/to-hana/cfUtil.js +7 -2
  19. package/bin/deploy/to-hana/hana.js +20 -25
  20. package/bin/deploy/to-hana/hdiDeployUtil.js +13 -2
  21. package/bin/serve.js +7 -4
  22. package/lib/compile/{index.js → cds-compile.js} +0 -0
  23. package/lib/compile/extend.js +15 -5
  24. package/lib/compile/minify.js +1 -15
  25. package/lib/compile/parse.js +1 -1
  26. package/lib/compile/resolve.js +2 -2
  27. package/lib/compile/to/srvinfo.js +6 -4
  28. package/lib/{deploy.js → dbs/cds-deploy.js} +9 -8
  29. package/lib/env/{index.js → cds-env.js} +1 -17
  30. package/lib/env/{requires.js → cds-requires.js} +24 -3
  31. package/lib/env/defaults.js +7 -1
  32. package/lib/env/schemas/cds-package.json +11 -0
  33. package/lib/env/schemas/cds-rc.json +614 -0
  34. package/lib/index.js +19 -16
  35. package/lib/log/{errors.js → cds-error.js} +1 -1
  36. package/lib/log/{index.js → cds-log.js} +0 -0
  37. package/lib/ql/Query.js +9 -3
  38. package/lib/ql/SELECT.js +2 -2
  39. package/lib/ql/{index.js → cds-ql.js} +0 -9
  40. package/lib/req/context.js +49 -17
  41. package/lib/req/locale.js +5 -1
  42. package/lib/{serve → srv}/adapters.js +23 -19
  43. package/lib/{connect → srv}/bindings.js +0 -0
  44. package/lib/{connect/index.js → srv/cds-connect.js} +1 -1
  45. package/lib/{serve/index.js → srv/cds-serve.js} +1 -1
  46. package/lib/{serve → srv}/factory.js +1 -1
  47. package/lib/{serve/Service-api.js → srv/srv-api.js} +22 -6
  48. package/lib/{serve/Service-dispatch.js → srv/srv-dispatch.js} +13 -8
  49. package/lib/{serve/Service-handlers.js → srv/srv-handlers.js} +10 -0
  50. package/lib/{serve/Service-methods.js → srv/srv-methods.js} +10 -8
  51. package/lib/srv/srv-models.js +207 -0
  52. package/lib/{serve/Transaction.js → srv/srv-tx.js} +57 -40
  53. package/lib/utils/{tests.js → cds-test.js} +2 -2
  54. package/lib/utils/cds-utils.js +146 -0
  55. package/lib/utils/index.js +2 -145
  56. package/lib/utils/jest.js +43 -0
  57. package/lib/utils/resources/index.js +15 -25
  58. package/lib/utils/resources/tar.js +18 -41
  59. package/libx/_runtime/auth/index.js +14 -11
  60. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +7 -19
  61. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -4
  62. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -4
  63. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -4
  64. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +1 -4
  65. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +2 -2
  66. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +6 -19
  67. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +1 -4
  68. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +1 -1
  69. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +1 -4
  70. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/PrimitiveValueDecoder.js +0 -2
  71. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +3 -1
  72. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +38 -4
  73. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +1 -3
  74. package/libx/_runtime/cds-services/services/utils/differ.js +4 -0
  75. package/libx/_runtime/cds-services/util/errors.js +1 -29
  76. package/libx/_runtime/common/i18n/messages.properties +2 -1
  77. package/libx/_runtime/common/perf/index.js +10 -15
  78. package/libx/_runtime/common/utils/binary.js +3 -4
  79. package/libx/_runtime/common/utils/cqn2cqn4sql.js +0 -1
  80. package/libx/_runtime/common/utils/entityFromCqn.js +8 -5
  81. package/libx/_runtime/common/utils/resolveView.js +1 -1
  82. package/libx/_runtime/common/utils/template.js +1 -1
  83. package/libx/_runtime/db/Service.js +2 -14
  84. package/libx/_runtime/db/expand/expandCQNToJoin.js +28 -25
  85. package/libx/_runtime/db/expand/rawToExpanded.js +7 -6
  86. package/libx/_runtime/db/generic/input.js +8 -1
  87. package/libx/_runtime/db/sql-builder/SelectBuilder.js +37 -18
  88. package/libx/_runtime/extensibility/activate.js +47 -47
  89. package/libx/_runtime/extensibility/add.js +22 -13
  90. package/libx/_runtime/extensibility/addExtension.js +17 -13
  91. package/libx/_runtime/extensibility/defaults.js +25 -30
  92. package/libx/_runtime/extensibility/handler/transformREAD.js +20 -18
  93. package/libx/_runtime/extensibility/linter/allowlist_checker.js +373 -0
  94. package/libx/_runtime/extensibility/linter/annotations_checker.js +113 -0
  95. package/libx/_runtime/extensibility/linter/checker_base.js +20 -0
  96. package/libx/_runtime/extensibility/linter/namespace_checker.js +180 -0
  97. package/libx/_runtime/extensibility/linter.js +32 -0
  98. package/libx/_runtime/extensibility/push.js +77 -20
  99. package/libx/_runtime/extensibility/service.js +29 -12
  100. package/libx/_runtime/extensibility/token.js +56 -0
  101. package/libx/_runtime/extensibility/utils.js +8 -6
  102. package/libx/_runtime/extensibility/validation.js +6 -9
  103. package/libx/_runtime/fiori/generic/new.js +0 -11
  104. package/libx/_runtime/hana/Service.js +0 -1
  105. package/libx/_runtime/hana/conversion.js +12 -1
  106. package/libx/_runtime/hana/customBuilder/CustomFunctionBuilder.js +4 -3
  107. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +5 -0
  108. package/libx/_runtime/hana/pool.js +6 -10
  109. package/libx/_runtime/hana/search2Contains.js +0 -5
  110. package/libx/_runtime/hana/search2cqn4sql.js +1 -0
  111. package/libx/_runtime/messaging/outbox/utils.js +1 -1
  112. package/libx/_runtime/messaging/service.js +11 -6
  113. package/libx/_runtime/remote/utils/data.js +5 -0
  114. package/libx/_runtime/sqlite/Service.js +7 -6
  115. package/libx/_runtime/sqlite/execute.js +41 -28
  116. package/libx/odata/afterburner.js +79 -2
  117. package/libx/odata/cqn2odata.js +9 -7
  118. package/libx/odata/grammar.pegjs +157 -76
  119. package/libx/odata/index.js +9 -3
  120. package/libx/odata/parser.js +1 -1
  121. package/libx/odata/utils.js +39 -5
  122. package/libx/rest/RestAdapter.js +3 -7
  123. package/libx/rest/middleware/delete.js +4 -5
  124. package/libx/rest/middleware/parse.js +3 -2
  125. package/package.json +3 -3
  126. package/server.js +1 -1
  127. package/srv/extensibility-service.cds +6 -3
  128. package/srv/model-provider.cds +3 -1
  129. package/srv/model-provider.js +84 -104
  130. package/srv/mtx.js +7 -1
  131. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +0 -240
@@ -3,10 +3,10 @@ const path = require('path')
3
3
  const BuildTaskHandlerEdmx = require('../buildTaskHandlerEdmx')
4
4
  const { BuildError } = require('../../util')
5
5
  const { BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY, ODATA_VERSION, ODATA_VERSION_V2, FOLDER_GEN, BUILD_NODEJS_EDMX_GENERAION, EDMX_GENERATION,
6
- SKIP_PACKAGE_JSON_GENERATION, CONTENT_EDMX, CONTENT_PACKAGE_JSON, CONTENT_PACKAGELOCK_JSON, CONTENT_NPMRC, CONTENT_CDSRC_JSON, CONTENT_ENV, CONTENT_DEFAULT_ENV_JSON } = require('../../constants')
7
- const { WARNING } = require('../../buildTaskHandler')
6
+ SKIP_PACKAGE_JSON_GENERATION, CONTENT_EDMX, CONTENT_PACKAGE_JSON, CONTENT_PACKAGELOCK_JSON, CONTENT_NPMRC, CONTENT_CDSRC_JSON, CONTENT_ENV, CONTENT_DEFAULT_ENV_JSON, SEMVER_REGEX } = require('../../constants')
7
+ const { WARNING, ERROR } = BuildTaskHandlerEdmx
8
8
 
9
- class NodeCfModuleBuilder extends BuildTaskHandlerEdmx {
9
+ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
10
10
  init() {
11
11
  super.init()
12
12
  // set unified option values in order to ease access later on
@@ -28,6 +28,12 @@ class NodeCfModuleBuilder extends BuildTaskHandlerEdmx {
28
28
  this.destSrv = this.isStagingBuild() ? path.resolve(this.task.dest, this.env.folders.srv) : path.join(this.task.dest, FOLDER_GEN)
29
29
  }
30
30
 
31
+ options() {
32
+ const options = super.options(), {cds} = this
33
+ if (cds.requires.extensibility || cds.requires.toggles) options.flavor = 'xtended'
34
+ return options
35
+ }
36
+
31
37
  async build() {
32
38
  const destSrv = this.isStagingBuild() ? this.destSrv : path.resolve(this.destSrv, this.env.folders.srv)
33
39
  const destRoot = this.isStagingBuild() ? this.task.dest : this.destSrv
@@ -48,7 +54,8 @@ class NodeCfModuleBuilder extends BuildTaskHandlerEdmx {
48
54
  await this.collectAllLanguageBundles(dictionary, sources, destSrv, destRoot)
49
55
 
50
56
  if (this.hasBuildOption(CONTENT_EDMX, true)) {
51
- await this.compileToEdmx(dictionary.base, this.destSrv)
57
+ const m = await this.cds.load(sources.base, super.options()) // REVISIT: Quick hack to get inferred model as expected by cds.compile.to.edmx()
58
+ await this.compileToEdmx(m, this.destSrv)
52
59
  }
53
60
 
54
61
  if (!this.hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY)) {
@@ -77,11 +84,11 @@ class NodeCfModuleBuilder extends BuildTaskHandlerEdmx {
77
84
  }
78
85
 
79
86
  /**
80
- * Copy files from the given <em>src</em>' folder (e.g. 'project/srv') to either <em>destRoot</em> (e.g. 'project/gen/srv')
87
+ * Copy files from the given <em>src</em>' folder (e.g. 'project/srv') to either <em>destRoot</em> (e.g. 'project/gen/srv')
81
88
  * or <em>destSrv</em> (e.g. 'project/gen/srv/srv') folders according to the file semantics.
82
- * Files with project semantics like 'package.json' or '.npmrc' file are copied to <em>destRoot</em> while others like '.js' service handlers
89
+ * Files with project semantics like 'package.json' or '.npmrc' file are copied to <em>destRoot</em> while others like '.js' service handlers
83
90
  * are copied to <em>destSrv</em>.
84
- * @param {*} src
91
+ * @param {*} src
85
92
  * @param {*} destRoot - folder name representing the app root folder (e.g. gen/srv)
86
93
  * @param {*} destSrv - folder name representing the app sub-folder (e.g. gen/srv/srv)
87
94
  * @returns the list of files that have been copied
@@ -98,7 +105,8 @@ class NodeCfModuleBuilder extends BuildTaskHandlerEdmx {
98
105
  // TODO shall not copy language bundles - return !/(\/|\\)(node_modules|_i18n)(\/|\\)?$/.test(entry)
99
106
  return !/(\/|\\)node_modules(\/|\\)?$/.test(entry)
100
107
  }
101
- if (srvRootBlockList.test(entry)) {
108
+ // make sure the file exists on srv root level - see https://github.tools.sap/cap/issues/issues/12077
109
+ if (srvRootBlockList.test(entry) && path.dirname(entry) === src) {
102
110
  srvRootFileNames.push(path.basename(entry))
103
111
  return false
104
112
  }
@@ -125,8 +133,8 @@ class NodeCfModuleBuilder extends BuildTaskHandlerEdmx {
125
133
  /**
126
134
  * Copy dedicated files (files with project semantics like package.json, .npmrc, .cdsrc, etc.)
127
135
  * from the given <em>src</em> folder (e.g. 'project') into the given <em>dest</em> folder (e.g. 'project/gen/srv')
128
- * @param {*} src
129
- * @param {*} dest
136
+ * @param {*} src
137
+ * @param {*} dest
130
138
  * @param {*} filter - copy file if filter function returns true
131
139
  */
132
140
  async _copyProjectRootContent(src, dest, filter) {
@@ -163,12 +171,18 @@ class NodeCfModuleBuilder extends BuildTaskHandlerEdmx {
163
171
  async _modifyPackageJson(file) {
164
172
  function _addEnginesField(content) {
165
173
  if (!content.engines || !content.engines.node) {
174
+ // use minimum node version of @sap/cds
166
175
  const { engines } = require('../../../../package.json')
167
176
  if (engines && engines.node) {
168
177
  this.logger.log(`${this.task.for}: adding node engines version to package.json ${engines.node}`)
169
178
  content.engines = content.engines || {}
170
- content.engines.node = engines.node
171
- return true
179
+ const nodeVersion = NodejsModuleBuilder._convertToClosedVersionRange(engines.node)
180
+ if (nodeVersion) {
181
+ content.engines.node = nodeVersion
182
+ return true
183
+ } else {
184
+ this.pushMessage(`Invalid node version defined in engines definition for @sap/cds ${engines.node} - skip node engines definition`, ERROR)
185
+ }
172
186
  }
173
187
  }
174
188
  return false
@@ -184,6 +198,13 @@ class NodeCfModuleBuilder extends BuildTaskHandlerEdmx {
184
198
  await this.write(content).to(file)
185
199
  }
186
200
  }
201
+
202
+ static _convertToClosedVersionRange(rtVersion) {
203
+ rtVersion = rtVersion.trim()
204
+ if (rtVersion.match(SEMVER_REGEX)) {
205
+ return rtVersion.replace(SEMVER_REGEX, '^$2$3$4')
206
+ }
207
+ }
187
208
  }
188
209
 
189
- module.exports = NodeCfModuleBuilder
210
+ module.exports = NodejsModuleBuilder
@@ -77,7 +77,12 @@ class CfUtil {
77
77
  async _cfRequest(urlPath, queryObj, bodyObj) {
78
78
  if (queryObj) {
79
79
  const entries = Object.entries(queryObj);
80
- const queryStr = entries.map((entry) => `${entry[0]}=${encodeURIComponent(entry[1])}`).join('&');
80
+ const queryStr = entries.map(([key, value]) => {
81
+ // commas cause problems in cf curl when not double encoded
82
+ value = value.replace(/,/g, encodeURIComponent(','));
83
+ return `${key}=${encodeURIComponent(value)}`;
84
+ }).join('&');
85
+
81
86
  urlPath = urlPath + `?${queryStr}`;
82
87
  }
83
88
 
@@ -125,7 +130,7 @@ class CfUtil {
125
130
  async getCfTargetFromCli() {
126
131
  const result = await this._cfRun('target');
127
132
  return {
128
- apiEndpoint: this._extract(result, /api endpoint\s*:\s*(.+)/i, `CF API endpoint is missing. Use 'cf login' to login.`),
133
+ apiEndpoint: this._extract(result, /api endpoint\s*:\s*([^\s]+)/i, `CF API endpoint is missing. Use 'cf login' to login.`),
129
134
  user: this._extract(result, /user\s*:\s*(.+)/i, `CF user is missing. Use 'cf login' to login.`),
130
135
  org: this._extract(result, /org\s*:\s*(.+)/i, `CF org is missing. Use 'cf target -o <ORG> to specify.`),
131
136
  space: this._extract(result, /space\s*:\s*(.+)/i, `CF space is missing. Use 'cf target -s <SPACE>' to specify.`),
@@ -35,7 +35,7 @@ class HanaDeployer {
35
35
  this.logger = logger;
36
36
 
37
37
  this.logger.log(`${bold('Starting deploy to SAP HANA ...')}`);
38
- if (vcapFile) {
38
+ if (vcapFile) {
39
39
  this.logger.log();
40
40
  this.logger.log(`Using VCAP_SERVICES from file ${vcapFile} (beta feature).`);
41
41
  bindCallback = null // credentials are given - no cds bind then
@@ -49,9 +49,9 @@ class HanaDeployer {
49
49
 
50
50
  const { buildResults } = await this._build(buildTaskOptions, model);
51
51
 
52
- let vcapServices;
52
+ let vcapFileEnv;
53
53
  if (vcapFile) {
54
- vcapServices = await this._loadVCAPServices(vcapFile);
54
+ vcapFileEnv = await this._loadDefaultEnv(vcapFile);
55
55
  }
56
56
 
57
57
  for (const buildResult of buildResults) {
@@ -71,7 +71,7 @@ class HanaDeployer {
71
71
  serviceKeyName = cfServiceInstanceKeyName;
72
72
  serviceName = serviceName || cfServiceInstanceName;
73
73
 
74
- vcapServices = this._getVCAPServicesEntry(cfServiceInstanceName, serviceKey);
74
+ vcapFileEnv = this._getVCAPServicesEntry(cfServiceInstanceName, serviceKey);
75
75
 
76
76
  if (!noSave && !bindCallback) {
77
77
  await this._addInstanceToDefaultEnvJson([currentModelFolder, projectPath], cfServiceInstanceName, serviceKey);
@@ -95,7 +95,7 @@ class HanaDeployer {
95
95
  });
96
96
  }
97
97
 
98
- await hdiDeployUtil.deploy(currentModelFolder, vcapServices, hdiOptions);
98
+ await hdiDeployUtil.deploy(currentModelFolder, vcapFileEnv, hdiOptions);
99
99
 
100
100
  if (bindCallback) {
101
101
  const args = [path.relative(projectPath, buildResult.task.src)];
@@ -204,16 +204,12 @@ class HanaDeployer {
204
204
  }
205
205
 
206
206
 
207
- async _loadVCAPServices(vcapFile) {
207
+ async _loadDefaultEnv(defaultEnvFile) {
208
208
  try {
209
- const content = JSON.parse(await fs.readFile(vcapFile));
210
- if (!content.VCAP_SERVICES) {
211
- throw new Error(`The vcap file ${vcapFile} does not contain a VCAP_SERVICES entry.`);
212
- }
213
-
214
- return content.VCAP_SERVICES;
209
+ const content = await fs.readFile(defaultEnvFile);
210
+ return JSON.parse(content);
215
211
  } catch (err) {
216
- throw new Error(`Error reading vcap file: ${err.message}`);
212
+ throw new Error(`Error reading default env file: ${err.message}`);
217
213
  }
218
214
  }
219
215
 
@@ -237,13 +233,10 @@ class HanaDeployer {
237
233
  }
238
234
 
239
235
  const hanaEntry = this._getVCAPServicesEntry(serviceInstanceName, serviceKey)
240
- defaultEnvJson.VCAP_SERVICES = {
241
- ...defaultEnvJson.VCAP_SERVICES,
242
- ...hanaEntry
243
- }
236
+ Object.assign(defaultEnvJson, hanaEntry);
244
237
 
245
238
  this.logger.log(`Writing ${defaultEnvJsonPath}`);
246
- await fs.mkdir(path.dirname(defaultEnvJsonPath), {recursive: true})
239
+ await fs.mkdir(path.dirname(defaultEnvJsonPath), { recursive: true })
247
240
  await fs.writeFile(defaultEnvJsonPath, JSON.stringify(defaultEnvJson, null, 2));
248
241
  }
249
242
  }
@@ -251,13 +244,15 @@ class HanaDeployer {
251
244
 
252
245
  _getVCAPServicesEntry(serviceInstanceName, serviceKey) {
253
246
  return {
254
- hana: [
255
- {
256
- name: serviceInstanceName,
257
- tags: ['hana'],
258
- credentials: serviceKey
259
- }
260
- ]
247
+ VCAP_SERVICES: {
248
+ hana: [
249
+ {
250
+ name: serviceInstanceName,
251
+ tags: ['hana'],
252
+ credentials: serviceKey
253
+ }
254
+ ]
255
+ }
261
256
  };
262
257
  }
263
258
 
@@ -20,7 +20,10 @@ class HdiDeployUtil {
20
20
  await this._executeDeploy(dbDir, env, logger);
21
21
  }
22
22
 
23
- async deploy(dbDir, vcapServices, options = {}) {
23
+ async deploy(dbDir, vcapEnv, options) {
24
+ vcapEnv = vcapEnv || {}; // handles null and undefined
25
+ options = options || {};
26
+
24
27
  LOG.log();
25
28
  LOG.log(`Deploying to HANA from ${dbDir}`);
26
29
 
@@ -34,7 +37,15 @@ class HdiDeployUtil {
34
37
  deployerEnv = hdiDeployLib.clean_env(deployerEnv);
35
38
  }
36
39
 
37
- deployerEnv.VCAP_SERVICES = JSON.stringify(vcapServices);
40
+ if (vcapEnv.VCAP_SERVICES) {
41
+ deployerEnv.VCAP_SERVICES = JSON.stringify(vcapEnv.VCAP_SERVICES);
42
+ }
43
+ if (vcapEnv.SERVICE_REPLACEMENTS) {
44
+ deployerEnv.SERVICE_REPLACEMENTS = JSON.stringify(vcapEnv.SERVICE_REPLACEMENTS);
45
+ }
46
+ if (vcapEnv.TARGET_CONTAINER) {
47
+ deployerEnv.TARGET_CONTAINER = vcapEnv.TARGET_CONTAINER;
48
+ }
38
49
 
39
50
  if (options.autoUndeploy) {
40
51
  LOG.log(`Hdi deployer automatically undeploys deleted resources using --auto-undeploy.`);
package/bin/serve.js CHANGED
@@ -181,7 +181,7 @@ async function serve (all=[], o={}) { // NOSONAR
181
181
  log ('loading plugin:', {impl})
182
182
  // TODO support ESM plugins. But see cap/cds/pull/1838#issuecomment-1177200 !
183
183
  const plugin = require(impl)
184
- if (plugin.activate) plugin.activate(each)
184
+ if (plugin.activate) await plugin.activate(each)
185
185
  }
186
186
 
187
187
  // bootstrap server from project-local server.js or from @sap/cds/server.js
@@ -258,16 +258,19 @@ function _prepare_logging () { // NOSONAR
258
258
  cds.on ('serving', (srv) => {
259
259
  const details = {}
260
260
  if (srv.path) details.path = srv.path
261
- if (srv._source) details.impl = local( srv._source.startsWith('@sap') ? require.resolve(srv._source) : srv._source )
261
+ if (srv._source && !srv._source.startsWith('@sap'))
262
+ details.impl = local(srv._source)
262
263
  log (`${srv.mocked ? 'mocking' : 'serving'} ${srv.name}`, details)
263
264
  })
264
265
 
266
+ cds.on ('served', ()=> console.log())
267
+
265
268
  // print info when we are finally on air
266
269
  cds.once ('listening', ({url})=>{
267
- console.log ()
270
+ console.log()
268
271
  log ('server listening on',{url})
269
272
  _timer && console.timeEnd (_timer)
270
- if (process.stdin.isTTY) log (`[ terminate with ^C ]`)
273
+ if (process.stdin.isTTY) log (`[ terminate with ^C ]\n`)
271
274
  })
272
275
  }
273
276
 
File without changes
@@ -1,8 +1,18 @@
1
+ const compile = require ('./cds-compile')
1
2
  const { extend } = require ('../lazy')
2
- const compile = require ('./index')
3
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)
4
+ module.exports = o => o.definitions ? { with(...csns) {
5
+
6
+ const csn=o, merged = { definitions: {}, extensions: [] }
7
+ for (const { definitions, extensions } of csns) {
8
+ if (definitions) Object.assign(merged.definitions, definitions)
9
+ if (extensions) merged.extensions.push(...extensions)
10
+ }
11
+ const extended = compile({
12
+ 'base.csn': compile.to.json(csn),
13
+ 'ext.csn': compile.to.json(merged)
14
+ })
15
+ extended.$sources = csn.$sources // required to load resources like i18n later on
16
+ return extended
17
+
8
18
  }} : extend(o)
@@ -35,7 +35,7 @@ module.exports = function cds_minify (csn, _roots) { // IMPORTANT: don't add cds
35
35
  }
36
36
  function _visit (d) {
37
37
  if (typeof d === 'string') {
38
- if (isInReservedNamespace(d)) return
38
+ if (cds.compiler.model.isInReservedNamespace(d)) return
39
39
  else d = all[d]
40
40
  } else if (d.ref) return d.ref.reduce((p,n) => {
41
41
  let d = (p.elements || csn.definitions[p.target].elements)[n.id || n] // > n.id -> view with parameters
@@ -61,18 +61,4 @@ module.exports = function cds_minify (csn, _roots) { // IMPORTANT: don't add cds
61
61
  return minified
62
62
  }
63
63
 
64
- /**
65
- * TODO - keep in sync with @sap/cds-compiler. Use their public API once it is available!
66
- * Checks whether the given absolute path is inside a reserved namespace.
67
- *
68
- * @param {string} absolute
69
- * @returns {boolean}
70
- */
71
- function isInReservedNamespace(absolute) {
72
- return absolute.startsWith( 'cds.') &&
73
- !absolute.match(/^cds\.foundation(\.|$)/) &&
74
- !absolute.match(/^cds\.outbox(\.|$)/) && // Requested by Node runtime
75
- !absolute.match(/^cds\.xt(\.|$)/); // Requested by Mtx
76
- }
77
-
78
64
  const _minified = Symbol('minified')
@@ -56,7 +56,7 @@ const tagged = (parse, strings, ...values) => {
56
56
  all[2*i] = s
57
57
  all[2*i+1] = v instanceof cds.entity ? v.name : ':'+i
58
58
  if (typeof v === 'string' && s.endsWith(' like ') && !v.includes('%')) values[i] = `%${v}%`
59
- if (Array.isArray(v) && s.endsWith(' in ')) values[i] = {list: v.map(cxn4)}
59
+ if (Array.isArray(v) && s.toLowerCase().endsWith(' in ')) values[i] = {list: v.map(cxn4)}
60
60
  }
61
61
  all[2*i] = strings[i]
62
62
  return merge (parse(all.join('')), values)
@@ -20,7 +20,7 @@ module.exports = exports = function cds_resolve (model, o={}) { // NOSONAR
20
20
  if (model === '*') return _resolve_all(o,this)
21
21
  if (Array.isArray(model)) {
22
22
  const resolved = [... new Set(model)] .reduce ((prev,next) => prev.concat (this.resolve(next,o)||[]), [])
23
- return o.dry || o === false ? resolved : _resolved (resolved)
23
+ return o.dry || o === false ? [...new Set(resolved.flat())] : _resolved (resolved)
24
24
  }
25
25
  if (model.endsWith('/*')) return _resolve_subdirs_in(model,o,this)
26
26
 
@@ -69,7 +69,7 @@ const _required = (cds,env=cds.env) => Object.values(env.requires) .map (r => r.
69
69
  const _resolve = require('module')._resolveFilename
70
70
 
71
71
  function _resolve_all (o,cds) {
72
- const {roots} = o.env || cds.env; if (o.dry || o === false) return [ ...roots, ..._required(cds) ]
72
+ const {roots} = o.env || cds.env; if (o.dry || o === false) return [ ...roots, ...new Set(_required(cds).flat()) ]
73
73
  const cache = o.cache || exports.cache
74
74
  const cached = cache['*']; if (cached) return cached
75
75
  cache['*'] = [] // important to avoid endless recursion on '*'
@@ -69,21 +69,23 @@ module.exports = (model, options={}) => {
69
69
 
70
70
  function _javaPrefix() {
71
71
  let is_java
72
+ const javaPrefixDefault = 'odata/v4/'
72
73
  for (let s of model.$sources) {
73
74
  const file = isfile (join (s,'../src/main/resources/application.yaml'))
74
75
  if (file) {
75
76
  const yaml = cds.load.yaml(file)
76
- for (let {cds} of Array.isArray(yaml) ? yaml : [yaml]) {
77
+ for (let yamlDoc of Array.isArray(yaml) ? yaml : [yaml]) {
78
+ const cds = yamlDoc?.cds;
77
79
  if (!cds) continue
78
80
  return cds['odataV4.endpoint.path'] || cds['odataV2.endpoint.path'] // https://cap.cloud.sap/docs/java/application-services#configure-base-path
79
81
  || cds['odata-v4.endpoint.path'] || cds['odata-v2.endpoint.path'] // older/intermediate config, keep for backward compatibility
80
-
82
+ || javaPrefixDefault
81
83
  }
82
- return 'odata/v4/'
84
+ return javaPrefixDefault
83
85
  }
84
86
  else if (isfile (join(s,'../pom.xml'))) is_java = true
85
87
  }
86
- return is_java && 'odata/v4/'
88
+ return is_java && javaPrefixDefault
87
89
  }
88
90
 
89
91
  }
@@ -1,4 +1,4 @@
1
- const cds = require('./index'), { local, inspect } = cds.utils
1
+ const cds = require('../index'), { local, inspect } = cds.utils
2
2
  const DEBUG = cds.debug('deploy')
3
3
  /* eslint-disable no-console */
4
4
 
@@ -111,7 +111,7 @@ exports.create = async function (db, csn=db.model, o) {
111
111
  console.log(); for (let each of drops) console.log(each)
112
112
  console.log(); for (let each of creates) console.log(each,'\n')
113
113
  return
114
- } else return db.tx (async tx => {
114
+ } else return db.run (async tx => {
115
115
  await tx.run(drops)
116
116
  await tx.run(creates)
117
117
  return true
@@ -120,8 +120,9 @@ exports.create = async function (db, csn=db.model, o) {
120
120
  }
121
121
 
122
122
 
123
- exports.init = (db, csn=db.model, log=()=>{}) => db.tx (async tx => {
124
- const resources = await exports.resources(csn), inits=[]
123
+ exports.init = (db, csn=db.model, log=()=>{}) => db.run (async tx => {
124
+ const resources = await exports.resources(csn, {testdata: cds.env.features.test_data})
125
+ const inits=[]
125
126
  for (let [file,e] of Object.entries(resources)) {
126
127
  if (e === '*') { // init.js/ts
127
128
  let x = await cds.utils._import(file); if (!x) continue
@@ -143,9 +144,9 @@ exports.init = (db, csn=db.model, log=()=>{}) => db.tx (async tx => {
143
144
  })
144
145
 
145
146
 
146
- exports.resources = async function (csn) {
147
+ exports.resources = async function (csn, opts) {
147
148
  if (!csn || !csn.definitions) csn = await cds.load (csn||'*') .then (cds.minify)
148
- const folders = await exports.resources.folders(csn)
149
+ const folders = await exports.resources.folders(csn, opts)
149
150
  const found={}, ts = process.env.CDS_TYPESCRIPT
150
151
  for (let folder of folders) {
151
152
  // fetching init.js files
@@ -173,11 +174,11 @@ exports.resources = async function (csn) {
173
174
  }
174
175
 
175
176
 
176
- exports.resources.folders = async function (csn) {
177
+ exports.resources.folders = async function (csn, o={}) {
177
178
  if (!csn || !csn.definitions) csn = await cds.load (csn||'*') .then (cds.minify)
178
179
  const folders = new Set (csn.$sources.map (path.dirname) .filter (f => f !== cds.home))
179
180
  if (cds.env.folders.db) folders.add (path.resolve(cds.root, cds.env.folders.db))
180
- if (cds.env.features.test_data) folders.add (path.resolve(cds.root,'test/'))
181
+ if (o.testdata) folders.add (path.resolve(cds.root,'test/'))
181
182
  return folders
182
183
  }
183
184
 
@@ -1,4 +1,4 @@
1
- const { isfile, fs, path } = require('../utils')
1
+ const { isfile, fs, path } = require('../utils/cds-utils')
2
2
  const DEFAULTS = require('./defaults'), defaults = require.resolve ('./defaults')
3
3
  const compat = require('./compat')
4
4
  const presets = require('./presets')
@@ -130,22 +130,6 @@ class Config {
130
130
  return option.split('.').reduce ((p,n)=> p && p[n], this)
131
131
  }
132
132
 
133
- /**
134
- * This is `this.requires` plus additional entries for all cds.required.<name>.service
135
- * @returns {import('./requires')}
136
- */
137
- get required_services_or_defs() {
138
- const dict = Object.create (this.requires)
139
- for (let [name,e] of Object.entries (this.requires)) if (e.service) {
140
- if (e.service in dict && !e.override && e.service !== name) {
141
- console.error (`Datasource name '${e.service}' conflicts with 'service' definition referred to in 'cds.requires.${name}':`, e)
142
- throw new Error (`Datasource name '${e.service}' conflicts with service definition`)
143
- }
144
- else dict[e.service] = { ...e, name }
145
- }
146
- return super.required_services_or_defs = dict
147
- }
148
-
149
133
  get profiles() {
150
134
  return super.profiles = Array.from (this._profiles)
151
135
  }
@@ -11,6 +11,23 @@ exports = module.exports = {
11
11
  '[production]': { kind: 'jwt-auth' }
12
12
  },
13
13
 
14
+
15
+ /**
16
+ * This is the implementation for `cds.requires` which is `cds.env.requires`
17
+ * plus additional entries for all cds.required.<name>.service
18
+ * @returns {import('./cds-requires')}
19
+ */
20
+ _resolved() {
21
+ const dict = Object.create (this)
22
+ for (let [name,e] of Object.entries (this)) if (e.service) {
23
+ if (e.service in dict && !e.override && e.service !== name) {
24
+ console.error (`Datasource name '${e.service}' conflicts with 'service' definition referred to in 'cds.requires.${name}':`, e) // eslint-disable-line no-console
25
+ throw new Error (`Datasource name '${e.service}' conflicts with service definition`)
26
+ }
27
+ else dict[e.service] = { ...e, name }
28
+ }
29
+ return dict
30
+ }
14
31
  }
15
32
 
16
33
 
@@ -85,6 +102,10 @@ const _databases = {
85
102
  '[development]': { kind: 'sqlite', credentials: { url: ':memory:' } },
86
103
  '[production]': { kind: 'hana' },
87
104
  },
105
+ "sql-mt": {
106
+ '[development]': { kind: 'sqlite' },
107
+ '[production]': { kind: 'hana-mt' },
108
+ },
88
109
  "sqlite": _compat_to_use({
89
110
  credentials: { url: 'sqlite.db' },
90
111
  impl: `${_runtime}/sqlite/Service.js`,
@@ -208,11 +229,11 @@ const _mtx_services = {
208
229
  model: "@sap/cds/srv/extensions",
209
230
  },
210
231
  "extensibility": {
211
- model: [ "@sap/cds/srv/mtx", "@sap/cds/srv/extensions" ]
232
+ model: [ "@sap/cds/srv/mtx", "@sap/cds/srv/extensions" ],
233
+ tenantCheckInterval: 10000
212
234
  },
213
235
  "cds.xt.ExtensibilityService": {
214
- model: "@sap/cds/srv/extensibility-service",
215
- tenantCheckInterval: 10000
236
+ model: "@sap/cds/srv/extensibility-service"
216
237
  },
217
238
  "uiflex": "extensibility", // REVISIT: temporary for compatibility with cds-mtxs tests -> eliminate "uiflex" later
218
239
 
@@ -1,9 +1,15 @@
1
+ const path = require('path')
1
2
  const [,major,minor] = /v(\d+)\.(\d+)/.exec(process.version)
2
3
  const production = process.env.NODE_ENV === 'production'
3
4
 
4
5
  module.exports = {
5
6
 
6
- requires: require('./requires'),
7
+ requires: require('./cds-requires'),
8
+
9
+ schemas: {
10
+ 'cds-rc.json': path.join(__dirname, 'schemas/cds-rc.json'),
11
+ 'cds-package.json': path.join(__dirname, 'schemas/cds-package.json'),
12
+ },
7
13
 
8
14
  features: {
9
15
  folders: 'fts/*', // where to find feature toggles -> switch on by default when released
@@ -0,0 +1,11 @@
1
+ {
2
+ "title": "JSON schema for CDS configuration in package.json",
3
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
4
+ "description": "This is a JSON schema representation of the CDS project configuration inside a project root level package.json",
5
+ "type": "object",
6
+ "properties": {
7
+ "cds": {
8
+ "$ref": "cdsJsonSchema://schemas/cds-rc.json"
9
+ }
10
+ }
11
+ }