@sap/cds-dk 7.3.1 → 7.3.2
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 -0
- package/bin/build.js +2 -1
- package/bin/cds.js +1 -1
- package/bin/compile/index.js +11 -0
- package/bin/init.js +1 -1
- package/lib/build/provider/edmxBuildPlugin.js +7 -3
- package/lib/build/provider/featureToggleBuildPlugin.js +2 -3
- package/lib/build/provider/nodejs/index.js +3 -2
- package/lib/client/base_command.js +8 -18
- package/lib/client/download_legacy.js +0 -3
- package/lib/client/params.js +5 -4
- package/lib/client/settings_manager.js +22 -21
- package/lib/deploy/to-hana/hana.js +53 -35
- package/lib/index.js +1 -1
- package/lib/init/constants.js +2 -1
- package/lib/init/template/enterprise-messaging/files/event-mesh.json.hbs +1 -1
- package/lib/init/template/enterprise-messaging/index.js +1 -1
- package/lib/init/template/enterprise-messaging-shared/files/event-mesh.json.hbs +1 -1
- package/lib/init/template/enterprise-messaging-shared/index.js +19 -5
- package/lib/init/template/helm/index.js +1 -1
- package/lib/init/template/local-messaging/files/package.json.hbs +15 -0
- package/lib/init/template/local-messaging/index.js +25 -0
- package/lib/init/template/nodejs/files/.gitignore.hbs +3 -0
- package/lib/init/template/notifications/files/package.json.hbs +1 -10
- package/lib/init/template/notifications/index.js +7 -16
- package/lib/init/template/typer/files/package.json +1 -1
- package/lib/init/util/projectReader.js +1 -0
- package/npm-shrinkwrap.json +82 -42
- package/package.json +1 -1
- package/lib/init/template/notifications/files/notificationtypes.json.hbs +0 -16
package/bin/add.js
CHANGED
|
@@ -27,6 +27,8 @@ module.exports = Object.assign(add, {
|
|
|
27
27
|
|
|
28
28
|
*approuter* - add support for application routing
|
|
29
29
|
|
|
30
|
+
*local-messaging* - add support for local messaging
|
|
31
|
+
|
|
30
32
|
*file-based-messaging* - add support for file-based messaging
|
|
31
33
|
|
|
32
34
|
*enterprise-messaging* - add support for SAP Event Mesh
|
package/bin/build.js
CHANGED
|
@@ -88,8 +88,9 @@ module.exports = Object.assign(build, {
|
|
|
88
88
|
|
|
89
89
|
async function build([project], options = {}) {
|
|
90
90
|
const path = require('path')
|
|
91
|
+
|
|
91
92
|
const cds = require('../lib/cds')
|
|
92
|
-
|
|
93
|
+
|
|
93
94
|
// plugins are loaded based on cds.root path
|
|
94
95
|
cds.root = path.resolve(process.cwd(), project || options.project || '.')
|
|
95
96
|
|
package/bin/cds.js
CHANGED
|
@@ -30,7 +30,7 @@ const cli = { //NOSONAR
|
|
|
30
30
|
if (cmd in this.Shortcuts) cmd = process.argv[2] = this.Shortcuts[cmd]
|
|
31
31
|
if (argv.some(a => this.Shortcuts[a]==='help')) return this.help (cmd)
|
|
32
32
|
|
|
33
|
-
const cds = require('../lib
|
|
33
|
+
const cds = require('../lib') // make sure to load cds-dk API for plugins
|
|
34
34
|
DEBUG?.(`[cds] - @sap/cds ${cds.version} loaded: ${cds.home}`)
|
|
35
35
|
|
|
36
36
|
try {
|
package/bin/compile/index.js
CHANGED
|
@@ -53,6 +53,17 @@ module.exports = Object.assign ( compile, {
|
|
|
53
53
|
|
|
54
54
|
- odata
|
|
55
55
|
- sql
|
|
56
|
+
|
|
57
|
+
*--dialect* <dialect>
|
|
58
|
+
|
|
59
|
+
Needs option *--to*.
|
|
60
|
+
Specify the dialect in combination with *--to sql*.
|
|
61
|
+
Currently supported:
|
|
62
|
+
|
|
63
|
+
- sqlite
|
|
64
|
+
- h2
|
|
65
|
+
- postgres
|
|
66
|
+
- hana
|
|
56
67
|
|
|
57
68
|
*-s* | *--service* <name> | all
|
|
58
69
|
|
package/bin/init.js
CHANGED
|
@@ -40,7 +40,7 @@ module.exports = Object.assign(init, {
|
|
|
40
40
|
|
|
41
41
|
*approuter* - add support for application routing
|
|
42
42
|
|
|
43
|
-
*
|
|
43
|
+
*local-messaging* - add support for local messaging
|
|
44
44
|
|
|
45
45
|
*enterprise-messaging* - add support for SAP Event Mesh
|
|
46
46
|
|
|
@@ -51,9 +51,13 @@ class EdmxBuildPlugin extends FeatureToggleBuildPlugin {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
_isOdataProtocol(srv) {
|
|
54
|
-
let protocols = cds.service.
|
|
55
|
-
if (
|
|
56
|
-
|
|
54
|
+
let protocols = cds.service.protocols4?.(srv)
|
|
55
|
+
if (!protocols) {
|
|
56
|
+
// compatibility with @sap/cds < 7.3.0
|
|
57
|
+
protocols = cds.service.protocols.protocol4(srv)
|
|
58
|
+
if (typeof protocols === "string") {
|
|
59
|
+
protocols = [protocols]
|
|
60
|
+
}
|
|
57
61
|
}
|
|
58
62
|
return protocols.some(p => p && p.startsWith('odata'))
|
|
59
63
|
}
|
|
@@ -124,9 +124,8 @@ class FeatureToggleBuildPlugin extends InternalBuildPlugin {
|
|
|
124
124
|
|
|
125
125
|
_validateFeature(ftCsn, baseCsn) {
|
|
126
126
|
// features must not have other dependencies than base model dependencies - cross feature dependencies are not supported
|
|
127
|
-
//
|
|
128
|
-
|
|
129
|
-
cds.compiler.compileSources({ [ftCsn.requires[0]]: baseCsn, 'feature.csn': ftCsn }, this.options())
|
|
127
|
+
// assuming only core compiler options are relevant for validation scenario
|
|
128
|
+
cds.compiler.compileSources({ [ftCsn.requires[0]]: baseCsn, 'feature.csn': ftCsn }, { ...super.options(), ...cds.env.cdsc ?? {}})
|
|
130
129
|
}
|
|
131
130
|
}
|
|
132
131
|
|
|
@@ -44,9 +44,10 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
|
|
|
44
44
|
await this.collectAllLanguageBundles(dictionary, sources, destSrv, destRoot)
|
|
45
45
|
|
|
46
46
|
if (!this.hasBuildOption(CONTENT_EDMX, false)) {
|
|
47
|
-
//REVISIT: inferred model expected by cds.compile.to.edmx()
|
|
48
47
|
const compileOptions = { [FLAVOR_LOCALIZED_EDMX]: this.hasBuildOption(FLAVOR_LOCALIZED_EDMX, true) }
|
|
49
|
-
|
|
48
|
+
// inferred flavor is required by edmx compiler backend
|
|
49
|
+
// using cds.compile instead of cds.compiler.compileSources ensures that cds.env options are correctly read
|
|
50
|
+
const baseModel = dictionary.base.meta.flavor !== 'inferred' ? await cds.compile(sources.base, super.options(), 'inferred') : dictionary.base
|
|
50
51
|
await this.compileToEdmx(baseModel, path.join(this.destSrv, 'odata', cds.env.odata.version), compileOptions)
|
|
51
52
|
}
|
|
52
53
|
|
|
@@ -38,27 +38,17 @@ module.exports = class BaseCommand {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
static concatUrls(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return trimBeginSlash(trimEndSlash(str));
|
|
50
|
-
}
|
|
51
|
-
const concatenated = new URL(base);
|
|
52
|
-
concatenated.pathname = trimEndSlash(concatenated.pathname) + '/' +
|
|
53
|
-
paths.map((p, i) => i < paths.length - 1 ? trimSlashes(p) : trimBeginSlash(p)).join('/');
|
|
54
|
-
return concatenated.toString();
|
|
41
|
+
static concatUrls(...parts) {
|
|
42
|
+
return parts
|
|
43
|
+
.map((p, i) => i === 0
|
|
44
|
+
? p.replace(/\/$/ /*end slash*/, '')
|
|
45
|
+
: i === parts.length - 1
|
|
46
|
+
? p.replace(/^\// /*begin slash*/, '')
|
|
47
|
+
: p.replace(/^\/|\/$/g /*both*/, ''))
|
|
48
|
+
.join('/');
|
|
55
49
|
}
|
|
56
50
|
|
|
57
51
|
static getMtxApiUrl(url, action) {
|
|
58
52
|
return BaseCommand.concatUrls(url, MTX_URL_PATH_PREFIX, action);
|
|
59
53
|
}
|
|
60
|
-
|
|
61
|
-
static getMtxApiUrlSync(url, action) {
|
|
62
|
-
return BaseCommand.concatUrls(url, MTX_URL_PATH_PREFIX, action);
|
|
63
|
-
}
|
|
64
54
|
}
|
package/lib/client/params.js
CHANGED
|
@@ -170,7 +170,7 @@ class BasicParams {
|
|
|
170
170
|
this.clearOtherTokenStorage = new Param('clearOtherTokenStorage', false, { internal: true });
|
|
171
171
|
|
|
172
172
|
// - persisted
|
|
173
|
-
this.appUrl = new Param('appUrl', '', { persist: Persistence.setting });
|
|
173
|
+
this.appUrl = new Param('appUrl', '', { persist: Persistence.setting }); // any trailing slash (/) is removed
|
|
174
174
|
this.url = this.appUrl.newAlias('url');
|
|
175
175
|
this.from = this.appUrl.newAlias('from');
|
|
176
176
|
this.to = this.appUrl.newAlias('to');
|
|
@@ -276,6 +276,7 @@ class ParamCollection {
|
|
|
276
276
|
|
|
277
277
|
/**
|
|
278
278
|
* Get the name of a main param (as opposed to an alias).
|
|
279
|
+
* Throws an error if the param is not part of this collection.
|
|
279
280
|
* @param name the param in question
|
|
280
281
|
*/
|
|
281
282
|
static mainName(name) {
|
|
@@ -287,7 +288,7 @@ class ParamCollection {
|
|
|
287
288
|
}
|
|
288
289
|
|
|
289
290
|
/**
|
|
290
|
-
*
|
|
291
|
+
* Return whether the given param is included in this collection.
|
|
291
292
|
* @param name the param in question
|
|
292
293
|
*/
|
|
293
294
|
has(name) {
|
|
@@ -295,7 +296,7 @@ class ParamCollection {
|
|
|
295
296
|
}
|
|
296
297
|
|
|
297
298
|
/**
|
|
298
|
-
* Return the value of the given param.
|
|
299
|
+
* Return the value of the given param or `undefined` if not part of this collection.
|
|
299
300
|
* @param name the param in question
|
|
300
301
|
*/
|
|
301
302
|
get(name) {
|
|
@@ -304,7 +305,7 @@ class ParamCollection {
|
|
|
304
305
|
}
|
|
305
306
|
|
|
306
307
|
/**
|
|
307
|
-
* Return the `Param` object for the given param.
|
|
308
|
+
* Return the `Param` object for the given param or `undefined` if not part of this collection.
|
|
308
309
|
* @param name the param in question
|
|
309
310
|
*/
|
|
310
311
|
getParam(name) {
|
|
@@ -96,19 +96,17 @@ async function getTokenBaseUrl(params, renewUrl) {
|
|
|
96
96
|
if (!params.has('appUrl')) {
|
|
97
97
|
throw 'Failed to determine token URL: app URL not given';
|
|
98
98
|
}
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
url.pathname = cleanPathname(pathname);
|
|
104
|
-
return url;
|
|
99
|
+
const appUrlWith = pathname => {
|
|
100
|
+
const url = new URL(params.get('appUrl'));
|
|
101
|
+
url.pathname = url.pathname.replace(/\/*$/, pathname);
|
|
102
|
+
return url.toString();
|
|
105
103
|
};
|
|
106
104
|
|
|
107
105
|
let existingPath;
|
|
108
106
|
const oauthPaths = [OAUTH_PATH_STREAMLINED_MTX];
|
|
109
107
|
for (const path of oauthPaths) {
|
|
110
108
|
try {
|
|
111
|
-
await axios.head(
|
|
109
|
+
await axios.head(appUrlWith(path));
|
|
112
110
|
existingPath = path;
|
|
113
111
|
break;
|
|
114
112
|
} catch (error) {
|
|
@@ -119,7 +117,7 @@ async function getTokenBaseUrl(params, renewUrl) {
|
|
|
119
117
|
}
|
|
120
118
|
}
|
|
121
119
|
existingPath = existingPath ?? OAUTH_PATH_OLD_MTX; // REVISIT can this just be included in the array?
|
|
122
|
-
return
|
|
120
|
+
return appUrlWith(existingPath);
|
|
123
121
|
}
|
|
124
122
|
|
|
125
123
|
function getKeyringAccountName(params) {
|
|
@@ -194,7 +192,6 @@ class SettingsManager {
|
|
|
194
192
|
|
|
195
193
|
const loadedSettings = await this._loadFromFile(projectFolder);
|
|
196
194
|
DEBUG?.(`Loaded project settings for ${params.get('projectFolder')}: ${loadedSettings.format()}`);
|
|
197
|
-
const loadedAppUrl = loadedSettings.get('appUrl');
|
|
198
195
|
|
|
199
196
|
if (params.has('passcode') && loadedSettings.has('username')) {
|
|
200
197
|
console.log(`Unsetting username${(loadedSettings.has('isEmptyPassword') ? ' and empty-password hint' : '')}`);
|
|
@@ -207,14 +204,14 @@ class SettingsManager {
|
|
|
207
204
|
if (params.has('username')) {
|
|
208
205
|
params.set('skipToken', true);
|
|
209
206
|
}
|
|
210
|
-
await this.updateUrls(params, logout,
|
|
207
|
+
await this.updateUrls(params, logout, loadedSettings);
|
|
211
208
|
await this.setKeytar(params, logout);
|
|
212
209
|
await this.addAuth(params, logout);
|
|
213
210
|
DEBUG?.(`Effective project settings: ${params.format()}`);
|
|
214
211
|
}
|
|
215
212
|
|
|
216
|
-
static async updateUrls(params, logout,
|
|
217
|
-
async function
|
|
213
|
+
static async updateUrls(params, logout, loadedSettings) {
|
|
214
|
+
async function updateAppUrl() {
|
|
218
215
|
if (!logout && !params.has('appUrl')) {
|
|
219
216
|
// Get appUrl from CF
|
|
220
217
|
DEBUG?.('App URL not given');
|
|
@@ -240,19 +237,23 @@ class SettingsManager {
|
|
|
240
237
|
DEBUG?.(`Added schema to app URL: ${appUrl}`);
|
|
241
238
|
}
|
|
242
239
|
params.set('appUrl', appUrl);
|
|
240
|
+
if (params.get('appUrl') !== loadedSettings.get('appUrl')) {
|
|
241
|
+
DEBUG?.(`Updated app URL from '${loadedSettings.get('appUrl')}' (loaded) to '${appUrl}'`);
|
|
242
|
+
}
|
|
243
243
|
}
|
|
244
|
-
async function
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
params.
|
|
244
|
+
async function updateTokenUrl() {
|
|
245
|
+
const tokenUrl = params.get('tokenUrl');
|
|
246
|
+
const renewUrl = !logout && !params.get('skipToken')
|
|
247
|
+
&& (!tokenUrl || tokenUrl.includes('//-/cds') || params.get('appUrl') !== loadedSettings.get('appUrl'));
|
|
248
|
+
if (renewUrl) {
|
|
249
|
+
const tokenUrl = await getTokenBaseUrl(params, renewUrl);
|
|
250
|
+
params.set('tokenUrl', tokenUrl);
|
|
251
|
+
DEBUG?.(`Updated token URL from '${loadedSettings.get('tokenUrl')}' (loaded) to '${tokenUrl}'`);
|
|
248
252
|
}
|
|
249
253
|
}
|
|
250
254
|
|
|
251
|
-
await
|
|
252
|
-
|
|
253
|
-
if (!logout && !params.get('skipToken')) {
|
|
254
|
-
await renewTokenUrl();
|
|
255
|
-
}
|
|
255
|
+
await updateAppUrl();
|
|
256
|
+
await updateTokenUrl();
|
|
256
257
|
}
|
|
257
258
|
|
|
258
259
|
static async setKeytar(params, logout = false) {
|
|
@@ -45,13 +45,16 @@ class HanaDeployer {
|
|
|
45
45
|
// arg object
|
|
46
46
|
// async deploy(model, serviceName, tunnelAddress, buildTaskOptions, vcapFile, undeployWhitelist, hdiOptions, bindCallback) { // NOSONAR
|
|
47
47
|
async deploy(options = {}) { // NOSONAR
|
|
48
|
-
let { model, serviceName, tunnelAddress, vcapFile, undeployWhitelist, hdiOptions } = options;
|
|
48
|
+
let { model, serviceName, tunnelAddress, vcapFile, undeployWhitelist, hdiOptions, dry } = options;
|
|
49
49
|
|
|
50
50
|
if (model) {
|
|
51
51
|
throw 'Specifying a model for hana deployment is not supported.';
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
if (dry) {
|
|
55
|
+
console.log('-- running in dry mode ---');
|
|
56
|
+
}
|
|
57
|
+
|
|
55
58
|
if (vcapFile) {
|
|
56
59
|
console.log(`Using VCAP_SERVICES from file ${vcapFile} (beta feature).`);
|
|
57
60
|
}
|
|
@@ -66,51 +69,66 @@ class HanaDeployer {
|
|
|
66
69
|
globalVcapEnv.TARGET_CONTAINER = process.env.TARGET_CONTAINER;
|
|
67
70
|
}
|
|
68
71
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
if (dry) {
|
|
73
|
+
for (const buildResult of buildResults) {
|
|
74
|
+
const model = await cds.load(buildResult.task.src);
|
|
75
|
+
const data = cds.compile.to.hdbtable(model);
|
|
76
|
+
for (const [src, { file }] of data) {
|
|
77
|
+
console.log(`-- ${file}`);
|
|
78
|
+
console.log(src);
|
|
79
|
+
console.log();
|
|
80
|
+
}
|
|
77
81
|
}
|
|
82
|
+
} else {
|
|
83
|
+
for (const buildResult of buildResults) {
|
|
84
|
+
let vcapEnv = globalVcapEnv;
|
|
85
|
+
let serviceKeyName;
|
|
86
|
+
const currentModelFolder = buildResult.task.dest;
|
|
87
|
+
|
|
88
|
+
if (undeployWhitelist) {
|
|
89
|
+
console.log('Writing undeploy.json');
|
|
90
|
+
await fs.write(path.join(currentModelFolder, 'undeploy.json'), JSON.stringify(undeployWhitelist, null, 2));
|
|
91
|
+
}
|
|
78
92
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
93
|
+
const hasVCAPEnv = Object.keys(vcapEnv).length > 0;
|
|
94
|
+
if (!serviceName && hasVCAPEnv) {
|
|
95
|
+
await fs.mkdir(currentModelFolder, { recursive: true });
|
|
96
|
+
} else {
|
|
97
|
+
const { cfServiceInstanceName, cfServiceInstanceKeyName, serviceKey } =
|
|
98
|
+
await this._getOrCreateCFService(projectPath, currentModelFolder, serviceName, tunnelAddress);
|
|
99
|
+
serviceKeyName = cfServiceInstanceKeyName;
|
|
100
|
+
serviceName = serviceName || cfServiceInstanceName;
|
|
87
101
|
|
|
88
|
-
|
|
102
|
+
vcapEnv = this._getVCAPServicesEntry(cfServiceInstanceName, serviceKey);
|
|
89
103
|
|
|
90
|
-
|
|
91
|
-
|
|
104
|
+
if (options.storeCredentials) {
|
|
105
|
+
await this._addInstanceToDefaultEnvJson([currentModelFolder, projectPath], cfServiceInstanceName, serviceKey);
|
|
106
|
+
}
|
|
92
107
|
}
|
|
93
|
-
}
|
|
94
108
|
|
|
95
|
-
|
|
96
|
-
|
|
109
|
+
console.log(`${bold('Starting deployment to SAP HANA ...')}`);
|
|
110
|
+
|
|
111
|
+
hdiOptions = hdiOptions || {}
|
|
112
|
+
await hdiDeployUtil.deploy(currentModelFolder, vcapEnv, hdiOptions);
|
|
97
113
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
114
|
+
if (!hasVCAPEnv) {
|
|
115
|
+
const bindOptions = {
|
|
116
|
+
...options,
|
|
117
|
+
to: `${serviceName}:${serviceKeyName}`,
|
|
118
|
+
for: options.for,
|
|
119
|
+
kind: buildResult.task.for
|
|
120
|
+
}
|
|
121
|
+
await bind(bindOptions);
|
|
104
122
|
}
|
|
105
|
-
await bind(bindOptions);
|
|
106
123
|
}
|
|
107
|
-
}
|
|
108
124
|
|
|
109
|
-
|
|
110
|
-
|
|
125
|
+
if (options.storeCredentials) {
|
|
126
|
+
await gitUtil.ensureFileIsGitignored('default-env.json', projectPath);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log(`If not already done, use ${info('cds add hana')} to configure the project for SAP HANA.\n`);
|
|
111
130
|
}
|
|
112
131
|
|
|
113
|
-
console.log(`If not already done, use ${info('cds add hana')} to configure the project for SAP HANA.\n`);
|
|
114
132
|
console.log(`Done.`);
|
|
115
133
|
|
|
116
134
|
return { buildResults };
|
package/lib/index.js
CHANGED
|
@@ -21,7 +21,7 @@ if (cds.compile.to.constructor.name === 'Object') { // not a class as in cds 7
|
|
|
21
21
|
|
|
22
22
|
cds.schema = cds.schema || {} // backward compatibility
|
|
23
23
|
cds.extend (cds.schema) .with ( {
|
|
24
|
-
overlay4
|
|
24
|
+
get overlay4() { return super.overlay4 = require('./env/schema') }
|
|
25
25
|
})
|
|
26
26
|
|
|
27
27
|
// set build API, potentially removing the old API facade from sap/cds 7
|
package/lib/init/constants.js
CHANGED
|
@@ -27,9 +27,10 @@ module.exports = {
|
|
|
27
27
|
POSTGRES: 'postgres',
|
|
28
28
|
SQLITE: 'sqlite',
|
|
29
29
|
H2: 'h2',
|
|
30
|
+
LOCAL_MESSAGING: 'local-messaging',
|
|
31
|
+
FILE_BASED_MESSAGING: 'file-based-messaging',
|
|
30
32
|
ENTERPRISE_MESSAGING: 'enterprise-messaging',
|
|
31
33
|
ENTERPRISE_MESSAGING_SHARED: 'enterprise-messaging-shared',
|
|
32
|
-
FILE_BASED_MESSAGING: 'file-based-messaging',
|
|
33
34
|
REDIS_MESSAGING: 'redis-messaging',
|
|
34
35
|
APPROUTER: 'approuter',
|
|
35
36
|
DESTINATION: 'destination',
|
|
@@ -23,6 +23,7 @@ module.exports = class EnterpriseMessagingTemplate extends require('../templateB
|
|
|
23
23
|
const project = await readProject()
|
|
24
24
|
const { configFile } = project
|
|
25
25
|
await mergeJSON(configFile, join(__dirname, 'files', 'package.json.hbs'), project)
|
|
26
|
+
await mergeJSON('event-mesh.json', join(__dirname, 'files', 'event-mesh.json.hbs'), project)
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
async runDependentMerging(context = {}) {
|
|
@@ -30,7 +31,6 @@ module.exports = class EnterpriseMessagingTemplate extends require('../templateB
|
|
|
30
31
|
const { hasXsuaa, hasHelm, hasMta, isNodejs, srvPath, appName } = project
|
|
31
32
|
|
|
32
33
|
if (hasMta) {
|
|
33
|
-
await mergeJSON('event-mesh.json', join(__dirname, 'files', 'event-mesh.json.hbs'), project)
|
|
34
34
|
const srv = (isNodejs ? srvNode4 : srvJava4)(srvPath)
|
|
35
35
|
await mergeYAML(
|
|
36
36
|
'mta.yaml',
|
|
@@ -3,14 +3,14 @@ const cds = require('../../../cds')
|
|
|
3
3
|
const { read } = cds.utils
|
|
4
4
|
const { readProject } = require('../../util/projectReader')
|
|
5
5
|
const { mergeJSON, mergeYAML } = require('../../util/merge')
|
|
6
|
+
const { parseMd5File, writeMd5File } = require('../../util/md5')
|
|
6
7
|
const { srvNode4, srvJava4, enterpriseMessaging } = require('../_merging/registry-mta')
|
|
7
8
|
|
|
8
9
|
module.exports = class EnterpriseMessagingSharedTemplate extends require('../templateBase') {
|
|
9
10
|
|
|
10
11
|
async canRun() {
|
|
11
|
-
const {
|
|
12
|
-
if (isJava)
|
|
13
|
-
if (hasHelm && !hasMta) throw `'cds add enterprise-messaging-shared' is not available for Kyma yet`
|
|
12
|
+
const { isJava } = await readProject()
|
|
13
|
+
if (isJava) throw `'cds add enterprise-messaging-shared' is not available for Java yet`
|
|
14
14
|
return true
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -26,9 +26,9 @@ module.exports = class EnterpriseMessagingSharedTemplate extends require('../tem
|
|
|
26
26
|
await mergeJSON('event-mesh.json', join(__dirname, 'files', 'event-mesh.json.hbs'), project)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
async runDependentMerging() {
|
|
29
|
+
async runDependentMerging(context = {}) {
|
|
30
30
|
const project = await readProject()
|
|
31
|
-
const { hasMta, isNodejs, srvPath } = project
|
|
31
|
+
const { hasMta, hasHelm, isNodejs, srvPath } = project
|
|
32
32
|
|
|
33
33
|
if (hasMta) {
|
|
34
34
|
const srv = (isNodejs ? srvNode4 : srvJava4)(srvPath)
|
|
@@ -45,5 +45,19 @@ module.exports = class EnterpriseMessagingSharedTemplate extends require('../tem
|
|
|
45
45
|
}
|
|
46
46
|
)
|
|
47
47
|
}
|
|
48
|
+
|
|
49
|
+
if (hasHelm) {
|
|
50
|
+
let isIndependentCommand = false
|
|
51
|
+
if (Object.keys(context).length == 0){
|
|
52
|
+
isIndependentCommand = true
|
|
53
|
+
context.oldTrackingData = context.newTrackingData = await parseMd5File(join('chart', '.cds-add-helm-files.md5'))
|
|
54
|
+
}
|
|
55
|
+
await mergeYAML(join('chart', 'values.yaml'), join(__dirname, 'files', 'values.yaml'))
|
|
56
|
+
const HelmTemplate = require(`../helm`);
|
|
57
|
+
const helmTemplate = new HelmTemplate(context.oldTrackingData, context.newTrackingData);
|
|
58
|
+
isIndependentCommand = isIndependentCommand | await helmTemplate.addSubChart('service-instance');
|
|
59
|
+
await helmTemplate.addDependency('service-instance', 'event-mesh');
|
|
60
|
+
if (isIndependentCommand) await writeMd5File(join('chart', '.cds-add-helm-files.md5'), context.newTrackingData)
|
|
61
|
+
}
|
|
48
62
|
}
|
|
49
63
|
}
|
|
@@ -14,7 +14,7 @@ module.exports = class HelmTemplate extends require('../templateBase') {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
getRelatedFacets() {
|
|
17
|
-
return ['hana', 'xsuaa', 'enterprise-messaging', 'destination' , 'multitenancy', 'extensibility', 'approuter', 'connectivity']
|
|
17
|
+
return ['hana', 'xsuaa', 'enterprise-messaging', 'enterprise-messaging-shared', 'destination' , 'multitenancy', 'extensibility', 'approuter', 'connectivity']
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
static hasFacet() {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const { join } = require('path')
|
|
2
|
+
const cds = require('../../../cds')
|
|
3
|
+
const { read } = cds.utils
|
|
4
|
+
const { readProject } = require('../../util/projectReader')
|
|
5
|
+
const { mergeJSON } = require('../../util/merge')
|
|
6
|
+
|
|
7
|
+
module.exports = class LocalMessagingTemplate extends require('../templateBase') {
|
|
8
|
+
|
|
9
|
+
async canRun() {
|
|
10
|
+
const { isJava } = await readProject()
|
|
11
|
+
if (isJava) throw `'cds add local-messaging' is not available for Java yet`
|
|
12
|
+
return true
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static hasFacet(env) {
|
|
16
|
+
return env.requires?.messaging?.kind === 'local-messaging'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async run() {
|
|
20
|
+
const project = await readProject()
|
|
21
|
+
const { configFile } = project
|
|
22
|
+
project.shortcut = !(await read(configFile)).cds?.requires?.messaging?.kind
|
|
23
|
+
await mergeJSON(configFile, join(__dirname, 'files', 'package.json.hbs'), project)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
const { join } = require('path')
|
|
2
|
-
const cds = require('../../../cds')
|
|
3
|
-
const { exists } = cds.utils
|
|
4
2
|
const { readProject } = require('../../util/projectReader')
|
|
5
3
|
const { mergeJSON, mergeYAML } = require('../../util/merge')
|
|
6
4
|
const { DESTINATION } = require('../../constants').OPTIONS
|
|
@@ -17,7 +15,7 @@ module.exports = class NotificationsTemplate extends require('../templateBase')
|
|
|
17
15
|
const project = await readProject()
|
|
18
16
|
const { isJava } = project
|
|
19
17
|
if (isJava) {
|
|
20
|
-
throw `'cds add notifications' is not available for
|
|
18
|
+
throw `'cds add notifications' is not available for Java yet`
|
|
21
19
|
}
|
|
22
20
|
return true
|
|
23
21
|
}
|
|
@@ -27,26 +25,19 @@ module.exports = class NotificationsTemplate extends require('../templateBase')
|
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
async run() {
|
|
30
|
-
const project = await readProject()
|
|
31
|
-
const { configFile } = project
|
|
32
|
-
|
|
33
|
-
// create notificationtypes if it doesn't exist
|
|
34
|
-
if (!exists('notificationtypes.json')) {
|
|
35
|
-
await mergeJSON('notificationtypes.json', join(__dirname, 'files', 'notificationtypes.json.hbs'), project);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
//update package.json
|
|
39
|
-
await mergeJSON(configFile, join(__dirname, 'files', 'package.json.hbs'), project);
|
|
28
|
+
const project = await readProject()
|
|
29
|
+
const { configFile } = project
|
|
30
|
+
await mergeJSON(configFile, join(__dirname, 'files', 'package.json.hbs'), project)
|
|
40
31
|
}
|
|
41
32
|
|
|
42
33
|
async runDependentMerging() {
|
|
43
|
-
const project = await readProject()
|
|
44
|
-
const { hasMta } = project
|
|
34
|
+
const project = await readProject()
|
|
35
|
+
const { hasMta } = project
|
|
45
36
|
|
|
46
37
|
if (hasMta) {
|
|
47
38
|
await mergeYAML('mta.yaml', join(__dirname, 'files', 'mta.yaml.hbs'), project, {
|
|
48
39
|
additions: [notificationTypesDeployer]
|
|
49
|
-
})
|
|
40
|
+
})
|
|
50
41
|
}
|
|
51
42
|
}
|
|
52
43
|
}
|
|
@@ -83,6 +83,7 @@ module.exports = new class ProjectReader {
|
|
|
83
83
|
get appPath() { return env.folders.app ?? 'app' },
|
|
84
84
|
get appVersion() { return appVersion },
|
|
85
85
|
get appName() { return appName },
|
|
86
|
+
get cleanedAppName() { return appName.replace('_', '-') }, // Required when _ isn't allowed in the namespace
|
|
86
87
|
get appId() { return appId },
|
|
87
88
|
get appDescription() { return appDescription },
|
|
88
89
|
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/cds-dk",
|
|
3
|
-
"version": "7.3.
|
|
3
|
+
"version": "7.3.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@sap/cds-dk",
|
|
9
|
-
"version": "7.3.
|
|
9
|
+
"version": "7.3.2",
|
|
10
10
|
"license": "SEE LICENSE IN LICENSE",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@sap/cds": "^7",
|
|
@@ -28,20 +28,6 @@
|
|
|
28
28
|
"cds": "bin/cds.js",
|
|
29
29
|
"cds-ts": "bin/cds-ts.js"
|
|
30
30
|
},
|
|
31
|
-
"devDependencies": {
|
|
32
|
-
"@cap-js/sqlite": "^1",
|
|
33
|
-
"@types/jest": "^29",
|
|
34
|
-
"chai": "^4.3.7",
|
|
35
|
-
"chai-as-promised": "^7.1.1",
|
|
36
|
-
"chai-subset": "^1.6.0",
|
|
37
|
-
"eslint-plugin-cds-dk": "file:.eslint",
|
|
38
|
-
"jest": "^29",
|
|
39
|
-
"nock": "^13.3.1",
|
|
40
|
-
"semver": "^7",
|
|
41
|
-
"tar": "^6.2.0",
|
|
42
|
-
"ts-node": "^10.9.1",
|
|
43
|
-
"typescript": "^5.0.4"
|
|
44
|
-
},
|
|
45
31
|
"optionalDependencies": {
|
|
46
32
|
"sqlite3": "^5.0.4"
|
|
47
33
|
}
|
|
@@ -67,7 +53,7 @@
|
|
|
67
53
|
}
|
|
68
54
|
},
|
|
69
55
|
"node_modules/@eslint-community/regexpp": {
|
|
70
|
-
"version": "4.
|
|
56
|
+
"version": "4.10.0",
|
|
71
57
|
"license": "MIT",
|
|
72
58
|
"engines": {
|
|
73
59
|
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
|
|
@@ -95,7 +81,7 @@
|
|
|
95
81
|
}
|
|
96
82
|
},
|
|
97
83
|
"node_modules/@eslint/js": {
|
|
98
|
-
"version": "8.
|
|
84
|
+
"version": "8.52.0",
|
|
99
85
|
"license": "MIT",
|
|
100
86
|
"engines": {
|
|
101
87
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
|
@@ -107,10 +93,10 @@
|
|
|
107
93
|
"optional": true
|
|
108
94
|
},
|
|
109
95
|
"node_modules/@humanwhocodes/config-array": {
|
|
110
|
-
"version": "0.11.
|
|
96
|
+
"version": "0.11.13",
|
|
111
97
|
"license": "Apache-2.0",
|
|
112
98
|
"dependencies": {
|
|
113
|
-
"@humanwhocodes/object-schema": "^
|
|
99
|
+
"@humanwhocodes/object-schema": "^2.0.1",
|
|
114
100
|
"debug": "^4.1.1",
|
|
115
101
|
"minimatch": "^3.0.5"
|
|
116
102
|
},
|
|
@@ -130,7 +116,7 @@
|
|
|
130
116
|
}
|
|
131
117
|
},
|
|
132
118
|
"node_modules/@humanwhocodes/object-schema": {
|
|
133
|
-
"version": "
|
|
119
|
+
"version": "2.0.1",
|
|
134
120
|
"license": "BSD-3-Clause"
|
|
135
121
|
},
|
|
136
122
|
"node_modules/@mapbox/node-pre-gyp": {
|
|
@@ -225,7 +211,7 @@
|
|
|
225
211
|
}
|
|
226
212
|
},
|
|
227
213
|
"node_modules/@sap/cds": {
|
|
228
|
-
"version": "7.3.
|
|
214
|
+
"version": "7.3.1",
|
|
229
215
|
"license": "SEE LICENSE IN LICENSE",
|
|
230
216
|
"dependencies": {
|
|
231
217
|
"@sap/cds-compiler": "^4",
|
|
@@ -241,7 +227,7 @@
|
|
|
241
227
|
}
|
|
242
228
|
},
|
|
243
229
|
"node_modules/@sap/cds-compiler": {
|
|
244
|
-
"version": "4.3.
|
|
230
|
+
"version": "4.3.2",
|
|
245
231
|
"license": "SEE LICENSE IN LICENSE",
|
|
246
232
|
"dependencies": {
|
|
247
233
|
"antlr4": "4.9.3"
|
|
@@ -608,6 +594,10 @@
|
|
|
608
594
|
"node": ">= 6"
|
|
609
595
|
}
|
|
610
596
|
},
|
|
597
|
+
"node_modules/@ungap/structured-clone": {
|
|
598
|
+
"version": "1.2.0",
|
|
599
|
+
"license": "ISC"
|
|
600
|
+
},
|
|
611
601
|
"node_modules/abbrev": {
|
|
612
602
|
"version": "1.1.1",
|
|
613
603
|
"license": "ISC",
|
|
@@ -873,11 +863,12 @@
|
|
|
873
863
|
"optional": true
|
|
874
864
|
},
|
|
875
865
|
"node_modules/call-bind": {
|
|
876
|
-
"version": "1.0.
|
|
866
|
+
"version": "1.0.5",
|
|
877
867
|
"license": "MIT",
|
|
878
868
|
"dependencies": {
|
|
879
|
-
"function-bind": "^1.1.
|
|
880
|
-
"get-intrinsic": "^1.
|
|
869
|
+
"function-bind": "^1.1.2",
|
|
870
|
+
"get-intrinsic": "^1.2.1",
|
|
871
|
+
"set-function-length": "^1.1.1"
|
|
881
872
|
},
|
|
882
873
|
"funding": {
|
|
883
874
|
"url": "https://github.com/sponsors/ljharb"
|
|
@@ -1020,6 +1011,18 @@
|
|
|
1020
1011
|
"version": "0.1.4",
|
|
1021
1012
|
"license": "MIT"
|
|
1022
1013
|
},
|
|
1014
|
+
"node_modules/define-data-property": {
|
|
1015
|
+
"version": "1.1.1",
|
|
1016
|
+
"license": "MIT",
|
|
1017
|
+
"dependencies": {
|
|
1018
|
+
"get-intrinsic": "^1.2.1",
|
|
1019
|
+
"gopd": "^1.0.1",
|
|
1020
|
+
"has-property-descriptors": "^1.0.0"
|
|
1021
|
+
},
|
|
1022
|
+
"engines": {
|
|
1023
|
+
"node": ">= 0.4"
|
|
1024
|
+
}
|
|
1025
|
+
},
|
|
1023
1026
|
"node_modules/delayed-stream": {
|
|
1024
1027
|
"version": "1.0.0",
|
|
1025
1028
|
"license": "MIT",
|
|
@@ -1128,16 +1131,17 @@
|
|
|
1128
1131
|
}
|
|
1129
1132
|
},
|
|
1130
1133
|
"node_modules/eslint": {
|
|
1131
|
-
"version": "8.
|
|
1134
|
+
"version": "8.52.0",
|
|
1132
1135
|
"license": "MIT",
|
|
1133
1136
|
"dependencies": {
|
|
1134
1137
|
"@eslint-community/eslint-utils": "^4.2.0",
|
|
1135
1138
|
"@eslint-community/regexpp": "^4.6.1",
|
|
1136
1139
|
"@eslint/eslintrc": "^2.1.2",
|
|
1137
|
-
"@eslint/js": "8.
|
|
1138
|
-
"@humanwhocodes/config-array": "^0.11.
|
|
1140
|
+
"@eslint/js": "8.52.0",
|
|
1141
|
+
"@humanwhocodes/config-array": "^0.11.13",
|
|
1139
1142
|
"@humanwhocodes/module-importer": "^1.0.1",
|
|
1140
1143
|
"@nodelib/fs.walk": "^1.2.8",
|
|
1144
|
+
"@ungap/structured-clone": "^1.2.0",
|
|
1141
1145
|
"ajv": "^6.12.4",
|
|
1142
1146
|
"chalk": "^4.0.0",
|
|
1143
1147
|
"cross-spawn": "^7.0.2",
|
|
@@ -1500,13 +1504,13 @@
|
|
|
1500
1504
|
}
|
|
1501
1505
|
},
|
|
1502
1506
|
"node_modules/get-intrinsic": {
|
|
1503
|
-
"version": "1.2.
|
|
1507
|
+
"version": "1.2.2",
|
|
1504
1508
|
"license": "MIT",
|
|
1505
1509
|
"dependencies": {
|
|
1506
|
-
"function-bind": "^1.1.
|
|
1507
|
-
"has": "^1.0.3",
|
|
1510
|
+
"function-bind": "^1.1.2",
|
|
1508
1511
|
"has-proto": "^1.0.1",
|
|
1509
|
-
"has-symbols": "^1.0.3"
|
|
1512
|
+
"has-symbols": "^1.0.3",
|
|
1513
|
+
"hasown": "^2.0.0"
|
|
1510
1514
|
},
|
|
1511
1515
|
"funding": {
|
|
1512
1516
|
"url": "https://github.com/sponsors/ljharb"
|
|
@@ -1553,6 +1557,16 @@
|
|
|
1553
1557
|
"url": "https://github.com/sponsors/sindresorhus"
|
|
1554
1558
|
}
|
|
1555
1559
|
},
|
|
1560
|
+
"node_modules/gopd": {
|
|
1561
|
+
"version": "1.0.1",
|
|
1562
|
+
"license": "MIT",
|
|
1563
|
+
"dependencies": {
|
|
1564
|
+
"get-intrinsic": "^1.1.3"
|
|
1565
|
+
},
|
|
1566
|
+
"funding": {
|
|
1567
|
+
"url": "https://github.com/sponsors/ljharb"
|
|
1568
|
+
}
|
|
1569
|
+
},
|
|
1556
1570
|
"node_modules/graceful-fs": {
|
|
1557
1571
|
"version": "4.2.11",
|
|
1558
1572
|
"license": "ISC",
|
|
@@ -1562,13 +1576,6 @@
|
|
|
1562
1576
|
"version": "1.4.0",
|
|
1563
1577
|
"license": "MIT"
|
|
1564
1578
|
},
|
|
1565
|
-
"node_modules/has": {
|
|
1566
|
-
"version": "1.0.4",
|
|
1567
|
-
"license": "MIT",
|
|
1568
|
-
"engines": {
|
|
1569
|
-
"node": ">= 0.4.0"
|
|
1570
|
-
}
|
|
1571
|
-
},
|
|
1572
1579
|
"node_modules/has-flag": {
|
|
1573
1580
|
"version": "4.0.0",
|
|
1574
1581
|
"license": "MIT",
|
|
@@ -1576,6 +1583,16 @@
|
|
|
1576
1583
|
"node": ">=8"
|
|
1577
1584
|
}
|
|
1578
1585
|
},
|
|
1586
|
+
"node_modules/has-property-descriptors": {
|
|
1587
|
+
"version": "1.0.1",
|
|
1588
|
+
"license": "MIT",
|
|
1589
|
+
"dependencies": {
|
|
1590
|
+
"get-intrinsic": "^1.2.2"
|
|
1591
|
+
},
|
|
1592
|
+
"funding": {
|
|
1593
|
+
"url": "https://github.com/sponsors/ljharb"
|
|
1594
|
+
}
|
|
1595
|
+
},
|
|
1579
1596
|
"node_modules/has-proto": {
|
|
1580
1597
|
"version": "1.0.1",
|
|
1581
1598
|
"license": "MIT",
|
|
@@ -1601,6 +1618,16 @@
|
|
|
1601
1618
|
"license": "ISC",
|
|
1602
1619
|
"optional": true
|
|
1603
1620
|
},
|
|
1621
|
+
"node_modules/hasown": {
|
|
1622
|
+
"version": "2.0.0",
|
|
1623
|
+
"license": "MIT",
|
|
1624
|
+
"dependencies": {
|
|
1625
|
+
"function-bind": "^1.1.2"
|
|
1626
|
+
},
|
|
1627
|
+
"engines": {
|
|
1628
|
+
"node": ">= 0.4"
|
|
1629
|
+
}
|
|
1630
|
+
},
|
|
1604
1631
|
"node_modules/http-cache-semantics": {
|
|
1605
1632
|
"version": "4.1.1",
|
|
1606
1633
|
"license": "BSD-2-Clause",
|
|
@@ -1818,7 +1845,7 @@
|
|
|
1818
1845
|
}
|
|
1819
1846
|
},
|
|
1820
1847
|
"node_modules/livereload-js": {
|
|
1821
|
-
"version": "4.0.
|
|
1848
|
+
"version": "4.0.2",
|
|
1822
1849
|
"license": "MIT"
|
|
1823
1850
|
},
|
|
1824
1851
|
"node_modules/locate-path": {
|
|
@@ -2205,7 +2232,7 @@
|
|
|
2205
2232
|
}
|
|
2206
2233
|
},
|
|
2207
2234
|
"node_modules/object-inspect": {
|
|
2208
|
-
"version": "1.13.
|
|
2235
|
+
"version": "1.13.1",
|
|
2209
2236
|
"license": "MIT",
|
|
2210
2237
|
"funding": {
|
|
2211
2238
|
"url": "https://github.com/sponsors/ljharb"
|
|
@@ -2618,6 +2645,19 @@
|
|
|
2618
2645
|
"license": "ISC",
|
|
2619
2646
|
"optional": true
|
|
2620
2647
|
},
|
|
2648
|
+
"node_modules/set-function-length": {
|
|
2649
|
+
"version": "1.1.1",
|
|
2650
|
+
"license": "MIT",
|
|
2651
|
+
"dependencies": {
|
|
2652
|
+
"define-data-property": "^1.1.1",
|
|
2653
|
+
"get-intrinsic": "^1.2.1",
|
|
2654
|
+
"gopd": "^1.0.1",
|
|
2655
|
+
"has-property-descriptors": "^1.0.0"
|
|
2656
|
+
},
|
|
2657
|
+
"engines": {
|
|
2658
|
+
"node": ">= 0.4"
|
|
2659
|
+
}
|
|
2660
|
+
},
|
|
2621
2661
|
"node_modules/setprototypeof": {
|
|
2622
2662
|
"version": "1.2.0",
|
|
2623
2663
|
"license": "ISC"
|
package/package.json
CHANGED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"NotificationTypeKey": "{{ appName }}-notification",
|
|
4
|
-
"NotificationTypeVersion": "1",
|
|
5
|
-
"Templates": [
|
|
6
|
-
{
|
|
7
|
-
"Language": "en",
|
|
8
|
-
"TemplatePublic": "Hello world!",
|
|
9
|
-
"TemplateSensitive": "Hello SAP!",
|
|
10
|
-
"TemplateGrouped": "Hello universe!",
|
|
11
|
-
"TemplateLanguage": "mustache",
|
|
12
|
-
"Subtitle": "Hello {{ appName }}!"
|
|
13
|
-
}
|
|
14
|
-
]
|
|
15
|
-
}
|
|
16
|
-
]
|