@sap/cds-dk 4.7.1 → 4.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.
Files changed (59) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/bin/activate.js +5 -1
  3. package/bin/add.js +2 -2
  4. package/lib/bind/bindingUtil.js +6 -1
  5. package/lib/client/activate.js +8 -3
  6. package/lib/client/base_command.js +5 -1
  7. package/lib/client/helper/cf_helper.js +5 -2
  8. package/lib/client/params.js +16 -2
  9. package/lib/client/settings_manager.js +15 -8
  10. package/lib/compile/openapi/csdl2openapi.js +6 -8
  11. package/lib/compile/to-xsuaa.js +0 -14
  12. package/lib/import/utils/MetadataConverter.js +6 -4
  13. package/lib/import/v2parser.js +59 -35
  14. package/lib/import/v4parser.js +70 -47
  15. package/lib/init/constants.js +10 -4
  16. package/lib/init/index.js +13 -7
  17. package/lib/init/template/_merging/existences.js +161 -0
  18. package/lib/init/template/approuter/files/default-env.json +9 -0
  19. package/lib/init/template/approuter/files/mta.yaml.hbs +48 -0
  20. package/lib/init/template/approuter/files/package.json +9 -0
  21. package/lib/init/template/approuter/files/xs-app.json +8 -0
  22. package/lib/init/template/approuter/index.js +66 -0
  23. package/lib/init/template/auditlog/files/dependencies.package.json +5 -0
  24. package/lib/init/template/auditlog/files/mta.yaml.hbs +13 -0
  25. package/lib/init/template/auditlog/index.js +38 -0
  26. package/lib/init/template/cf-manifest/index.js +1 -1
  27. package/lib/init/template/hana/files/mta.yaml.hbs +42 -0
  28. package/lib/init/template/hana/files/nodejs/dependencies.package.json +5 -0
  29. package/lib/init/template/hana/index.js +53 -24
  30. package/lib/init/template/java/index.js +1 -5
  31. package/lib/init/template/mta/files/mta.yaml.hbs +24 -220
  32. package/lib/init/template/mta/index.js +25 -17
  33. package/lib/init/template/mtx/files/cds.package.json +24 -0
  34. package/lib/init/template/mtx/files/dependencies.package.json +5 -0
  35. package/lib/init/template/mtx/files/mta.yaml.hbs +74 -0
  36. package/lib/init/template/mtx/files/xs-security.json.hbs +23 -9
  37. package/lib/init/template/mtx/index.js +126 -29
  38. package/lib/init/template/templateBase.js +14 -0
  39. package/lib/init/template/xsuaa/files/cds.package.json +9 -0
  40. package/lib/init/template/xsuaa/files/dependencies.package.json +6 -0
  41. package/lib/init/template/xsuaa/files/mta.yaml.hbs +17 -0
  42. package/lib/init/template/xsuaa/index.js +75 -0
  43. package/lib/init/util/fsUtil.js +17 -3
  44. package/lib/init/util/logger.js +3 -1
  45. package/lib/init/util/mvnArchetypeUtil.js +3 -1
  46. package/lib/init/{template/mta → util}/projectReader.js +26 -11
  47. package/lib/init/util/templateUtil.js +267 -3
  48. package/lib/lint/Linter.js +0 -1
  49. package/lib/lint/io.js +2 -2
  50. package/lib/util/exec.d.ts +54 -0
  51. package/lib/util/exec.js +210 -0
  52. package/lib/watch/livereload-server.js +43 -31
  53. package/lib/watch/open.js +1 -1
  54. package/npm-shrinkwrap.json +3819 -16606
  55. package/package.json +7 -8
  56. package/readme.md +1 -1
  57. package/lib/import/internalDoc/edm2cdsTypeMapping.md +0 -38
  58. package/lib/init/template/mtx/package/cds.package.json +0 -29
  59. package/lib/init/template/mtx/package/dependencies.package.json +0 -9
