@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.
- package/bin/add.js +2 -2
- package/bin/bind.js +0 -1
- package/bin/cds.js +6 -4
- package/bin/deploy/to-hana.js +1 -1
- package/bin/init.js +2 -2
- package/lib/bind/cfKeyProvider.js +1 -0
- package/lib/bind/index.js +3 -0
- package/lib/bind/userCdsrcJsonStorageProvider.js +2 -10
- package/lib/build/constants.js +16 -14
- package/lib/build/provider/mtx/resourcesTarBuilder.js +2 -6
- package/lib/build/provider/mtx-sidecar/index.js +13 -8
- package/lib/client/base_command.js +3 -6
- package/lib/compile/asyncapi/components/messages/index.js +41 -18
- package/lib/compile/asyncapi/index.js +23 -2
- package/lib/import/odata/metadataconverter/MetadataConverter.js +1 -0
- package/lib/init/constants.js +1 -1
- package/lib/init/index.js +58 -66
- package/lib/init/template/approuter/index.js +3 -4
- package/lib/init/template/auditlog/index.js +3 -4
- package/lib/init/template/cf-manifest/files/manifest.yml.hbs +15 -23
- package/lib/init/template/cf-manifest/files/services-manifest.yml.hbs +22 -17
- package/lib/init/template/cf-manifest/index.js +2 -2
- package/lib/init/template/connectivity/index.js +2 -3
- package/lib/init/template/data/index.js +5 -5
- package/lib/init/template/destination/index.js +3 -5
- package/lib/init/template/enterprise-messaging/index.js +4 -5
- package/lib/init/template/enterprise-messaging-shared/index.js +3 -4
- package/lib/init/template/extensibility/index.js +2 -3
- package/lib/init/template/file-based-messaging/index.js +2 -2
- package/lib/init/template/hana/files/mta.yaml.hbs +1 -1
- package/lib/init/template/hana/index.js +39 -63
- package/lib/init/template/helm/index.js +15 -14
- package/lib/init/template/html5-repo/index.js +3 -7
- package/lib/init/template/java/index.js +3 -3
- package/lib/init/template/kibana/index.js +3 -4
- package/lib/init/template/lint/index.js +8 -7
- package/lib/init/template/mta/index.js +3 -2
- package/lib/init/template/multitenancy/files/package.sidecar.json.hbs +1 -1
- package/lib/init/template/multitenancy/index.js +16 -37
- package/lib/init/template/nodejs/index.js +2 -3
- package/lib/init/template/pipeline/index.js +2 -2
- package/lib/init/template/redis-messaging/index.js +3 -4
- package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +1 -1
- package/lib/init/template/sample/files/app/browse/webapp/manifest.json +1 -1
- package/lib/init/template/sample/files/srv/admin-service.cds +1 -1
- package/lib/init/template/sample/files/srv/cat-service.cds +1 -1
- package/lib/init/template/sample/index.js +6 -5
- package/lib/init/template/sqlite/index.js +1 -1
- package/lib/init/template/templateBase.js +9 -19
- package/lib/init/template/tiny-sample/index.js +5 -5
- package/lib/init/template/toggles/index.js +1 -2
- package/lib/init/template/typer/files/xsconfig.json +0 -1
- package/lib/init/template/typer/index.js +1 -1
- package/lib/init/template/xsuaa/index.js +3 -4
- package/lib/init/util/indent.js +57 -0
- package/lib/init/util/merge.js +71 -15
- package/lib/init/util/mvn.js +23 -9
- package/lib/init/util/projectReader.js +52 -168
- package/lib/init/util/template.js +12 -11
- package/lib/init/util/xml.js +6 -6
- package/lib/util/pruneAxiosErrors.js +22 -0
- package/lib/watch/open.js +6 -3
- package/npm-shrinkwrap.json +11 -17
- package/package.json +1 -2
package/lib/init/util/merge.js
CHANGED
|
@@ -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 (
|
|
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
|
-
|
|
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
|
|
137
|
+
if (!target?.contents) {
|
|
83
138
|
if (typeof into === 'string' && typeof from === 'string') {
|
|
84
|
-
|
|
139
|
+
project ? await writeYAML(into, source) : await copy(from, into)
|
|
140
|
+
return source
|
|
85
141
|
}
|
|
86
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
})
|
package/lib/init/util/mvn.js
CHANGED
|
@@ -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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
93
|
-
|
|
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 {
|
|
2
|
+
const { join, resolve, basename } = require('path')
|
|
3
|
+
const { fs, exists } = cds.utils
|
|
3
4
|
const { parseXml } = require('./xml')
|
|
4
|
-
const
|
|
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(
|
|
31
|
-
// if (options?.cwd) cds.root = options.cwd
|
|
32
|
-
|
|
55
|
+
async readProject() {
|
|
33
56
|
const env = this.getEnv()
|
|
34
|
-
|
|
57
|
+
const { appVersion, appName, appId, appDescription } = this
|
|
58
|
+
DEBUG?.({ env })
|
|
35
59
|
|
|
36
|
-
const _hasFacet = template => require(`../template/${template}`).hasFacet(env
|
|
60
|
+
const _hasFacet = template => require(`../template/${template}`).hasFacet(env)
|
|
37
61
|
const project = {
|
|
38
|
-
profile
|
|
39
|
-
language:
|
|
40
|
-
srv
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
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(
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
20
|
+
return copy(src, dest)
|
|
20
21
|
}
|
|
21
|
-
}
|
|
22
|
+
}))
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
async function render(src, templateValues) {
|
package/lib/init/util/xml.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
const
|
|
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 =
|
|
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
|
-
|
|
54
|
-
|
|
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
|
}
|