@sap/cds-dk 7.6.0 → 7.7.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/bin/cds.js +1 -1
- package/bin/import.js +2 -2
- package/bin/version.js +21 -20
- package/bin/watch.js +6 -0
- package/lib/build/buildTaskEngine.js +6 -4
- package/lib/build/buildTaskProviderFactory.js +5 -0
- package/lib/build/provider/edmxBuildPlugin.js +1 -1
- package/lib/build/provider/internalBuildTaskProvider.js +12 -10
- package/lib/build/provider/mtx/index.js +2 -2
- package/lib/build/provider/mtx/resourcesTarBuilder.js +1 -1
- package/lib/build/provider/mtx-extension/extensionCompilation.js +50 -0
- package/lib/build/provider/mtx-extension/index.js +50 -48
- package/lib/build/provider/mtx-sidecar/index.js +3 -3
- package/lib/build/util.js +9 -4
- package/lib/deploy/to-hana/hana.js +46 -76
- package/lib/deploy/to-hana/hdiDeployUtil.js +15 -18
- package/lib/deploy/to-hana/mtaUtil.js +1 -1
- package/lib/env/schema.js +1 -0
- package/lib/env/schemas/cds-rc.js +54 -1
- package/lib/import/asyncapi/parserContext.js +6 -0
- package/lib/import/common.js +18 -23
- package/lib/import/odata/metadataconverter/MetadataConverter.js +12 -8
- package/lib/import/odata/v4parser.js +67 -12
- package/lib/init/add.js +2 -0
- package/lib/init/constants.js +2 -4
- package/lib/init/merge.js +31 -13
- package/lib/init/projectReader.js +9 -1
- package/lib/init/registries/mta.js +19 -1
- package/lib/init/template/approuter/files/mta.yaml.hbs +1 -1
- package/lib/init/template/approuter/index.js +5 -5
- package/lib/init/template/connectivity/index.js +3 -4
- package/lib/init/template/html5-repo/files/app-package.json.hbs +16 -0
- package/lib/init/template/html5-repo/files/mta.yaml.hbs +83 -0
- package/lib/init/template/html5-repo/files/package.json.hbs +17 -0
- package/lib/init/template/html5-repo/files/ui5.yaml.hbs +33 -0
- package/lib/init/template/html5-repo/files/xs-app.json.hbs +19 -0
- package/lib/init/template/html5-repo/index.js +56 -12
- package/lib/init/template/mta/files/mta.yaml.hbs +1 -0
- package/lib/init/template/mta/index.js +5 -0
- package/lib/init/template/nodejs/files/.vscode/extensions.json +1 -2
- package/lib/init/template/nodejs/files/README.md +1 -1
- package/lib/init/template/sample/files/app/index.html +8 -6
- package/lib/init/template/sample/index.js +12 -2
- package/lib/lint/Linter.js +2 -2
- package/lib/lint/checks.js +0 -51
- package/lib/mtx/auth_manager.js +46 -55
- package/lib/mtx/settings_manager.js +14 -19
- package/lib/mtx/util/dependencies.js +1 -1
- package/lib/mtx/util/logging.js +10 -8
- package/lib/util/axios.js +5 -0
- package/lib/util/cf.js +1 -0
- package/npm-shrinkwrap.json +96 -42
- package/package.json +1 -10
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
const cp = require('child_process');
|
|
2
1
|
const path = require('path');
|
|
3
|
-
const util = require('util');
|
|
4
2
|
|
|
5
3
|
const fs = require('fs').promises;
|
|
6
4
|
|
|
7
5
|
const cds = require('../../../lib/cds');
|
|
6
|
+
const { exists, read } = cds.utils;
|
|
8
7
|
|
|
9
8
|
const { build } = require('../../build');
|
|
10
9
|
const buildConstants = require('../../build/constants');
|
|
11
10
|
|
|
12
|
-
const
|
|
11
|
+
const hdi = require('./hdiDeployUtil');
|
|
13
12
|
const cfUtil = require('../../util/cf');
|
|
14
13
|
const gitUtil = require('../../util/git');
|
|
15
14
|
const mtaUtil = require('./mtaUtil');
|
|
@@ -18,15 +17,9 @@ const { bold, info } = require('../../util/term');
|
|
|
18
17
|
|
|
19
18
|
const { bind } = require('../../bind');
|
|
20
19
|
|
|
21
|
-
const
|
|
20
|
+
const DEBUG = cds.debug('deploy');
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
class HanaDeployer {
|
|
25
|
-
|
|
26
|
-
constructor() {
|
|
27
|
-
this.execAsync = util.promisify(cp.exec);
|
|
28
|
-
this.spawnSync = cp.spawnSync;
|
|
29
|
-
}
|
|
22
|
+
module.exports = new class HanaDeployer {
|
|
30
23
|
|
|
31
24
|
getFromEnv(...varNames) {
|
|
32
25
|
const result = {}
|
|
@@ -42,11 +35,8 @@ class HanaDeployer {
|
|
|
42
35
|
return result;
|
|
43
36
|
}
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
async deploy(options = {}) { // NOSONAR
|
|
48
|
-
let { model, serviceName, tunnelAddress, vcapFile, undeployWhitelist, hdiOptions, dry } = options;
|
|
49
|
-
|
|
38
|
+
async deploy(options = {}) {
|
|
39
|
+
let { model, serviceName, tunnelAddress, vcapFile, undeployWhitelist, hdiOptions = {}, dry, storeCredentials } = options;
|
|
50
40
|
if (model) {
|
|
51
41
|
throw `Specifying a CDS model for HANA deployment is not supported.
|
|
52
42
|
'cds deploy --to hana' performs 'cds build --for hana' which in turn uses the configured HANA build task.
|
|
@@ -65,15 +55,16 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
65
55
|
|
|
66
56
|
const buildResults = await this._build();
|
|
67
57
|
|
|
68
|
-
|
|
69
|
-
|
|
58
|
+
const fromFile = vcapFile && exists(vcapFile) ? await read(vcapFile) : {};
|
|
59
|
+
const fromEnv = this.getFromEnv('VCAP_SERVICES', 'SERVICE_REPLACEMENTS');
|
|
60
|
+
let vcapEnv = { ...fromFile, ...fromEnv };
|
|
70
61
|
if (process.env.TARGET_CONTAINER) {
|
|
71
|
-
|
|
62
|
+
vcapEnv.TARGET_CONTAINER = process.env.TARGET_CONTAINER;
|
|
72
63
|
}
|
|
73
64
|
|
|
74
65
|
if (dry) {
|
|
75
|
-
for (const
|
|
76
|
-
const model = await cds.load(
|
|
66
|
+
for (const { task } of buildResults) {
|
|
67
|
+
const model = await cds.load(task.src);
|
|
77
68
|
const data = cds.compile.to.hdbtable(model);
|
|
78
69
|
for (const [src, { file }] of data) {
|
|
79
70
|
console.log(`-- ${file}`);
|
|
@@ -82,49 +73,47 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
82
73
|
}
|
|
83
74
|
}
|
|
84
75
|
} else {
|
|
85
|
-
for (const
|
|
86
|
-
|
|
87
|
-
let serviceKeyName;
|
|
88
|
-
const currentModelFolder = buildResult.task.dest;
|
|
76
|
+
for (const { task } of buildResults) {
|
|
77
|
+
const dest = task.dest;
|
|
89
78
|
|
|
90
79
|
if (undeployWhitelist) {
|
|
91
80
|
console.log('Writing undeploy.json');
|
|
92
|
-
await fs.write(path.join(
|
|
81
|
+
await fs.write(path.join(dest, 'undeploy.json'), JSON.stringify(undeployWhitelist, null, 2));
|
|
93
82
|
}
|
|
94
83
|
|
|
95
84
|
const hasVCAPEnv = Object.keys(vcapEnv).length > 0;
|
|
96
85
|
if (!serviceName && hasVCAPEnv) {
|
|
97
|
-
await fs.mkdir(
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
await this._getOrCreateCFService(projectPath,
|
|
101
|
-
serviceKeyName = cfServiceInstanceKeyName;
|
|
102
|
-
serviceName = serviceName || cfServiceInstanceName;
|
|
86
|
+
await fs.mkdir(dest, { recursive: true });
|
|
87
|
+
}
|
|
88
|
+
const { cfServiceInstanceName, cfServiceInstanceKeyName, serviceKey } =
|
|
89
|
+
await this._getOrCreateCFService(projectPath, dest, serviceName, tunnelAddress);
|
|
103
90
|
|
|
104
|
-
|
|
91
|
+
serviceName ??= cfServiceInstanceName;
|
|
92
|
+
if (!vcapEnv.VCAP_SERVICES || serviceName) {
|
|
93
|
+
vcapEnv.VCAP_SERVICES = this._getVCAPServicesEntry(cfServiceInstanceName, serviceKey);
|
|
94
|
+
}
|
|
105
95
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
96
|
+
if (storeCredentials) {
|
|
97
|
+
await this._addInstanceToDefaultEnvJson([dest, projectPath], cfServiceInstanceName, serviceKey);
|
|
109
98
|
}
|
|
110
99
|
|
|
111
100
|
console.log(`${bold('Starting deployment to SAP HANA ...')}`);
|
|
112
101
|
|
|
113
|
-
|
|
114
|
-
await
|
|
102
|
+
DEBUG?.({ dest, vcapEnv, hdiOptions })
|
|
103
|
+
await hdi.deploy(dest, vcapEnv, hdiOptions);
|
|
115
104
|
|
|
116
105
|
if (!hasVCAPEnv) {
|
|
117
106
|
const bindOptions = {
|
|
118
107
|
...options,
|
|
119
|
-
to: `${serviceName}:${
|
|
108
|
+
to: `${serviceName}:${cfServiceInstanceKeyName}`,
|
|
120
109
|
for: options.for,
|
|
121
|
-
kind:
|
|
110
|
+
kind: task.for
|
|
122
111
|
}
|
|
123
112
|
await bind(bindOptions);
|
|
124
113
|
}
|
|
125
114
|
}
|
|
126
115
|
|
|
127
|
-
if (
|
|
116
|
+
if (storeCredentials) {
|
|
128
117
|
await gitUtil.ensureFileIsGitignored('default-env.json', projectPath);
|
|
129
118
|
}
|
|
130
119
|
|
|
@@ -155,7 +144,7 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
155
144
|
this._validateServiceInstanceName(cfServiceInstanceName);
|
|
156
145
|
console.log(`Using container ${bold(cfServiceInstanceName)}`);
|
|
157
146
|
|
|
158
|
-
|
|
147
|
+
const cfConfig = cfServiceInstanceMta?.parameters?.config;
|
|
159
148
|
const serviceInstance = await this.createHanaService(cfServiceInstanceName, cfConfig);
|
|
160
149
|
|
|
161
150
|
const cfServiceInstanceKeyName = `${cfServiceInstanceName}-key`;
|
|
@@ -191,13 +180,17 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
191
180
|
|
|
192
181
|
_validateServiceKey(serviceKey, cfServiceInstanceKey) {
|
|
193
182
|
if (!serviceKey) {
|
|
194
|
-
throw
|
|
183
|
+
throw `Could not create service key ${bold(cfServiceInstanceKey)}.`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (serviceKey['sm_url']) {
|
|
187
|
+
throw `Service key credentials are matching to a Service Manager instance of a multitenant environment. Make sure there is no conflict with ${cfServiceInstanceKey}.`;
|
|
195
188
|
}
|
|
196
189
|
|
|
197
190
|
const fields = ['schema', 'user', 'password', 'url'];
|
|
198
191
|
for (const field of fields) {
|
|
199
192
|
if (!serviceKey[field]) {
|
|
200
|
-
throw
|
|
193
|
+
throw `Service key is missing mandatory field '${field}'. Make sure you are ${bold('not')} using a managed service.`;
|
|
201
194
|
}
|
|
202
195
|
}
|
|
203
196
|
}
|
|
@@ -209,40 +202,21 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
209
202
|
}
|
|
210
203
|
|
|
211
204
|
console.log(`Running build`);
|
|
212
|
-
|
|
213
|
-
return buildResults;
|
|
205
|
+
return build(buildTaskOptions);
|
|
214
206
|
}
|
|
215
207
|
|
|
216
|
-
async _loadEnvFile(defaultEnvFile) {
|
|
217
|
-
try {
|
|
218
|
-
const content = await fs.readFile(defaultEnvFile);
|
|
219
|
-
return JSON.parse(content);
|
|
220
|
-
} catch (err) {
|
|
221
|
-
throw new Error(`Error reading default env file: ${err.message}`);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
|
|
226
208
|
async _addInstanceToDefaultEnvJson(currentFolders, serviceInstanceName, serviceKey) {
|
|
227
209
|
for (const currentFolder of currentFolders) {
|
|
228
|
-
let defaultEnvJson = {};
|
|
229
210
|
const defaultEnvJsonPath = path.join(currentFolder, 'default-env.json');
|
|
211
|
+
const defaultEnvJson = exists(defaultEnvJsonPath) ? await read(defaultEnvJsonPath) : {};
|
|
230
212
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
// ignore any errors
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
defaultEnvJson.VCAP_SERVICES = defaultEnvJson.VCAP_SERVICES || {};
|
|
238
|
-
for (const serviceKey of Object.keys(defaultEnvJson.VCAP_SERVICES)) {
|
|
239
|
-
defaultEnvJson.VCAP_SERVICES[serviceKey] = defaultEnvJson.VCAP_SERVICES[serviceKey].filter((service) => {
|
|
240
|
-
return (service.name !== serviceInstanceName);
|
|
241
|
-
});
|
|
213
|
+
defaultEnvJson.VCAP_SERVICES ??= {};
|
|
214
|
+
for (const [serviceKey, services] of Object.entries(defaultEnvJson.VCAP_SERVICES)) {
|
|
215
|
+
defaultEnvJson.VCAP_SERVICES[serviceKey] = services.filter(service => service.name !== serviceInstanceName);
|
|
242
216
|
}
|
|
243
217
|
|
|
244
218
|
const hanaEntry = this._getVCAPServicesEntry(serviceInstanceName, serviceKey)
|
|
245
|
-
Object.assign(defaultEnvJson, hanaEntry);
|
|
219
|
+
Object.assign(defaultEnvJson.VCAP_SERVICES, hanaEntry);
|
|
246
220
|
|
|
247
221
|
console.log(`Writing ${defaultEnvJsonPath}`);
|
|
248
222
|
await fs.mkdir(path.dirname(defaultEnvJsonPath), { recursive: true })
|
|
@@ -253,15 +227,13 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
253
227
|
|
|
254
228
|
_getVCAPServicesEntry(serviceInstanceName, serviceKey) {
|
|
255
229
|
return {
|
|
256
|
-
VCAP_SERVICES: {
|
|
257
230
|
hana: [
|
|
258
231
|
{
|
|
259
232
|
name: serviceInstanceName,
|
|
260
233
|
tags: ['hana'],
|
|
261
234
|
credentials: serviceKey
|
|
262
235
|
}
|
|
263
|
-
|
|
264
|
-
}
|
|
236
|
+
]
|
|
265
237
|
};
|
|
266
238
|
}
|
|
267
239
|
|
|
@@ -269,14 +241,14 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
269
241
|
_validateServiceInstanceName(serviceInstanceName) {
|
|
270
242
|
// valid service name chars: alpha-numeric, hyphens, and underscores
|
|
271
243
|
if (/[^\w-_]+/g.exec(serviceInstanceName)) {
|
|
272
|
-
throw
|
|
244
|
+
throw `Service name ${serviceInstanceName} must only contain alpha-numeric, hyphens, and underscores.`;
|
|
273
245
|
}
|
|
274
246
|
}
|
|
275
247
|
|
|
276
248
|
|
|
277
249
|
_injectTunnelAddress(serviceKey, tunnelAddress) {
|
|
278
250
|
if (!/\w+:\d+/.test(tunnelAddress)) {
|
|
279
|
-
throw
|
|
251
|
+
throw `Invalid tunnel address '${tunnelAddress}' - must be in form 'host:port'`;
|
|
280
252
|
}
|
|
281
253
|
const [tunnelHost, tunnelPort] = tunnelAddress.split(':')
|
|
282
254
|
const { host, port } = serviceKey
|
|
@@ -288,5 +260,3 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
288
260
|
return serviceKey
|
|
289
261
|
}
|
|
290
262
|
}
|
|
291
|
-
|
|
292
|
-
module.exports = new HanaDeployer();
|
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const util = require('util');
|
|
3
|
+
const { bold } = require('../../util/term');
|
|
3
4
|
|
|
4
5
|
const HDI_DEPLOY_LIB = '@sap/hdi-deploy';
|
|
5
6
|
|
|
6
|
-
class HdiDeployUtil {
|
|
7
|
+
module.exports = new class HdiDeployUtil {
|
|
7
8
|
|
|
8
|
-
async deploy(dbDir, vcapEnv = {},
|
|
9
|
+
async deploy(dbDir, vcapEnv = {}, { autoUndeploy } = {}) {
|
|
9
10
|
console.log(`Deploying to HANA from ${dbDir}`);
|
|
10
11
|
|
|
11
|
-
const
|
|
12
|
+
const { clean_env, deploy } = this._loadHdiDeployLib(dbDir);
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
const deployerEnv = JSON.parse(JSON.stringify(process.env));
|
|
14
15
|
const hdiDeployOptions = deployerEnv.HDI_DEPLOY_OPTIONS ? JSON.parse(deployerEnv.HDI_DEPLOY_OPTIONS) : {};
|
|
15
16
|
delete hdiDeployOptions.root;
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
deployerEnv = hdiDeployLib.clean_env(deployerEnv);
|
|
19
|
-
}
|
|
18
|
+
clean_env?.(deployerEnv);
|
|
20
19
|
|
|
21
20
|
if (vcapEnv.VCAP_SERVICES) {
|
|
22
21
|
deployerEnv.VCAP_SERVICES = JSON.stringify(vcapEnv.VCAP_SERVICES);
|
|
@@ -28,8 +27,8 @@ class HdiDeployUtil {
|
|
|
28
27
|
deployerEnv.TARGET_CONTAINER = vcapEnv.TARGET_CONTAINER;
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
if (
|
|
32
|
-
console.log(`
|
|
30
|
+
if (autoUndeploy) {
|
|
31
|
+
console.log(`HDI deployer automatically undeploys deleted resources using --auto-undeploy.`);
|
|
33
32
|
hdiDeployOptions.auto_undeploy = true;
|
|
34
33
|
}
|
|
35
34
|
|
|
@@ -37,12 +36,12 @@ class HdiDeployUtil {
|
|
|
37
36
|
deployerEnv.HDI_DEPLOY_OPTIONS = JSON.stringify(hdiDeployOptions);
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
const asyncDeploy = util.promisify(
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
43
|
-
let message = `HDI deployment failed with exit code ${
|
|
44
|
-
if (
|
|
45
|
-
message += `. ${
|
|
39
|
+
const asyncDeploy = util.promisify(deploy);
|
|
40
|
+
const { exitCode, signal } = await asyncDeploy(dbDir, deployerEnv);
|
|
41
|
+
if (exitCode) {
|
|
42
|
+
let message = `HDI deployment failed with exit code ${exitCode}`;
|
|
43
|
+
if (signal) {
|
|
44
|
+
message += `. ${signal}`;
|
|
46
45
|
}
|
|
47
46
|
throw new Error(message);
|
|
48
47
|
}
|
|
@@ -55,7 +54,7 @@ class HdiDeployUtil {
|
|
|
55
54
|
if (!libPath) {
|
|
56
55
|
throw new Error(`Required library '${HDI_DEPLOY_LIB}' not found in
|
|
57
56
|
${searchPaths.join('\n ')}
|
|
58
|
-
Add it either
|
|
57
|
+
Add it either to ${bold('devDependencies')} using 'npm install -D ${HDI_DEPLOY_LIB}' or install it globally using 'npm install -g ${HDI_DEPLOY_LIB}'.`);
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
console.log(`Using HDI deployer from ${libPath}`)
|
|
@@ -64,5 +63,3 @@ Add it either as a devDependency using 'npm install -D ${HDI_DEPLOY_LIB}' or ins
|
|
|
64
63
|
return require(libPath);
|
|
65
64
|
}
|
|
66
65
|
}
|
|
67
|
-
|
|
68
|
-
module.exports = new HdiDeployUtil();
|
|
@@ -98,7 +98,7 @@ async function _getProjectInfo(projectPath, mta) {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
async function _getMta(projectPath) {
|
|
101
|
-
// yaml.parse
|
|
101
|
+
// yaml.parse doesn't like null
|
|
102
102
|
const mtaFilePath = path.join(projectPath, MTA_YAML)
|
|
103
103
|
|
|
104
104
|
const existsMtaYaml = existsSync(mtaFilePath)
|
package/lib/env/schema.js
CHANGED
|
@@ -65,6 +65,7 @@ _robustRequire.resolve = function (id, options = {}) {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
function mergePluginSchema(pluginSchema) {
|
|
68
|
+
// contribution points also appear in schema file ./schemas/cds-rc.js
|
|
68
69
|
return {
|
|
69
70
|
into: async (targetSchema) => {
|
|
70
71
|
if (pluginSchema.buildTaskType) {
|
|
@@ -5,6 +5,59 @@ module.exports = {
|
|
|
5
5
|
$defs: {
|
|
6
6
|
cdsRoot: {
|
|
7
7
|
properties: {
|
|
8
|
+
// declaration for schema node for cds plugins contributing cds configuration
|
|
9
|
+
schema: {
|
|
10
|
+
type: "object",
|
|
11
|
+
description: "Schema declaration for cds plugins contributing cds configuration",
|
|
12
|
+
additionalProperties: false,
|
|
13
|
+
properties: {
|
|
14
|
+
cds: {
|
|
15
|
+
type: "object",
|
|
16
|
+
description: "Containing json schema declarations for root level entries under cds node.",
|
|
17
|
+
patternProperties: {
|
|
18
|
+
// new keyword must start with word character or opening brackets
|
|
19
|
+
"^[\\[\\(\\w].*": {
|
|
20
|
+
$ref: "https://json-schema.org/draft/2020-12/schema"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
buildTaskType: {
|
|
26
|
+
type: "object",
|
|
27
|
+
additionalProperties: false,
|
|
28
|
+
required: ["name"],
|
|
29
|
+
properties: {
|
|
30
|
+
name: {
|
|
31
|
+
type: "string",
|
|
32
|
+
minLength: 1,
|
|
33
|
+
description: "The name of the build task type."
|
|
34
|
+
},
|
|
35
|
+
description: {
|
|
36
|
+
type: "string",
|
|
37
|
+
description: "The description of the build task type."
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
databaseType: {
|
|
43
|
+
type: "object",
|
|
44
|
+
additionalProperties: false,
|
|
45
|
+
required: ["name"],
|
|
46
|
+
properties: {
|
|
47
|
+
name: {
|
|
48
|
+
type: "string",
|
|
49
|
+
minLength: 1,
|
|
50
|
+
description: "The name of the database type."
|
|
51
|
+
},
|
|
52
|
+
description: {
|
|
53
|
+
type: "string",
|
|
54
|
+
description: "The description of the database type."
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
|
|
8
61
|
// build specific schema
|
|
9
62
|
build: {
|
|
10
63
|
type: "object",
|
|
@@ -115,4 +168,4 @@ module.exports = {
|
|
|
115
168
|
]
|
|
116
169
|
}
|
|
117
170
|
}
|
|
118
|
-
}
|
|
171
|
+
}
|
|
@@ -319,6 +319,12 @@ module.exports = class ParserContext {
|
|
|
319
319
|
*/
|
|
320
320
|
getPropertyObject(propertyContent) {
|
|
321
321
|
const property = {};
|
|
322
|
+
if ('$ref' in propertyContent) {
|
|
323
|
+
const refPathParts = propertyContent['$ref'].split('/');
|
|
324
|
+
const propertyName = refPathParts[refPathParts.length - 1];
|
|
325
|
+
property['type'] = { 'ref': [refPathParts[3], propertyName] };
|
|
326
|
+
return property;
|
|
327
|
+
}
|
|
322
328
|
const inputType = this.getTypeFromArray(propertyContent.type);
|
|
323
329
|
const cdsType = this.getDataType(inputType, propertyContent.format);
|
|
324
330
|
|
package/lib/import/common.js
CHANGED
|
@@ -148,30 +148,11 @@ async function _copyToSrvExternal(file, dst, cwd) {
|
|
|
148
148
|
return file;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
async function isValidJsonObject(options){
|
|
152
|
-
let config;
|
|
153
|
-
try {
|
|
154
|
-
if (options.config) {
|
|
155
|
-
config = JSON.parse(options.config);
|
|
156
|
-
return config;
|
|
157
|
-
}
|
|
158
|
-
} catch (error) {
|
|
159
|
-
throw new Error(messages.SPECIFY_CONFIG);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
151
|
async function preProcess(file, options, cwd) {
|
|
164
152
|
let srcFilePath = await isValidInputFile(path.resolve(cwd, file));
|
|
165
153
|
if (options.as && !_supportedAsFormats().includes(options.as))
|
|
166
154
|
throw new Error(messages.INVALID_AS_OPTION);
|
|
167
|
-
|
|
168
|
-
const external = path.resolve(cwd, 'srv/external');
|
|
169
|
-
const destFilePath = path.join(external, path.basename(srcFilePath));
|
|
170
|
-
|
|
171
|
-
if (!options.dry && !options.no_copy)
|
|
172
|
-
srcFilePath = await _copyToSrvExternal(srcFilePath, destFilePath, cwd);
|
|
173
|
-
|
|
174
|
-
return [srcFilePath, destFilePath];
|
|
155
|
+
return srcFilePath;
|
|
175
156
|
}
|
|
176
157
|
|
|
177
158
|
async function processOptions(cliOptions, odataOptions) {
|
|
@@ -359,14 +340,28 @@ async function _updatePackageJson(dest, services, kind, cwd, config) {
|
|
|
359
340
|
}
|
|
360
341
|
}
|
|
361
342
|
|
|
343
|
+
function _parseValidJsonConfig(config) {
|
|
344
|
+
try {
|
|
345
|
+
if (config) {
|
|
346
|
+
return JSON.parse(config);
|
|
347
|
+
}
|
|
348
|
+
} catch (error) {
|
|
349
|
+
throw new Error(messages.SPECIFY_CONFIG);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
362
353
|
async function postProcess(filePath, options, csn, cwd) {
|
|
363
354
|
const services = _getServiceNames(csn);
|
|
364
|
-
let config =
|
|
365
|
-
|
|
355
|
+
let config = _parseValidJsonConfig(options.config);
|
|
356
|
+
const external = path.resolve(cwd, 'srv/external');
|
|
357
|
+
const destFilePath = path.join(external, path.basename(filePath));
|
|
358
|
+
if (options.out) options.out = _validateAndComputeOutputFilePath(options.out, options.as, destFilePath);
|
|
366
359
|
const kind = options.inputFileKind;
|
|
367
|
-
const result = await _getResult(csn, options.as, options.out ||
|
|
360
|
+
const result = await _getResult(csn, options.as, options.out || destFilePath.replace(/\.[^.]+$/, ''), options.force, kind);
|
|
368
361
|
const written = options.dry || await _write(result, services, cwd);
|
|
369
362
|
const registered = options.dry || options.no_save || await _updatePackageJson(result[1].replace(/\.[^.]+$/, ''), services, kind, cwd, config);
|
|
363
|
+
if (!options.dry && !options.no_copy)
|
|
364
|
+
await _copyToSrvExternal(filePath, destFilePath, cwd);
|
|
370
365
|
if (options.dry) return console.log(result[0]);
|
|
371
366
|
return Promise.all([written, registered]);
|
|
372
367
|
}
|
|
@@ -1025,7 +1025,6 @@ MetadataConverter.Context = Context;
|
|
|
1025
1025
|
* @param {Object} result The metadata abstract syntax tree of the depended metadata document
|
|
1026
1026
|
*/
|
|
1027
1027
|
|
|
1028
|
-
|
|
1029
1028
|
/**
|
|
1030
1029
|
* This callback is displayed as part of the DefaultXmlStrategy class.
|
|
1031
1030
|
* This factory is called when the converter can not find a needed type within the current metadata document.
|
|
@@ -1073,7 +1072,6 @@ The result must be returned via 'return' statement.
|
|
|
1073
1072
|
* @returns {Expression} The expression to interpret the current element node
|
|
1074
1073
|
*/
|
|
1075
1074
|
|
|
1076
|
-
|
|
1077
1075
|
/**
|
|
1078
1076
|
* This callback is displayed as part of the DefaultXmlStrategy class.
|
|
1079
1077
|
* This callback is called on finishing a strategy.
|
|
@@ -1083,7 +1081,6 @@ The result must be returned via 'return' statement.
|
|
|
1083
1081
|
* @param {Any} result The output of the Strategy
|
|
1084
1082
|
*/
|
|
1085
1083
|
|
|
1086
|
-
|
|
1087
1084
|
class DefaultXmlStrategy extends Strategy {
|
|
1088
1085
|
constructor(options) {
|
|
1089
1086
|
super(undefined, options);
|
|
@@ -1243,7 +1240,6 @@ class DefaultXmlStrategy extends Strategy {
|
|
|
1243
1240
|
}
|
|
1244
1241
|
MetadataConverter.DefaultXmlStrategy = DefaultXmlStrategy;
|
|
1245
1242
|
|
|
1246
|
-
|
|
1247
1243
|
class EdmxRootExpression extends Expression {
|
|
1248
1244
|
constructor(element, expression, context) { super(element, expression, context); }
|
|
1249
1245
|
interpret() {
|
|
@@ -1441,7 +1437,6 @@ class AbstractLogicalExpression extends Expression {
|
|
|
1441
1437
|
}
|
|
1442
1438
|
}
|
|
1443
1439
|
|
|
1444
|
-
|
|
1445
1440
|
class AndExpression extends AbstractLogicalExpression {
|
|
1446
1441
|
constructor(element, expression, context) { super(element, expression, context, '$And'); }
|
|
1447
1442
|
}
|
|
@@ -1671,8 +1666,18 @@ class PropertyValueExpression extends Expression {
|
|
|
1671
1666
|
this.setTarget(target);
|
|
1672
1667
|
this.buildTypeCollectionAndFacets('$Type');
|
|
1673
1668
|
|
|
1669
|
+
/**
|
|
1670
|
+
* We can have instances of EDMX where we see value for a particular attribute as:
|
|
1671
|
+
* AnnotationPath="part/icipants/@UI.LineItem" or Path="SAP__Messages/random”, etc
|
|
1672
|
+
*
|
|
1673
|
+
* In such cases we need to replace '/' with '.' and only then the CSN generated will
|
|
1674
|
+
* be treated correctly in the later steps.
|
|
1675
|
+
* * For now this is only done for AnnotationPath value in #1680 to resolve an issue
|
|
1676
|
+
* * TODO: In future we might have to do it for many such value.
|
|
1677
|
+
*/
|
|
1674
1678
|
if (element.attributes.Path) target[name] = { "=": element.attributes.Path };
|
|
1675
1679
|
if (element.attributes.PropertyPath) target[name] = { "=": element.attributes.PropertyPath };
|
|
1680
|
+
if (element.attributes.AnnotationPath) target[name] = { "=": (element.attributes.AnnotationPath).replaceAll('/', '.') };
|
|
1676
1681
|
if (element.attributes.EnumMember) target[name] = { "#": element.attributes.EnumMember.substring(element.attributes.EnumMember.lastIndexOf('/') + 1) };
|
|
1677
1682
|
if (element.attributes.NavigationPropertyPath) target[name] = { "=": element.attributes.NavigationPropertyPath };
|
|
1678
1683
|
if (element.attributes.String) target[name] = element.attributes.String;
|
|
@@ -1684,8 +1689,8 @@ class PropertyValueExpression extends Expression {
|
|
|
1684
1689
|
if (element.attributes.Bool) target[name] = element.attributes.Bool === 'true';
|
|
1685
1690
|
/**
|
|
1686
1691
|
* If the property valus is of type `Date`, import it as a string itself
|
|
1687
|
-
* <PropertyValue Property="Date" Date="2021-08-24"
|
|
1688
|
-
* We might have to have a strict check in the
|
|
1692
|
+
* <PropertyValue Property="Date" Date="2021-08-24"/>
|
|
1693
|
+
* We might have to have a strict check in the future.. 'YYYY-MM_DD`
|
|
1689
1694
|
*/
|
|
1690
1695
|
if (element.attributes.Date) target[name] = element.attributes.Date;
|
|
1691
1696
|
|
|
@@ -2411,7 +2416,6 @@ class OnDeleteExpression extends Expression {
|
|
|
2411
2416
|
|
|
2412
2417
|
}
|
|
2413
2418
|
|
|
2414
|
-
|
|
2415
2419
|
MetadataConverter.createOdataV4MetadataXmlToOdataV4CsdlStrategy = (name_space) => {
|
|
2416
2420
|
MetadataConverter.custom_namespace = name_space;
|
|
2417
2421
|
return new MetadataConverter.DefaultXmlStrategy()
|