package/CHANGELOG.md CHANGED
@@ -6,6 +6,55 @@ This project adheres to [Semantic Versioning](http://semver.org/).
6
6
 
7
7
  The format is based on [Keep a Changelog](http://keepachangelog.com/).
8
8
 
9
+ ## Version 4.8.1 - 2022-02-15
10
+
11
+ ### Changed
12
+
13
+ - `cds init` uses latest Maven Java archetype version `1.22.0` for creating Java projects.
14
+
15
+ ### Fixed
16
+
17
+ - Bump `follow-redirects` package to 1.14.8 (CVE-2022-0536)
18
+ - `cds bind --exec` no command output (STDOUT) displayed on Windows.
19
+
20
+ ## Version 4.8.0 - 2022-01-28
21
+
22
+ ### Added
23
+
24
+ - `cds import` now reflects the entity set and entity container level annotations in the csn.
25
+ - `cds activate --sync` allows to use the synchronous server API for extension upload.
26
+
27
+ ### Changed
28
+
29
+ - The forked package `@mendix/sqlite3` is now used instead of `sqlite3` to overcome CVE-2021-32804. No code changes in applications are needed, as the new package installed by `npm` with the same name `sqlite3`.
30
+ - [beta] The templating for `cds init` and `cds add` has been rewritten from scratch. This will allow for some new, more complex commands, such as `cds add mtx` or `cds add xsuaa`.
31
+
32
+ ### Fixed
33
+
34
+ - `cds import` now omits function imports with `put/delete` kind.
35
+ - `cds import` has fixed the entity type to entity set mapping in OData V2.
36
+ - `cds import` now supports collection type.
37
+ - `cds watch` now picks a free livereload port if the standard port 35729 is already bound
38
+ - `cds extend`, `cds activate`, `cds login`, and `cds logout` now prioritize command-line options over saved settings
39
+ - MTX client now logs fewer characters of secrets in debug output
40
+ - MTX client now handles incomplete error responses better
41
+
42
+ ## Version 4.7.3 - 2022-01-14
43
+
44
+ ### Changed
45
+
46
+ - Bump `follow-redirects` package to 1.14.7 (CVE-2022-0155)
47
+
48
+ ## Version 4.7.2 - 2021-12-15
49
+
50
+ ### Fixed
51
+
52
+ - `cds add lint` uses JSONC parser to read ESLint config files of type `.eslintrc[.js,.cjs,.json]`.
53
+
54
+ ### Changed
55
+
56
+ - `cds init` uses latest Maven Java archetype version `1.21.0` for creating Java projects.
57
+
9
58
  ## Version 4.7.1 - 2021-12-06
10
59
 
11
60
  ### Fixed
package/bin/activate.js CHANGED
@@ -1,5 +1,5 @@
1
1
  module.exports = Object.assign(activate, {
2
- options: ['--subdomain', '--passcode', '--username', '--clientid', '--to'],
2
+ options: ['--subdomain', '--passcode', '--username', '--clientid', '--to', '--sync'],
3
3
  flags: ['--undeploy', '--wsdl'],
4
4
  shortcuts: ['-s', '-p', '-u', '-c'],
5
5
  help: `
@@ -39,6 +39,10 @@ module.exports = Object.assign(activate, {
39
39
 
40
40
  Undeploy a previously deployed extension.
41
41
 
42
+ *--sync*
43
+
44
+ Use the synchronous server API to upload extensions.
45
+
42
46
  `})
43
47
 
44
48
 
package/bin/add.js CHANGED
@@ -36,8 +36,8 @@ module.exports = Object.assign(add, {
36
36
 
37
37
  *-4* | *--for* <entity name or prefix>
38
38
 
39
- Filters entity names with the given string. If it contains meta characters like ^ or *
40
- it is treated like a regular expression, otherwise as a prefix.
39
+ Filters entity names with the given string. If it contains meta characters like
40
+ '^' or '*', it is treated like a regular expression, otherwise as a prefix.
41
41
 
42
42
  *-o* | *--out* <target folder>
43
43
 
@@ -1,3 +1,7 @@
1
+
2
+ const os = require('os');
3
+ const IS_WIN = os.platform() === 'win32';
4
+
1
5
  const KindToRequiresNameMap = {
2
6
  xsuaa: 'uaa',
3
7
  hana: 'db',
@@ -82,7 +86,8 @@ function determineKinds(services) {
82
86
 
83
87
  function execAndExit(command, ...args) {
84
88
  const { spawnSync } = require('child_process');
85
- const result = spawnSync(command, args, { stdio: 'inherit', shell: false });
89
+ // use shell with Windows only; without output is not visible and some commands cannot be run
90
+ const result = spawnSync(command, args, { stdio: 'inherit', shell: IS_WIN });
86
91
  process.exit(result.status);
87
92
  }
88
93
 
@@ -8,6 +8,7 @@ const { collectFileContent, collectFiles, toUnixPath } = require('./helper/fs_he
8
8
  const Logger = require('./helper/logging');
9
9
 
10
10
  const COMMAND_URL_SUFFIX = 'asyncActivate';
11
+ const COMMAND_URL_SUFFIX_SYNC = 'activate';
11
12
  const UPDATE_CUSTOM_FILE_URL_SUFFIX = 'updateCustomTenantContent';
12
13
 
13
14
  const I18N_WHITELIST = ['.properties', '.csv'];
@@ -36,6 +37,7 @@ async function checkFinished(jobId, statusUrl, params, finisher) {
36
37
  module.exports = class Activate extends BaseCommand {
37
38
 
38
39
  static get COMMAND_URL_SUFFIX() { return COMMAND_URL_SUFFIX; }
40
+ static get COMMAND_URL_SUFFIX_SYNC() { return COMMAND_URL_SUFFIX_SYNC; }
39
41
  static get UPDATE_CUSTOM_FILE_URL_SUFFIX() { return UPDATE_CUSTOM_FILE_URL_SUFFIX; }
40
42
 
41
43
  static async run(injection, paramValues) {
@@ -46,7 +48,8 @@ module.exports = class Activate extends BaseCommand {
46
48
  Logger.log('Activating extension with parameters ' + JSON.stringify({
47
49
  appUrl: params.get('appUrl'),
48
50
  projectFolder: params.get('projectFolder'),
49
- undeploy: params.get('undeploy')
51
+ undeploy: params.get('undeploy'),
52
+ sync: params.get('sync')
50
53
  }));
51
54
  } else {
52
55
  Logger.log(`Activating extension located at ${params.get('projectFolder')} to URL ${params.get('appUrl')}`);
@@ -155,6 +158,7 @@ module.exports = class Activate extends BaseCommand {
155
158
 
156
159
  static async runActivate(cds, params) {
157
160
  let activationUrl;
161
+ const isSync = params.get('sync');
158
162
 
159
163
  return Activate.collectExtensionFiles(cds, params.get('projectFolder'))
160
164
  .then(allFiles => {
@@ -164,7 +168,8 @@ module.exports = class Activate extends BaseCommand {
164
168
 
165
169
  Logger.log('Activating extension...');
166
170
 
167
- activationUrl = Activate.getMtxApiUrl(params.get('appUrl'), COMMAND_URL_SUFFIX);
171
+ const urlSuffix = isSync ? Activate.COMMAND_URL_SUFFIX_SYNC : Activate.COMMAND_URL_SUFFIX;
172
+ activationUrl = Activate.getMtxApiUrl(params.get('appUrl'), urlSuffix);
168
173
  const requestOptions = Object.assign({
169
174
  headers: {}
170
175
  }, params.get('reqAuth'));
@@ -176,7 +181,7 @@ module.exports = class Activate extends BaseCommand {
176
181
  }, requestOptions);
177
182
  })
178
183
  .then(response => {
179
- if (response === null) {
184
+ if (isSync || response === null) {
180
185
  return;
181
186
  }
182
187
 
@@ -19,7 +19,7 @@ module.exports = class BaseCommand {
19
19
  throw error;
20
20
  }
21
21
 
22
- switch (error.response.status) {
22
+ switch (error.response && error.response.status) {
23
23
  case 401:
24
24
  Logger.error('Request failed: authorization might have expired. Please retry with valid passcode' +
25
25
  (params.has('passcodeUrl') ? ` from ${params.get('passcodeUrl')}` : ''), error);
@@ -52,4 +52,8 @@ module.exports = class BaseCommand {
52
52
  static getMtxApiUrl(url, action) {
53
53
  return BaseCommand.concatUrls(url, MTX_URL_PATH_PREFIX, action);
54
54
  }
55
+
56
+ static getMtxApiUrlSync(url, action) {
57
+ return BaseCommand.concatUrls(url, MTX_URL_PATH_PREFIX, action);
58
+ }
55
59
  }
@@ -154,12 +154,13 @@ async function getSubdomain(appName) {
154
154
  }
155
155
  }
156
156
 
157
+ Logger.debug(`Subdomain determined from CF app environment: ${value}`);
157
158
  return resolve(value);
158
159
  });
159
160
  });
160
161
 
161
162
  function errorResult(message) {
162
- Logger.debug(message);
163
+ Logger.debug(`Failed to get subdomain: ${message}`);
163
164
  return undefined;
164
165
  }
165
166
  }
@@ -174,8 +175,10 @@ async function getAppName(appUrl) {
174
175
  const app = apps.find(app => equalIgnoringTls(app.url, appUrl));
175
176
  if (!app) {
176
177
  Logger.debug(`Failed to find app with URL ${appUrl}. Running apps: ${apps.map(app => app.name)}`);
178
+ return;
177
179
  }
178
- return app && app.name;
180
+ Logger.debug(`App name determined from CF: ${app.name}`);
181
+ return app.name;
179
182
  }
180
183
 
181
184
  module.exports = {
@@ -20,7 +20,7 @@ const ObfuscationLevel = Object.freeze({
20
20
  full: 1,
21
21
  partial: 2 // Allows a few start and end characters displayed in DEBUG output; only use with string-type params
22
22
  });
23
- const DISCLOSURE_LENGTH = 3;
23
+ const DISCLOSURE_LENGTH = 2;
24
24
 
25
25
  /**
26
26
  * Parameters hold a name and a value and can only be set to compatible values.
@@ -205,6 +205,7 @@ class AllParams extends BasicParams {
205
205
  // `activate` PARAMS
206
206
  this.undeploy = new Param('undeploy', false);
207
207
  this.wsdl = new Param('wsdl', false);
208
+ this.sync = new Param('sync', false);
208
209
 
209
210
  // `logout` PARAMS
210
211
  this.deleteSettings = new Param('deleteSettings', false);
@@ -331,7 +332,7 @@ class ParamCollection {
331
332
  }
332
333
 
333
334
  /**
334
- * Add all params contained in another collection to this.
335
+ * Add all params contained in another collection to this, replacing existing params.
335
336
  * @param params the other `ParamCollection`
336
337
  */
337
338
  merge(params) {
@@ -341,6 +342,19 @@ class ParamCollection {
341
342
  }
342
343
  }
343
344
 
345
+ /**
346
+ * Add all params contained in another collection to this, not replacing existing params.
347
+ * @param params the other `ParamCollection`
348
+ */
349
+ mergeLower(params) {
350
+ this.checkFrozen();
351
+ for (const param of params) {
352
+ if (!this.has(param.name)) {
353
+ this.set(param.name, param.val);
354
+ }
355
+ }
356
+ }
357
+
344
358
  /**
345
359
  * Iterate through all contained param objects.
346
360
  */
@@ -71,13 +71,18 @@ async function getAppUrlAndSubdomainFromSuggestions(params) {
71
71
  params.set('appUrl', app.url);
72
72
 
73
73
  if (!params.has('subdomain')) {
74
- params.set('subdomain', await getSubdomain(app.name));
75
- if (params.has('subdomain')) {
76
- Logger.log(`Subdomain determined from CF app environment: ${params.get('subdomain')}`);
77
- } else {
78
- Logger.log('Failed to determine subdomain from CF app environment');
79
- }
74
+ params.set('subdomain', await getSubdomainFromCfAndNotify(app.name));
75
+ }
76
+ }
77
+
78
+ async function getSubdomainFromCfAndNotify(appName) {
79
+ const subdomain = await getSubdomain(appName);
80
+ if (subdomain) {
81
+ Logger.log(`Subdomain determined from CF app environment: ${subdomain}`);
82
+ } else {
83
+ Logger.log('Failed to determine subdomain from CF app environment');
80
84
  }
85
+ return subdomain;
81
86
  }
82
87
 
83
88
  function getTokenBaseUrl(params, renewUrl) {
@@ -156,7 +161,7 @@ class SettingsManager {
156
161
  const loadedSettings = await this._loadFromFile(projectFolder);
157
162
  Logger.debug(`Loaded project settings: ${loadedSettings.format()}`);
158
163
 
159
- params.merge(loadedSettings);
164
+ params.mergeLower(loadedSettings);
160
165
 
161
166
  await this.updateUrls(params, logout);
162
167
  await this.checkKeytar(params, logout);
@@ -172,12 +177,13 @@ class SettingsManager {
172
177
  }
173
178
  if (!logout) {
174
179
  if (!params.has('appUrl')) {
180
+ Logger.debug('App URL not given');
175
181
  await getAppUrlAndSubdomainFromSuggestions(params);
176
182
  } else if (!params.has('subdomain')) {
177
183
  Logger.debug('Subdomain not given');
178
184
  const appName = await getAppName(params.get('appUrl'));
179
185
  if (appName) {
180
- params.set('subdomain', await getSubdomain(appName));
186
+ params.set('subdomain', await getSubdomainFromCfAndNotify(appName));
181
187
  }
182
188
  }
183
189
  }
@@ -189,6 +195,7 @@ class SettingsManager {
189
195
  if (!params.has('subdomain')) {
190
196
  throw new Error('Subdomain not given');
191
197
  }
198
+ Logger.log(`Subdomain determined from app URL: ${params.get('subdomain')}`);
192
199
  }
193
200
  }
194
201
  function renewTokenUrl() {
@@ -1,7 +1,5 @@
1
1
  /**
2
2
  * Converts OData CSDL JSON to OpenAPI 3.0.2
3
- *
4
- * Latest version: https://github.wdf.sap.corp/d037427/odata-openapi/blob/master/lib/csdl2openapi.js
5
3
  */
6
4
 
7
5
  var pluralize = require('pluralize')
@@ -776,7 +774,7 @@ module.exports.csdl2openapi = function (
776
774
 
777
775
  /**
778
776
  * Unpack EnumMember value if it uses CSDL JSON CS01 style, like CAP does
779
- * @param {string or object} path Qualified name of referenced type
777
+ * @param {string or object} path Qualified name of referenced type
780
778
  * @return {object} Reference Object
781
779
  */
782
780
  function enumMember(member) {
@@ -788,7 +786,7 @@ module.exports.csdl2openapi = function (
788
786
 
789
787
  /**
790
788
  * Unpack NavigationPropertyPath value if it uses CSDL JSON CS01 style, like CAP does
791
- * @param {string or object} path Qualified name of referenced type
789
+ * @param {string or object} path Qualified name of referenced type
792
790
  * @return {object} Reference Object
793
791
  */
794
792
  function navigationPropertyPath(path) {
@@ -800,7 +798,7 @@ module.exports.csdl2openapi = function (
800
798
 
801
799
  /**
802
800
  * Unpack PropertyPath value if it uses CSDL JSON CS01 style, like CAP does
803
- * @param {string or object} path Qualified name of referenced type
801
+ * @param {string or object} path Qualified name of referenced type
804
802
  * @return {object} Reference Object
805
803
  */
806
804
  function propertyPath(path) {
@@ -2147,7 +2145,7 @@ module.exports.csdl2openapi = function (
2147
2145
 
2148
2146
  /**
2149
2147
  * Augment OpenAPI document with Security Requirements Object
2150
- * @param {object} openapi OpenAPI document to augment
2148
+ * @param {object} openapi OpenAPI document to augment
2151
2149
  * @param {object} entityContainer Entity Container object
2152
2150
  */
2153
2151
  function security(openapi, entityContainer) {
@@ -2162,7 +2160,7 @@ module.exports.csdl2openapi = function (
2162
2160
 
2163
2161
  /**
2164
2162
  * a qualified name consists of a namespace or alias, a dot, and a simple name
2165
- * @param {string} qualifiedName
2163
+ * @param {string} qualifiedName
2166
2164
  * @return {string} namespace-qualified name
2167
2165
  */
2168
2166
  function namespaceQualifiedName(qualifiedName) {
@@ -2172,7 +2170,7 @@ module.exports.csdl2openapi = function (
2172
2170
 
2173
2171
  /**
2174
2172
  * a qualified name consists of a namespace or alias, a dot, and a simple name
2175
- * @param {string} qualifiedName
2173
+ * @param {string} qualifiedName
2176
2174
  * @return {object} with components qualifier and name
2177
2175
  */
2178
2176
  function nameParts(qualifiedName) {
@@ -107,8 +107,6 @@ module.exports = function compile_to_xsuaa (model, options) {
107
107
 
108
108
  function mergeConfig(base, generated) {
109
109
  const result = base
110
- result.xsappname = base.xsappname || appName()
111
- result['tenant-mode'] = base['tenant-mode'] || 'dedicated'
112
110
 
113
111
  const mergeByName = (type) => {
114
112
  if (!generated[type]) return
@@ -140,15 +138,3 @@ function getBaseModel(options = {}) {
140
138
  }
141
139
  return options.base || {}
142
140
  }
143
-
144
- function appName() {
145
- const cwd = cds.root || process.cwd()
146
- let name
147
- try {
148
- name = require(path.resolve(cwd,'package.json')).name
149
- } catch (err) { // no package.json
150
- name = path.basename(cwd)
151
- }
152
- // remove any @foo/ from @foo/bar, truncate to max length
153
- return name.split('/').pop().substring(0, 100)
154
- }
@@ -396,7 +396,6 @@ class Expression {
396
396
  const target = this.getTarget();
397
397
  const element = this.getElement();
398
398
  const attribs = element.attributes;
399
- let custom_namespace = MetadataConverter.custom_namespace;
400
399
  if (attribs == null) return;
401
400
  let realType = attribs.Type || attribs.EntityType || attribs.UnderlyingType;
402
401
  const propertyName = attribs.UnderlyingType == null ? '$Type' : '$UnderlyingType';
@@ -574,6 +573,9 @@ class Expression {
574
573
  !(target.$Kind === 'NavigationProperty' && target.$Collection === true)) {
575
574
  target.$Nullable = true;
576
575
  }
576
+ this.includeExternalAnnotations(element,target);
577
+ }
578
+ includeExternalAnnotations(element,target){
577
579
  const nonStdAttributes = this.getNonStdAttributes(element.attributes);
578
580
  for (let i = 0; i < nonStdAttributes.length; i++) {
579
581
  let attr = nonStdAttributes[i];
@@ -581,7 +583,7 @@ class Expression {
581
583
  if(attrName)
582
584
  attrName = attrName.replace(/-/g,'.');
583
585
  let attribute;
584
- if ((custom_namespace.includes(prefix) && attrName !== undefined) || custom_namespace.includes(true))
586
+ if ((MetadataConverter.custom_namespace.includes(prefix) && attrName !== undefined) || MetadataConverter.custom_namespace.includes(true))
585
587
  attribute = "@" + prefix + "." + attrName;
586
588
  else if (attrName === undefined)
587
589
  attribute = "$" + prefix;
@@ -2221,10 +2223,10 @@ class EntityContainerExpression extends Expression {
2221
2223
  interpret() {
2222
2224
  const element = this.getElement();
2223
2225
  const target = { $Kind: 'EntityContainer' };
2226
+ this.includeExternalAnnotations(element,target);
2224
2227
  this.setTarget(target);
2225
2228
  super.interpret();
2226
-
2227
- return { [element.attributes.Name]: target };
2229
+ return { [element.attributes.Name]: target};
2228
2230
  }
2229
2231
  }
2230
2232
 
@@ -9,6 +9,7 @@ let allEntities = [];
9
9
  let allEntitiesMC = [];
10
10
  let allEntitySetMap = {};
11
11
  let allEntitySetMapMC = {};
12
+ let allEntitySetNamespaces = {};
12
13
  let allFunctionImports = [];
13
14
  let allFunctionImportsMap = {};
14
15
  let allAssociations = {};
@@ -248,6 +249,9 @@ function _extractEntityFromEntitySet(entitySet) {
248
249
  allEntitySetMapMC[entitySet[key].EntityType] = [];
249
250
  allEntitySetMapMC[entitySet[key].EntityType].push(entitySet[key].Name);
250
251
  entityName = _extractEntityFromNamespace(entitySet[key].EntityType);
252
+ let namespaces = namespaceAttributeFilter(entitySet[key]);
253
+ if(namespaces.length)
254
+ allEntitySetNamespaces[entitySet[key].Name] = namespaces;
251
255
  allEntities.push(entityName);
252
256
  allEntitiesMC.push(entitySet[key].Name);
253
257
  }
@@ -414,9 +418,16 @@ function _getServiceEntityProperty(
414
418
  displayFormat,
415
419
  isKey,
416
420
  documentation,
417
- isCollection,
418
421
  allowedNamespaceAttributes = -1
419
422
  ) {
423
+ let isCollection = -1;
424
+ //preprocessing of datatype in case it is a collection
425
+ if(dataType.substring(0,10) === 'Collection')
426
+ {
427
+ dataType = dataType.substring(11,dataType.length-1);
428
+ isCollection = 1;
429
+ }
430
+
420
431
  let cdsDataType = null;
421
432
  let hasInvalidPrecision = false;
422
433
  let propertyJson='';
@@ -452,7 +463,7 @@ function _getServiceEntityProperty(
452
463
 
453
464
  //Possibility of entity type or entity set as the data type
454
465
  if (!cdsDataType && allEntitySetMapMC[dataType]) {
455
- cdsDataType = serviceNamespace + "." + allEntitySetMapMC[dataType];
466
+ cdsDataType = serviceNamespace + "." + allEntitySetMapMC[dataType][0];
456
467
  } else if(!cdsDataType && allEntitySetMap[_extractEntityFromNamespace(dataType)]) {
457
468
  cdsDataType = dataType;
458
469
  }
@@ -553,7 +564,6 @@ function _getServiceComplexType(complexTypeKey, complexType) {
553
564
  complexTypeProperty["sap:display-format"],
554
565
  false,
555
566
  complexTypeProperty.doc,
556
- -1,
557
567
  complexTypePropertyNamespace
558
568
  );
559
569
  complexTypeCSN = complexTypeCSN + complexProperty;
@@ -583,7 +593,6 @@ function _getServiceComplexType(complexTypeKey, complexType) {
583
593
  complexTypeProperty["sap:display-format"],
584
594
  false,
585
595
  complexTypeProperty.doc,
586
- -1,
587
596
  complexTypePropertyNamespace
588
597
  );
589
598
  }
@@ -630,28 +639,20 @@ function _parseParameter(parameter)
630
639
  {
631
640
  let csn = "";
632
641
  let doc;
633
- let isCollection = -1;
634
642
  if(parameter.Documentation)
635
643
  doc = _parseDocumentationTag(parameter.Documentation);
636
644
 
637
645
  let namespaceAttributes = namespaceAttributeFilter(parameter._attributes);
638
646
  const parameterNamespaceAttributes = namespaceAttributes.length ? namespaceAttributes : -1;
639
647
 
640
- let type = parameter["_attributes"].Type;
641
- if(type.substring(0,10) === 'Collection')
642
- {
643
- type = type.substring(11,type.length-1);
644
- isCollection = 1;
645
- }
646
648
  csn = csn + _getServiceEntityProperty(parameter["_attributes"].Name,
647
- type,
649
+ parameter["_attributes"].Type,
648
650
  parameter["_attributes"].MaxLength,
649
651
  parameter["_attributes"].Precision,
650
652
  parameter["_attributes"].Scale,
651
653
  parameter["_attributes"]["sap:display-format"],
652
654
  false,
653
655
  doc,
654
- isCollection,
655
656
  parameterNamespaceAttributes);
656
657
  return csn;
657
658
  }
@@ -693,6 +694,10 @@ function _getFunctionImport(functionImport,isBounded) {
693
694
  } else if(functionImport[1] === "POST")
694
695
  {
695
696
  csn = csn + "action";
697
+ } else {
698
+ let message = functionImport[0].substring(functionImport[0].lastIndexOf(".") + 1) + " has kind " + functionImport[1] + " which is not supported.";
699
+ console.log(warn(message));
700
+ return "";
696
701
  }
697
702
  csn = csn + '"';
698
703
  if(functionImport[3] !== -1)
@@ -711,19 +716,13 @@ function _getFunctionImport(functionImport,isBounded) {
711
716
  }
712
717
  if(functionImport[2] !== -1)
713
718
  {
714
- let isCollection = -1;
715
- if(functionImport[2].substring(0,10) === 'Collection')
716
- {
717
- functionImport[2] = functionImport[2].substring(11,functionImport[2].length-1);
718
- isCollection = 1;
719
- }
720
- let returnCsn = _getServiceEntityProperty(noValue,functionImport[2],noValue,noValue,noValue,noValue,noValue,noValue,isCollection);
719
+ let returnCsn = _getServiceEntityProperty(noValue,functionImport[2],noValue,noValue,noValue,noValue,noValue,noValue);
721
720
  if(returnCsn != "")
722
721
  {
723
722
  csn = csn + ", \n";
724
723
  csn = csn + '"returns": { \n';
725
724
  //return type
726
- csn = csn + _getServiceEntityProperty(noValue,functionImport[2],noValue,noValue,noValue,noValue,noValue,noValue,isCollection);
725
+ csn = csn + returnCsn;
727
726
  csn = csn + '\n }';
728
727
  }
729
728
  }
@@ -737,21 +736,25 @@ function _getFunctionImport(functionImport,isBounded) {
737
736
  }
738
737
 
739
738
  function _getAllFunctionImports(functionImports,isBounded) {
740
- let functionImportCSN = "";
739
+ let functionImportsCSN = "";
741
740
  let i;
741
+ let functionImportCSN = "";
742
742
  for(i=0; i<functionImports.length;i++)
743
743
  {
744
- functionImportCSN =
745
- functionImportCSN +
746
- _getFunctionImport(functionImports[i],isBounded);
747
- if(i!== functionImports.length - 1) {
748
- functionImportCSN = functionImportCSN + ",\n";
744
+ functionImportCSN = _getFunctionImport(functionImports[i],isBounded);
745
+ functionImportsCSN = functionImportsCSN + functionImportCSN;
746
+ if(i!== functionImports.length - 1 && functionImportCSN !== "" ) {
747
+ functionImportsCSN = functionImportsCSN + ",\n";
749
748
  } else {
750
- functionImportCSN = functionImportCSN + "\n";
749
+ functionImportsCSN = functionImportsCSN + "\n";
751
750
  }
752
751
  }
753
752
 
754
- return functionImportCSN;
753
+ //if last function import is omitted, remove the extra characters
754
+ if (functionImportsCSN && (functionImportsCSN.endsWith(",\n") || functionImportsCSN.endsWith(",\n\n"))) {
755
+ functionImportsCSN= functionImportsCSN.substring(0, functionImportsCSN.lastIndexOf(",\n")) + "\n";
756
+ }
757
+ return functionImportsCSN;
755
758
  }
756
759
 
757
760
  function _getEntityName(entity) {
@@ -893,7 +896,6 @@ function _generateCSNEntityKeys(entityKeysList, entityPropertiesMap) {
893
896
  propAttributes[4],
894
897
  true,
895
898
  propAttributes[5],
896
- -1,
897
899
  propAttributes[6]
898
900
  );
899
901
  csnEntity = csnEntity + keyCsn;
@@ -935,7 +937,6 @@ function _generateCSNEntityProperties(entityKeysList, entityPropertiesMap) {
935
937
  propAttributes[4],
936
938
  false,
937
939
  propAttributes[5],
938
- -1,
939
940
  propAttributes[6]
940
941
  );
941
942
  csnEntity = csnEntity + propertyString;
@@ -1306,6 +1307,8 @@ function _constructServiceEntity(
1306
1307
  mockServerUc,
1307
1308
  documentation
1308
1309
  ) {
1310
+ let filteredNamespaces = {};
1311
+ let namespaces = [];
1309
1312
  let serviceEntityName = serviceNamespaceParam + "." + entityName;
1310
1313
  let csnEntity = '"' + serviceEntityName + '": {\n';
1311
1314
  let csnKeys;
@@ -1322,10 +1325,29 @@ function _constructServiceEntity(
1322
1325
  csnEntity = csnEntity + '"abstract": true,\n';
1323
1326
  }
1324
1327
 
1325
- if(entityAttributes.allowedNamespaceAttributes.length)
1326
- {
1327
- csnEntity += _generateNamespaceAttributes(entityAttributes.allowedNamespaceAttributes, false);
1328
+ /* Merging the namespace list from entity set and entity type
1329
+ And in case of same label, entity set's value will have precedence */
1330
+ if(allEntitySetNamespaces[entityName]){
1331
+ allEntitySetNamespaces[entityName].forEach((item) => {
1332
+ filteredNamespaces[Object.keys(item)[0]] = Object.values(item)[0];
1333
+ });
1328
1334
  }
1335
+ if(entityAttributes.allowedNamespaceAttributes.length){
1336
+ entityAttributes.allowedNamespaceAttributes.forEach((item) => {
1337
+ if(!filteredNamespaces[Object.keys(item)[0]]){
1338
+ filteredNamespaces[Object.keys(item)[0]] = Object.values(item)[0];
1339
+ }
1340
+ });
1341
+ }
1342
+ Object.keys(filteredNamespaces).forEach((item) => {
1343
+ let obj = {};
1344
+ obj[item] = filteredNamespaces[item];
1345
+ namespaces.push(obj)
1346
+ });
1347
+
1348
+ if(namespaces)
1349
+ csnEntity += _generateNamespaceAttributes(namespaces, false);
1350
+
1329
1351
  // if documentation exists for the entity
1330
1352
  if (documentation) {
1331
1353
  csnEntity = csnEntity + `"doc": "${documentation}",` + '\n';
@@ -1563,13 +1585,15 @@ function _generateEDMXJSON2CSN(
1563
1585
 
1564
1586
  if (mockServerUc) {
1565
1587
  let def = '"' + serviceNamespaceParam + '": {"kind": "service"' + ',\n' + '"@cds.external": true';
1566
-
1567
1588
  let serviceDoc = jsonObj["edmx:Edmx"]["edmx:DataServices"]["Schema"].EntityContainer.Documentation;
1589
+ let namespaceAttributes = namespaceAttributeFilter(jsonObj["edmx:Edmx"]["edmx:DataServices"]["Schema"].EntityContainer["_attributes"]);
1568
1590
  // if documentation exists for service
1569
1591
  if (serviceDoc) {
1570
1592
  let doc = _parseDocumentationTag(serviceDoc);
1571
1593
  if (doc) def += ',\n' + `"doc": "${doc}"`;
1572
1594
  }
1595
+ if(namespaceAttributes.length)
1596
+ def += _generateNamespaceAttributes(namespaceAttributes,true);
1573
1597
  def += "}";
1574
1598
  definitions.push(def);
1575
1599
  }