@sap/cds-dk 7.0.2 → 7.0.3

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 (64) hide show
  1. package/bin/add.js +2 -2
  2. package/bin/bind.js +0 -1
  3. package/bin/cds.js +6 -4
  4. package/bin/deploy/to-hana.js +1 -1
  5. package/bin/init.js +2 -2
  6. package/lib/bind/cfKeyProvider.js +1 -0
  7. package/lib/bind/index.js +3 -0
  8. package/lib/bind/userCdsrcJsonStorageProvider.js +2 -10
  9. package/lib/build/constants.js +16 -14
  10. package/lib/build/provider/mtx/resourcesTarBuilder.js +2 -6
  11. package/lib/build/provider/mtx-sidecar/index.js +13 -8
  12. package/lib/client/base_command.js +3 -6
  13. package/lib/compile/asyncapi/components/messages/index.js +41 -18
  14. package/lib/compile/asyncapi/index.js +23 -2
  15. package/lib/import/odata/metadataconverter/MetadataConverter.js +1 -0
  16. package/lib/init/constants.js +1 -1
  17. package/lib/init/index.js +58 -66
  18. package/lib/init/template/approuter/index.js +3 -4
  19. package/lib/init/template/auditlog/index.js +3 -4
  20. package/lib/init/template/cf-manifest/files/manifest.yml.hbs +15 -23
  21. package/lib/init/template/cf-manifest/files/services-manifest.yml.hbs +22 -17
  22. package/lib/init/template/cf-manifest/index.js +2 -2
  23. package/lib/init/template/connectivity/index.js +2 -3
  24. package/lib/init/template/data/index.js +5 -5
  25. package/lib/init/template/destination/index.js +3 -5
  26. package/lib/init/template/enterprise-messaging/index.js +4 -5
  27. package/lib/init/template/enterprise-messaging-shared/index.js +3 -4
  28. package/lib/init/template/extensibility/index.js +2 -3
  29. package/lib/init/template/file-based-messaging/index.js +2 -2
  30. package/lib/init/template/hana/files/mta.yaml.hbs +1 -1
  31. package/lib/init/template/hana/index.js +39 -63
  32. package/lib/init/template/helm/index.js +15 -14
  33. package/lib/init/template/html5-repo/index.js +3 -7
  34. package/lib/init/template/java/index.js +3 -3
  35. package/lib/init/template/kibana/index.js +3 -4
  36. package/lib/init/template/lint/index.js +8 -7
  37. package/lib/init/template/mta/index.js +3 -2
  38. package/lib/init/template/multitenancy/files/package.sidecar.json.hbs +1 -1
  39. package/lib/init/template/multitenancy/index.js +16 -37
  40. package/lib/init/template/nodejs/index.js +2 -3
  41. package/lib/init/template/pipeline/index.js +2 -2
  42. package/lib/init/template/redis-messaging/index.js +3 -4
  43. package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +1 -1
  44. package/lib/init/template/sample/files/app/browse/webapp/manifest.json +1 -1
  45. package/lib/init/template/sample/files/srv/admin-service.cds +1 -1
  46. package/lib/init/template/sample/files/srv/cat-service.cds +1 -1
  47. package/lib/init/template/sample/index.js +6 -5
  48. package/lib/init/template/sqlite/index.js +1 -1
  49. package/lib/init/template/templateBase.js +9 -19
  50. package/lib/init/template/tiny-sample/index.js +5 -5
  51. package/lib/init/template/toggles/index.js +1 -2
  52. package/lib/init/template/typer/files/xsconfig.json +0 -1
  53. package/lib/init/template/typer/index.js +1 -1
  54. package/lib/init/template/xsuaa/index.js +3 -4
  55. package/lib/init/util/indent.js +57 -0
  56. package/lib/init/util/merge.js +71 -15
  57. package/lib/init/util/mvn.js +23 -9
  58. package/lib/init/util/projectReader.js +52 -168
  59. package/lib/init/util/template.js +12 -11
  60. package/lib/init/util/xml.js +6 -6
  61. package/lib/util/pruneAxiosErrors.js +22 -0
  62. package/lib/watch/open.js +6 -3
  63. package/npm-shrinkwrap.json +11 -17
  64. package/package.json +1 -2
