@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.
Files changed (53) hide show
  1. package/bin/cds.js +1 -1
  2. package/bin/import.js +2 -2
  3. package/bin/version.js +21 -20
  4. package/bin/watch.js +6 -0
  5. package/lib/build/buildTaskEngine.js +6 -4
  6. package/lib/build/buildTaskProviderFactory.js +5 -0
  7. package/lib/build/provider/edmxBuildPlugin.js +1 -1
  8. package/lib/build/provider/internalBuildTaskProvider.js +12 -10
  9. package/lib/build/provider/mtx/index.js +2 -2
  10. package/lib/build/provider/mtx/resourcesTarBuilder.js +1 -1
  11. package/lib/build/provider/mtx-extension/extensionCompilation.js +50 -0
  12. package/lib/build/provider/mtx-extension/index.js +50 -48
  13. package/lib/build/provider/mtx-sidecar/index.js +3 -3
  14. package/lib/build/util.js +9 -4
  15. package/lib/deploy/to-hana/hana.js +46 -76
  16. package/lib/deploy/to-hana/hdiDeployUtil.js +15 -18
  17. package/lib/deploy/to-hana/mtaUtil.js +1 -1
  18. package/lib/env/schema.js +1 -0
  19. package/lib/env/schemas/cds-rc.js +54 -1
  20. package/lib/import/asyncapi/parserContext.js +6 -0
  21. package/lib/import/common.js +18 -23
  22. package/lib/import/odata/metadataconverter/MetadataConverter.js +12 -8
  23. package/lib/import/odata/v4parser.js +67 -12
  24. package/lib/init/add.js +2 -0
  25. package/lib/init/constants.js +2 -4
  26. package/lib/init/merge.js +31 -13
  27. package/lib/init/projectReader.js +9 -1
  28. package/lib/init/registries/mta.js +19 -1
  29. package/lib/init/template/approuter/files/mta.yaml.hbs +1 -1
  30. package/lib/init/template/approuter/index.js +5 -5
  31. package/lib/init/template/connectivity/index.js +3 -4
  32. package/lib/init/template/html5-repo/files/app-package.json.hbs +16 -0
  33. package/lib/init/template/html5-repo/files/mta.yaml.hbs +83 -0
  34. package/lib/init/template/html5-repo/files/package.json.hbs +17 -0
  35. package/lib/init/template/html5-repo/files/ui5.yaml.hbs +33 -0
  36. package/lib/init/template/html5-repo/files/xs-app.json.hbs +19 -0
  37. package/lib/init/template/html5-repo/index.js +56 -12
  38. package/lib/init/template/mta/files/mta.yaml.hbs +1 -0
  39. package/lib/init/template/mta/index.js +5 -0
  40. package/lib/init/template/nodejs/files/.vscode/extensions.json +1 -2
  41. package/lib/init/template/nodejs/files/README.md +1 -1
  42. package/lib/init/template/sample/files/app/index.html +8 -6
  43. package/lib/init/template/sample/index.js +12 -2
  44. package/lib/lint/Linter.js +2 -2
  45. package/lib/lint/checks.js +0 -51
  46. package/lib/mtx/auth_manager.js +46 -55
  47. package/lib/mtx/settings_manager.js +14 -19
  48. package/lib/mtx/util/dependencies.js +1 -1
  49. package/lib/mtx/util/logging.js +10 -8
  50. package/lib/util/axios.js +5 -0
  51. package/lib/util/cf.js +1 -0
  52. package/npm-shrinkwrap.json +96 -42
  53. 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 hdiDeployUtil = require('./hdiDeployUtil');
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 UTF_8 = 'utf-8';
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
- // arg object
46
- // async deploy(model, serviceName, tunnelAddress, buildTaskOptions, vcapFile, undeployWhitelist, hdiOptions, bindCallback) { // NOSONAR
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
- let globalVcapEnv = vcapFile ? await this._loadEnvFile(vcapFile) : {};
69
- globalVcapEnv = { ...globalVcapEnv, ...this.getFromEnv('VCAP_SERVICES', 'SERVICE_REPLACEMENTS') };
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
- globalVcapEnv.TARGET_CONTAINER = process.env.TARGET_CONTAINER;
62
+ vcapEnv.TARGET_CONTAINER = process.env.TARGET_CONTAINER;
72
63
  }
