@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
@@ -8,9 +8,6 @@ const fs = require('fs').promises;
8
8
  const { BuildTaskEngine, BuildTaskFactory } = require('../../build');
9
9
  const buildConstants = require('../../build/constants');
10
10
 
11
- const cds = require('../../../lib');
12
- const LOG = cds.log ? cds.log('deploy') : console;
13
-
14
11
  const cfUtil = require('./cfUtil');
15
12
  const hdiDeployUtil = require('./hdiDeployUtil');
16
13
  const gitUtil = require('./gitUtil');
@@ -29,41 +26,53 @@ class HanaDeployer {
29
26
  this.spawnSync = cp.spawnSync;
30
27
  }
31
28
 
29
+ getFromEnv(...varNames) {
30
+ const result = {}
31
+ for (const varName of varNames) {
32
+ if (process.env[varName]) {
33
+ try {
34
+ result[varName] = JSON.parse(process.env[varName]);
35
+ } catch (err) {
36
+ throw new Error(`Error parsing environment variable ${varName}`);
37
+ }
38
+ }
39
+ }
40
+ return result;
41
+ }
32
42
 
33
- async deploy(model, serviceName, noSave, tunnelAddress, buildTaskOptions, vcapFile, undeployWhitelist, hdiOptions, bindCallback, logger = LOG) { // NOSONAR
43
+ async deploy(model, serviceName, noSave, tunnelAddress, buildTaskOptions, vcapFile, undeployWhitelist, hdiOptions, bindCallback) { // NOSONAR
34
44
  hdiOptions = hdiOptions || {}
35
- this.logger = logger;
36
45
 
37
- this.logger.log(`${bold('Starting deploy to SAP HANA ...')}`);
46
+ console.log(`${bold('Starting deploy to SAP HANA ...')}`);
38
47
  if (vcapFile) {
39
- this.logger.log();
40
- this.logger.log(`Using VCAP_SERVICES from file ${vcapFile} (beta feature).`);
48
+ console.log(`Using VCAP_SERVICES from file ${vcapFile} (beta feature).`);
41
49
  bindCallback = null // credentials are given - no cds bind then
50
+ } else if (bindCallback) {
51
+ console.log('Using cds bind');
42
52
  }
43
- else if (bindCallback) {
44
- this.logger.log('Using cds bind');
45
- }
46
- this.logger.log();
47
53
 
48
54
  const projectPath = path.resolve(process.env._TEST_CWD || process.cwd());
49
55
 
50
- const { buildResults } = await this._build(buildTaskOptions, model);
56
+ const { buildResults } = await this._build(model, buildTaskOptions);
51
57
 
52
- let vcapFileEnv;
53
- if (vcapFile) {
54
- vcapFileEnv = await this._loadDefaultEnv(vcapFile);
58
+ let globalVcapEnv = vcapFile ? await this._loadEnvFile(vcapFile) : {};
59
+ globalVcapEnv = { ...globalVcapEnv, ...this.getFromEnv('VCAP_SERVICES', 'SERVICE_REPLACEMENTS') };
60
+ if (process.env.TARGET_CONTAINER) {
61
+ globalVcapEnv.TARGET_CONTAINER = process.env.TARGET_CONTAINER;
55
62
  }
56
63
 
57
64
  for (const buildResult of buildResults) {
65
+ let vcapEnv = globalVcapEnv;
58
66
  let serviceKeyName;
59
67
  const currentModelFolder = buildResult.result.dest;
60
68
 
61
69
  if (undeployWhitelist) {
62
- this.logger.log('Writing undeploy.json');
70
+ console.log('Writing undeploy.json');
63
71
  await fs.write(path.join(currentModelFolder, 'undeploy.json'), JSON.stringify(undeployWhitelist, null, 2));
64
72
  }
65
73
 
66
- if (vcapFile) {
74
+ const isEmpty = !Object.keys(vcapEnv).length;
75
+ if (!isEmpty) {
67
76
  await fs.mkdir(currentModelFolder, { recursive: true });
68
77
  } else {
69
78
  const { cfServiceInstanceName, cfServiceInstanceKeyName, serviceKey } =
@@ -71,7 +80,7 @@ class HanaDeployer {
71
80
  serviceKeyName = cfServiceInstanceKeyName;
72
81
  serviceName = serviceName || cfServiceInstanceName;
73
82
 
74
- vcapFileEnv = this._getVCAPServicesEntry(cfServiceInstanceName, serviceKey);
83
+ vcapEnv = this._getVCAPServicesEntry(cfServiceInstanceName, serviceKey);
75
84
 
76
85
  if (!noSave && !bindCallback) {
77
86
  await this._addInstanceToDefaultEnvJson([currentModelFolder, projectPath], cfServiceInstanceName, serviceKey);
@@ -81,7 +90,7 @@ class HanaDeployer {
81
90
  // Check if deployer is already installed, otherwise only install this one, not the rest of dependencies.
82
91
  if (!await hdiDeployUtil.findHdiDeployLib(currentModelFolder)) {
83
92
  const { deployerName, deployerVersionSpec } = hdiDeployUtil;
84
- this.logger.log(`Installing ${deployerName}`);
93
+ console.log(`Installing ${deployerName}`);
85
94
 
86
95
  // spawn has not callback and cannot be promisified, using sync instead
87
96
  this.spawnSync(`npm`, [`install`, `${deployerName}@${deployerVersionSpec}`, (noSave ? '--no-save' : '--save-dev')], {
@@ -95,12 +104,12 @@ class HanaDeployer {
95
104
  });
96
105
  }
97
106
 
98
- await hdiDeployUtil.deploy(currentModelFolder, vcapFileEnv, hdiOptions);
107
+ await hdiDeployUtil.deploy(currentModelFolder, vcapEnv, hdiOptions);
99
108
 
100
109
  if (bindCallback) {
101
110
  const args = [path.relative(projectPath, buildResult.task.src)];
102
111
  const options = { to: `${serviceName}:${serviceKeyName}`, kind: buildResult.task.for }
103
- await bindCallback(args, options, LOG);
112
+ await bindCallback(args, options);
104
113
  }
105
114
  }
106
115
 
@@ -108,8 +117,8 @@ class HanaDeployer {
108
117
  await gitUtil.ensureFileIsGitignored('default-env.json', projectPath);
109
118
  }
110
119
 
111
- this.logger.log(`If not already done, use ${info('cds add hana')} to configure the project for SAP HANA.\n`);
112
- this.logger.log(`Done.`);
120
+ console.log(`If not already done, use ${info('cds add hana')} to configure the project for SAP HANA.\n`);
121
+ console.log(`Done.`);
113
122
 
114
123
  return { buildResults };
115
124
  }
@@ -124,14 +133,14 @@ class HanaDeployer {
124
133
  if (serviceName) {
125
134
  cfServiceInstanceName = serviceName;
126
135
  } else {
127
- const cfServiceDescriptor = await mtaUtil.getHanaDbModuleDescriptor(projectPath, modelName, this.logger);
136
+ const cfServiceDescriptor = await mtaUtil.getHanaDbModuleDescriptor(projectPath, modelName);
128
137
  cfServiceInstanceName = cfServiceDescriptor.hdiServiceName;
129
138
  cfServiceInstanceMta = cfServiceDescriptor.hdiService
130
139
  }
131
140
 
132
- this.logger.log();
141
+ console.log();
133
142
  this._validateServiceInstanceName(cfServiceInstanceName);
134
- this.logger.log(`Using container ${bold(cfServiceInstanceName)}`);
143
+ console.log(`Using container ${bold(cfServiceInstanceName)}`);
135
144
 
136
145
  let cfConfig = cfServiceInstanceMta && cfServiceInstanceMta.parameters && cfServiceInstanceMta.parameters.config;
137
146
  const serviceInstance = await this.createHanaService(cfServiceInstanceName, cfConfig);
@@ -141,7 +150,7 @@ class HanaDeployer {
141
150
  this._validateServiceKey(serviceKey, cfServiceInstanceKeyName);
142
151
 
143
152
  if (tunnelAddress) {
144
- this.logger.log(`Using tunnel address ${bold(tunnelAddress)} (beta feature)`);
153
+ console.log(`Using tunnel address ${bold(tunnelAddress)} (beta feature)`);
145
154
  serviceKey = this._injectTunnelAddress(serviceKey, tunnelAddress)
146
155
  }
147
156
 
@@ -155,11 +164,11 @@ class HanaDeployer {
155
164
  return await cfUtil.getOrCreateService('hana', 'hdi-shared', instanceName, cfConfig);
156
165
  } catch (error) {
157
166
  if (error.command && /offering .* not found/i.test(error.command.stderr)) {
158
- this.logger.log(`Falling back to 'hanatrial'`);
167
+ console.log(`Falling back to 'hanatrial'`);
159
168
  return await cfUtil.getOrCreateService('hanatrial', 'hdi-shared', instanceName, cfConfig);
160
169
  }
161
170
  else if (error.command && /no database/i.test(error.command.stderr)) {
162
- this.logger.log(`No database connected to 'hana' service. Falling back to 'hanatrial'`);
171
+ console.log(`No database connected to 'hana' service. Falling back to 'hanatrial'`);
163
172
  return await cfUtil.getOrCreateService('hanatrial', 'hdi-shared', instanceName, cfConfig);
164
173
  }
165
174
  throw error;
@@ -181,7 +190,7 @@ class HanaDeployer {
181
190
  }
182
191
 
183
192
 
184
- async _build(buildTaskOptions, model) {
193
+ async _build(model, buildTaskOptions) {
185
194
  buildTaskOptions = buildTaskOptions || {
186
195
  root: process.env._TEST_CWD || process.cwd(),
187
196
  cli: true,
@@ -193,18 +202,16 @@ class HanaDeployer {
193
202
  if (typeof model === 'string') {
194
203
  model = [model];
195
204
  }
196
-
197
- this.logger.log(`Creating build tasks`);
198
- const buildTaskFactory = new BuildTaskFactory(null, cds);
205
+ console.log(`Creating build tasks`);
206
+ const buildTaskFactory = new BuildTaskFactory();
199
207
  const hanaTasks = await buildTaskFactory.getTasks(buildTaskOptions);
200
- this.logger.log(`Running build`);
208
+ console.log(`Running build`);
201
209
 
202
210
  const buildResults = await new BuildTaskEngine().processTasks(hanaTasks, buildTaskOptions);
203
211
  return { buildResults, hanaTasks }
204
212
  }
205
213
 
206
-
207
- async _loadDefaultEnv(defaultEnvFile) {
214
+ async _loadEnvFile(defaultEnvFile) {
208
215
  try {
209
216
  const content = await fs.readFile(defaultEnvFile);
210
217
  return JSON.parse(content);
@@ -235,7 +242,7 @@ class HanaDeployer {
235
242
  const hanaEntry = this._getVCAPServicesEntry(serviceInstanceName, serviceKey)
236
243
  Object.assign(defaultEnvJson, hanaEntry);
237
244
 
238
- this.logger.log(`Writing ${defaultEnvJsonPath}`);
245
+ console.log(`Writing ${defaultEnvJsonPath}`);
239
246
  await fs.mkdir(path.dirname(defaultEnvJsonPath), { recursive: true })
240
247
  await fs.writeFile(defaultEnvJsonPath, JSON.stringify(defaultEnvJson, null, 2));
241
248
  }
@@ -16,16 +16,15 @@ class HdiDeployUtil {
16
16
  this.deployerVersionSpec = '^4'
17
17
  }
18
18
 
19
- async deployTenant(dbDir, env, logger) {
20
- await this._executeDeploy(dbDir, env, logger);
19
+ async deployTenant(dbDir, env) {
20
+ await this._executeDeploy(dbDir, env);
21
21
  }
22
22
 
23
23
  async deploy(dbDir, vcapEnv, options) {
24
24
  vcapEnv = vcapEnv || {}; // handles null and undefined
25
25
  options = options || {};
26
26
 
27
- LOG.log();
28
- LOG.log(`Deploying to HANA from ${dbDir}`);
27
+ console.log(`Deploying to HANA from ${dbDir}`);
29
28
 
30
29
  const hdiDeployLib = await this._getHdiDeployLib(dbDir);
31
30
 
@@ -48,7 +47,7 @@ class HdiDeployUtil {
48
47
  }
49
48
 
50
49
  if (options.autoUndeploy) {
51
- LOG.log(`Hdi deployer automatically undeploys deleted resources using --auto-undeploy.`);
50
+ console.log(`Hdi deployer automatically undeploys deleted resources using --auto-undeploy.`);
52
51
  hdiDeployOptions.auto_undeploy = true;
53
52
  }
54
53
 
@@ -90,7 +89,7 @@ class HdiDeployUtil {
90
89
  Add it either as a devDependency using 'npm install -D ${this.deployerName}' or install it globally using 'npm install -g ${this.deployerName}'.`);
91
90
  }
92
91
 
93
- LOG.log(`Using HDI deployer from ${libPath}`)
92
+ console.log(`Using HDI deployer from ${libPath}`)
94
93
 
95
94
  // let any error go through and abort deploy
96
95
  return require(libPath);
@@ -104,14 +103,14 @@ Add it either as a devDependency using 'npm install -D ${this.deployerName}' or
104
103
  }
105
104
 
106
105
 
107
- async _executeDeploy(dbDir, env, logger) {
106
+ async _executeDeploy(dbDir, env) {
108
107
  const hdiDeployLib = await this._getHdiDeployLib(dbDir);
109
108
  return new Promise((resolve, reject) => {
110
109
  const callbacks = {
111
- stderrCB: error => (logger || LOG).error(error.toString())
110
+ stderrCB: error => console.error(error.toString())
112
111
  }
113
112
  if (LOG.level !== SILENT) {
114
- callbacks.stdoutCB = (data) => (logger || LOG).log(data.toString());
113
+ callbacks.stdoutCB = (data) => console.log(data.toString());
115
114
  }
116
115
 
117
116
  hdiDeployLib.deploy(dbDir, env, (error, response) => {
@@ -8,11 +8,11 @@ const UTF_8 = 'utf-8'
8
8
  const MTA_YAML = 'mta.yaml'
9
9
  const LOG = cds.log ? cds.log('deploy') : console;
10
10
 
11
- async function getHanaDbModuleDescriptor(projectPath, moduleName, logger = LOG) {
11
+ async function getHanaDbModuleDescriptor(projectPath, moduleName) {
12
12
  // mta might be null if mta.yaml does not exist
13
- const mta = await _getMta(projectPath, logger)
14
- const projectInfo = await _getProjectInfo(projectPath, mta, logger)
15
- const hdiService = _findHdiResource(mta, moduleName, logger)
13
+ const mta = await _getMta(projectPath)
14
+ const projectInfo = await _getProjectInfo(projectPath, mta)
15
+ const hdiService = _findHdiResource(mta, moduleName)
16
16
  const appName = _getApplicationName(mta, moduleName, 'hdb')
17
17
 
18
18
  return {
@@ -23,10 +23,10 @@ async function getHanaDbModuleDescriptor(projectPath, moduleName, logger = LOG)
23
23
  }
24
24
  }
25
25
 
26
- async function getServiceModuleDescriptor(projectPath, moduleName, moduleType, logger = LOG) {
26
+ async function getServiceModuleDescriptor(projectPath, moduleName, moduleType) {
27
27
  // mta might be null if mta.yaml does not exist
28
- const mta = await _getMta(projectPath, logger)
29
- const projectInfo = await _getProjectInfo(projectPath, mta, logger)
28
+ const mta = await _getMta(projectPath)
29
+ const projectInfo = await _getProjectInfo(projectPath, mta)
30
30
  const appName = _getApplicationName(mta, moduleName, moduleType)
31
31
 
32
32
  return {
@@ -35,7 +35,7 @@ async function getServiceModuleDescriptor(projectPath, moduleName, moduleType, l
35
35
  }
36
36
  }
37
37
 
38
- async function _getProjectInfo(projectPath, mta, logger) {
38
+ async function _getProjectInfo(projectPath, mta) {
39
39
  const details = {
40
40
  }
41
41
  // 1. use mta data
@@ -76,8 +76,7 @@ async function _getProjectInfo(projectPath, mta, logger) {
76
76
  }
77
77
  }
78
78
  } catch (e) {
79
- logger.error(`Failed to load ${packageJsonPath} - skip application data`)
80
- logger.error(e)
79
+ console.error(`Failed to load ${packageJsonPath} - skip application data`, e)
81
80
  }
82
81
  }
83
82
  // 3. use project name and static default values
@@ -97,13 +96,13 @@ async function _getProjectInfo(projectPath, mta, logger) {
97
96
  return details
98
97
  }
99
98
 
100
- async function _getMta(projectPath, logger) {
99
+ async function _getMta(projectPath) {
101
100
  // yaml.parse oesn't like null
102
101
  const mtaFilePath = path.join(projectPath, MTA_YAML)
103
102
 
104
103
  const existsMtaYaml = existsSync(mtaFilePath)
105
104
  if (!existsMtaYaml) {
106
- logger.debug('mta.yaml not existing')
105
+ LOG.debug('mta.yaml not existing')
107
106
  return null
108
107
  }
109
108
 
@@ -140,7 +139,7 @@ function _findModule(mta, moduleName, moduleType) {
140
139
  return null
141
140
  }
142
141
 
143
- function _findHdiResource(mta, moduleName, logger) {
142
+ function _findHdiResource(mta, moduleName) {
144
143
  if (mta && Array.isArray(mta.resources)) {
145
144
  const hdiResources = mta.resources.filter(resource => HDI_CONTAINER_TYPES.includes(resource.type))
146
145
 
@@ -156,7 +155,7 @@ function _findHdiResource(mta, moduleName, logger) {
156
155
  }
157
156
  }
158
157
 
159
- logger.warn(`No matching hdi resource found for ${moduleName}. Using ${hdiResources[0].name}.`)
158
+ console.warn(`No matching hdi resource found for ${moduleName}. Using ${hdiResources[0].name}.`)
160
159
  return hdiResources[0]
161
160
  }
162
161
  }
package/bin/mtx/in-cds.js CHANGED
@@ -1,6 +1,8 @@
1
1
  const cds = require('../build/cds')
2
+ const { _oldMtx } = cds.utils
3
+
2
4
  const _is_streamlined_mtx = ()=>{
3
- if (process.env.OLD_MTX) return false
5
+ if (_oldMtx()) return false
4
6
  try { return !!require.resolve('@sap/cds-mtxs/srv/deployment-service') }
5
7
  catch {/* ignored */}
6
8
  }
package/bin/serve.js CHANGED
@@ -238,7 +238,7 @@ async function _local_server_js() {
238
238
 
239
239
  function _prepare_logging () { // NOSONAR
240
240
 
241
- const LOG = cds.log('serve|server',{label:'cds'}); if (!LOG._info) return; else log = LOG.info
241
+ const LOG = cds.log('cds.serve|server',{label:'cds'}); if (!LOG._info) return; else log = LOG.info
242
242
  const _timer = `[cds] - launched at ${new Date().toLocaleString()}, version: ${cds.version}, in`
243
243
  console.time (_timer)
244
244
 
package/bin/version.js CHANGED
@@ -58,6 +58,7 @@ function info(o) {
58
58
  const { npmGlobalModules } = require('./utils/modules');
59
59
  const main = _findPackage (require.main.filename)
60
60
  return {
61
+ // REVISIT: Why do we need all these different hard-coded ways, including proliferation of arguments?
61
62
  ..._versions4(main, {}, true), // usually sap/cds-dk or sap/cds
62
63
  ..._versions4('@sap/cds-dk', {}, null, o), // make sure cds-dk is there, cds-maven-plugin uses it
63
64
  ..._versions4('@sap/cds-dk', {}, null, {...o, label: '@sap/cds-dk (global)', pkg: join(npmGlobalModules(), '@sap/cds-dk')}),
@@ -84,7 +85,7 @@ function _versions4 (pkg_name, info, parent, o={}) {
84
85
  const pkg = require(path)
85
86
  info[o.label || pkg.name] = pkg.version
86
87
  if (!parent || o.all) for (let d in pkg.dependencies) { // recurse sap packages in dependencies...
87
- if (!(d in info) && d.startsWith('@sap/')) _versions4(d, info, pkg.name, o)
88
+ if (!(d in info) && (d.startsWith('@sap/') || d.startsWith('@cap-js/'))) _versions4(d, info, pkg.name, o)
88
89
  }
89
90
  } catch (e) {
90
91
  if (e.code !== 'MODULE_NOT_FOUND') info[pkg_name] = MISSING // unknown error
@@ -12,6 +12,7 @@ const compile = module.exports = Object.assign (cds_compile, {
12
12
  java: require('./for/java'),
13
13
  nodejs: require('./for/nodejs'),
14
14
  drafts: require('./for/drafts'),
15
+ lean_drafts: require('./for/lean_drafts'),
15
16
  odata: require('./for/odata'),
16
17
  sql: require('./for/sql'),
17
18
  }),
@@ -124,6 +124,7 @@ module.exports = exports = {__proto__:compile, _options,
124
124
  }),
125
125
  hdbcds: (csn,o) => compile.to.hdbcds (csn, _options.for.hana(o) ),
126
126
  sql: (csn,o) => compile.to.sql (csn, _options.for.sql(o) ),
127
+ deltaSql: (csn, o, beforeCsn) => compile.to.sql.migration(csn, o, beforeCsn), // or like hdi.migration
127
128
  cdl: (csn,o) => compile.to.cdl (csn, _options4(o||{}) ),
128
129
  },
129
130
  }
@@ -17,7 +17,7 @@ const _been_here = Symbol('is _localized')
17
17
  */
18
18
  function unfold_ddl (ddl, csn, o={}) { // NOSONAR
19
19
  const _locales = _locales_4sql[o.dialect]; if (!_locales) return ddl
20
- const localized_views = ddl.filter (each => each.startsWith('CREATE VIEW localized_'))
20
+ const localized_views = ddl.filter (each => each.startsWith('CREATE VIEW localized_') || each.startsWith('DROP VIEW localized_'))
21
21
  for (const localized_view of localized_views) {
22
22
  for (const locale of _locales) ddl.push (localized_view
23
23
  .replace (/localized_/g, `localized_${locale}_`)
@@ -45,7 +45,7 @@ function unfold_csn (m) { // NOSONAR
45
45
  const pass2 = []
46
46
 
47
47
  const _conf = env.requires.db || env.requires.sql || env.requires.kinds && env.requires.kinds.sql
48
- const _on_sqlite = _conf.kind === 'sqlite' || _conf.dialect === 'sqlite'
48
+ const _on_sqlite = _conf.kind === 'sqlite' || _conf.kind === 'better-sqlite' || _conf.dialect === 'sqlite'
49
49
  const _locales = _on_sqlite && _locales_4sql.sqlite
50
50
 
51
51
  // Pass 1 - add localized.<locale> entities and views
@@ -0,0 +1,83 @@
1
+ const cds = require ('../../index')
2
+ module.exports = function cds_compile_for_lean_drafts(csn, o) {
3
+ const DRAFT_ELEMENTS = new Set([
4
+ 'IsActiveEntity',
5
+ 'HasDraftEntity',
6
+ 'HasActiveEntity',
7
+ 'DraftAdministrativeData',
8
+ 'DraftAdministrativeData_DraftUUID',
9
+ 'SiblingEntity'
10
+ ])
11
+ function _redirect(assoc, target, keys) {
12
+ assoc.target = target.name
13
+ assoc._target = target
14
+ if (keys) assoc.on = _onCondition(assoc.name, keys)
15
+ }
16
+
17
+ function _onCondition(left, keys, right) {
18
+ const on = []
19
+ for (let k in keys)
20
+ DRAFT_ELEMENTS.has(k) || on.push({ ref: [left, k] }, '=', { ref: right ? [right, k] : [k] }, 'and')
21
+ on.pop()
22
+ return on
23
+ }
24
+
25
+ const { Draft } = cds.linked(`
26
+ entity ActiveEntity { key ID: UUID; }
27
+ entity Draft {
28
+ virtual IsActiveEntity : Boolean; // REVISIT: these are calculated fields, aren't they?
29
+ virtual HasDraftEntity : Boolean; // REVISIT: these are calculated fields, aren't they?
30
+ HasActiveEntity : Boolean; // This should be written !!!
31
+ DraftAdministrativeData : Association to DRAFT.DraftAdministrativeData;
32
+ DraftAdministrativeData_DraftUUID : UUID;
33
+ // SiblingEntity : Association to ActiveEntity; // REVISIT: Why didn't we use a managed assoc here?
34
+ }
35
+ entity DRAFT.DraftAdministrativeData {
36
+ key DraftUUID : UUID;
37
+ LastChangedByUser : String(256); LastChangeDateTime : Timestamp;
38
+ CreatedByUser : String(256); CreationDateTime : Timestamp;
39
+ InProcessByUser : String(256);
40
+ DraftIsCreatedByMe : Boolean; // REVISIT: these are calculated fields, aren't they?
41
+ DraftIsProcessedByMe : Boolean; // REVISIT: these are calculated fields, aren't they?
42
+ }
43
+ `).definitions
44
+ function draftEntity(active, model) {
45
+ const _draftEntity = active.name + '.drafts'
46
+ const d = model.definitions[_draftEntity]
47
+ if (d) return d
48
+ // We need to construct a fake draft entity definition
49
+ const draft = { __proto__: active, name: _draftEntity, elements: { ...active.elements, ...Draft.elements } }
50
+ Object.defineProperty(model.definitions, _draftEntity, { value: draft })
51
+ Object.defineProperty(active, 'drafts', { value: draft })
52
+ draft['@cds.persistence.table'] = _draftEntity
53
+ // Recursively add drafts for compositions
54
+ for (const each in draft.elements) {
55
+ const e = draft.elements[each]
56
+ const newEl = Object.create(e)
57
+ if (e.isComposition || (e.isAssociation && e['@odata.draft.enclosed']) || e._isBacklink) {
58
+ _redirect(newEl, draftEntity(e._target, model))
59
+ }
60
+ newEl.parent = draft
61
+ draft.elements[each] = newEl
62
+ }
63
+ // TODO: Redirect associations to localized
64
+ return draft
65
+ }
66
+ for (const name in csn.definitions) {
67
+ const def = csn.definitions[name]
68
+ if (!def._isDraftEnabled || def.name.endsWith('.DraftAdministrativeData'))
69
+ continue
70
+ // so that database ignores them
71
+ ;[
72
+ 'IsActiveEntity',
73
+ 'HasDraftEntity',
74
+ 'HasActiveEntity',
75
+ 'DraftAdministrativeData_DraftUUID',
76
+ 'DraftAdministrativeData'
77
+ ].forEach(s => {
78
+ def.elements[s].virtual = true
79
+ })
80
+ // will insert drafts entities, so that others can use `.drafts` even without incoming draft requests
81
+ draftEntity(def, csn, Draft)
82
+ }
83
+ }
@@ -6,6 +6,7 @@ module.exports = function cds_compile_for_nodejs (csn,o) {
6
6
  dsn = cds.compile.for.drafts (csn,o) //> creates a partial copy -> avoid any cds.linked() before
7
7
  dsn = cds.compile._localized.unfold_csn (dsn)
8
8
  dsn = cds.linked (dsn)
9
+ if (cds.env.features.lean_draft) cds.compile.for.lean_drafts(dsn, o)
9
10
  Object.defineProperty (csn, '_4nodejs', {value:dsn})
10
11
  Object.defineProperty (dsn, '_4nodejs', {value:dsn})
11
12
  return dsn
@@ -3,7 +3,7 @@ const cds = require('../index')
3
3
  module.exports = function cds_minify (csn, _roots) { // IMPORTANT: don't add cds.env.features.skip_unused as default for _roots here, as that will break VSCode's IntelliSense
4
4
  const roots = _roots !== undefined ? _roots : cds.env.features.skip_unused
5
5
  if (roots === false) return csn
6
- if (csn[_minified]) return csn; else Object.defineProperty (csn,_minified,{value:true})
6
+ if (csn[_minified]) return csn
7
7
  const all = csn.definitions, reached = new Set
8
8
  if (roots === 'services') {
9
9
  for (let n in all) if (all[n].kind === 'service') _visit_service(n)
@@ -58,6 +58,7 @@ module.exports = function cds_minify (csn, _roots) { // IMPORTANT: don't add cds
58
58
  const minified = Object.create (csn.__proto__, Object.getOwnPropertyDescriptors(csn))
59
59
  const less = minified.definitions = {}
60
60
  for (let n in all) if (reached.has(all[n])) less[n] = all[n]
61
+ Object.defineProperty (minified,_minified,{value:true})
61
62
  return minified
62
63
  }
63
64
 
@@ -16,7 +16,8 @@ const parse = module.exports = Object.assign (cds_parse, {
16
16
  }},
17
17
  path: (x,...values) => {
18
18
  if (x && x.raw) return tagged (parse.path,x,...values)
19
- if (/^[A-Za-z_$][A-Za-z_0-9.$]*$/.test(x)) return {ref:[x]}
19
+ if (/^[A-Za-z_0-9.$]*$/.test(x)) return {ref:[x]}
20
+ if ((cds.context?.model || cds.model)?.definitions[x]) return {ref:[x]}
20
21
  let {SELECT} = parse.cql('SELECT from '+x)
21
22
  return SELECT.from
22
23
  },
@@ -1,6 +1,6 @@
1
1
  const cds = require ('../..')
2
2
  // eslint-disable-next-line cds/no-missing-dependencies -- needs to be added by app dev
3
- const { SchemaGenerator } = require('@sap/cds-graphql/lib/schema')
3
+ const { SchemaGenerator } = require('@cap-js/graphql/lib/schema')
4
4
 
5
5
  function cds_compile_to_gql (csn) {
6
6
  const m = cds.linked(csn)
@@ -17,6 +17,15 @@ function cds_compile_to_hdbtable (csn,o) {
17
17
  return _2many(all)
18
18
  }
19
19
 
20
+ function cds_compile_to_deltaSql (csn, o, beforeCsn) {
21
+ const options = cdsc._options.for.sql(o);
22
+ const { afterImage, drops, createsAndAlters } = cdsc.to.deltaSql (csn, options, beforeCsn || {definitions: {}, $version: '2.0'} ); // FIXME: As default value in compiler API?
23
+ return {
24
+ afterImage,
25
+ drops: cds.compile._localized.unfold_ddl(drops.map (each => each.replace(/^-- .+\n/,'')), csn, options),
26
+ createsAndAlters: cds.compile._localized.unfold_ddl(createsAndAlters.map (each => each.replace(/^-- .+\n/,'')), csn, options)
27
+ };
28
+ }
20
29
 
21
30
  function cds_compile_to_hdbcds (csn,o) {
22
31
  const all = cdsc.to.hdbcds (cds.minify(csn),o)
@@ -38,7 +47,8 @@ function* _2many (all,_file=f=>f) {
38
47
  module.exports = Object.assign (cds_compile_to_sql, {
39
48
  hdbcds: cds_compile_to_hdbcds,
40
49
  hdbtable: cds_compile_to_hdbtable,
41
- sqlite: { keywords }
50
+ delta: cds_compile_to_deltaSql,
51
+ sqlite: { keywords },
42
52
  })
43
53
 
44
54
 
@@ -13,7 +13,7 @@ class entity extends struct {
13
13
  }
14
14
  get drafts() {
15
15
  return this.own('_drafts') || this.set('_drafts',
16
- this.elements.HasDraftEntity && { name: this.name + '_drafts', keys: this.keys }
16
+ this.elements?.HasDraftEntity && { name: this.name + '_drafts', keys: this.keys }
17
17
  )
18
18
  }
19
19
  _elements (filter) {
package/lib/core/index.js CHANGED
@@ -35,13 +35,12 @@ const roots = _roots ({
35
35
  function _roots (defs) {
36
36
  const linked = { any: classes.any.prototype }
37
37
  for (const t in defs) {
38
- if (t in classes) {
39
- linked[t] = classes[t].prototype
40
- continue
38
+ if (t in classes) linked[t] = classes[t].prototype
39
+ else {
40
+ const c = class extends classes[defs[t].type || 'any'] {}
41
+ classes[t] = Object.defineProperty (c, 'name', {value:t})
42
+ linked[t] = Object.defineProperty (c.prototype, 'name', {value:t})
41
43
  }
42
- const c = class extends classes[defs[t].type || 'any'] {}
43
- linked[t] = Object.defineProperty (c.prototype, 'name', {value:t})
44
- classes[t] = Object.defineProperty (c, 'name', {value:t})
45
44
  }
46
45
  return linked
47
46
  }
@@ -56,9 +55,9 @@ const types = _common ({ __proto__: roots,
56
55
  Int16: {type:'Integer'},
57
56
  Int32: {type:'Integer'},
58
57
  Int64: {type:'Integer'},
59
- Integer16: {type:'Integer'},
60
- Integer32: {type:'Integer'},
61
- Integer64: {type:'Integer'},
58
+ Integer16: {type:'Int16'},
59
+ Integer32: {type:'Int32'},
60
+ Integer64: {type:'Int64'},
62
61
  Decimal: {type:'number'},
63
62
  DecimalFloat: {type:'number'},
64
63
  Float: {type:'number'},
package/lib/core/infer.js CHANGED
@@ -5,6 +5,7 @@ module.exports = (q,defs) => {
5
5
  if (!q._target || q._target.kind !== 'entity') Object.defineProperty (q, '_target', {value:(
6
6
  q.SELECT ? _resolve (q.SELECT.from, defs) :
7
7
  q.INSERT ? _resolve (q.INSERT.into, defs) :
8
+ q.UPSERT ? _resolve (q.UPSERT.into, defs) :
8
9
  q.UPDATE ? _resolve (q.UPDATE.entity, defs) :
9
10
  q.DELETE ? _resolve (q.DELETE.from, defs) :
10
11
  _resolve (undefined)