@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.
- package/CHANGELOG.md +87 -0
- package/apis/cds.d.ts +1 -1
- package/apis/core.d.ts +118 -90
- package/apis/cqn.d.ts +11 -2
- package/apis/internal/inference.d.ts +7 -2
- package/apis/ql.d.ts +45 -11
- package/apis/serve.d.ts +8 -1
- package/apis/services.d.ts +303 -305
- package/bin/build/buildTaskEngine.js +28 -36
- package/bin/build/buildTaskFactory.js +32 -81
- package/bin/build/buildTaskHandler.js +3 -2
- package/bin/build/buildTaskProvider.js +2 -2
- package/bin/build/buildTaskProviderFactory.js +5 -14
- package/bin/build/constants.js +0 -1
- package/bin/build/provider/buildTaskHandlerEdmx.js +7 -6
- package/bin/build/provider/buildTaskHandlerFeatureToggles.js +6 -5
- package/bin/build/provider/buildTaskHandlerInternal.js +9 -30
- package/bin/build/provider/buildTaskProviderInternal.js +70 -58
- package/bin/build/provider/fiori/index.js +6 -5
- package/bin/build/provider/hana/2migration.js +20 -3
- package/bin/build/provider/hana/2tabledata.js +1 -0
- package/bin/build/provider/hana/index.js +40 -17
- package/bin/build/provider/java/index.js +10 -10
- package/bin/build/provider/mtx/index.js +25 -16
- package/bin/build/provider/mtx/resourcesTarBuilder.js +22 -27
- package/bin/build/provider/mtx-extension/index.js +3 -2
- package/bin/build/provider/mtx-sidecar/index.js +16 -15
- package/bin/build/provider/nodejs/index.js +14 -56
- package/bin/build/util.js +56 -16
- package/bin/deploy/to-hana/cfUtil.js +4 -1
- package/bin/deploy/to-hana/gitUtil.js +1 -1
- package/bin/deploy/to-hana/hana.js +45 -38
- package/bin/deploy/to-hana/hdiDeployUtil.js +8 -9
- package/bin/deploy/to-hana/mtaUtil.js +13 -14
- package/bin/mtx/in-cds.js +3 -1
- package/bin/serve.js +1 -1
- package/bin/version.js +2 -1
- package/lib/compile/cds-compile.js +1 -0
- package/lib/compile/cdsc.js +1 -0
- package/lib/compile/etc/_localized.js +2 -2
- package/lib/compile/for/lean_drafts.js +83 -0
- package/lib/compile/for/nodejs.js +1 -0
- package/lib/compile/minify.js +2 -1
- package/lib/compile/parse.js +2 -1
- package/lib/compile/to/gql.js +1 -1
- package/lib/compile/to/sql.js +11 -1
- package/lib/core/entities.js +1 -1
- package/lib/core/index.js +8 -9
- package/lib/core/infer.js +1 -0
- package/lib/dbs/cds-deploy.js +97 -41
- package/lib/env/cds-env.js +9 -10
- package/lib/env/cds-requires.js +8 -2
- package/lib/env/defaults.js +0 -4
- package/lib/env/schemas/cds-rc.json +38 -0
- package/lib/ql/SELECT.js +10 -4
- package/lib/srv/bindings.js +1 -1
- package/lib/srv/factory.js +1 -1
- package/lib/srv/protocols/index.js +3 -1
- package/lib/srv/srv-methods.js +1 -1
- package/lib/utils/cds-utils.js +11 -0
- package/lib/utils/inflect.js +13 -12
- package/lib/utils/tar.js +53 -10
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +1 -15
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/errors/UriSyntaxError.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +6 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/utils/BufferedWriter.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/ConditionalRequestValidator.js +0 -12
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/oDataConfiguration.js +1 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +4 -0
- package/libx/_runtime/cds-services/services/Service.js +23 -1
- package/libx/_runtime/cds-services/util/assert.js +0 -41
- package/libx/_runtime/common/composition/data.js +5 -1
- package/libx/_runtime/common/generic/auth/utils.js +3 -3
- package/libx/_runtime/common/generic/input.js +4 -24
- package/libx/_runtime/common/generic/paging.js +3 -3
- package/libx/_runtime/common/utils/csn.js +21 -15
- package/libx/_runtime/common/utils/draft.js +2 -1
- package/libx/_runtime/common/utils/resolveView.js +25 -4
- package/libx/_runtime/common/utils/rewriteAsterisks.js +3 -1
- package/libx/_runtime/common/utils/rowUUIDGenerator.js +21 -0
- package/libx/_runtime/common/utils/templateProcessor.js +12 -15
- package/libx/_runtime/common/utils/templateProcessorPathSerializer.js +23 -0
- package/libx/_runtime/db/expand/expandCQNToJoin.js +29 -12
- package/libx/_runtime/db/generic/input.js +7 -13
- package/libx/_runtime/db/sql-builder/UpsertBuilder.js +47 -0
- package/libx/_runtime/db/sql-builder/index.js +2 -0
- package/libx/_runtime/db/sql-builder/sqlFactory.js +9 -0
- package/libx/_runtime/db/utils/columns.js +4 -2
- package/libx/_runtime/fiori/generic/read.js +1 -12
- package/libx/_runtime/fiori/lean-draft.js +657 -0
- package/libx/_runtime/fiori/utils/handler.js +1 -1
- package/libx/_runtime/hana/pool.js +16 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +2 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging.js +2 -3
- package/libx/_runtime/messaging/outbox/utils.js +109 -70
- package/libx/_runtime/messaging/service.js +16 -7
- package/libx/_runtime/remote/Service.js +15 -2
- package/libx/_runtime/remote/utils/client.js +41 -11
- package/libx/_runtime/sqlite/Service.js +3 -0
- package/libx/_runtime/sqlite/convertDraftAdminPathExpression.js +56 -0
- package/libx/_runtime/sqlite/customBuilder/CustomUpsertBuilder.js +59 -0
- package/libx/_runtime/sqlite/customBuilder/index.js +5 -0
- package/libx/_runtime/sqlite/execute.js +1 -1
- package/libx/_runtime/types/api.js +2 -2
- package/libx/rest/RestAdapter.js +15 -13
- package/package.json +1 -1
- 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
|
|
43
|
+
async deploy(model, serviceName, noSave, tunnelAddress, buildTaskOptions, vcapFile, undeployWhitelist, hdiOptions, bindCallback) { // NOSONAR
|
|
34
44
|
hdiOptions = hdiOptions || {}
|
|
35
|
-
this.logger = logger;
|
|
36
45
|
|
|
37
|
-
|
|
46
|
+
console.log(`${bold('Starting deploy to SAP HANA ...')}`);
|
|
38
47
|
if (vcapFile) {
|
|
39
|
-
|
|
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(
|
|
56
|
+
const { buildResults } = await this._build(model, buildTaskOptions);
|
|
51
57
|
|
|
52
|
-
let
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
112
|
-
|
|
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
|
|
136
|
+
const cfServiceDescriptor = await mtaUtil.getHanaDbModuleDescriptor(projectPath, modelName);
|
|
128
137
|
cfServiceInstanceName = cfServiceDescriptor.hdiServiceName;
|
|
129
138
|
cfServiceInstanceMta = cfServiceDescriptor.hdiService
|
|
130
139
|
}
|
|
131
140
|
|
|
132
|
-
|
|
141
|
+
console.log();
|
|
133
142
|
this._validateServiceInstanceName(cfServiceInstanceName);
|
|
134
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
20
|
-
await this._executeDeploy(dbDir, env
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =>
|
|
110
|
+
stderrCB: error => console.error(error.toString())
|
|
112
111
|
}
|
|
113
112
|
if (LOG.level !== SILENT) {
|
|
114
|
-
callbacks.stdoutCB = (data) =>
|
|
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
|
|
11
|
+
async function getHanaDbModuleDescriptor(projectPath, moduleName) {
|
|
12
12
|
// mta might be null if mta.yaml does not exist
|
|
13
|
-
const mta = await _getMta(projectPath
|
|
14
|
-
const projectInfo = await _getProjectInfo(projectPath, mta
|
|
15
|
-
const hdiService = _findHdiResource(mta, moduleName
|
|
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
|
|
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
|
|
29
|
-
const projectInfo = await _getProjectInfo(projectPath, mta
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 (
|
|
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
|
}),
|
package/lib/compile/cdsc.js
CHANGED
|
@@ -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
|
package/lib/compile/minify.js
CHANGED
|
@@ -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
|
|
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
|
|
package/lib/compile/parse.js
CHANGED
|
@@ -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-
|
|
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
|
},
|
package/lib/compile/to/gql.js
CHANGED
|
@@ -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('@
|
|
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)
|
package/lib/compile/to/sql.js
CHANGED
|
@@ -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
|
-
|
|
50
|
+
delta: cds_compile_to_deltaSql,
|
|
51
|
+
sqlite: { keywords },
|
|
42
52
|
})
|
|
43
53
|
|
|
44
54
|
|
package/lib/core/entities.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
40
|
-
|
|
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:'
|
|
60
|
-
Integer32: {type:'
|
|
61
|
-
Integer64: {type:'
|
|
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)
|