73
64
 
74
65
  if (dry) {
75
- for (const buildResult of buildResults) {
76
- const model = await cds.load(buildResult.task.src);
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 buildResult of buildResults) {
86
- let vcapEnv = globalVcapEnv;
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(currentModelFolder, 'undeploy.json'), JSON.stringify(undeployWhitelist, null, 2));
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(currentModelFolder, { recursive: true });
98
- } else {
99
- const { cfServiceInstanceName, cfServiceInstanceKeyName, serviceKey } =
100
- await this._getOrCreateCFService(projectPath, currentModelFolder, serviceName, tunnelAddress);
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
- vcapEnv = this._getVCAPServicesEntry(cfServiceInstanceName, serviceKey);
91
+ serviceName ??= cfServiceInstanceName;
92
+ if (!vcapEnv.VCAP_SERVICES || serviceName) {
93
+ vcapEnv.VCAP_SERVICES = this._getVCAPServicesEntry(cfServiceInstanceName, serviceKey);
94
+ }
105
95
 
106
- if (options.storeCredentials) {
107
- await this._addInstanceToDefaultEnvJson([currentModelFolder, projectPath], cfServiceInstanceName, serviceKey);
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
- hdiOptions = hdiOptions || {}
114
- await hdiDeployUtil.deploy(currentModelFolder, vcapEnv, hdiOptions);
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}:${serviceKeyName}`,
108
+ to: `${serviceName}:${cfServiceInstanceKeyName}`,
120
109
  for: options.for,
121
- kind: buildResult.task.for
110
+ kind: task.for
122
111
  }
123
112
  await bind(bindOptions);
124
113
  }
125
114
  }
126
115
 
127
- if (options.storeCredentials) {
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
- let cfConfig = cfServiceInstanceMta && cfServiceInstanceMta.parameters && cfServiceInstanceMta.parameters.config;
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 new Error(`Could not create service key ${bold(cfServiceInstanceKey)}.`);
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 new Error(`Service key is missing mandatory field '${field}'. Make sure you are ${bold('not')} using a managed service.`);
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
- const buildResults = await build(buildTaskOptions);
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
- try {
232
- defaultEnvJson = JSON.parse(await fs.readFile(defaultEnvJsonPath, UTF_8));
233
- } catch (err) {
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 new Error(`Service name ${serviceInstanceName} must only contain alpha-numeric, hyphens, and underscores.`);
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 new Error(`Invalid tunnel address '${tunnelAddress}' - must be in form 'host:port'`)
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 = {}, options = {}) {
9
+ async deploy(dbDir, vcapEnv = {}, { autoUndeploy } = {}) {
9
10
  console.log(`Deploying to HANA from ${dbDir}`);
10
11
 
11
- const hdiDeployLib = await this._loadHdiDeployLib(dbDir);
12
+ const { clean_env, deploy } = this._loadHdiDeployLib(dbDir);
12
13
 
13
- let deployerEnv = JSON.parse(JSON.stringify(process.env));
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
- if (typeof hdiDeployLib.clean_env === 'function') {
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 (options.autoUndeploy) {
32
- console.log(`Hdi deployer automatically undeploys deleted resources using --auto-undeploy.`);
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(hdiDeployLib.deploy);
41
- const response = await asyncDeploy(dbDir, deployerEnv);
42
- if (response?.exitCode) {
43
- let message = `HDI deployment failed with exit code ${response.exitCode}`;
44
- if (response.signal) {
45
- message += `. ${response.signal}`;
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 as a devDependency using 'npm install -D ${HDI_DEPLOY_LIB}' or install it globally using 'npm install -g ${HDI_DEPLOY_LIB}'.`);
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 oesn't like null
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
 
@@ -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 = await isValidJsonObject(options);
365
- if (options.out) options.out = _validateAndComputeOutputFilePath(options.out, options.as, filePath);
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 || filePath.replace(/\.[^.]+$/, ''), options.force, kind);
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 futore.. 'YYYY-MM_DD`
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()