@sap/cds-dk 9.3.2 → 9.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/bin/bind.js +1 -1
- package/bin/cds.js +3 -0
- package/bin/compile/index.js +53 -3
- package/bin/data-products/get-csn-via-dp-cli.js +41 -0
- package/bin/data-products/get-csn.js +76 -0
- package/bin/data-products/import.js +114 -0
- package/bin/data-products/index.js +30 -0
- package/bin/data-products/merge-csns.js +46 -0
- package/bin/data-products/prepare-csn.js +282 -0
- package/bin/data-products/prepare-files.js +69 -0
- package/bin/export.js +240 -0
- package/bin/help/export.md +37 -0
- package/bin/help/import.md +11 -0
- package/bin/help/minify.md +23 -0
- package/bin/import.js +13 -3
- package/bin/inspect.js +226 -0
- package/bin/minify.js +123 -0
- package/bin/util/cli.js +132 -0
- package/bin/util/log.js +2 -0
- package/bin/version.js +2 -1
- package/bin/watch.js +21 -3
- package/lib/bind/shared.js +3 -1
- package/lib/build/buildTaskEngine.js +2 -1
- package/lib/build/plugins/helm/files/templates/NOTES.txt +1 -1
- package/lib/build/plugins/helm/index.js +1 -1
- package/lib/build/provider/internalBuildTaskProvider.js +3 -3
- package/lib/build/provider/nodejs/index.js +62 -3
- package/lib/deploy/index.js +1 -1
- package/lib/deploy/to-hana/hana.js +18 -16
- package/lib/import/odata/v2parser.js +2 -2
- package/lib/import/odata/v4parser.js +21 -2
- package/lib/import/odata.package.json +4 -4
- package/lib/init/constants.js +2 -1
- package/lib/init/index.js +2 -0
- package/lib/init/mvn.js +3 -2
- package/lib/init/projectReader.js +1 -0
- package/lib/init/registries/mta.js +6 -0
- package/lib/init/template/ams/files/manifest.yml.hbs +1 -1
- package/lib/init/template/ams/files/mta.yaml.hbs +1 -1
- package/lib/init/template/app-frontend/files/app-package.json.hbs +20 -0
- package/lib/init/template/app-frontend/files/index.html.hbs +34 -0
- package/lib/init/template/app-frontend/files/manifest.json +13 -0
- package/lib/init/template/app-frontend/files/mta.yaml.hbs +71 -0
- package/lib/init/template/app-frontend/files/package.json.hbs +17 -0
- package/lib/init/template/app-frontend/files/ui5-deploy.yaml.hbs +25 -0
- package/lib/init/template/app-frontend/files/ui5.yaml.hbs +6 -0
- package/lib/init/template/app-frontend/files/values.yaml.hbs +20 -0
- package/lib/init/template/app-frontend/files/xs-app.json.hbs +17 -0
- package/lib/init/template/app-frontend/index.js +98 -0
- package/lib/init/template/approuter/files/mta.yaml.hbs +0 -2
- package/lib/init/template/attachments/index.js +4 -10
- package/lib/init/template/audit-logging/files/package.json.hbs +5 -0
- package/lib/init/template/handler/index.js +1 -0
- package/lib/init/template/helm/index.js +5 -1
- package/lib/init/template/html5-repo/files/mta.yaml.hbs +7 -2
- package/lib/init/template/ias/files/manifest.yml.hbs +1 -1
- package/lib/init/template/ias/files/mta.yaml.hbs +6 -5
- package/lib/init/template/ias/files/values.yaml.hbs +10 -1
- package/lib/init/template/multitenancy/files/mta.yaml.hbs +16 -3
- package/lib/init/template/multitenancy/files/package.json.hbs +5 -0
- package/lib/init/template/multitenancy/files/values.yaml.hbs +1 -6
- package/lib/init/template/portal/files/mta.yaml.hbs +12 -2
- package/lib/init/template/sample/files/db/schema.cds +5 -8
- package/lib/init/template/typer/files/xsconfig.json +2 -1
- package/lib/init/template/typescript/files/package.json +1 -1
- package/lib/init/template/typescript/files/tsconfig.json +4 -1
- package/lib/init/template/workzone-standard/files/mta.yaml.hbs +4 -0
- package/lib/init/template/workzone-standard/index.js +3 -2
- package/lib/init/template/xsuaa/files/package.json.hbs +3 -0
- package/lib/lint/eslintHelp.json +1 -1
- package/lib/util/term.js +2 -4
- package/npm-shrinkwrap.json +25 -25
- package/package.json +1 -1
|
@@ -6,10 +6,13 @@ const EdmxBuildPlugin = require('../edmxBuildPlugin')
|
|
|
6
6
|
const { BuildError, getWorkspaces, findWorkspaceRoot } = require('../../util')
|
|
7
7
|
const { OUTPUT_MODE, OUTPUT_MODE_FILESYSTEM, ODATA_VERSION_V2, FOLDER_GEN, CONTENT_EDMX, CONTENT_PACKAGELOCK_JSON,
|
|
8
8
|
CONTENT_NPMRC, CONTENT_CDSRC_JSON, CONTENT_ENV, CONTENT_DEFAULT_ENV_JSON, FLAVOR_LOCALIZED_EDMX,
|
|
9
|
-
OPTION_WS_PACK} = require('../../constants')
|
|
9
|
+
OPTION_WS_PACK} = require('../../constants');
|
|
10
10
|
const { WARNING } = EdmxBuildPlugin
|
|
11
|
-
const LOG = cds.log('cli|build')
|
|
11
|
+
const LOG = cds.log('cli|build')
|
|
12
|
+
const DEBUG = cds.debug('cli|build')
|
|
12
13
|
const NODE_ENGINE_VERSION = '>=20'
|
|
14
|
+
const { exec } = require('node:child_process')
|
|
15
|
+
const pexec = require('node:util').promisify(exec);
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* @param {T | T[]} x
|
|
@@ -84,9 +87,65 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
|
|
|
84
87
|
await this._packWorkspaceDependencies(destRoot)
|
|
85
88
|
}
|
|
86
89
|
}
|
|
90
|
+
|
|
91
|
+
await this.#cleanupPackageJson(destRoot)
|
|
92
|
+
|
|
87
93
|
return this._result
|
|
88
94
|
}
|
|
89
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Removes devDeps that are invalid semver, pointing to workspace or file locations,
|
|
98
|
+
* and also removes workspace entries. If needed, creates a new package-lock.json in build destination.
|
|
99
|
+
*/
|
|
100
|
+
async #cleanupPackageJson() {
|
|
101
|
+
/** @param {string | undefined} version */
|
|
102
|
+
const valid = version => version && !version.match('^(?:workspace|file):')
|
|
103
|
+
|
|
104
|
+
const packageJsonPath = path.join(this.task.dest, 'package.json')
|
|
105
|
+
try {
|
|
106
|
+
const packageJson = await cds.utils.read(packageJsonPath)
|
|
107
|
+
|
|
108
|
+
DEBUG?.(`Sanitizing the package.json for "${this.task.src}'...`)
|
|
109
|
+
|
|
110
|
+
let modified = false
|
|
111
|
+
// remove the workspace configuration
|
|
112
|
+
if (packageJson.workspaces) {
|
|
113
|
+
delete packageJson.workspaces
|
|
114
|
+
modified = true
|
|
115
|
+
}
|
|
116
|
+
// remove the workspace dev dependencies and the cds-plugin-ui5
|
|
117
|
+
if (packageJson.devDependencies) {
|
|
118
|
+
packageJson.devDependencies = Object.entries(packageJson.devDependencies)
|
|
119
|
+
.reduce((acc, [dep, version]) => {
|
|
120
|
+
if (valid(version)) {
|
|
121
|
+
acc[dep] = version
|
|
122
|
+
}
|
|
123
|
+
return acc
|
|
124
|
+
}, {})
|
|
125
|
+
modified = true
|
|
126
|
+
}
|
|
127
|
+
// overwrite the package.json if it was modified only
|
|
128
|
+
if (modified) {
|
|
129
|
+
await cds.utils.write(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf-8')
|
|
130
|
+
// update the package-lock.json if it exists
|
|
131
|
+
if (cds.utils.exists(path.join(this.task.dest, 'package-lock.json'))) {
|
|
132
|
+
DEBUG?.(`Updating the package-lock.json for '${this.task.src}'...`)
|
|
133
|
+
// run the npm install --package-lock-only to only update the package-lock.json
|
|
134
|
+
// without installing the dependencies to node_modules
|
|
135
|
+
try {
|
|
136
|
+
await pexec('npm install --package-lock-only', { cwd: this.task.dest })
|
|
137
|
+
} catch (e) {
|
|
138
|
+
DEBUG?.(`Failed to update the package-lock.json for '${this.task.src}'! Error: ${e.code} - ${e.message}`)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
} catch (e) {
|
|
143
|
+
// ENOENT: package.json not found -> this is okay for monorepos
|
|
144
|
+
if (e.code !== 'ENOENT') throw e
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
}
|
|
148
|
+
|
|
90
149
|
async clean() {
|
|
91
150
|
// staging build content is deleted by BuildTaskEngine
|
|
92
151
|
if (cds.env.build.target === '.') {
|
|
@@ -186,7 +245,7 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
|
|
|
186
245
|
async _packWorkspaceDependencies(dest) {
|
|
187
246
|
const wsRoot = findWorkspaceRoot(cds.root)
|
|
188
247
|
const appPkg = require(path.join(dest, 'package.json'))
|
|
189
|
-
const allWorkspaces = await getWorkspaces(wsRoot, true, false)
|
|
248
|
+
const allWorkspaces = await getWorkspaces(wsRoot, true, false)
|
|
190
249
|
let workspaces
|
|
191
250
|
if (this.task.options.workspaces) {
|
|
192
251
|
const relevantWorkspaces = new Set(arrayify(this.task.options.workspaces))
|
package/lib/deploy/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
options: /** @type{const} */(['--to', '--for', '--out', '--on', '--tunnel-address', '--vcap-file', '--delta-from']),
|
|
3
|
-
flags: /** @type{const} */(['--no-save', '--auto-undeploy', '--dry', '--xdry', '--with-mocks', '--store-credentials', '--no-build', '--model-only', '--script', '--with-auto-schema-evolution']),
|
|
3
|
+
flags: /** @type{const} */(['--no-save', '--auto-undeploy', '--dry', '--xdry', '--with-mocks', '--store-credentials', '--no-build', '--model-only', '--script', '--with-auto-schema-evolution', '--resolve-bindings']),
|
|
4
4
|
shortcuts: ['-2', '-4', '-o', '-n']
|
|
5
5
|
}
|
|
@@ -30,7 +30,8 @@ const allowList = [
|
|
|
30
30
|
'--tunnel-address',
|
|
31
31
|
'--vcap-file',
|
|
32
32
|
'--with-mocks',
|
|
33
|
-
'--no-build'
|
|
33
|
+
'--no-build',
|
|
34
|
+
'--resolve-bindings'
|
|
34
35
|
]
|
|
35
36
|
|
|
36
37
|
module.exports = new class HanaDeployer {
|
|
@@ -190,7 +191,22 @@ spec:
|
|
|
190
191
|
serviceName ??= cfServiceInstanceName;
|
|
191
192
|
serviceKeyName ??= cfServiceInstanceKeyName
|
|
192
193
|
if (!vcapEnv.VCAP_SERVICES || serviceName) {
|
|
193
|
-
|
|
194
|
+
const base = process.env.VCAP_SERVICES ? JSON.parse(process.env.VCAP_SERVICES) : (vcapEnv.VCAP_SERVICES ?? {});
|
|
195
|
+
const hanas = Array.isArray(base.hana) ? base.hana : [];
|
|
196
|
+
let hanaTagged = hanas.filter(s => s?.name === cfServiceInstanceName || (Array.isArray(s?.tags) && s.tags.includes('hana')));
|
|
197
|
+
if (hanaTagged.length === 0) {
|
|
198
|
+
const entry = { name: cfServiceInstanceName, label: 'hana', tags: ['hana'], credentials: {} };
|
|
199
|
+
hanas.push(entry);
|
|
200
|
+
hanaTagged = [entry];
|
|
201
|
+
}
|
|
202
|
+
for (const entry of hanaTagged) {
|
|
203
|
+
entry.name ??= cfServiceInstanceName;
|
|
204
|
+
entry.tags = Array.from(new Set([...entry.tags || [], 'hana']));
|
|
205
|
+
entry.credentials = { ...(entry.credentials || {}), ...serviceKey };
|
|
206
|
+
}
|
|
207
|
+
base.hana = hanas;
|
|
208
|
+
vcapEnv.VCAP_SERVICES = base;
|
|
209
|
+
process.env.VCAP_SERVICES = JSON.stringify(base);
|
|
194
210
|
}
|
|
195
211
|
|
|
196
212
|
if (!hasVCAPEnv) {
|
|
@@ -229,7 +245,6 @@ spec:
|
|
|
229
245
|
return buildResult;
|
|
230
246
|
}
|
|
231
247
|
|
|
232
|
-
|
|
233
248
|
async _getOrCreateCFService(currentModelFolder, serviceName, serviceKeyName, tunnelAddress) {
|
|
234
249
|
|
|
235
250
|
// get from param
|
|
@@ -297,19 +312,6 @@ spec:
|
|
|
297
312
|
return buildResult[0].result;
|
|
298
313
|
}
|
|
299
314
|
|
|
300
|
-
_getVCAPServicesEntry(serviceInstanceName, serviceKey) {
|
|
301
|
-
return {
|
|
302
|
-
hana: [
|
|
303
|
-
{
|
|
304
|
-
name: serviceInstanceName,
|
|
305
|
-
tags: ['hana'],
|
|
306
|
-
credentials: serviceKey
|
|
307
|
-
}
|
|
308
|
-
]
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
|
|
313
315
|
_injectTunnelAddress(credentials, tunnelAddress) {
|
|
314
316
|
if (!/\w+:\d+/.test(tunnelAddress)) {
|
|
315
317
|
throw `Invalid tunnel address '${tunnelAddress}' - must be in form 'host:port'`;
|
|
@@ -10,7 +10,7 @@ let versionInfo = require('../../../package.json').version;
|
|
|
10
10
|
const { warn } = require('../../util/term');
|
|
11
11
|
|
|
12
12
|
const edmxncdsdatatype = {
|
|
13
|
-
"Edm.String": "cds.
|
|
13
|
+
"Edm.String": "cds.String",
|
|
14
14
|
"Edm.Boolean": "cds.Boolean",
|
|
15
15
|
"Edm.Int16": "cds.Integer",
|
|
16
16
|
"Edm.Int32": "cds.Integer",
|
|
@@ -592,7 +592,7 @@ function _getServiceEntityProperty(
|
|
|
592
592
|
}
|
|
593
593
|
|
|
594
594
|
if (defaultValue !== -1 && defaultValue) {
|
|
595
|
-
propertyJson = propertyJson + ', \n"default": {\n"val":
|
|
595
|
+
propertyJson = propertyJson + ', \n"default": {\n"val": ' + JSON.stringify(defaultValue) + '\n}';
|
|
596
596
|
}
|
|
597
597
|
|
|
598
598
|
if (propertyName)
|
|
@@ -33,7 +33,7 @@ const edmToCdsTypeMapping = {
|
|
|
33
33
|
'Edm.Int16': 'cds.Integer',
|
|
34
34
|
'Edm.Int32': 'cds.Integer',
|
|
35
35
|
'Edm.Int64': 'cds.Integer64',
|
|
36
|
-
'Edm.String': 'cds.
|
|
36
|
+
'Edm.String': 'cds.String',
|
|
37
37
|
'Edm.TimeOfDay': 'cds.Time',
|
|
38
38
|
};
|
|
39
39
|
|
|
@@ -261,7 +261,26 @@ function _captureAnnotationInCSN(targetString, annotationObject, csnDefsTargetOb
|
|
|
261
261
|
annotationObject = { "=": annotationObject.$Path };
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
-
|
|
264
|
+
/**
|
|
265
|
+
* Annotations need to be flattened in CSN. E.g.
|
|
266
|
+
*
|
|
267
|
+
* @Core.ContentDisposition : {
|
|
268
|
+
* Type : 'inline',
|
|
269
|
+
* Filename: typedImageFileName
|
|
270
|
+
* }
|
|
271
|
+
*
|
|
272
|
+
* is represented as:
|
|
273
|
+
*
|
|
274
|
+
* @Core.ContentDisposition.Type : 'inline',
|
|
275
|
+
* @Core.ContentDisposition.Filename : {'=': 'typedImageFileName'},
|
|
276
|
+
*/
|
|
277
|
+
if (annotationObject instanceof Object && !(annotationObject instanceof Array) && !(Object.keys(annotationObject).length === 1 && (Object.keys(annotationObject)[0] === '=' || Object.keys(annotationObject)[0] === 'xpr' || Object.keys(annotationObject)[0] === '#'))) {
|
|
278
|
+
Object.keys(annotationObject).forEach(innerTargetString => {
|
|
279
|
+
_captureAnnotationInCSN(targetString + '.' + innerTargetString, annotationObject[innerTargetString], csnDefsTargetObject);
|
|
280
|
+
})
|
|
281
|
+
} else {
|
|
282
|
+
csnDefsTargetObject[targetString] = annotationObject;
|
|
283
|
+
}
|
|
265
284
|
}
|
|
266
285
|
|
|
267
286
|
function _replaceSpecialCharacters(text) {
|
package/lib/init/constants.js
CHANGED
|
@@ -5,7 +5,7 @@ module.exports = {
|
|
|
5
5
|
COMMAND_ADD: 'add',
|
|
6
6
|
|
|
7
7
|
JAVA_LTS_VERSIONS: [17, 21],
|
|
8
|
-
MAVEN_ARCHETYPE_VERSION: '4.
|
|
8
|
+
MAVEN_ARCHETYPE_VERSION: '4.4.0',
|
|
9
9
|
|
|
10
10
|
OPTIONS: Object.freeze({
|
|
11
11
|
COMPLETION: 'completion',
|
|
@@ -54,6 +54,7 @@ module.exports = {
|
|
|
54
54
|
CONNECTIVITY: 'connectivity',
|
|
55
55
|
DESTINATION: 'destination',
|
|
56
56
|
HTML5_REPO: 'html5-repo',
|
|
57
|
+
APP_FRONTEND: 'app-frontend',
|
|
57
58
|
PORTAL: 'portal',
|
|
58
59
|
APPLICATION_LOGGING: 'application-logging',
|
|
59
60
|
AUDIT_LOGGING: 'audit-logging',
|
package/lib/init/index.js
CHANGED
|
@@ -233,6 +233,7 @@ module.exports = class CDSGenerator {
|
|
|
233
233
|
const templates = new Map
|
|
234
234
|
for (let plugin of plugins) {
|
|
235
235
|
// Compat
|
|
236
|
+
if (plugin === 'appfront') plugin = 'app-front'
|
|
236
237
|
if (plugin === 'helm' && cds.cli.options['internal-unified-runtime-charts']) {
|
|
237
238
|
plugin = 'helm-unified-runtime'
|
|
238
239
|
cds.cli.options.add.delete('helm')
|
|
@@ -242,6 +243,7 @@ module.exports = class CDSGenerator {
|
|
|
242
243
|
if (plugin in { 'kibana-logging': 1, 'kibana': 1 }) plugin = 'application-logging'
|
|
243
244
|
if (plugin === 'postgresql') plugin = 'postgres'
|
|
244
245
|
if (plugin === 'gha') plugin = 'github-actions'
|
|
246
|
+
if (plugin === 'app-front') plugin = 'app-frontend'
|
|
245
247
|
|
|
246
248
|
if (!templates.has(plugin)) {
|
|
247
249
|
const template = await this._createTemplate(plugin)
|
package/lib/init/mvn.js
CHANGED
|
@@ -21,8 +21,9 @@ module.exports.add = async function (feature, featureOptions={}) {
|
|
|
21
21
|
}
|
|
22
22
|
params.set(`-Dfeature`, feature)
|
|
23
23
|
|
|
24
|
-
const profile = cds.cli.options.for
|
|
25
|
-
params.set(`-Dprofile`, profile
|
|
24
|
+
const profile = cds.cli.options.for === 'production' ? 'cloud' : cds.cli.options.for ?? 'default'
|
|
25
|
+
params.set(`-Dprofile`, profile)
|
|
26
|
+
if (feature === 'mtx' && profile === 'default') params.set('-Dprofile', 'with-mtx')
|
|
26
27
|
|
|
27
28
|
Object.entries(featureOptions).forEach(([k,v]) => params.set(`-Doptions.${k}`, v))
|
|
28
29
|
|
|
@@ -141,6 +141,7 @@ module.exports = new class ProjectReader {
|
|
|
141
141
|
devDependencies: () => pkgJson().devDependencies,
|
|
142
142
|
has: x => _inProd(x),
|
|
143
143
|
profile: () => cds.cli.options?.for,
|
|
144
|
+
profileSpecified: () => !!cds.cli.options.for,
|
|
144
145
|
language: () => _isJava() ? 'java' : 'nodejs',
|
|
145
146
|
isJava: () => _isJava(),
|
|
146
147
|
isNodejs: () => !_isJava(),
|
|
@@ -10,6 +10,11 @@ const srv4 = isJava ? srvJava4 : srvNode4
|
|
|
10
10
|
|
|
11
11
|
const approuter = { in: 'modules', where: { type: 'approuter.nodejs' } }
|
|
12
12
|
|
|
13
|
+
const appFront = {
|
|
14
|
+
in: 'resources',
|
|
15
|
+
where: { type: 'org.cloudfoundry.managed-service', 'parameters.service': 'app-front' }
|
|
16
|
+
}
|
|
17
|
+
|
|
13
18
|
const xsuaa = {
|
|
14
19
|
in: 'resources',
|
|
15
20
|
where: { type: 'org.cloudfoundry.managed-service', 'parameters.service': 'xsuaa', 'parameters.service-plan': 'application' }
|
|
@@ -233,6 +238,7 @@ module.exports = {
|
|
|
233
238
|
applicationLogging,
|
|
234
239
|
approuterMTXRoute,
|
|
235
240
|
approuterMTXRouteJava,
|
|
241
|
+
appFront,
|
|
236
242
|
html5RepoHost,
|
|
237
243
|
html5Runtime,
|
|
238
244
|
appDeployer,
|
|
@@ -18,7 +18,7 @@ applications:
|
|
|
18
18
|
buildpack: nodejs_buildpack
|
|
19
19
|
command: (npm start && echo "This application may now be stopped to free resources." || echo "AMS Policy Deployment unsuccessful.") && sleep infinity
|
|
20
20
|
services:
|
|
21
|
-
- name: {{appName}}-
|
|
21
|
+
- name: {{appName}}-ias
|
|
22
22
|
parameters:
|
|
23
23
|
credential-type: X509_GENERATED
|
|
24
24
|
app-identifier: ams-policy-deployer
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{app}}"{{#hasUI5}},
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "webapp/index.html",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "ui5 build preload --clean-dest --config ui5-deploy.yaml",
|
|
7
|
+
"build-local": "ui5 build preload --clean-dest",
|
|
8
|
+
"start": "ui5 serve"
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@ui5/cli": "^4",
|
|
12
|
+
"ui5-task-zipper": "^3"
|
|
13
|
+
},
|
|
14
|
+
"ui5": {
|
|
15
|
+
"dependencies": [
|
|
16
|
+
"ui5-task-zipper"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
{{/hasUI5}}
|
|
20
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{appName}} - {{app}}</title>
|
|
7
|
+
<style>
|
|
8
|
+
html, body, body > div, #container, #container-uiarea {
|
|
9
|
+
height: 100%;
|
|
10
|
+
}
|
|
11
|
+
</style>
|
|
12
|
+
<script
|
|
13
|
+
id="sap-ui-bootstrap"
|
|
14
|
+
src="https://sapui5.hana.ondemand.com/1.140.0/resources/sap-ui-core.js"
|
|
15
|
+
data-sap-ui-theme="sap_horizon"
|
|
16
|
+
data-sap-ui-resourceroots='{
|
|
17
|
+
"{{appName}}.{{app}}": "./"
|
|
18
|
+
}'
|
|
19
|
+
data-sap-ui-oninit="module:sap/ui/core/ComponentSupport"
|
|
20
|
+
data-sap-ui-compatVersion="edge"
|
|
21
|
+
data-sap-ui-async="true"
|
|
22
|
+
data-sap-ui-frameOptions="trusted"
|
|
23
|
+
></script>
|
|
24
|
+
</head>
|
|
25
|
+
<body class="sapUiBody sapUiSizeCompact" id="content">
|
|
26
|
+
<div
|
|
27
|
+
data-sap-ui-component
|
|
28
|
+
data-name="{{appName}}.{{app}}"
|
|
29
|
+
data-id="container"
|
|
30
|
+
data-settings='{"id" : "{{appName}}.{{app}}" }'
|
|
31
|
+
data-handle-validation="true"
|
|
32
|
+
></div>
|
|
33
|
+
</body>
|
|
34
|
+
</html>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
parameters:
|
|
3
|
+
deploy_mode: html5-repo
|
|
4
|
+
modules:
|
|
5
|
+
- name: {{appName}}-srv
|
|
6
|
+
type: {{language}}
|
|
7
|
+
path: {{& srvPath}}
|
|
8
|
+
|
|
9
|
+
- name: {{appName}}
|
|
10
|
+
type: approuter.nodejs
|
|
11
|
+
path: {{& approuterPath}}
|
|
12
|
+
requires:
|
|
13
|
+
- name: {{appName}}-html5-runtime
|
|
14
|
+
|
|
15
|
+
- name: {{appName}}-app-deployer
|
|
16
|
+
type: com.sap.application.content
|
|
17
|
+
path: {{#isNodejs}}gen{{/isNodejs}}{{#isJava}}.{{/isJava}}
|
|
18
|
+
build-parameters:
|
|
19
|
+
build-result: {{#isNodejs}}{{& appPath }}{{/isNodejs}}{{#isJava}}resources{{/isJava}}
|
|
20
|
+
requires:
|
|
21
|
+
{{#apps}}
|
|
22
|
+
- name: {{strippedAppName}}{{strippedApp}}
|
|
23
|
+
artifacts:
|
|
24
|
+
- {{app}}.zip
|
|
25
|
+
target-path: {{#isNodejs}}{{& appPath }}{{/isNodejs}}{{#isJava}}resources{{/isJava}}
|
|
26
|
+
{{/apps}}
|
|
27
|
+
requires:
|
|
28
|
+
- name: srv-api
|
|
29
|
+
{{#hasXsuaa}}
|
|
30
|
+
- name: {{appName}}-auth
|
|
31
|
+
{{/hasXsuaa}}
|
|
32
|
+
{{#hasIas}}
|
|
33
|
+
- name: {{appName}}-ias
|
|
34
|
+
parameters:
|
|
35
|
+
config:
|
|
36
|
+
credential-type: X509_GENERATED
|
|
37
|
+
app-identifier: app-deployer
|
|
38
|
+
{{/hasIas}}
|
|
39
|
+
- name: {{appName}}-app-front
|
|
40
|
+
parameters:
|
|
41
|
+
content-target: true
|
|
42
|
+
parameters:
|
|
43
|
+
config:
|
|
44
|
+
destinations:
|
|
45
|
+
- name: srv-api
|
|
46
|
+
url: ~{srv-api/srv-url}
|
|
47
|
+
forwardAuthToken: true
|
|
48
|
+
{{#hasUI5}}
|
|
49
|
+
- name: ui5
|
|
50
|
+
url: https://ui5.sap.com
|
|
51
|
+
{{/hasUI5}}
|
|
52
|
+
|
|
53
|
+
{{#apps}}
|
|
54
|
+
- name: {{strippedAppName}}{{strippedApp}}
|
|
55
|
+
type: html5
|
|
56
|
+
path: {{& appPath}}{{app}}
|
|
57
|
+
build-parameters:
|
|
58
|
+
build-result: dist
|
|
59
|
+
builder: custom
|
|
60
|
+
commands:
|
|
61
|
+
- npm ci
|
|
62
|
+
- npm run build
|
|
63
|
+
supported-platforms: []
|
|
64
|
+
{{/apps}}
|
|
65
|
+
|
|
66
|
+
resources:
|
|
67
|
+
- name: {{appName}}-app-front
|
|
68
|
+
type: org.cloudfoundry.managed-service
|
|
69
|
+
parameters:
|
|
70
|
+
service: app-front
|
|
71
|
+
service-plan: developer
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json
|
|
3
|
+
specVersion: '3.1'
|
|
4
|
+
metadata:
|
|
5
|
+
name: {{app}}
|
|
6
|
+
type: application
|
|
7
|
+
resources:
|
|
8
|
+
configuration:
|
|
9
|
+
propertiesFileSourceEncoding: UTF-8
|
|
10
|
+
builder:
|
|
11
|
+
resources:
|
|
12
|
+
excludes:
|
|
13
|
+
- "/test/**"
|
|
14
|
+
- "/localService/**"
|
|
15
|
+
customTasks:
|
|
16
|
+
- name: ui5-task-zipper
|
|
17
|
+
afterTask: generateVersionInfo
|
|
18
|
+
configuration:
|
|
19
|
+
archiveName: {{app}}
|
|
20
|
+
additionalFiles:
|
|
21
|
+
- xs-app.json
|
|
22
|
+
{{#isTypescript}}
|
|
23
|
+
- name: ui5-tooling-transpile-task
|
|
24
|
+
afterTask: replaceVersion
|
|
25
|
+
{{/isTypescript}}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
app-front:
|
|
3
|
+
serviceOfferingName: app-front
|
|
4
|
+
servicePlanName: developer
|
|
5
|
+
html5-apps-deployer:
|
|
6
|
+
env:
|
|
7
|
+
SAP_CLOUD_SERVICE: {{strippedAppName}}.service
|
|
8
|
+
image:
|
|
9
|
+
repository: {{appName}}-html5-deployer
|
|
10
|
+
bindings:
|
|
11
|
+
app-front:
|
|
12
|
+
serviceInstanceName: app-front
|
|
13
|
+
{{=<% %>=}}
|
|
14
|
+
envFrom:
|
|
15
|
+
- configMapRef:
|
|
16
|
+
name: "{{ .Release.Name }}-app-front-configmap"
|
|
17
|
+
<%={{ }}=%>
|
|
18
|
+
backendDestinations:
|
|
19
|
+
srv-api:
|
|
20
|
+
service: srv
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"welcomeFile": "/index.html",
|
|
3
|
+
"authenticationMethod": "route",
|
|
4
|
+
"routes": [
|
|
5
|
+
{
|
|
6
|
+
"source": "^/?odata/(.*)$",
|
|
7
|
+
"target": "/odata/$1",
|
|
8
|
+
"destination": "srv-api"{{#hasXsuaa}},
|
|
9
|
+
"authenticationType": "xsuaa"{{/hasXsuaa}},
|
|
10
|
+
"csrfProtection": false
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"source": ".*",
|
|
14
|
+
"service": "app-front"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const { join } = require('path')
|
|
2
|
+
const { readProject } = require('../../projectReader')
|
|
3
|
+
const { merge } = require('../../merge')
|
|
4
|
+
const { srv4, appDeployer, appFront } = require('../../registries/mta')
|
|
5
|
+
const { odataApi } = require('../../registries/xs-app')
|
|
6
|
+
const { renderAndCopy } = require('../../template')
|
|
7
|
+
const { exists } = require('../../../cds').utils
|
|
8
|
+
|
|
9
|
+
module.exports = class extends require('../../plugin') {
|
|
10
|
+
|
|
11
|
+
static help() {
|
|
12
|
+
return 'SAP BTP AppFront service' // TODO: Product name?
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static hasInProduction(env) {
|
|
16
|
+
return !!env.requires['app-front']
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async run() {
|
|
20
|
+
const project = readProject()
|
|
21
|
+
const { appUIPaths, apps, appPath, configFile, hasUI5 } = project
|
|
22
|
+
await merge(__dirname, 'files/package.json.hbs').into(configFile, { project })
|
|
23
|
+
|
|
24
|
+
if (hasUI5) {
|
|
25
|
+
for (const { app } of apps) {
|
|
26
|
+
project.app = app
|
|
27
|
+
await merge(__dirname, 'files/ui5.yaml.hbs').into(join(appPath, app, 'ui5.yaml'), { project })
|
|
28
|
+
}
|
|
29
|
+
for (const { app } of apps) {
|
|
30
|
+
project.app = app
|
|
31
|
+
await merge(__dirname, 'files/ui5-deploy.yaml.hbs').into(join(appPath, app, 'ui5-deploy.yaml'), { project })
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
for (const { app } of apps) {
|
|
36
|
+
project.app = app
|
|
37
|
+
await merge(__dirname, 'files/app-package.json.hbs').into(join(appPath, app, 'package.json'), { project })
|
|
38
|
+
if (hasUI5) {
|
|
39
|
+
await renderAndCopy(join(__dirname, 'files/index.html.hbs'), join(appPath, app, 'webapp/index.html'), project)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// app deployer requires a manifest.json
|
|
44
|
+
await Promise.all(appUIPaths.map(p =>
|
|
45
|
+
merge(__dirname, 'files/manifest.json')
|
|
46
|
+
.into(join(appPath, p, 'webapp/manifest.json'), { project })
|
|
47
|
+
))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async combine() {
|
|
51
|
+
const project = readProject()
|
|
52
|
+
const { srvPath, appPath, hasMta, apps } = project
|
|
53
|
+
|
|
54
|
+
await Promise.all(apps.map(async ({ app }) => {
|
|
55
|
+
project.app = app
|
|
56
|
+
project.hasIndexHtml = exists(join(appPath, app, 'webapp/index.html'))
|
|
57
|
+
const additions = [odataApi]
|
|
58
|
+
await merge(__dirname, 'files/xs-app.json.hbs').into(join(appPath, app, 'xs-app.json'), { with: project, additions })
|
|
59
|
+
}))
|
|
60
|
+
|
|
61
|
+
// later -> rather consider adding to app-front package.json
|
|
62
|
+
// if (hasMultitenancy) {
|
|
63
|
+
// await merge({ cds: { requires: { 'app-front':true } } }).into('mtx/sidecar/package.json')
|
|
64
|
+
// }
|
|
65
|
+
|
|
66
|
+
if (hasMta) {
|
|
67
|
+
const srv = srv4(srvPath)
|
|
68
|
+
project.apps = apps
|
|
69
|
+
const appModules = project.apps.map(app => ({
|
|
70
|
+
in: 'modules',
|
|
71
|
+
where: { type: 'html5', path: `${appPath}${app.app}` }
|
|
72
|
+
}))
|
|
73
|
+
const additions = [srv, appDeployer, appFront, ...appModules]
|
|
74
|
+
const relationships = [{
|
|
75
|
+
insert: [appFront, 'name'],
|
|
76
|
+
into: [appDeployer, 'requires', 'name'],
|
|
77
|
+
}]
|
|
78
|
+
await merge(__dirname, 'files/mta.yaml.hbs').into('mta.yaml', {
|
|
79
|
+
with: project,
|
|
80
|
+
additions,
|
|
81
|
+
relationships
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// When Helm support is added
|
|
86
|
+
// if (hasContainerize) {
|
|
87
|
+
// const additions = [{
|
|
88
|
+
// in: 'modules',
|
|
89
|
+
// where: { name: `${project.appName}-content-deployer` }
|
|
90
|
+
// }]
|
|
91
|
+
// await merge(__dirname, '/files/containerize.yaml.hbs').into('containerize.yaml', { with: project, additions })
|
|
92
|
+
// }
|
|
93
|
+
// if (hasHelm || hasHelmUnifiedRuntime) {
|
|
94
|
+
// await renderAndCopy(join(__dirname, 'files/ui-resources'), 'ui-resources', project)
|
|
95
|
+
// await merge(__dirname, 'files/values.yaml.hbs').into('chart/values.yaml', { with: project })
|
|
96
|
+
// }
|
|
97
|
+
}
|
|
98
|
+
}
|