@@ -2,7 +2,7 @@ const YAML = require('@sap/cds-foss').yaml
2
2
  const { copy, read, write, exists } = require('../../cds').utils
3
3
  const { readYAML, writeYAML, readJSON } = require('../../util/fs')
4
4
  const { EOL } = require('node:os')
5
-
5
+ const { detectIndent } = require('../util/indent')
6
6
 
7
7
  function _isObject(item) {
8
8
  return item && typeof item === 'object' && !Array.isArray(item)
@@ -53,15 +53,57 @@ async function mergeGitignore (into, from) {
53
53
  return enhanced
54
54
  }
55
55
 
56
+ /**
57
+ * @typedef {Object} MatchedNode
58
+ * @property {string | MatchedNode} in - key path or matched node to start search from
59
+ * @property {Object[]} where - list of constraints
60
+ * @property {string} where[].property - property key path
61
+ * @property {string} where[].isEqualTo - comparison string
62
+ */
63
+
64
+ /**
65
+ * @typedef {Object} Overwrites
66
+ * @property {(string | MatchedNode)[]} item - path to item to be overwritten
67
+ * @property {string} withValue - replacement string
68
+ */
69
+
70
+ /**
71
+ * @typedef {Object} Deletion
72
+ * @property {MatchedNode} item - matched node to delete
73
+ * @property {Object[]} relationships - list of relationships to delete
74
+ * @property {string} relationships[].removeProperty - key path to to-be-removed property
75
+ * @property {string[]} relationships[].allWithin - key path to relationship
76
+ */
77
+
78
+ /**
79
+ * @typedef {Object} Relationship
80
+ * @property {(string | MatchedNode)[]} insert - source path to matched node
81
+ * @property {(string | MatchedNode)[]} into - target path for inserted relationship
82
+ */
83
+
84
+ /**
85
+ * @typedef {Object} MergingSemantics
86
+ * @property {boolean} [forceOverwrite] - overwrite values for existing keys
87
+ * @property {MatchedNode[]} [additions] - matched nodes to be added from source template
88
+ * @property {Overwrites[]} [overwrites] - matched keys to be overwritten with given string
89
+ * @property {Deletion[]} [deletions] - matched nodes to be deleted
90
+ * @property {Relationship[]} [relationships] - relationships to be inserted
91
+ */
92
+
93
+ /**
94
+ * @param {string | JSON} into - file path or JSON to merge into
95
+ * @param {string | JSON} from - file path or JSON to merge from
96
+ * @param {import('./projectReader').readProject} [project] - project descriptor used for Mustache replacements
97
+ * @param {MergingSemantics} [semantics] - merging algorithm semantics
98
+ * @returns {Promise<JSON>} - merged JSON
99
+ */
56
100
  async function mergeJSON(into, from, project, semantics) {
57
101
  const source = typeof from === 'string' ? await readJSON(from, project) : from
58
102
 
59
- if (typeof into === 'string' && !exists(into)) {
60
- return write(into, source, { spaces: 2 })
61
- }
62
-
63
103
  let result
64
- if (semantics) {
104
+ if (typeof into === 'string' && !exists(into)) {
105
+ result = source
106
+ } else if (semantics) {
65
107
  const target = await read(into)
66
108
  const targetYAML = YAML.parseDocument(YAML.stringify(target))
67
109
  const sourceYAML = YAML.parseDocument(YAML.stringify(source))
@@ -71,19 +113,33 @@ async function mergeJSON(into, from, project, semantics) {
71
113
  const target = typeof into === 'string' ? await read(into) : into
72
114
  result = _deepMerge(target, source)
73
115
  }
74
- if (typeof into === 'string') await write(into, result, { spaces: 2 })
116
+ // we need to stringify result on site to be able to apply the appropriate indentation.
117
+ // cds-utils.write is currently hardcoded to use spaces for indentation.
118
+ if (typeof into === 'string') {
119
+ const indent = exists(into) && detectIndent((await read(into, 'utf-8')).split(EOL))
120
+ await write(into, JSON.stringify(result, null, indent ?? 2))
121
+ }
75
122
  return result
76
123
  }
77
124
 
125
+ /**
126
+ * @param {string | YAML.Document} into - file path or YAML to merge into
127
+ * @param {string | YAML.Document} from - file path or YAML to merge from
128
+ * @param {Object} [project] - project descriptor used for Mustache replacements
129
+ * @param {MergingSemantics} [semantics] - merging algorithm semantics
130
+ * @returns {Promise<YAML.Document>} - merged YAML
131
+ * @throws {Error} Throws an error if empty target YAML is passed in
132
+ */
78
133
  async function mergeYAML(into, from, project, semantics = {}) {
79
134
  const target = typeof into === 'string' ? await readYAML(into) : into instanceof YAML.Document ? into : new YAML.Document(into)
80
135
  const source = typeof from === 'string' ? await readYAML(from, project) : from instanceof YAML.Document ? from : new YAML.Document(from)
81
136
 
82
- if (!target || !target.contents) {
137
+ if (!target?.contents) {
83
138
  if (typeof into === 'string' && typeof from === 'string') {
84
- return project ? writeYAML(into, source) : copy(from, into)
139
+ project ? await writeYAML(into, source) : await copy(from, into)
140
+ return source
85
141
  }
86
- return /* has no YAML */
142
+ throw new Error(`Target YAML doesn't exist`)
87
143
  }
88
144
 
89
145
  const entryMap = new Map, templateEntryMap = new Map()
@@ -148,7 +204,7 @@ async function mergeYAML(into, from, project, semantics = {}) {
148
204
 
149
205
  if (node.items) {
150
206
  if (YAML.isSeq(node) && templateNode?.items) {
151
- actions?.mergeCollection && actions.mergeCollection(node, templateNode)
207
+ actions.mergeCollection?.(node, templateNode)
152
208
  _traverseYAMLCollection(node, actions, templateNode)
153
209
  } else {
154
210
  _traverseYAMLCollection(node, actions, templateNode)
@@ -194,12 +250,12 @@ async function mergeYAML(into, from, project, semantics = {}) {
194
250
  const targetNode = collection.items?.[i]
195
251
  if (targetNode) {
196
252
  _traverseYAMLNode(targetNode, templateIndex, actions, templateNode)
197
- } else if (actions.mergePair) {
198
- actions.mergePair(collection, templateNode)
253
+ } else {
254
+ actions.mergePair?.(collection, templateNode)
199
255
  }
200
256
  } else if (YAML.isMap(templateNode)) {
201
257
  const targetNode = collection.items[templateIndexToIndex.get(templateIndex)]
202
- if (actions.mergeCollection) actions.mergeCollection(targetNode, templateNode)
258
+ actions.mergeCollection?.(targetNode, templateNode)
203
259
  if (targetNode) {
204
260
  _traverseYAMLNode(targetNode, templateIndex, actions, templateNode)
205
261
  }
@@ -266,7 +322,7 @@ async function mergeYAML(into, from, project, semantics = {}) {
266
322
  .filter(addition => {
267
323
  const keyPath = typeof addition.in === 'string' ? addition.in : Array.isArray(addition.in) && typeof addition.in[0] === 'string' ? addition.in[0] : addition.in[1]
268
324
  const keys = keyPath.split('.')
269
- if (keys.length > collectionStack.length + 1) return
325
+ if (keys.length > collectionStack.length + 1) return false
270
326
  if (keys.length > 1) parent = collectionStack[keys.length]
271
327
  return _getYAMLProperty(parent, keyPath) === targetNode
272
328
  })
@@ -1,5 +1,5 @@
1
1
 
2
- const { MAVEN_ARCHETYPE_VERSION, OPTIONS: { JAVA_MVN, JAVA_PACKAGE, SAMPLES, HANA }, REGEX_JAVA_PACKAGE } = require('../constants');
2
+ const { URLS, MAVEN_ARCHETYPE_VERSION, OPTIONS: { JAVA_MVN, JAVA_PACKAGE, SAMPLES, HANA }, REGEX_JAVA_PACKAGE } = require('../constants');
3
3
 
4
4
  const { CliError } = require('../../client/helper/errors');
5
5
 
@@ -7,7 +7,8 @@ const { CliError } = require('../../client/helper/errors');
7
7
  const cp = require('child_process');
8
8
  const util = require('util');
9
9
  const execAsync = util.promisify(cp.exec);
10
-
10
+ const term = require('../../util/term');
11
+ const cmd = require('../../util/command');
11
12
 
12
13
  const DEFAULT_GROUP_ID = 'customer';
13
14
 
@@ -15,7 +16,6 @@ const LOG = console
15
16
  const cds = require('../../cds')
16
17
  const DEBUG = cds.debug('cli')
17
18
 
18
-
19
19
  class MvnArchetypeUtil {
20
20
 
21
21
  _scanMvnParams(mvnParams, params) {
@@ -51,11 +51,12 @@ class MvnArchetypeUtil {
51
51
  return Array.from(params, ([arg, val]) => val !== null && val !== undefined ? `${arg}=${val}` : arg)
52
52
  }
53
53
 
54
- getAddSamplesCmdArgs(options) {
54
+ getAddSamplesCmdArgs() {
55
55
  const params = new Map();
56
56
 
57
57
  params.set(`com.sap.cds:cds-maven-plugin:${MAVEN_ARCHETYPE_VERSION}:addSample`);
58
58
 
59
+ const { options } = cds.cli
59
60
  if (options[JAVA_MVN]) {
60
61
  this._scanMvnParams(options[JAVA_MVN], params);
61
62
  }
@@ -67,11 +68,12 @@ class MvnArchetypeUtil {
67
68
  return this._finalize(params);
68
69
  }
69
70
 
70
- getAddTestsCmdArgs(options) {
71
+ getAddTestsCmdArgs() {
71
72
  const params = new Map();
72
73
 
73
74
  params.set(`com.sap.cds:cds-maven-plugin:${MAVEN_ARCHETYPE_VERSION}:addIntegrationTest`);
74
75
 
76
+ const { options } = cds.cli
75
77
  if (options[JAVA_MVN]) {
76
78
  this._scanMvnParams(options[JAVA_MVN], params);
77
79
  }
@@ -83,14 +85,15 @@ class MvnArchetypeUtil {
83
85
  return this._finalize(params);
84
86
  }
85
87
 
86
- getAddHanaCmdArgs(options) {
88
+ getAddHanaCmdArgs() {
87
89
  const params = new Map();
88
90
 
89
91
  params.set(`com.sap.cds:cds-maven-plugin:${MAVEN_ARCHETYPE_VERSION}:addTargetPlatform`);
90
92
  params.set(`-DtargetPlatform`, `cloudfoundry`);
91
93
 
92
- if (options[JAVA_MVN]) {
93
- this._scanMvnParams(options[JAVA_MVN], params);
94
+ const javaMvn = cds.cli.options[JAVA_MVN]
95
+ if (javaMvn) {
96
+ this._scanMvnParams(javaMvn, params);
94
97
  }
95
98
 
96
99
  return this._finalize(params);
@@ -100,7 +103,7 @@ class MvnArchetypeUtil {
100
103
  return packageName.toLowerCase().replace(/-/g, '_').replace(/\.([0-9])/g, '._$1').replace(/^([0-9])/, '_$1').replace(/\.+/g, '.');
101
104
  }
102
105
 
103
- async getGenerateCmdArgs(projectName, options) {
106
+ async getGenerateCmdArgs(projectName, options = cds.cli.options) {
104
107
  const params = new Map();
105
108
 
106
109
  // set header
@@ -184,6 +187,17 @@ class MvnArchetypeUtil {
184
187
  artifactId
185
188
  };
186
189
  }
190
+
191
+ async execMvn(args) {
192
+ try {
193
+ await cmd.spawnCommand('mvn', args, { cwd: cds.root });
194
+ } catch (err) {
195
+ if (err.code === 'ENOENT' && err.path === 'mvn') {
196
+ throw `Maven executable 'mvn' not found, follow ${term.info(URLS.MAVEN_INSTALL_HELP)} and install Maven on your machine.`;
197
+ }
198
+ throw err;
199
+ }
200
+ }
187
201
  }
188
202
 
189
203
  module.exports = new MvnArchetypeUtil();
@@ -1,15 +1,40 @@
1
1
  const cds = require('../../cds')
2
- const { path, read, exists } = cds.utils
2
+ const { join, resolve, basename } = require('path')
3
+ const { fs, exists } = cds.utils
3
4
  const { parseXml } = require('./xml')
4
- const P_LANGUAGE_JAVA = 'java'
5
- const P_LANGUAGE_NODEJS = 'nodejs'
6
- const DEFAULT_P_LANGUAGE = P_LANGUAGE_NODEJS
7
- const DEBUG = /\b(y|all|cli)\b/.test(process.env.DEBUG) && console.debug
5
+ const DEBUG = cds.debug('cli')
8
6
 
9
7
  module.exports = new class ProjectReader {
10
8
 
11
9
  constructor() {
12
10
  this.readProject = this.readProject.bind(this)
11
+
12
+ // 1. Use project name and static default values
13
+ this.appVersion = cds.cli.options.add?.has('java') ? '1.0.0-SNAPSHOT' : '1.0.0'
14
+ this.appName = this.appId = basename(cds.root)
15
+ this.appDescription = 'A simple CAP project.'
16
+
17
+ // 2. Use either pom.xml or package.json
18
+ if (exists('pom.xml')) {
19
+ const { artifactId = [], description = [], version = [], properties = [] } = parseXml(resolve(cds.root, 'pom.xml')) ?? {}
20
+ const v = version[0] === '${revision}' ? properties[0]?.revision[0] : version[0]
21
+ if (v) {
22
+ // check compliance with mta version format - copied from mbt error message
23
+ if (/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$/.test(v)) {
24
+ this.appVersion = v
25
+ }
26
+ }
27
+ if (artifactId[0]) this.appName = this.appId = artifactId[0].split(/-parent/)[0]
28
+ if (description[0]) this.appDescription = description[0]
29
+ }
30
+ else if (exists('package.json')) {
31
+ const { name, version, description } = JSON.parse(fs.readFileSync(join(cds.root, 'package.json')))
32
+ this.appVersion = version
33
+ const segments = name.trim().replace(/@/g, '').split('/').map(encodeURIComponent)
34
+ this.appName = segments[segments.length - 1]
35
+ this.appId = segments.join('.')
36
+ this.appDescription = description ?? this.appDescription
37
+ }
13
38
  }
14
39
 
15
40
  // REVISIT: There should be a better (and parallelizable) API provided by cds.env
@@ -27,25 +52,29 @@ module.exports = new class ProjectReader {
27
52
  }
28
53
  }
29
54
 
30
- async readProject(options) {
31
- // if (options?.cwd) cds.root = options.cwd
32
-
55
+ async readProject() {
33
56
  const env = this.getEnv()
34
- DEBUG && DEBUG({ env })
57
+ const { appVersion, appName, appId, appDescription } = this
58
+ DEBUG?.({ env })
35
59
 
36
- const _hasFacet = template => require(`../template/${template}`).hasFacet(env, options)
60
+ const _hasFacet = template => require(`../template/${template}`).hasFacet(env)
37
61
  const project = {
38
- profile: options?.for,
39
- language: DEFAULT_P_LANGUAGE,
40
- srv: {},
41
- db: [],
42
- requires: [],
43
- get hasRequires() { return this.requires.length > 0 },
44
- get needsSidecar() { return (options?.add.has('hana') || this.db.length > 0) && (this.isJava || !this.isMultitenant) },
62
+ get profile() { return cds.cli.options?.for },
63
+ get language() { return exists('pom.xml') ? 'java' : 'nodejs' },
64
+ get srvPath() { return join(env.build.target, env.folders.srv.replace(/\/$/, '')).replace(/\\/g, '/') },
65
+ get archiveName() {
66
+ const pom = parseXml(resolve(cds.root, env.folders.srv, 'pom.xml'))
67
+ return (pom?.artifactId?.[0] ?? basename(cds.root)) + '-exec.' + pom?.packaging?.[0]
68
+ },
69
+ get db() {
70
+ const folder = env.folders.db
71
+ const name = folder.replace(/\/$/, '')
72
+ const path = join(env.build.target, name).replace(/\\/g, '/')
73
+ return { folder, name, path }
74
+ },
45
75
  get configFile() { return exists('pom.xml') ? '.cdsrc.json' : 'package.json' },
46
76
  get isNodejs() { return _hasFacet('nodejs') },
47
77
  get isJava() { return _hasFacet('java') },
48
- get hasSidecar() { return exists('mtx', 'sidecar') },
49
78
  get isMultitenant() { return _hasFacet('multitenancy') },
50
79
  get isExtensible() { return _hasFacet('extensibility') },
51
80
  get hasHana() { return _hasFacet('hana') },
@@ -57,158 +86,13 @@ module.exports = new class ProjectReader {
57
86
  get hasMta() { return _hasFacet('mta') },
58
87
  get hasHelm() { return _hasFacet('helm') },
59
88
  get appPath() { return env.folders.app ?? 'app' },
89
+ get appVersion() { return appVersion },
90
+ get appName() { return appName },
91
+ get appId() { return appId },
92
+ get appDescription() { return appDescription },
60
93
  }
61
94
 
62
- // setting the effective env as it might have changed by already executed 'add' commands, e.g. cds init foo --add hana,mta
63
- const cds = require('../../cds')
64
- cds.env // make sure env is initialized, otherwise we cannot set it (exotic case)
65
- cds.env = env
66
-
67
- // check whether we have a hana service binding defined as a hana build task is returned by default for compatibility reasons if neither hana, nor sqlite has been defined
68
- if (env?.requires?.db?.kind === 'hana') {
69
- const dbFolder = env.folders.db.replace(/\/$/, '')
70
- const relDbDestPath = path.join(env.build.target, dbFolder).replace(/\\/g, '/')
71
- project.db.push({ path: relDbDestPath, name: dbFolder })
72
- }
73
-
74
- const srvFolder = env.folders.srv.replace(/\/$/, '')
75
- const relSrvDestPath = path.join(env.build.target, srvFolder).replace(/\\/g, '/')
76
- project.srvPath = relSrvDestPath
77
- project.srvName = srvFolder
78
-
79
- if (env['project-nature'] === 'java') {
80
- project.language = P_LANGUAGE_JAVA
81
- project.srvExt = await this._getJavaExtDescriptor(path.resolve(cds.root, srvFolder))
82
- }
83
-
84
- // setting app name, app description...
85
- await this._setAppDetails(project)
86
-
87
- // setting required resource information
88
- this._setResourceDependencies(env, project)
89
-
90
- DEBUG && DEBUG({ project })
95
+ DEBUG?.({ project })
91
96
  return project
92
97
  }
93
-
94
- /**
95
- * Determines java specific properties for the given module source path.
96
- * @param {object} srcPath
97
- */
98
- async _getJavaExtDescriptor(srcPath) {
99
- // by default spring-boot:repackage is used creating an executable jar archive
100
- const pom = await parseXml(path.join(srcPath, 'pom.xml'))
101
- if (pom) {
102
- const dependencies = pom?.dependencies?.[0]?.dependency
103
- const isOldJavaRuntime = [...dependencies].some(d => /com\.sap\.cloud\.servicesdk/.test(d?.groupId?.[0]))
104
- if (isOldJavaRuntime) {
105
- throw "Projects using the classic CAP Java SDK are not supported"
106
- }
107
-
108
- const descriptor = { archiveName: path.basename(cds.root) + "-exec.jar" }
109
- const archiveName = pom?.artifactId?.[0]
110
- const suffix = pom?.packaging?.[0]
111
- if (archiveName) descriptor.archiveName = archiveName + '-exec.' + suffix
112
- return descriptor
113
- }
114
- }
115
-
116
- async _setAppDetails(cap) {
117
- if (cap.language !== P_LANGUAGE_JAVA && cap.language !== P_LANGUAGE_NODEJS) {
118
- throw Error(`${cap.language} not supported`)
119
- }
120
-
121
- // Use either pom.xml or package.json
122
- if (exists('pom.xml')) {
123
- const pom = await parseXml('pom.xml')
124
- const [artifactId] = pom?.artifactId ?? []
125
- const [description] = pom?.description ?? []
126
- let [version] = pom?.version ?? []
127
- // might need to resolve <version>${revision}</version> property
128
- if (version === '${revision}') {
129
- version = pom?.properties?.[0].revision[0]
130
- }
131
- if (version) {
132
- // check compliance with mta version format - copied from mbt build error message
133
- // if not compliant we use a static default value, see below...
134
- if (/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$/.test(version)) {
135
- cap.appVersion = version
136
- }
137
- }
138
- if (artifactId) {
139
- cap.appName = artifactId.split(/-parent/)[0]
140
- }
141
- if (description) {
142
- cap.appDescription = description
143
- }
144
- }
145
- else if (exists('package.json')) {
146
- const { name, version, description } = await read('package.json')
147
- cap.appDescription = cap.appDescription ?? description
148
- cap.appVersion = cap.appVersion ?? version
149
- if (name) {
150
- const segments = name.trim().replace(/@/g, '').split('/').map(encodeURIComponent)
151
- cap.appName = cap.appName ?? segments[segments.length - 1]
152
- cap.appId = cap.appId ?? segments.join('.')
153
- cap.appDescription = cap.appDescription ?? cap.appName
154
- }
155
- }
156
-
157
- // 2. use project name and static default values
158
- cap.appName = cap.appName ?? path.basename(cds.root)
159
- cap.appDescription = cap.appDescription ?? cap.appName + " application"
160
- cap.appId = cap.appId ?? cap.appName
161
- cap.appVersion = cap.appVersion ?? '1.0.0'
162
- }
163
-
164
- // REVISIT: Only used for orphaned cds add cf-manifest. Still needed?
165
- _setResourceDependencies(env, cap) {
166
- for (const [key, require] of Object.entries(env.requires)) {
167
- if (key === 'sql' || key === 'sqlite' || key === 'hana') continue // ignore abstract helper definitions like sql
168
- const resource = this._getReqResource(key, require, cap)
169
- if (resource) cap.requires.push({ resource }) // using production profile
170
- }
171
- }
172
-
173
- _getReqResource(key, reqEntry, cap) {
174
- const { kind, dialect, use, vcap = {}, multiTenant } = reqEntry
175
- const { appName, isMultitenant } = cap
176
- const resource = {
177
- name: !key.includes(appName) ? `${appName}-${key}` : key,
178
- service: '',
179
- vcap,
180
- get isMultitenant() { return isMultitenant || multiTenant || false },
181
- get isManagedHana() { return this.vcap.label === 'service-manager' || this.service === 'service-manager' },
182
- get isPlainHana() { return this.service === 'hana' && !this.isManagedHana },
183
- get isHana() { return this.isPlainHana || this.isManagedHana },
184
- get isXsuaa() { return this.service === 'xsuaa' },
185
- }
186
-
187
- switch (kind) {
188
- case 'sql':
189
- case 'hana':
190
- if (kind === 'sql' && dialect !== 'hana' && use !== 'hana') {
191
- break
192
- }
193
- // use different service name to avoid conflicts when switching from single tenant to multitenant
194
- // otherwise we would enforce that users have to delete the db container first
195
- if (resource.isMultitenant) {
196
- resource.service = 'service-manager'
197
- resource.vcap.plan = 'container'
198
- } else {
199
- resource.service = 'hana'
200
- resource.vcap.plan = 'hdi-shared'
201
- }
202
- break
203
-
204
- case 'xsuaa':
205
- resource.service = kind
206
- resource.vcap.plan = 'application'
207
- break
208
- default: {
209
- vcap.plan ? resource.service = kind : DEBUG && DEBUG(`Skip resource ${key}`)
210
- }
211
- }
212
- return resource.service ? resource : null
213
- }
214
98
  }
@@ -1,24 +1,25 @@
1
1
  const Mustache = require('mustache')
2
2
  const { copy, read, write, exists, readdir, path, isdir } = require('../../cds').utils
3
3
 
4
- async function copyFiles(templatePath, destinationPath, templateValues, overwrite = false) {
5
- for (const dirent of await readdir(templatePath)) {
6
- const fileNew = Mustache.render(dirent, templateValues)
7
- const src = path.join(templatePath, dirent);
8
- const dest = path.join(destinationPath, fileNew);
4
+ async function copyFiles(from, into, replacements, overwrite = false) {
5
+ const dirents = await readdir(from)
6
+ await Promise.all(dirents.map(async dirent => {
7
+ const fileNew = Mustache.render(dirent, replacements)
8
+ const src = path.join(from, dirent)
9
+ const dest = path.join(into, fileNew)
9
10
 
10
11
  if (isdir(src)) {
11
- await copyFiles(path.join(templatePath, dirent), dest, templateValues, overwrite);
12
+ return copyFiles(path.join(from, dirent), dest, replacements, overwrite)
12
13
  } else if (path.extname(dirent) === '.hbs') {
13
- const destinationPath = dest.replace('.hbs', '');
14
+ const destinationPath = dest.replace('.hbs', '')
14
15
  if (overwrite || !exists(destinationPath)) {
15
- const content = await render(src, templateValues);
16
- await write(destinationPath, content, { spaces: 2 });
16
+ const content = await render(src, replacements)
17
+ await write(destinationPath, content, { spaces: 2 })
17
18
  }
18
19
  } else {
19
- await copy(src, dest);
20
+ return copy(src, dest)
20
21
  }
21
- }
22
+ }))
22
23
  }
23
24
 
24
25
  async function render(src, templateValues) {
@@ -1,5 +1,4 @@
1
- const cds = require('../../cds')
2
- const { read } = cds.utils
1
+ const { readFileSync } = require('fs')
3
2
 
4
3
  /*
5
4
  Remove the _text attributes and convert value it into array
@@ -17,6 +16,8 @@ function _removeTextAttributes(value, parentElement) {
17
16
  }
18
17
  }
19
18
 
19
+ // REVISIT: Simplify config
20
+ // REVISIT: fast-xml-parser
20
21
  /**
21
22
  * Parse a given string containing xml
22
23
  * used to remove the _text object from converted json i.e. from xml-js.
@@ -24,11 +25,10 @@ function _removeTextAttributes(value, parentElement) {
24
25
  * and after removing textAttributes (using this function): "groupId":["customer"]
25
26
  * @param xml string to parse
26
27
  * @returns parsed json object
27
- */
28
-
29
- async function parseXml(pomXmlPath) {
28
+ */
29
+ function parseXml(path) {
30
30
  const xmljs = require('xml-js')
31
- const xml = await read(pomXmlPath, 'utf-8')
31
+ const xml = readFileSync(path, { encoding: 'utf-8' })
32
32
  return JSON.parse(xmljs.xml2json(xml, {
33
33
  ignoreComment: true,
34
34
  compact: true,
@@ -0,0 +1,22 @@
1
+ require('axios').interceptors.response.use(
2
+ response => response,
3
+ axError => {
4
+ const { url, method } = axError.config ?? {};
5
+ const { code, response } = axError;
6
+ const { status, data } = response ?? {};
7
+ const reason = data?.error /* RFC 6749 */ ?? axError.message;
8
+ const message = (url && method ? `${method.toUpperCase()} ${url} ` : '') +
9
+ 'failed' +
10
+ (status || code ? ':' : '') +
11
+ (status ? ` ${status}` : '') +
12
+ (code ? ` ${code}` : '') +
13
+ `. ${reason}.` +
14
+ (data ? ` Details: '${data.error_description /* RFC 6749 */ || require('util').inspect(data)}'.` : '');
15
+
16
+ const error = new Error(message);
17
+ if (require('../').debug('req')) {
18
+ error.cause = axError;
19
+ }
20
+ return Promise.reject(error);
21
+ }
22
+ );
package/lib/watch/open.js CHANGED
@@ -50,7 +50,10 @@ function _open(args) {
50
50
  args = ['/c', 'start'].concat(args)
51
51
  }
52
52
 
53
- return childProcess.execFile(command, args, null, err => {
54
- err && console.error(`Error opening '${command} ${args}' : ${err.message}`)
55
- })
53
+ const delay = parseInt(process.env.CDS_WATCH_OPEN_DELAY) || 1000
54
+ setTimeout(() => {
55
+ childProcess.execFile(command, args, null, err => {
56
+ err && console.error(`Error opening '${command} ${args}' : ${err.message}`)
57
+ })
58
+ }, delay);
56
59
  }