@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/bin/add.js
CHANGED
|
@@ -91,8 +91,8 @@ module.exports = Object.assign(add, {
|
|
|
91
91
|
|
|
92
92
|
`});
|
|
93
93
|
|
|
94
|
-
async function add(args
|
|
94
|
+
async function add(args) {
|
|
95
95
|
const CDSGenerator = require('../lib/init');
|
|
96
96
|
const generator = new CDSGenerator();
|
|
97
|
-
await generator.addCmd(args[0]
|
|
97
|
+
await generator.addCmd(args[0]);
|
|
98
98
|
}
|
package/bin/bind.js
CHANGED
|
@@ -72,7 +72,6 @@ async function cds_bind(args, options) {
|
|
|
72
72
|
if (process.env.CDS_ENV && process.argv[2] === 'bind') {
|
|
73
73
|
throw new CliError('Option --profile is no longer supported. Use --for instead to specify the profile to use when storing connection settings.');
|
|
74
74
|
}
|
|
75
|
-
options.for = options.for ?? 'hybrid';
|
|
76
75
|
|
|
77
76
|
if (!options.to) {
|
|
78
77
|
throw new CliError(`Use option --to or -2 to specify the target instance, e.g. cds bind --to myInstance:myService`);
|
package/bin/cds.js
CHANGED
|
@@ -152,13 +152,15 @@ function _dieForUnknownCommand(cmd) {
|
|
|
152
152
|
|
|
153
153
|
let bestMatchText = '';
|
|
154
154
|
|
|
155
|
-
const excludeList = ['cds.js', 'cds-ts.js', 'fix-redirects.js'];
|
|
155
|
+
const excludeList = ['cds.js', 'cds-ts.js', 'fix-redirects.js', 'util'];
|
|
156
156
|
const { readdirSync } = require('fs');
|
|
157
157
|
const { resolve } = require('path');
|
|
158
|
-
|
|
158
|
+
let commands = readdirSync(__dirname, { withFileTypes: true })
|
|
159
159
|
.concat(readdirSync(resolve(_local('@sap/cds'), '..', '..', 'bin'), { withFileTypes: true }))
|
|
160
|
-
.filter((f) =>
|
|
161
|
-
.map((f) => f.name.split('.')[0])
|
|
160
|
+
.filter((f) => !excludeList.includes(f.name))
|
|
161
|
+
.map((f) => f.name.split('.')[0])
|
|
162
|
+
commands = [...new Set(commands)] // make unique
|
|
163
|
+
.sort();
|
|
162
164
|
|
|
163
165
|
const bestMatches = fuzzySearch(cmd, commands, DEBUG);
|
|
164
166
|
|
package/bin/deploy/to-hana.js
CHANGED
|
@@ -8,6 +8,6 @@ exports.deploy = (model, serviceName, options) => {
|
|
|
8
8
|
tunnelAddress: options['tunnel-address'],
|
|
9
9
|
vcapFile: options['vcap-file'],
|
|
10
10
|
hdiOptions: { autoUndeploy: options['auto-undeploy'] },
|
|
11
|
-
storeCredentials: options['store-credentials']
|
|
11
|
+
storeCredentials: options['store-credentials'] // TODO: Remove with cds-dk 8
|
|
12
12
|
});
|
|
13
13
|
}
|
package/bin/init.js
CHANGED
|
@@ -98,11 +98,11 @@ module.exports = Object.assign(init, {
|
|
|
98
98
|
|
|
99
99
|
`})
|
|
100
100
|
|
|
101
|
-
async function init (args
|
|
101
|
+
async function init (args) {
|
|
102
102
|
if (process.argv.some(a => legacyOptions.includes(a))) return _showWarningMessage()
|
|
103
103
|
const CDSGenerator = require('../lib/init')
|
|
104
104
|
const generator = new CDSGenerator()
|
|
105
|
-
await generator.initCmd(args[0]
|
|
105
|
+
await generator.initCmd(args[0])
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
|
|
@@ -4,6 +4,7 @@ const DEBUG = cds.debug('cli')
|
|
|
4
4
|
const cfUtil = require('../util/cfUtil');
|
|
5
5
|
|
|
6
6
|
const axios = require('axios');
|
|
7
|
+
require('../util/pruneAxiosErrors');
|
|
7
8
|
const { URL } = require('url');
|
|
8
9
|
const { bold, highlight } = require('../util/term');
|
|
9
10
|
const { CliError } = require('../client/helper/errors');
|
package/lib/bind/index.js
CHANGED
|
@@ -58,11 +58,7 @@ class UserCdsrcJsonStorageProvider {
|
|
|
58
58
|
|
|
59
59
|
cdsRoot.requires = cdsRoot.requires || {};
|
|
60
60
|
let section;
|
|
61
|
-
|
|
62
|
-
section = cdsRoot.requires[profileKey] = cdsRoot.requires[profileKey] || {};
|
|
63
|
-
} else {
|
|
64
|
-
section = cdsRoot.requires;
|
|
65
|
-
}
|
|
61
|
+
section = cdsRoot.requires[profileKey] = cdsRoot.requires[profileKey] || {};
|
|
66
62
|
|
|
67
63
|
for (const service of services) {
|
|
68
64
|
const { name, kind, binding, credentials } = service;
|
|
@@ -72,11 +68,7 @@ class UserCdsrcJsonStorageProvider {
|
|
|
72
68
|
}
|
|
73
69
|
|
|
74
70
|
await this.writeJsonFile(jsonPath, jsonObject);
|
|
75
|
-
|
|
76
|
-
logger.log(`Saving bindings to ${highlight(jsonPath)} in profile ${highlight(options.for)}`);
|
|
77
|
-
} else {
|
|
78
|
-
logger.log(`Saving bindings to ${highlight(jsonPath)}`);
|
|
79
|
-
}
|
|
71
|
+
logger.log(`Saving bindings to ${highlight(jsonPath)} in profile ${highlight(options.for)}`);
|
|
80
72
|
}
|
|
81
73
|
|
|
82
74
|
async readJsonSafely(filePath) {
|
package/lib/build/constants.js
CHANGED
|
@@ -19,20 +19,22 @@ exports.BUILD_TASKS = [this.BUILD_TASK_JAVA, this.BUILD_TASK_JAVA_CF, this.BUILD
|
|
|
19
19
|
exports.ODATA_VERSION_V2 = "v2"
|
|
20
20
|
exports.ODATA_VERSION_V4 = "v4"
|
|
21
21
|
|
|
22
|
-
exports.CONTENT_LANGUAGE_BUNDLES = "contentLanguageBundles"
|
|
23
|
-
exports.CONTENT_DEFAULT_CSN = "contentDefaultCsn"
|
|
24
|
-
exports.CONTENT_EDMX = "contentEdmx"
|
|
25
|
-
exports.CONTENT_PACKAGE_JSON = "contentPackageJson"
|
|
26
|
-
exports.CONTENT_PACKAGELOCK_JSON = "contentPackageLockJson"
|
|
27
|
-
exports.CONTENT_HDBTABLEDATA = "contentHdbtabledata"
|
|
28
|
-
exports.CONTENT_NPMRC = "contentNpmrc"
|
|
29
|
-
exports.CONTENT_CDSRC_JSON = "contentCdsrcJson"
|
|
30
|
-
exports.CONTENT_NODE_MODULES = "contentNodeModules"
|
|
31
|
-
exports.CONTENT_ENV = "contentEnv"
|
|
32
|
-
exports.CONTENT_DEFAULT_ENV_JSON = "contentDefaultEnvJson"
|
|
33
|
-
|
|
34
|
-
exports.
|
|
35
|
-
exports.
|
|
22
|
+
exports.CONTENT_LANGUAGE_BUNDLES = "contentLanguageBundles" // create i18n.json language bundles - build task specific
|
|
23
|
+
exports.CONTENT_DEFAULT_CSN = "contentDefaultCsn" // create default CSN format flavor: "inferred" - build task specific
|
|
24
|
+
exports.CONTENT_EDMX = "contentEdmx" // create EDMX for required languages - build task specific
|
|
25
|
+
exports.CONTENT_PACKAGE_JSON = "contentPackageJson" // create package.json file if not existing, or modify existing package.json - ENABLED by default
|
|
26
|
+
exports.CONTENT_PACKAGELOCK_JSON = "contentPackageLockJson" // copy package-lock.json file if existing into deployment folder - ENABLED by default
|
|
27
|
+
exports.CONTENT_HDBTABLEDATA = "contentHdbtabledata" // create .hdbtabledata files for .csv files if not existing - ENABLED by default
|
|
28
|
+
exports.CONTENT_NPMRC = "contentNpmrc" // copy .npmrc file if existing into deployment folder - ENABLED by default
|
|
29
|
+
exports.CONTENT_CDSRC_JSON = "contentCdsrcJson" // copy .cdsrc.json file if existing into deployment folder - ENABLED by default
|
|
30
|
+
exports.CONTENT_NODE_MODULES = "contentNodeModules" // copy node_modules folder if existing into deployment folder - DISABLED by default
|
|
31
|
+
exports.CONTENT_ENV = "contentEnv" // copy .env file if existing into deployment folder - DISABLED by default
|
|
32
|
+
exports.CONTENT_DEFAULT_ENV_JSON = "contentDefaultEnvJson" // copy default-env.json file if existing into deployment folder - DISABLED by default
|
|
33
|
+
|
|
34
|
+
exports.CSV_FILE_DETECTION = "csvFileDetection" // detect CSV files based on CDS model location - ENABLED by default
|
|
35
|
+
exports.EXTENSION_POINT_VALIDATION = "extensionPointValidation" // validate extension point restrictions defined by the extended SaaS app - ENABLED by default
|
|
36
|
+
exports.DEFAULT_BUILT_IN_MODELS = "defaultBuiltInModels" // add @sap/cds* built-in models to build tasks - ENABLED by default
|
|
37
|
+
exports.MTX_SIDECAR_DB_VALIDATION = "mtxSidecarDbValidation" // validate consistency of app and sidecar db configuration - ENABLED by default
|
|
36
38
|
|
|
37
39
|
exports.FOLDER_GEN = "gen"
|
|
38
40
|
exports.FILE_EXT_CDS = ".cds"
|
|
@@ -83,12 +83,8 @@ class ResourcesTarBuilder {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
async _getHanaTenantDbDest() {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
this.handler.pushMessage(`Found SQLite database configuration. Ensure that productive cloud deployments requiring SAP HANA database are built with 'production' profile.`, WARNING)
|
|
89
|
-
return null
|
|
90
|
-
}
|
|
91
|
-
return hanaTask.dest
|
|
86
|
+
const hanaTask = this.handler.context.tasks.find(task => task.for === BUILD_TASK_HANA)
|
|
87
|
+
return hanaTask?.dest
|
|
92
88
|
}
|
|
93
89
|
}
|
|
94
90
|
module.exports = ResourcesTarBuilder
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
2
|
const cds = require('../../../cds')
|
|
3
3
|
const fs = require('fs')
|
|
4
|
-
const { FOLDER_GEN, DEFAULT_CSN_FILE_NAME } = require('../../constants')
|
|
4
|
+
const { FOLDER_GEN, DEFAULT_CSN_FILE_NAME, MTX_SIDECAR_DB_VALIDATION } = require('../../constants')
|
|
5
5
|
const NodeCfModuleBuilder = require('../nodejs')
|
|
6
6
|
const ResourcesTarProvider = require('../mtx/resourcesTarBuilder')
|
|
7
|
-
const {
|
|
7
|
+
const { ERROR } = NodeCfModuleBuilder
|
|
8
8
|
const { relativePaths, BuildError, resolveRequiredSapModels, getI18nDefaultFolder } = require('../../util')
|
|
9
9
|
|
|
10
10
|
const DEFAULT_MAIN_FOLDER = "_main"
|
|
@@ -61,13 +61,18 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
61
61
|
if (!main) {
|
|
62
62
|
throw new BuildError(`Invalid MTX sidecar configuration. Make sure that the profile 'mtx-sidecar' is configured and an up-to-date version of @sap/cds-mtxs is installed.`)
|
|
63
63
|
}
|
|
64
|
-
|
|
64
|
+
if (!this.hasBuildOption(MTX_SIDECAR_DB_VALIDATION, false)
|
|
65
|
+
&& !cds.env.build.tasks?.length // disable if custom build tasks exist
|
|
66
|
+
&& sidecarEnv.requires.db && cds.env.requires.db // ensure database is configured
|
|
67
|
+
&& sidecarEnv.requires.db.kind !== cds.env.requires.db.kind) {
|
|
68
|
+
throw new BuildError(`Inconsistent database configuration found - application db '${cds.env.requires.db?.kind}', sidecar db '${sidecarEnv.requires.db?.kind}'`)
|
|
69
|
+
}
|
|
70
|
+
const profiles = cds.env.profiles ?? []
|
|
71
|
+
if (!profiles.includes("production") && !profiles.includes("prod")) {
|
|
72
|
+
// need to overwrite 'development' profile setting '../..' with '_main', although it is questionable why cds build is executed with development profile
|
|
73
|
+
main = DEFAULT_MAIN_FOLDER
|
|
74
|
+
}
|
|
65
75
|
|
|
66
|
-
if (!profiles.includes("production") && !profiles.includes("prod")) {
|
|
67
|
-
main = DEFAULT_MAIN_FOLDER
|
|
68
|
-
// root should represent the production use case and not development
|
|
69
|
-
this.pushMessage(`MTX sidecar build results are created in folder '${main}'. Enable 'production' or 'prod' profile if the folder is configured differently.`, WARNING)
|
|
70
|
-
}
|
|
71
76
|
const destRoot = this.task.dest
|
|
72
77
|
const destMain = path.join(destRoot, main)
|
|
73
78
|
const destMainSrv = path.join(destMain, cds.env.folders.srv)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const fs = require('fs').promises;
|
|
3
3
|
|
|
4
|
+
require('../util/pruneAxiosErrors');
|
|
5
|
+
|
|
4
6
|
const { CliError } = require('./helper/errors');
|
|
5
7
|
|
|
6
8
|
const MTX_URL_PATH_PREFIX = 'mtx/v1/model/';
|
|
@@ -26,17 +28,12 @@ module.exports = class BaseCommand {
|
|
|
26
28
|
case 403:
|
|
27
29
|
throw new CliError(prefix + ': insufficient authorization. Ensure client has all necessary roles.', { error, command });
|
|
28
30
|
case 404:
|
|
29
|
-
throw new CliError(prefix + ': resource not found.', { command });
|
|
31
|
+
throw new CliError(prefix + ': resource not found.', { error, command });
|
|
30
32
|
case 422:
|
|
31
33
|
// Compilation or native-extension error.
|
|
32
34
|
throw new CliError(prefix + '.', { error, command });
|
|
33
35
|
default:
|
|
34
36
|
// Unexpected error should be transparent to user.
|
|
35
|
-
if (!require('..').debug('axios')) {
|
|
36
|
-
error.config?.auth && (error.config.auth = '...'); // TODO use &&= with Node > 14
|
|
37
|
-
delete error.request;
|
|
38
|
-
delete error.response;
|
|
39
|
-
}
|
|
40
37
|
throw error;
|
|
41
38
|
}
|
|
42
39
|
}
|
|
@@ -16,11 +16,31 @@ function getHeaderProperties(name) {
|
|
|
16
16
|
return { type: { const: name } };
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
function nestedAnnotation(resObj, asyncapiProperty, keys, value) {
|
|
20
|
+
if (resObj[asyncapiProperty] === undefined) {
|
|
21
|
+
resObj[asyncapiProperty] = {};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let node = resObj[asyncapiProperty];
|
|
25
|
+
|
|
26
|
+
// traverse the annotation property and define the objects if they're not defined
|
|
27
|
+
for (let nestedIndex = 1; nestedIndex < keys.length - 1; nestedIndex++) {
|
|
28
|
+
const nestedElement = keys[nestedIndex];
|
|
29
|
+
if (node[nestedElement] === undefined) {
|
|
30
|
+
node[nestedElement] = {};
|
|
31
|
+
}
|
|
32
|
+
node = node[nestedElement];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// set value annotation property
|
|
36
|
+
node[keys[keys.length - 1]] = value;
|
|
37
|
+
}
|
|
38
|
+
|
|
19
39
|
function readAnnotations(definition, presets) {
|
|
20
40
|
let resObj = {};
|
|
21
41
|
|
|
22
42
|
for (const [key, value] of Object.entries(definition)) {
|
|
23
|
-
if (key.startsWith('@AsyncAPI')) {
|
|
43
|
+
if (key.startsWith('@AsyncAPI') && !key.startsWith('@AsyncAPI.Extensions')) {
|
|
24
44
|
// get annotation value after @AsyncAPI
|
|
25
45
|
const annotationProperties = key.split('@AsyncAPI.')[1];
|
|
26
46
|
const keys = annotationProperties.split('.');
|
|
@@ -35,23 +55,22 @@ function readAnnotations(definition, presets) {
|
|
|
35
55
|
if (keys.length === 1) {
|
|
36
56
|
resObj[asyncapiProperty] = value;
|
|
37
57
|
} else {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
let node = resObj[asyncapiProperty];
|
|
58
|
+
nestedAnnotation(resObj, asyncapiProperty, keys, value)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
43
61
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
node[nestedElement] = {};
|
|
49
|
-
}
|
|
50
|
-
node = node[nestedElement];
|
|
51
|
-
}
|
|
62
|
+
else if (key.startsWith('@AsyncAPI.Extensions')) {
|
|
63
|
+
const annotationProperties = key.split('@AsyncAPI.Extensions.')[1];
|
|
64
|
+
const keys = annotationProperties.split('.');
|
|
65
|
+
keys[0] = "x-" + keys[0];
|
|
52
66
|
|
|
53
|
-
|
|
54
|
-
|
|
67
|
+
if (keys.length === 1) {
|
|
68
|
+
// if the key is already present, that means, Annotations have already been considered
|
|
69
|
+
if (resObj[keys[0]]) continue;
|
|
70
|
+
// else we populate
|
|
71
|
+
resObj[keys[0]] = value;
|
|
72
|
+
} else {
|
|
73
|
+
nestedAnnotation(resObj, keys[0], keys, value);
|
|
55
74
|
}
|
|
56
75
|
}
|
|
57
76
|
}
|
|
@@ -142,9 +161,13 @@ function entriesToMessages(definitions, events, presets) {
|
|
|
142
161
|
/**
|
|
143
162
|
* Combining the definition objects from CSN into one schema
|
|
144
163
|
*/
|
|
145
|
-
|
|
164
|
+
function definitionsToMessages(definitions, events, presets) {
|
|
146
165
|
const messages = {};
|
|
147
|
-
|
|
148
166
|
Object.assign(messages, entriesToMessages(definitions, events, presets));
|
|
149
167
|
return messages;
|
|
150
168
|
}
|
|
169
|
+
|
|
170
|
+
module.exports = {
|
|
171
|
+
definitionsToMessages,
|
|
172
|
+
nestedAnnotation
|
|
173
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const csnToJSONSchema = require('./components/schemas');
|
|
2
2
|
const getMessageTraits = require('./components/messageTraits');
|
|
3
|
-
const definitionsToMessages = require('./components/messages');
|
|
3
|
+
const { definitionsToMessages, nestedAnnotation } = require('./components/messages');
|
|
4
4
|
const getChannels = require('./channels');
|
|
5
5
|
const cds = require('../../cds');
|
|
6
6
|
const messages = require("./message");
|
|
@@ -75,13 +75,14 @@ function _getAsyncApi(csn, events, serviceName, applicationNamespace, presets) {
|
|
|
75
75
|
const infoObject = getInfoObject(csn.definitions[serviceName]);
|
|
76
76
|
const stateObj = getStateInfo(csn.definitions[serviceName]);
|
|
77
77
|
const shortTextObj = getShortText(csn.definitions[serviceName]);
|
|
78
|
-
|
|
78
|
+
const extensionAnnotationObj = getExtensions(csn.definitions[serviceName], shortTextObj, stateObj);
|
|
79
79
|
return {
|
|
80
80
|
asyncapi: '2.0.0',
|
|
81
81
|
'x-sap-catalog-spec-version': '1.2',
|
|
82
82
|
'x-sap-application-namespace': applicationNamespace,
|
|
83
83
|
...Object.keys(stateObj).length && { 'x-sap-stateInfo': stateObj },
|
|
84
84
|
...Object.keys(shortTextObj).length && { 'x-sap-shortText': shortTextObj },
|
|
85
|
+
...Object.keys(extensionAnnotationObj).length && extensionAnnotationObj,
|
|
85
86
|
info: infoObject,
|
|
86
87
|
defaultContentType: 'application/json',
|
|
87
88
|
channels: getChannels(csn.definitions, events),
|
|
@@ -89,6 +90,26 @@ function _getAsyncApi(csn, events, serviceName, applicationNamespace, presets) {
|
|
|
89
90
|
};
|
|
90
91
|
}
|
|
91
92
|
|
|
93
|
+
function getExtensions(serviceCsn, shortTextObj, stateObj) {
|
|
94
|
+
let extensionObj = {};
|
|
95
|
+
for (const [key, value] of Object.entries(serviceCsn)) {
|
|
96
|
+
if (key.startsWith('@AsyncAPI.Extensions')) {
|
|
97
|
+
const annotationProperties = key.split('@AsyncAPI.Extensions.')[1];
|
|
98
|
+
const keys = annotationProperties.split('.');
|
|
99
|
+
keys[0] = "x-" + keys[0];
|
|
100
|
+
if ((keys[0] === 'x-sap-shortText' && Object.keys(shortTextObj).length > 0) ||
|
|
101
|
+
(keys[0] === 'x-sap-stateInfo' && Object.keys(stateObj).length > 0)
|
|
102
|
+
) continue;
|
|
103
|
+
if (keys.length === 1) {
|
|
104
|
+
extensionObj[keys[0]] = value;
|
|
105
|
+
} else {
|
|
106
|
+
nestedAnnotation(extensionObj, keys[0], keys, value);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return extensionObj;
|
|
111
|
+
}
|
|
112
|
+
|
|
92
113
|
function getShortText(serviceCsn, presetValue = undefined) {
|
|
93
114
|
let shortTextValue = presetValue || serviceCsn["@AsyncAPI.ShortText"];
|
|
94
115
|
if (shortTextValue) {
|
|
@@ -371,6 +371,7 @@ class Expression {
|
|
|
371
371
|
annotate(prefix = '', target, annotationExpressionResult) {
|
|
372
372
|
let _target = target;
|
|
373
373
|
for (const key of Object.keys(annotationExpressionResult)) {
|
|
374
|
+
if (prefix !== '') prefix = prefix + ".";
|
|
374
375
|
_target[prefix + key] = annotationExpressionResult[key];
|
|
375
376
|
}
|
|
376
377
|
return target;
|
package/lib/init/constants.js
CHANGED
package/lib/init/index.js
CHANGED
|
@@ -2,7 +2,7 @@ const path = require('path');
|
|
|
2
2
|
|
|
3
3
|
const term = require('../util/term');
|
|
4
4
|
|
|
5
|
-
const { OPTIONS,
|
|
5
|
+
const { OPTIONS, COMMAND_INIT, PROJECT_FILES, REGEX_PROJECT_NAME } = require('./constants')
|
|
6
6
|
const { NODEJS, JAVA, HANA, MTA,PIPELINE, SAMPLES, CF_MANIFEST } = OPTIONS
|
|
7
7
|
|
|
8
8
|
const cds = require('../cds'), { exists, readdir } = cds.utils
|
|
@@ -90,67 +90,56 @@ module.exports = class CDSGenerator {
|
|
|
90
90
|
* @param {string} facets, comma separated list of templates, no blanks
|
|
91
91
|
* @param {any} options, additional options
|
|
92
92
|
*/
|
|
93
|
-
async addCmd(facets
|
|
94
|
-
options._cmd = COMMAND_ADD;
|
|
95
|
-
options.add = facets;
|
|
96
|
-
|
|
93
|
+
async addCmd(facets) {
|
|
97
94
|
if (!facets) throw 'You must specify a facet to add to the project.'
|
|
98
95
|
|
|
99
|
-
this._initialize(null
|
|
100
|
-
this._greetings(
|
|
96
|
+
this._initialize(null);
|
|
97
|
+
this._greetings();
|
|
101
98
|
|
|
102
99
|
if (!PROJECT_FILES.find(exists)) {
|
|
103
100
|
throw `The current folder doesn't seem to contain a project. None of the following files found: ${PROJECT_FILES.join(', ')}.`
|
|
104
101
|
}
|
|
105
102
|
|
|
106
|
-
await this._process(
|
|
107
|
-
await this.stepEnd(
|
|
103
|
+
await this._process();
|
|
104
|
+
await this.stepEnd();
|
|
108
105
|
}
|
|
109
106
|
|
|
110
107
|
/**
|
|
111
108
|
* @param {string} projectName, the project name
|
|
112
|
-
* @param {any} options, additional options
|
|
113
109
|
*/
|
|
114
|
-
async initCmd(projectName
|
|
115
|
-
await this.stepInit(projectName
|
|
110
|
+
async initCmd(projectName) {
|
|
111
|
+
await this.stepInit(projectName);
|
|
116
112
|
await this.stepEnd();
|
|
117
113
|
}
|
|
118
114
|
|
|
119
|
-
async stepInit(projectName
|
|
120
|
-
options._cmd = COMMAND_INIT;
|
|
115
|
+
async stepInit(projectName) {
|
|
121
116
|
|
|
122
|
-
this._initialize(projectName
|
|
123
|
-
this._greetings(
|
|
117
|
+
this._initialize(projectName);
|
|
118
|
+
this._greetings();
|
|
124
119
|
|
|
125
120
|
await this._process();
|
|
126
121
|
}
|
|
127
122
|
|
|
128
|
-
_initialize(projectName
|
|
129
|
-
|
|
130
|
-
if (options.verbose) {
|
|
131
|
-
LOG.warn(`Parameter 'verbose' is no longer supported. Use environment variable 'DEBUG=true' to obtain detailed output.`);
|
|
132
|
-
}
|
|
133
|
-
|
|
123
|
+
_initialize(projectName) {
|
|
124
|
+
const { options } = cds.cli
|
|
134
125
|
this.cwd = options.cwd || process.cwd();
|
|
135
126
|
cds.root = path.resolve(this.cwd, projectName || '.');
|
|
136
127
|
this.projectName = path.basename(cds.root);
|
|
137
128
|
|
|
138
|
-
this.options = options;
|
|
139
129
|
this._cleanupOptions();
|
|
140
130
|
}
|
|
141
131
|
|
|
142
132
|
_greetings() {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
LOG.info(`Creating new CAP project in ${term.bold(folderName)}\n`);
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
133
|
+
if (cds.cli.command === COMMAND_INIT) {
|
|
134
|
+
const relativeProjectPath = path.relative(this.cwd, cds.root)
|
|
135
|
+
const folderName = relativeProjectPath ? `.${path.sep}${relativeProjectPath}` : 'current folder'
|
|
136
|
+
LOG.info(`Creating new CAP project in ${term.bold(folderName)}\n`)
|
|
150
137
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
138
|
+
if (cds.cli.options.for && typeof cds.cli.options.for !== 'string') {
|
|
139
|
+
throw 'The --for argument must not be empty.'
|
|
140
|
+
}
|
|
141
|
+
if (cds.cli.options.force) {
|
|
142
|
+
LOG.info(`Using '--force' ... existing files will be overwritten`)
|
|
154
143
|
}
|
|
155
144
|
}
|
|
156
145
|
|
|
@@ -161,9 +150,9 @@ module.exports = class CDSGenerator {
|
|
|
161
150
|
await this._fillTemplateList();
|
|
162
151
|
|
|
163
152
|
for (const template of this.templateList) {
|
|
164
|
-
|
|
165
|
-
LOG.info(msg);
|
|
153
|
+
LOG.info(`Adding feature '${template.name}'...`);
|
|
166
154
|
await template.run();
|
|
155
|
+
await template.runDependentMerging();
|
|
167
156
|
await template.runRelatedMerging();
|
|
168
157
|
}
|
|
169
158
|
|
|
@@ -176,7 +165,6 @@ module.exports = class CDSGenerator {
|
|
|
176
165
|
}
|
|
177
166
|
|
|
178
167
|
async _createTemplate(filename) {
|
|
179
|
-
|
|
180
168
|
try {
|
|
181
169
|
const TemplateClass = require(`./template/${filename}`);
|
|
182
170
|
return new TemplateClass(this);
|
|
@@ -199,21 +187,22 @@ module.exports = class CDSGenerator {
|
|
|
199
187
|
}
|
|
200
188
|
|
|
201
189
|
async _fillTemplateList() {
|
|
202
|
-
const templates = new Map
|
|
203
|
-
|
|
190
|
+
const templates = new Map
|
|
191
|
+
const { command, options, argv } = cds.cli
|
|
192
|
+
const facets = command === COMMAND_INIT ? options.add : new Set(argv[0].split(','))
|
|
193
|
+
|
|
194
|
+
for (let facet of facets) {
|
|
204
195
|
// Compat
|
|
205
|
-
if (
|
|
206
|
-
if (
|
|
196
|
+
if (facet === 'sample-tiny') facet = 'tiny-sample'
|
|
197
|
+
if (facet === 'kibana-logging') facet = 'kibana'
|
|
207
198
|
|
|
208
|
-
if (!templates.has(
|
|
209
|
-
const template = await this._createTemplate(
|
|
199
|
+
if (!templates.has(facet)) {
|
|
200
|
+
const template = await this._createTemplate(facet)
|
|
210
201
|
if (await template.canRun()) {
|
|
211
|
-
DEBUG?.(`Adding feature '${template.name}'`)
|
|
212
|
-
templates.set(
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
deps.forEach(this.options.add.add, this.options.add) // REVISIT: Ugly add.add
|
|
216
|
-
}
|
|
202
|
+
DEBUG?.(`Adding feature '${template.name}'`)
|
|
203
|
+
templates.set(facet, template)
|
|
204
|
+
const dependencies = await template.getDependencies()
|
|
205
|
+
dependencies?.forEach(d => facets.add(d))
|
|
217
206
|
}
|
|
218
207
|
}
|
|
219
208
|
}
|
|
@@ -236,56 +225,59 @@ module.exports = class CDSGenerator {
|
|
|
236
225
|
_cleanupOptions() {
|
|
237
226
|
let tokens = [];
|
|
238
227
|
|
|
239
|
-
|
|
240
|
-
|
|
228
|
+
const { options } = cds.cli
|
|
229
|
+
if (typeof options.add === 'string') {
|
|
230
|
+
tokens = options.add.split(/[,\s+]/g);
|
|
241
231
|
|
|
242
|
-
} else if (Array.isArray(
|
|
243
|
-
tokens =
|
|
232
|
+
} else if (Array.isArray(options.add)) {
|
|
233
|
+
tokens = options.add
|
|
244
234
|
|
|
245
|
-
} else if (
|
|
246
|
-
tokens = [...
|
|
235
|
+
} else if (options.add instanceof Set) {
|
|
236
|
+
tokens = [...options.add];
|
|
247
237
|
}
|
|
248
238
|
|
|
249
239
|
const trimmedTokens = tokens.map((token) => {
|
|
250
240
|
token = token.replace(/\s+/g, '');
|
|
251
241
|
const tokens = token.split(':'), [facet] = tokens
|
|
252
242
|
if (tokens.length > 1) {
|
|
253
|
-
if (
|
|
254
|
-
|
|
243
|
+
if (options[facet]) {
|
|
244
|
+
options[facet].add(token);
|
|
255
245
|
} else {
|
|
256
|
-
|
|
246
|
+
options[facet] = new Set([token]);
|
|
257
247
|
}
|
|
258
248
|
}
|
|
259
249
|
return facet;
|
|
260
250
|
}).filter(Boolean);
|
|
261
251
|
|
|
262
252
|
// set is ordered ... always
|
|
263
|
-
|
|
253
|
+
options.add = new Set(trimmedTokens);
|
|
264
254
|
|
|
265
|
-
if (
|
|
266
|
-
|
|
255
|
+
if (cds.cli.command === COMMAND_INIT && !options.add.has(JAVA)) {
|
|
256
|
+
options.add.add(NODEJS);
|
|
267
257
|
}
|
|
268
258
|
|
|
269
259
|
this._checkAdditionalOptions();
|
|
270
260
|
}
|
|
271
261
|
|
|
272
262
|
_checkAdditionalOptions() {
|
|
273
|
-
|
|
274
|
-
|
|
263
|
+
const { options } = cds.cli
|
|
264
|
+
for (const [key] of Object.entries(options)) {
|
|
265
|
+
const keySplit = key.split(/:/)
|
|
275
266
|
if (keySplit.length === 2) {
|
|
276
|
-
if (!
|
|
277
|
-
|
|
267
|
+
if (!options.add.has(keySplit[0])) {
|
|
268
|
+
console.log(`ignoring option ${key} since ${keySplit[0]} is not part of specified feature list: ${Array.from(options.add).join(', ')}`)
|
|
278
269
|
}
|
|
279
270
|
}
|
|
280
271
|
}
|
|
281
272
|
}
|
|
282
273
|
|
|
283
274
|
async _validateOptions() {
|
|
284
|
-
|
|
275
|
+
const { options, command } = cds.cli
|
|
276
|
+
if (options.force) {
|
|
285
277
|
return;
|
|
286
278
|
}
|
|
287
279
|
|
|
288
|
-
if (
|
|
280
|
+
if (command === COMMAND_INIT) {
|
|
289
281
|
await this._validateProjectName(this.projectName);
|
|
290
282
|
await this._validateProjectFolder(this.cwd);
|
|
291
283
|
}
|