@sap/cds 5.7.3 → 5.8.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.
- package/CHANGELOG.md +111 -0
- package/app/fiori/routes.js +1 -1
- package/bin/deploy/to-hana/cfUtil.js +251 -138
- package/bin/deploy/to-hana/gitUtil.js +55 -0
- package/bin/deploy/to-hana/hana.js +92 -93
- package/bin/deploy/to-hana/hdiDeployUtil.js +42 -27
- package/bin/deploy/to-hana/index.js +14 -13
- package/bin/mtx/in-cds.js +1 -0
- package/bin/serve.js +1 -1
- package/bin/version.js +1 -0
- package/lib/compile/cdsc.js +0 -6
- package/lib/compile/minify.js +1 -1
- package/lib/compile/resolve.js +1 -1
- package/lib/compile/to/srvinfo.js +1 -1
- package/lib/core/classes.js +21 -1
- package/lib/env/index.js +3 -2
- package/lib/env/requires.js +4 -0
- package/lib/i18n/localize.js +5 -8
- package/lib/index.js +1 -0
- package/lib/log/errors.js +1 -1
- package/lib/ql/SELECT.js +2 -2
- package/lib/req/cds-context.js +1 -1
- package/lib/req/context.js +1 -1
- package/lib/serve/Transaction.js +9 -5
- package/lib/serve/index.js +13 -21
- package/lib/utils/tests.js +90 -66
- package/libx/_runtime/audit/generic/personal/modification.js +0 -8
- package/libx/_runtime/auth/index.js +7 -6
- package/libx/_runtime/auth/strategies/dwc.js +43 -0
- package/libx/_runtime/auth/utils.js +24 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +11 -38
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +12 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +7 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +24 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +43 -38
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +11 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +13 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/boundToCQN.js +1 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/deleteToCQN.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/orderByToCQN.js +9 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +17 -30
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +12 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/AbstractEdmStructuredType.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriHelper.js +7 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriTokenizer.js +5 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/PrimitiveValueDecoder.js +19 -47
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/ValueConverter.js +4 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +7 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/CommandExecutor.js +0 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/ConditionalRequestControlCommand.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ContextURLFactory.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ErrorJsonSerializer.js +2 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/TrustedResourceJsonSerializer.js +2 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/ConditionalRequestValidator.js +6 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +18 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +41 -17
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +1 -17
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +80 -21
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +47 -10
- package/libx/_runtime/cds-services/adapter/rest/Rest.js +22 -1
- package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +8 -3
- package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +3 -0
- package/libx/_runtime/cds-services/services/Service.js +1 -1
- package/libx/_runtime/cds-services/services/utils/columns.js +5 -1
- package/libx/_runtime/cds-services/services/utils/compareJson.js +15 -16
- package/libx/_runtime/cds-services/services/utils/differ.js +2 -8
- package/libx/_runtime/common/aspects/Association.js +16 -0
- package/libx/_runtime/common/composition/data.js +28 -37
- package/libx/_runtime/common/composition/delete.js +107 -58
- package/libx/_runtime/common/composition/index.js +3 -3
- package/libx/_runtime/common/composition/insert.js +14 -27
- package/libx/_runtime/common/composition/update.js +39 -34
- package/libx/_runtime/common/error/frontend.js +19 -5
- package/libx/_runtime/common/generic/auth.js +20 -85
- package/libx/_runtime/common/generic/crud.js +22 -1
- package/libx/_runtime/common/i18n/messages.properties +2 -1
- package/libx/_runtime/common/utils/cqn.js +2 -6
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +95 -122
- package/libx/_runtime/common/utils/csn.js +15 -4
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +18 -1
- package/libx/_runtime/common/utils/keys.js +2 -1
- package/libx/_runtime/common/utils/path.js +1 -1
- package/libx/_runtime/common/utils/resolveView.js +12 -4
- package/libx/_runtime/common/utils/rewriteAsterisks.js +27 -13
- package/libx/_runtime/common/utils/search2cqn4sql.js +11 -6
- package/libx/_runtime/common/utils/structured.js +11 -5
- package/libx/_runtime/common/utils/vcap.js +27 -10
- package/libx/_runtime/db/data-conversion/post-processing.js +42 -35
- package/libx/_runtime/db/expand/expand-v2.js +21 -12
- package/libx/_runtime/db/expand/expandCQNToJoin.js +57 -29
- package/libx/_runtime/db/expand/index.js +3 -0
- package/libx/_runtime/db/generic/create.js +0 -10
- package/libx/_runtime/db/generic/index.js +3 -0
- package/libx/_runtime/db/generic/read.js +2 -24
- package/libx/_runtime/db/generic/rewrite.js +1 -3
- package/libx/_runtime/db/generic/update.js +1 -1
- package/libx/_runtime/db/query/delete.js +10 -4
- package/libx/_runtime/db/query/insert.js +3 -4
- package/libx/_runtime/db/query/read.js +4 -1
- package/libx/_runtime/db/query/update.js +5 -5
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +9 -2
- package/libx/_runtime/db/sql-builder/FunctionBuilder.js +3 -0
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +7 -3
- package/libx/_runtime/db/sql-builder/index.js +3 -0
- package/libx/_runtime/db/utils/columns.js +5 -2
- package/libx/_runtime/db/utils/deep.js +6 -8
- package/libx/_runtime/db/utils/generateAliases.js +56 -6
- package/libx/_runtime/fiori/generic/before.js +73 -49
- package/libx/_runtime/fiori/generic/edit.js +14 -18
- package/libx/_runtime/fiori/generic/patch.js +8 -11
- package/libx/_runtime/fiori/generic/read.js +22 -20
- package/libx/_runtime/fiori/generic/readOverDraft.js +1 -4
- package/libx/_runtime/fiori/utils/handler.js +1 -11
- package/libx/_runtime/hana/Service.js +1 -1
- package/libx/_runtime/hana/conversion.js +12 -1
- package/libx/_runtime/hana/execute.js +31 -16
- package/libx/_runtime/hana/localized.js +1 -1
- package/libx/_runtime/hana/search.js +3 -3
- package/libx/_runtime/hana/search2cqn4sql.js +23 -25
- package/libx/_runtime/hana/searchToContains.js +1 -1
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +4 -2
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +0 -1
- package/libx/_runtime/messaging/enterprise-messaging.js +1 -0
- package/libx/_runtime/messaging/file-based.js +3 -1
- package/libx/_runtime/messaging/service.js +16 -7
- package/libx/_runtime/remote/utils/client.js +37 -20
- package/libx/_runtime/remote/utils/data.js +53 -12
- package/libx/_runtime/sqlite/Service.js +1 -1
- package/libx/_runtime/sqlite/conversion.js +10 -0
- package/libx/_runtime/sqlite/localized.js +1 -1
- package/libx/_runtime/types/api.js +2 -2
- package/libx/gql/resolvers/crud/update.js +8 -5
- package/libx/gql/resolvers/parse/ast/enrich.js +1 -0
- package/libx/odata/afterburner.js +29 -6
- package/libx/odata/cqn2odata.js +9 -0
- package/libx/odata/grammar.pegjs +50 -22
- package/libx/odata/index.js +2 -2
- package/libx/odata/parser.js +1 -1
- package/libx/odata/utils.js +2 -2
- package/libx/rest/RestAdapter.js +29 -1
- package/libx/rest/middleware/auth.js +1 -3
- package/libx/rest/middleware/parse.js +1 -0
- package/package.json +1 -1
- package/server.js +1 -1
- package/bin/deploy/to-hana/logger.js +0 -27
- package/bin/deploy/to-hana/runCommand.js +0 -113
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +0 -37
- package/libx/_runtime/common/utils/auth.js +0 -16
|
@@ -1,90 +1,118 @@
|
|
|
1
|
-
|
|
2
|
-
const {fse} = require('@sap/cds-foss');
|
|
1
|
+
const cp = require('child_process');
|
|
3
2
|
const os = require('os');
|
|
4
3
|
const path = require('path');
|
|
4
|
+
const util = require('util');
|
|
5
|
+
|
|
6
|
+
const { fse } = require('@sap/cds-foss');
|
|
5
7
|
|
|
6
8
|
const { BuildTaskEngine, BuildTaskFactory } = require('../../build');
|
|
7
9
|
const buildConstants = require('../../build/constants');
|
|
10
|
+
|
|
8
11
|
const cds = require('../../../lib');
|
|
12
|
+
const LOG = cds.log ? cds.log('deploy') : console;
|
|
13
|
+
|
|
9
14
|
const cfUtil = require('./cfUtil');
|
|
10
|
-
const { defaultLogger, nullLogger } = require('./logger');
|
|
11
15
|
const hdiDeployUtil = require('./hdiDeployUtil');
|
|
16
|
+
const gitUtil = require('./gitUtil');
|
|
12
17
|
const mtaUtil = require('../../build/mtaUtil');
|
|
13
|
-
const runCommand = require('./runCommand');
|
|
14
|
-
const { bold, info } = require('../../utils/term');
|
|
15
18
|
|
|
19
|
+
const { bold, info } = require('../../utils/term');
|
|
16
20
|
|
|
17
21
|
const IS_WIN = (os.platform() === 'win32');
|
|
18
22
|
const UTF_8 = 'utf-8';
|
|
19
23
|
|
|
20
24
|
|
|
21
|
-
const DEBUG = process.env.DEBUG;
|
|
22
|
-
|
|
23
25
|
class HanaDeployer {
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
constructor() {
|
|
28
|
+
this.execAsync = util.promisify(cp.exec);
|
|
29
|
+
this.spawnSync = cp.spawnSync;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
async deploy(model, serviceName, noSave, tunnelAddress, buildTaskOptions, vcapFile, undeployWhitelist, hdiOptions, bindCallback, logger = LOG) { // NOSONAR
|
|
34
|
+
hdiOptions = hdiOptions || {}
|
|
35
|
+
this.logger = logger;
|
|
26
36
|
|
|
27
|
-
logger.log(
|
|
28
|
-
|
|
37
|
+
this.logger.log(`${bold('Starting deploy to SAP HANA ...')}`);
|
|
38
|
+
if (bindCallback) {
|
|
39
|
+
this.logger.log('Using advanced mode');
|
|
40
|
+
}
|
|
41
|
+
this.logger.log();
|
|
29
42
|
|
|
30
43
|
const projectPath = path.resolve(process.env._TEST_CWD || process.cwd());
|
|
31
44
|
|
|
32
|
-
const { buildResults } = await this._build(buildTaskOptions, model
|
|
45
|
+
const { buildResults } = await this._build(buildTaskOptions, model);
|
|
33
46
|
|
|
34
47
|
let vcapServices;
|
|
35
48
|
if (vcapFile) {
|
|
36
|
-
logger.log();
|
|
37
|
-
logger.log(`
|
|
49
|
+
this.logger.log();
|
|
50
|
+
this.logger.log(`Using vcap file ${vcapFile} (beta feature).`);
|
|
38
51
|
vcapServices = await this._loadVCAPServices(vcapFile);
|
|
39
52
|
}
|
|
40
53
|
|
|
41
54
|
for (const buildResult of buildResults) {
|
|
55
|
+
let serviceKeyName;
|
|
42
56
|
const currentModelFolder = buildResult.result.dest;
|
|
43
57
|
|
|
44
58
|
if (undeployWhitelist) {
|
|
45
|
-
logger.log('
|
|
59
|
+
this.logger.log('Writing undeploy.json');
|
|
46
60
|
await fse.writeJSON(path.join(currentModelFolder, 'undeploy.json'), undeployWhitelist);
|
|
47
61
|
}
|
|
48
62
|
|
|
49
63
|
if (vcapFile) {
|
|
50
64
|
await fse.mkdirp(currentModelFolder);
|
|
51
65
|
} else {
|
|
52
|
-
const { cfServiceInstanceName, serviceKey } =
|
|
53
|
-
await this._getOrCreateCFService(projectPath, currentModelFolder, serviceName, tunnelAddress
|
|
66
|
+
const { cfServiceInstanceName, cfServiceInstanceKeyName, serviceKey } =
|
|
67
|
+
await this._getOrCreateCFService(projectPath, currentModelFolder, serviceName, tunnelAddress);
|
|
68
|
+
serviceKeyName = cfServiceInstanceKeyName;
|
|
69
|
+
serviceName = serviceName || cfServiceInstanceName;
|
|
54
70
|
|
|
55
71
|
vcapServices = this._getVCAPServicesEntry(cfServiceInstanceName, serviceKey);
|
|
56
72
|
|
|
57
|
-
if (!noSave) {
|
|
58
|
-
await this._addInstanceToDefaultEnvJson([currentModelFolder, projectPath], cfServiceInstanceName, serviceKey
|
|
73
|
+
if (!noSave && !bindCallback) {
|
|
74
|
+
await this._addInstanceToDefaultEnvJson([currentModelFolder, projectPath], cfServiceInstanceName, serviceKey);
|
|
59
75
|
}
|
|
60
76
|
}
|
|
61
77
|
|
|
62
78
|
// Check if deployer is already installed, otherwise only install this one, not the rest of dependencies.
|
|
63
|
-
if (!await hdiDeployUtil.findHdiDeployLib(currentModelFolder
|
|
64
|
-
const { deployerName, deployerVersionSpec } = hdiDeployUtil
|
|
65
|
-
logger.log(`
|
|
66
|
-
|
|
67
|
-
|
|
79
|
+
if (!await hdiDeployUtil.findHdiDeployLib(currentModelFolder)) {
|
|
80
|
+
const { deployerName, deployerVersionSpec } = hdiDeployUtil;
|
|
81
|
+
this.logger.log(`Installing ${deployerName}`);
|
|
82
|
+
|
|
83
|
+
// spawn has not callback and cannot be promisified, using sync instead
|
|
84
|
+
this.spawnSync(`npm`, [`install`, `${deployerName}@${deployerVersionSpec}`, (noSave ? '--no-save' : '--save-dev')], {
|
|
68
85
|
cwd: currentModelFolder,
|
|
69
86
|
shell: IS_WIN,
|
|
70
87
|
stdio: 'inherit',
|
|
71
|
-
env: {
|
|
88
|
+
env: {
|
|
89
|
+
...process.env,
|
|
90
|
+
NODE_ENV: 'development' // for 'install --save-dev' to work, there must be no 'production' set
|
|
91
|
+
}
|
|
72
92
|
});
|
|
73
93
|
}
|
|
74
94
|
|
|
75
|
-
await hdiDeployUtil.deploy(currentModelFolder, vcapServices, hdiOptions
|
|
95
|
+
await hdiDeployUtil.deploy(currentModelFolder, vcapServices, hdiOptions);
|
|
96
|
+
|
|
97
|
+
if (bindCallback) {
|
|
98
|
+
const args = [path.relative(projectPath, buildResult.task.src)];
|
|
99
|
+
const options = { to: `${serviceName}:${serviceKeyName}`, kind: buildResult.task.for }
|
|
100
|
+
await bindCallback(args, options, LOG);
|
|
101
|
+
}
|
|
76
102
|
}
|
|
77
103
|
|
|
78
|
-
|
|
104
|
+
if (!bindCallback) {
|
|
105
|
+
await gitUtil.ensureFileIsGitignored('default-env.json', projectPath);
|
|
106
|
+
}
|
|
79
107
|
|
|
80
|
-
logger.log(`
|
|
81
|
-
logger.log(`
|
|
108
|
+
this.logger.log(`If not already done, use ${info('cds add hana')} to configure the project for SAP HANA.\n`);
|
|
109
|
+
this.logger.log(`Done.`);
|
|
82
110
|
|
|
83
111
|
return { buildResults };
|
|
84
112
|
}
|
|
85
113
|
|
|
86
114
|
|
|
87
|
-
async _getOrCreateCFService(projectPath, currentModelFolder, serviceName, tunnelAddress
|
|
115
|
+
async _getOrCreateCFService(projectPath, currentModelFolder, serviceName, tunnelAddress) {
|
|
88
116
|
const modelName = path.basename(currentModelFolder);
|
|
89
117
|
|
|
90
118
|
// get from param
|
|
@@ -93,77 +121,75 @@ class HanaDeployer {
|
|
|
93
121
|
if (serviceName) {
|
|
94
122
|
cfServiceInstanceName = serviceName;
|
|
95
123
|
} else {
|
|
96
|
-
const cfServiceDescriptor = await mtaUtil.getHanaDbModuleDescriptor(projectPath, modelName, logger);
|
|
124
|
+
const cfServiceDescriptor = await mtaUtil.getHanaDbModuleDescriptor(projectPath, modelName, this.logger);
|
|
97
125
|
cfServiceInstanceName = cfServiceDescriptor.hdiServiceName;
|
|
98
126
|
cfServiceInstanceMta = cfServiceDescriptor.hdiService
|
|
99
127
|
}
|
|
100
128
|
|
|
101
|
-
logger.log();
|
|
129
|
+
this.logger.log();
|
|
102
130
|
this._validateServiceInstanceName(cfServiceInstanceName);
|
|
103
|
-
logger.log(`
|
|
131
|
+
this.logger.log(`Using container ${bold(cfServiceInstanceName)}`);
|
|
104
132
|
|
|
105
133
|
let cfConfig = cfServiceInstanceMta && cfServiceInstanceMta.parameters && cfServiceInstanceMta.parameters.config;
|
|
106
|
-
await this.createHanaService(cfServiceInstanceName, cfConfig
|
|
134
|
+
const serviceInstance = await this.createHanaService(cfServiceInstanceName, cfConfig);
|
|
107
135
|
|
|
108
136
|
const cfServiceInstanceKeyName = `${cfServiceInstanceName}-key`;
|
|
109
|
-
let serviceKey = await cfUtil.
|
|
110
|
-
if (!serviceKey) {
|
|
111
|
-
serviceKey = await cfUtil.createServiceKey(cfServiceInstanceName, cfServiceInstanceKeyName, logger);
|
|
112
|
-
}
|
|
137
|
+
let serviceKey = await cfUtil.getOrCreateServiceKey(serviceInstance, cfServiceInstanceKeyName);
|
|
113
138
|
this._validateServiceKey(serviceKey, cfServiceInstanceKeyName);
|
|
114
139
|
|
|
115
140
|
if (tunnelAddress) {
|
|
116
|
-
logger.log(`
|
|
141
|
+
this.logger.log(`Using tunnel address ${bold(tunnelAddress)} (beta feature)`);
|
|
117
142
|
serviceKey = this._injectTunnelAddress(serviceKey, tunnelAddress)
|
|
118
143
|
}
|
|
119
144
|
|
|
120
|
-
return { cfServiceInstanceName, serviceKey }
|
|
145
|
+
return { cfServiceInstanceName, cfServiceInstanceKeyName, serviceKey, serviceInstance }
|
|
121
146
|
}
|
|
122
147
|
|
|
123
|
-
|
|
148
|
+
|
|
149
|
+
async createHanaService(instanceName, cfConfig) {
|
|
124
150
|
// hana or hanatrial, error if neither found
|
|
125
151
|
try {
|
|
126
|
-
return await cfUtil.
|
|
152
|
+
return await cfUtil.getOrCreateService('hana', 'hdi-shared', instanceName, cfConfig);
|
|
127
153
|
} catch (error) {
|
|
128
154
|
if (error.command && /offering .* not found/i.test(error.command.stderr)) {
|
|
129
|
-
logger.log(`
|
|
130
|
-
return await cfUtil.
|
|
155
|
+
this.logger.log(`Falling back to 'hanatrial'`);
|
|
156
|
+
return await cfUtil.getOrCreateService('hanatrial', 'hdi-shared', instanceName, cfConfig);
|
|
131
157
|
}
|
|
132
158
|
else if (error.command && /no database/i.test(error.command.stderr)) {
|
|
133
|
-
logger.log(`
|
|
134
|
-
return await cfUtil.
|
|
159
|
+
this.logger.log(`No database connected to 'hana' service. Falling back to 'hanatrial'`);
|
|
160
|
+
return await cfUtil.getOrCreateService('hanatrial', 'hdi-shared', instanceName, cfConfig);
|
|
135
161
|
}
|
|
136
162
|
throw error;
|
|
137
163
|
}
|
|
138
164
|
}
|
|
139
165
|
|
|
140
166
|
|
|
141
|
-
|
|
142
167
|
_validateServiceKey(serviceKey, cfServiceInstanceKey) {
|
|
143
168
|
if (!serviceKey) {
|
|
144
|
-
throw new Error(`
|
|
169
|
+
throw new Error(`Could not create service key ${bold(cfServiceInstanceKey)}.`);
|
|
145
170
|
}
|
|
146
171
|
|
|
147
172
|
const fields = ['schema', 'user', 'password', 'url'];
|
|
148
173
|
for (const field of fields) {
|
|
149
174
|
if (!serviceKey[field]) {
|
|
150
|
-
throw new Error(`
|
|
175
|
+
throw new Error(`Service key is missing mandatory field '${field}'. Make sure you are ${bold('not')} using a managed service.`);
|
|
151
176
|
}
|
|
152
177
|
}
|
|
153
178
|
}
|
|
154
179
|
|
|
155
180
|
|
|
156
|
-
async _build(buildTaskOptions, model
|
|
181
|
+
async _build(buildTaskOptions, model) {
|
|
157
182
|
buildTaskOptions = buildTaskOptions || {
|
|
158
|
-
root: process.env._TEST_CWD || process.cwd()
|
|
183
|
+
root: process.env._TEST_CWD || process.cwd(),
|
|
184
|
+
cli: true
|
|
159
185
|
};
|
|
160
186
|
|
|
161
187
|
if (typeof model === 'string') {
|
|
162
188
|
model = [model];
|
|
163
189
|
}
|
|
164
190
|
|
|
165
|
-
logger.log(`
|
|
166
|
-
const buildTaskFactory = new BuildTaskFactory(
|
|
191
|
+
this.logger.log(`Creating build tasks`);
|
|
192
|
+
const buildTaskFactory = new BuildTaskFactory(null, cds);
|
|
167
193
|
const allTasks = await buildTaskFactory.getTasks(buildTaskOptions);
|
|
168
194
|
|
|
169
195
|
const hanaTasks = allTasks.filter((task => {
|
|
@@ -185,9 +211,9 @@ class HanaDeployer {
|
|
|
185
211
|
});
|
|
186
212
|
}
|
|
187
213
|
|
|
188
|
-
logger.log(`
|
|
214
|
+
this.logger.log(`Running build`);
|
|
189
215
|
|
|
190
|
-
const buildResults = await new BuildTaskEngine(
|
|
216
|
+
const buildResults = await new BuildTaskEngine().processTasks(hanaTasks, buildTaskOptions);
|
|
191
217
|
return { buildResults, allTasks }
|
|
192
218
|
}
|
|
193
219
|
|
|
@@ -206,7 +232,7 @@ class HanaDeployer {
|
|
|
206
232
|
}
|
|
207
233
|
|
|
208
234
|
|
|
209
|
-
async _addInstanceToDefaultEnvJson(currentFolders, serviceInstanceName, serviceKey
|
|
235
|
+
async _addInstanceToDefaultEnvJson(currentFolders, serviceInstanceName, serviceKey) {
|
|
210
236
|
for (const currentFolder of currentFolders) {
|
|
211
237
|
let defaultEnvJson = {};
|
|
212
238
|
const defaultEnvJsonPath = path.join(currentFolder, 'default-env.json');
|
|
@@ -230,7 +256,7 @@ class HanaDeployer {
|
|
|
230
256
|
...hanaEntry
|
|
231
257
|
}
|
|
232
258
|
|
|
233
|
-
logger.log(`
|
|
259
|
+
this.logger.log(`Writing ${defaultEnvJsonPath}`);
|
|
234
260
|
await fse.outputJSON(defaultEnvJsonPath, defaultEnvJson, {
|
|
235
261
|
spaces: 2
|
|
236
262
|
});
|
|
@@ -238,42 +264,6 @@ class HanaDeployer {
|
|
|
238
264
|
}
|
|
239
265
|
|
|
240
266
|
|
|
241
|
-
async _addToGitignore(currentFolder, entry, logger) {
|
|
242
|
-
const gitIgnorePath = path.join(currentFolder, '.gitignore');
|
|
243
|
-
let entryMustBeAdded = true;
|
|
244
|
-
try {
|
|
245
|
-
const gitCheckCmd = await runCommand('git', ['check-ignore', entry], nullLogger);
|
|
246
|
-
if (gitCheckCmd.code === 0) {
|
|
247
|
-
// git verifies the chain of gitignore files, code === 0 file is ignored
|
|
248
|
-
entryMustBeAdded = false;
|
|
249
|
-
}
|
|
250
|
-
} catch (err) {
|
|
251
|
-
// git command not available or some problem occurred
|
|
252
|
-
logger.warn(`[cds.deploy] - Error while calling git: ${err}`);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (entryMustBeAdded) {
|
|
256
|
-
let gitIgnore = '';
|
|
257
|
-
try {
|
|
258
|
-
gitIgnore = await fse.readFile(gitIgnorePath, UTF_8);
|
|
259
|
-
if (gitIgnore.indexOf(entry) >= 0) {
|
|
260
|
-
// entry exists in file
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
} catch (err) {
|
|
264
|
-
// ignore file not found
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
logger.log(`[cds.deploy] - Adding ${entry} to ${gitIgnorePath}`);
|
|
268
|
-
gitIgnore = `${gitIgnore.trim()}
|
|
269
|
-
|
|
270
|
-
# added by cds deploy
|
|
271
|
-
${entry}
|
|
272
|
-
`;
|
|
273
|
-
await fse.outputFile(gitIgnorePath, gitIgnore);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
267
|
_getVCAPServicesEntry(serviceInstanceName, serviceKey) {
|
|
278
268
|
return {
|
|
279
269
|
hana: [
|
|
@@ -290,10 +280,11 @@ ${entry}
|
|
|
290
280
|
_validateServiceInstanceName(serviceInstanceName) {
|
|
291
281
|
// valid service name chars: alpha-numeric, hyphens, and underscores
|
|
292
282
|
if (/[^\w-_]+/g.exec(serviceInstanceName)) {
|
|
293
|
-
throw new Error(`
|
|
283
|
+
throw new Error(`Service name ${serviceInstanceName} must only contain alpha-numeric, hyphens, and underscores.`);
|
|
294
284
|
}
|
|
295
285
|
}
|
|
296
286
|
|
|
287
|
+
|
|
297
288
|
_injectTunnelAddress(serviceKey, tunnelAddress) {
|
|
298
289
|
if (!/\w+:\d+/.test(tunnelAddress)) {
|
|
299
290
|
throw new Error(`Invalid tunnel address '${tunnelAddress}' - must be in form 'host:port'`)
|
|
@@ -308,6 +299,14 @@ ${entry}
|
|
|
308
299
|
return serviceKey
|
|
309
300
|
}
|
|
310
301
|
|
|
302
|
+
|
|
303
|
+
async readFileSafely(file) {
|
|
304
|
+
try {
|
|
305
|
+
return await fse.readFile(file, 'utf8');
|
|
306
|
+
} catch (err) {
|
|
307
|
+
return '';
|
|
308
|
+
}
|
|
309
|
+
}
|
|
311
310
|
}
|
|
312
311
|
|
|
313
312
|
module.exports = new HanaDeployer();
|
|
@@ -4,7 +4,9 @@ const util = require('util');
|
|
|
4
4
|
|
|
5
5
|
const execAsync = util.promisify(cp.exec);
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const cds = require('../../../lib');
|
|
8
|
+
const { SILENT } = cds.log.levels;
|
|
9
|
+
const LOG = cds.log ? cds.log('deploy') : console;
|
|
8
10
|
|
|
9
11
|
class HdiDeployUtil {
|
|
10
12
|
|
|
@@ -14,41 +16,49 @@ class HdiDeployUtil {
|
|
|
14
16
|
this.deployerVersionSpec = '^4'
|
|
15
17
|
}
|
|
16
18
|
|
|
17
|
-
async deploy(dbDir, vcapServices, options = {}
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
async deploy(dbDir, vcapServices, options = {}) {
|
|
20
|
+
LOG.log();
|
|
21
|
+
LOG.log(`Deploying to HANA from ${dbDir}`);
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
const hdiDeployLib = await this._getHdiDeployLib(dbDir);
|
|
24
|
+
|
|
25
|
+
let deployerEnv = JSON.parse(JSON.stringify(process.env));
|
|
26
|
+
const hdiDeployOptions = deployerEnv.HDI_DEPLOY_OPTIONS ? JSON.parse(deployerEnv.HDI_DEPLOY_OPTIONS) : {};
|
|
27
|
+
delete hdiDeployOptions.root;
|
|
28
|
+
|
|
29
|
+
if (typeof hdiDeployLib.clean_env === 'function') {
|
|
24
30
|
deployerEnv = hdiDeployLib.clean_env(deployerEnv);
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
deployerEnv.VCAP_SERVICES = JSON.stringify(vcapServices);
|
|
28
34
|
|
|
29
35
|
if (options.autoUndeploy) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
LOG.log(`Hdi deployer automatically undeploys deleted resources using --auto-undeploy.`);
|
|
37
|
+
hdiDeployOptions.auto_undeploy = true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (Object.entries(hdiDeployOptions).length > 0) {
|
|
41
|
+
deployerEnv.HDI_DEPLOY_OPTIONS = JSON.stringify(hdiDeployOptions);
|
|
34
42
|
}
|
|
35
43
|
|
|
36
|
-
await this._executeDeploy(dbDir, deployerEnv
|
|
44
|
+
await this._executeDeploy(dbDir, deployerEnv);
|
|
37
45
|
}
|
|
38
46
|
|
|
47
|
+
|
|
39
48
|
async findHdiDeployLib(cwd) {
|
|
40
49
|
const searchPaths = await this._npmSearchPaths(cwd)
|
|
41
50
|
try {
|
|
42
|
-
return require.resolve(path.join(this.deployerName, 'library'), {paths: searchPaths});
|
|
51
|
+
return require.resolve(path.join(this.deployerName, 'library'), { paths: searchPaths });
|
|
43
52
|
} catch (err) {
|
|
44
53
|
// no luck
|
|
45
54
|
}
|
|
46
55
|
}
|
|
47
56
|
|
|
48
|
-
|
|
57
|
+
|
|
58
|
+
async _getHdiDeployLib(dbDir) {
|
|
49
59
|
let hdiDeployLib = this.hdiDeployLibs.get(dbDir)
|
|
50
60
|
if (!hdiDeployLib) {
|
|
51
|
-
hdiDeployLib = await this._loadHdiDeployLib(dbDir
|
|
61
|
+
hdiDeployLib = await this._loadHdiDeployLib(dbDir);
|
|
52
62
|
this.hdiDeployLibs.set(dbDir, hdiDeployLib);
|
|
53
63
|
}
|
|
54
64
|
|
|
@@ -56,46 +66,51 @@ class HdiDeployUtil {
|
|
|
56
66
|
}
|
|
57
67
|
|
|
58
68
|
|
|
59
|
-
async _loadHdiDeployLib(cwd
|
|
69
|
+
async _loadHdiDeployLib(cwd) {
|
|
60
70
|
const libPath = await this.findHdiDeployLib(cwd)
|
|
61
71
|
if (!libPath) {
|
|
62
72
|
const searchPaths = await this._npmSearchPaths(cwd)
|
|
63
|
-
throw new Error(`
|
|
73
|
+
throw new Error(`Required library '${this.deployerName}' not found in
|
|
64
74
|
${searchPaths.join('\n ')}
|
|
65
75
|
Add it either as a devDependency using 'npm install -D ${this.deployerName}' or install it globally using 'npm install -g ${this.deployerName}'.`);
|
|
66
76
|
}
|
|
67
77
|
|
|
68
|
-
|
|
78
|
+
LOG.log(`Using HDI deployer from ${libPath}`)
|
|
69
79
|
|
|
70
80
|
// let any error go through and abort deploy
|
|
71
81
|
return require(libPath);
|
|
72
82
|
}
|
|
73
83
|
|
|
74
84
|
|
|
75
|
-
async _npmSearchPaths
|
|
85
|
+
async _npmSearchPaths(cwd) {
|
|
76
86
|
const npmRootCall = await execAsync('npm root -g');
|
|
77
87
|
const globalNodeModules = npmRootCall.stdout.toString().trim();
|
|
78
88
|
return [cwd, globalNodeModules]
|
|
79
89
|
}
|
|
80
90
|
|
|
81
91
|
|
|
82
|
-
async _executeDeploy(dbDir, env
|
|
83
|
-
const hdiDeployLib = await this._getHdiDeployLib(dbDir
|
|
92
|
+
async _executeDeploy(dbDir, env) {
|
|
93
|
+
const hdiDeployLib = await this._getHdiDeployLib(dbDir);
|
|
84
94
|
return new Promise((resolve, reject) => {
|
|
95
|
+
const callbacks = {
|
|
96
|
+
stderrCB: error => LOG.error(error.toString())
|
|
97
|
+
}
|
|
98
|
+
if (LOG.level !== SILENT) {
|
|
99
|
+
callbacks.stdoutCB = (data) => console.log(data.toString());
|
|
100
|
+
}
|
|
101
|
+
|
|
85
102
|
hdiDeployLib.deploy(dbDir, env, (error, response) => {
|
|
86
103
|
if (error) {
|
|
87
104
|
return reject(error);
|
|
88
105
|
}
|
|
89
106
|
if (response && response.exitCode && response.exitCode !== 0) {
|
|
90
|
-
let message = `
|
|
91
|
-
if (response.signal)
|
|
107
|
+
let message = `HDI deployment failed with exit code ${response.exitCode}`
|
|
108
|
+
if (response.signal) message += `. ${response.signal}`
|
|
92
109
|
return reject(new Error(message));
|
|
93
110
|
}
|
|
94
111
|
return resolve();
|
|
95
|
-
},
|
|
96
|
-
|
|
97
|
-
stderrCB: error => logger.error(error.toString())
|
|
98
|
-
});
|
|
112
|
+
}, callbacks
|
|
113
|
+
);
|
|
99
114
|
});
|
|
100
115
|
}
|
|
101
116
|
}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
exports.deploy =
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
exports.deploy = (_model, _dbSpecificParameter, {
|
|
2
|
+
'no-save': no_save,
|
|
3
|
+
'auto-undeploy': autoUndeploy = false,
|
|
4
|
+
'tunnel-address': tunnelAddress,
|
|
5
|
+
'vcap-file': vcapFile,
|
|
6
|
+
}, bindCallback) => {
|
|
7
|
+
const hanaDeployer = require('./hana');
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
return hanaDeployer.deploy(
|
|
10
|
+
_model, _dbSpecificParameter,
|
|
11
|
+
no_save, tunnelAddress,
|
|
12
|
+
null, vcapFile, null,
|
|
13
|
+
{ autoUndeploy },
|
|
14
|
+
bindCallback
|
|
15
|
+
)
|
|
15
16
|
}
|
package/bin/mtx/in-cds.js
CHANGED
|
@@ -2,6 +2,7 @@ const cds = require('../build/cds')
|
|
|
2
2
|
if (cds.requires.multitenancy) try {
|
|
3
3
|
const mtx = module.exports = require ('@sap/cds-mtx')()
|
|
4
4
|
mtx.inject (cds)
|
|
5
|
+
cds.on('served', () => cds.emit('mtx'))
|
|
5
6
|
} catch(e) {
|
|
6
7
|
if (e.code === 'MODULE_NOT_FOUND') throw new Error('Error serving MTX APIs: @sap/cds-mtx is not installed')
|
|
7
8
|
else throw e
|
package/bin/serve.js
CHANGED
|
@@ -194,7 +194,7 @@ function _local_server_js() {
|
|
|
194
194
|
if (server_js) {
|
|
195
195
|
log && log ('Loading server from', { file: local(server_js) })
|
|
196
196
|
const fn = require (server_js)
|
|
197
|
-
return typeof fn === 'function' ? fn : cds.
|
|
197
|
+
return typeof fn === 'function' ? fn : cds.server
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
|
package/bin/version.js
CHANGED
|
@@ -54,6 +54,7 @@ function info(o) {
|
|
|
54
54
|
const main = _findPackage (require.main.filename)
|
|
55
55
|
return {
|
|
56
56
|
..._versions4(main, {}, true), // usually sap/cds-dk or sap/cds
|
|
57
|
+
..._versions4('@sap/cds-dk', {}, null, o), // make sure cds-dk is there, cds-maven-plugin uses it
|
|
57
58
|
..._versions4('@sap/eslint-plugin-cds', {}, null, o),
|
|
58
59
|
..._versions4(process.cwd(), {}, null, o),
|
|
59
60
|
..._versions4('..', {}, null, o),
|
package/lib/compile/cdsc.js
CHANGED
|
@@ -3,12 +3,6 @@ const constraints = {
|
|
|
3
3
|
assertIntegrity: features.assert_integrity,
|
|
4
4
|
assertIntegrityType: features.assert_integrity_type
|
|
5
5
|
}
|
|
6
|
-
// REVISIT: remove with compiler ^2.11
|
|
7
|
-
const { assertIntegrity: ai, assertIntegrityType: ait } = constraints
|
|
8
|
-
if ((typeof ai === 'string' && ai.match(/individual/i)) || (ait && ait.match(/db/i))) {
|
|
9
|
-
cdsc.beta = cdsc.beta || {}
|
|
10
|
-
cdsc.beta.foreignKeyConstraints = true
|
|
11
|
-
}
|
|
12
6
|
const compile = require ('@sap/cds-compiler')
|
|
13
7
|
const _4cdsc = Symbol('_4cdsc')
|
|
14
8
|
|
package/lib/compile/minify.js
CHANGED
|
@@ -35,7 +35,7 @@ module.exports = function cds_minify (csn, _roots = global.cds.env.features.skip
|
|
|
35
35
|
if (d.startsWith('cds.')) return
|
|
36
36
|
else d = all[d]
|
|
37
37
|
} else if (d.ref) return d.ref.reduce((p,n) => {
|
|
38
|
-
let d = p.elements[n.id || n] // > n.id -> view with parameters
|
|
38
|
+
let d = (p.elements || csn.definitions[p.target].elements)[n.id || n] // > n.id -> view with parameters
|
|
39
39
|
_visit(d)
|
|
40
40
|
return d
|
|
41
41
|
},{elements:all})
|
package/lib/compile/resolve.js
CHANGED
|
@@ -27,7 +27,7 @@ module.exports = exports = function cds_resolve (model, o={}) { // NOSONAR
|
|
|
27
27
|
if (id in cached && !o.skipModelCache) return cached[id]
|
|
28
28
|
|
|
29
29
|
// expand @sap/cds by cds.home
|
|
30
|
-
if (id.startsWith('@sap/cds')) id = global.cds.home +
|
|
30
|
+
if (id.startsWith('@sap/cds/')) id = global.cds.home + id.slice(8)
|
|
31
31
|
|
|
32
32
|
// fetch file with .cds/.csn suffix as is
|
|
33
33
|
if (/\.(csn|cds)$/.test(id)) try {
|
|
@@ -37,7 +37,7 @@ module.exports = (model, options={}) => {
|
|
|
37
37
|
function _makeNode(service) {
|
|
38
38
|
return {
|
|
39
39
|
name: service.name,
|
|
40
|
-
urlPath: _url4 (cds.
|
|
40
|
+
urlPath: _url4 (cds.service.path4(service)),
|
|
41
41
|
destination: 'srv-api', // the name to register in xs-app.json
|
|
42
42
|
runtime: 'Node.js',
|
|
43
43
|
location: service.$location
|
package/lib/core/classes.js
CHANGED
|
@@ -23,7 +23,27 @@ class any {
|
|
|
23
23
|
class type extends any {}
|
|
24
24
|
class action extends any {}
|
|
25
25
|
class context extends any {}
|
|
26
|
-
|
|
26
|
+
|
|
27
|
+
class service extends context {
|
|
28
|
+
|
|
29
|
+
get path() { return super.path = service.path4(this) }
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Resolve a service endpoint path to mount it to as follows...
|
|
33
|
+
* Use _path or def[@path] if given with leading '/' prepended if necessary.
|
|
34
|
+
* Otherwise, use the service definition name with stripped 'Service'
|
|
35
|
+
*/
|
|
36
|
+
static path4 (srv, _path = (srv.definition || srv)['@path']) {
|
|
37
|
+
if (_path) return _path.startsWith('/') ? _path : '/'+_path
|
|
38
|
+
else return '/' + ( // return a sluggified variant of the service's name
|
|
39
|
+
/[^.]+$/.exec(srv.name)[0] //> my.very.CatalogService --> CatalogService
|
|
40
|
+
.replace(/Service$/,'') //> CatalogService --> Catalog
|
|
41
|
+
.replace(/_/g,'-') //> foo_bar_baz --> foo-bar-baz
|
|
42
|
+
.replace(/([a-z0-9])([A-Z])/g, (_,c,C) => c+'-'+C) //> ODataFooBarX9 --> OData-Foo-Bar-X9
|
|
43
|
+
.toLowerCase() //> FOO --> foo
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
27
47
|
|
|
28
48
|
class array extends type { is(kind) { return kind === 'array' || super.is(kind) }}
|
|
29
49
|
class aspect extends type { is(kind) { return kind === 'aspect' || super.is(kind) }}
|
package/lib/env/index.js
CHANGED
|
@@ -338,7 +338,8 @@ function _merge (dst, src, _profiles, _cloned, _profiles_only = false) {
|
|
|
338
338
|
const v = pd.value
|
|
339
339
|
if (typeof v === 'object' && !Array.isArray(v)) {
|
|
340
340
|
if (!dst[p]) dst[p] = {}; else if (_cloned) dst[p] = _cloned(dst[p])
|
|
341
|
-
|
|
341
|
+
if (typeof dst[p] !== 'object') dst[p] = v
|
|
342
|
+
else _merge (dst[p], v, _profiles, _cloned, _profiles_only)
|
|
342
343
|
continue
|
|
343
344
|
}
|
|
344
345
|
|
|
@@ -453,7 +454,7 @@ function _readJson (file) {
|
|
|
453
454
|
}
|
|
454
455
|
|
|
455
456
|
function _determineProfilesFrom (env = process.env) {
|
|
456
|
-
if (env.NODE_ENV !== 'production' && !/\
|
|
457
|
+
if (env.NODE_ENV !== 'production' && !/\b(development|production)\b/.test(env.CDS_ENV)) {
|
|
457
458
|
if (env.CDS_ENV) env.CDS_ENV += ',development'
|
|
458
459
|
else env.CDS_ENV = 'development'
|
|
459
460
|
}
|
package/lib/env/requires.js
CHANGED
|
@@ -31,6 +31,9 @@ module.exports = {
|
|
|
31
31
|
"xsuaa-auth": {
|
|
32
32
|
strategy: 'xsuaa',
|
|
33
33
|
},
|
|
34
|
+
"dwc-auth": {
|
|
35
|
+
strategy: 'dwc',
|
|
36
|
+
},
|
|
34
37
|
destinations: {
|
|
35
38
|
vcap: {
|
|
36
39
|
label: 'destination'
|
|
@@ -93,6 +96,7 @@ module.exports = {
|
|
|
93
96
|
},
|
|
94
97
|
"enterprise-messaging-http": {
|
|
95
98
|
outbox: {},
|
|
99
|
+
deployForProvider: true,
|
|
96
100
|
impl: `${_runtime}/messaging/enterprise-messaging.js`,
|
|
97
101
|
vcap: { label: "enterprise-messaging" },
|
|
98
102
|
},
|