@sap/cds-dk 6.2.2 → 6.3.0
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 +38 -0
- package/bin/bind.js +4 -4
- package/bin/deploy/to-h2.js +4 -0
- package/bin/deploy/to-sqlite.js +6 -4
- package/bin/help.js +1 -1
- package/bin/login.js +1 -0
- package/bin/mock.js +2 -2
- package/bin/push.js +29 -7
- package/lib/bind/userCdsrcJsonStorageProvider.js +4 -4
- package/lib/cds.js +6 -3
- package/lib/client/activate.js +1 -1
- package/lib/client/base_command.js +10 -10
- package/lib/client/helper/dependency_helper.js +10 -1
- package/lib/client/helper/logging.js +6 -2
- package/lib/client/pull.js +6 -6
- package/lib/client/push.js +33 -14
- package/lib/client/settings_manager.js +52 -21
- package/lib/compile/openapi/csdl2openapi.js +1 -1
- package/lib/compile/to-xsuaa.js +1 -1
- package/lib/import/odata/v2parser.js +37 -1
- package/lib/import/odata/v4parser.js +101 -23
- package/lib/init/constants.js +1 -1
- package/lib/init/index.js +23 -45
- package/lib/init/template/approuter/index.js +44 -48
- package/lib/init/template/auditlog/index.js +14 -10
- package/lib/init/template/cf-manifest/index.js +9 -11
- package/lib/init/template/connectivity/index.js +10 -10
- package/lib/init/template/data/index.js +20 -19
- package/lib/init/template/destinations/index.js +14 -9
- package/lib/init/template/enterprise-messaging/index.js +14 -9
- package/lib/init/template/extensibility/index.js +14 -9
- package/lib/init/template/hana/index.js +51 -57
- package/lib/init/template/helm/index.js +27 -59
- package/lib/init/template/helm/subchart/templates/_helpers.tpl +38 -0
- package/lib/init/template/helm/subchart/templates/deployment.yaml +12 -12
- package/lib/init/template/helm/subchart/values.md +565 -194
- package/lib/init/template/helm/subchart/values.schema.json +163 -8
- package/lib/init/template/helm/subchart/values.yaml +17 -1
- package/lib/init/template/html5-repo/index.js +15 -11
- package/lib/init/template/java/index.js +15 -11
- package/lib/init/template/kibana-logging/index.js +30 -31
- package/lib/init/template/lint/index.js +17 -18
- package/lib/init/template/mta/index.js +18 -40
- package/lib/init/template/mtx/index.js +2 -3
- package/lib/init/template/multitenancy/index.js +27 -20
- package/lib/init/template/nodejs/index.js +18 -20
- package/lib/init/template/notebook/index.js +5 -434
- package/lib/init/template/pipeline/index.js +10 -10
- package/lib/init/template/samples/index.js +7 -16
- package/lib/init/template/templateBase.js +17 -18
- package/lib/init/template/toggles/files/cdsrc.json +5 -0
- package/lib/init/template/toggles/files/cdsrc.json.hbs +7 -0
- package/lib/init/template/toggles/index.js +12 -6
- package/lib/init/template/xsuaa/index.js +44 -41
- package/lib/init/types.d.ts +0 -59
- package/lib/init/util/commandUtil.js +6 -7
- package/lib/init/util/fsUtil.js +23 -109
- package/lib/init/util/merge.js +319 -0
- package/lib/init/util/mvnArchetypeUtil.js +6 -5
- package/lib/init/util/projectReader.js +125 -300
- package/lib/init/util/spinner.js +10 -8
- package/lib/init/util/templateUtil.js +25 -368
- package/lib/lint/Linter.js +2 -3
- package/lib/lint/checks.js +0 -1
- package/lib/lint/io.js +18 -34
- package/npm-shrinkwrap.json +115 -261
- package/package.json +3 -3
- package/lib/init/template/notebook/cap_magic.py +0 -118
- package/lib/init/template/notebook/files/{{{name}}}.ipynb.hbs +0 -90
- package/lib/init/template/notebook/files/{{{name}}}.json +0 -102
- package/lib/init/template/notebook/notebook.js +0 -394
- package/lib/init/util/logger.js +0 -42
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,41 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
9
9
|
|
|
10
|
+
## Version 6.3.0 - 2022-11-02
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `cds deploy --to h2 --dry` to create drop/create DDL for H2. Full deployment (w/o `--dry`) is not yet supported, though.
|
|
15
|
+
- `cds push <file>|<URL>` to specify a custom extension archive by local path or download URL.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- `cds add` support for classic Java projects is now removed.
|
|
20
|
+
- `cds import` now adds the annotation `@cds.external` for all the definitions in the CSN along with the service.
|
|
21
|
+
- `cds import` now adds `notNull` entry for all the parameters and properties in the CSN.
|
|
22
|
+
- MTX Client now trims a passcode entered via prompt for convenience when pasting
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- MTX Client now ignores a username potentially saved for the current project, if a passcode is given
|
|
27
|
+
- MTX Client now treats keytar as optional unless explicitly running `cds login`
|
|
28
|
+
- `cds import` now caputres the documentation properly for all the EntitySet referring to same EntityType.
|
|
29
|
+
- `cds deploy --to sql` now produces 'plain' SQL again, suitable for e.g. H2. In 6.2 it produced 'sqlite' dialect, erroneously.
|
|
30
|
+
- `cds compile --to openapi` now fixes the duplication of fields in `required` section.
|
|
31
|
+
- `cds lint` now recognizes ESLint configurations from _package.json_.
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## Version 6.2.3 - 2022-10-21
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- Use `@sap/cds` 6.2.3
|
|
39
|
+
- Use `@sap/hdi-deploy` 4.5.0, which brings support for Node 18
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- `cds mock` now prefers a local `@sap/cds` installation, like other `cds` commands as well.
|
|
44
|
+
|
|
10
45
|
## Version 6.2.2 - 2022-10-13
|
|
11
46
|
|
|
12
47
|
### Added
|
|
@@ -17,6 +52,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
17
52
|
|
|
18
53
|
- `cds deploy --to sql` now honors the `cds.sql.dialect` configuration to specify SQL dialects like `postgres`.
|
|
19
54
|
|
|
55
|
+
### Removed
|
|
56
|
+
- `cds add notebook` removed in favor of custom notebooks in VS Code (now part of the CDS Editor)
|
|
57
|
+
|
|
20
58
|
## Version 6.2.1 - 2022-10-10
|
|
21
59
|
|
|
22
60
|
### Fixed
|
package/bin/bind.js
CHANGED
|
@@ -89,7 +89,7 @@ async function cds_bind(args, options, logger = LOG) {
|
|
|
89
89
|
throw new Error(`The option '--kind' cannot be specified together with multiple targets ('--to') services. Use one service per call or omit the '--kind' option.`);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
const spinner = new Spinner(
|
|
92
|
+
const spinner = new Spinner();
|
|
93
93
|
|
|
94
94
|
let on;
|
|
95
95
|
if (options.on === 'k8s' || options.on === 'kubernetes') {
|
|
@@ -139,7 +139,7 @@ async function cds_bind(args, options, logger = LOG) {
|
|
|
139
139
|
const kindText = kind && `with kind ${highlight(kind)}`;
|
|
140
140
|
const nameText = !isJavaProject && name && highlight(name);
|
|
141
141
|
|
|
142
|
-
logger.
|
|
142
|
+
logger.info(['Binding', nameText, 'to', platformText(binding), bindingText(binding, highlight), kindText].filter(t => t).join(' '));
|
|
143
143
|
|
|
144
144
|
if (!resolvedService.name) {
|
|
145
145
|
const { binding } = resolvedService;
|
|
@@ -156,8 +156,8 @@ async function cds_bind(args, options, logger = LOG) {
|
|
|
156
156
|
logger
|
|
157
157
|
});
|
|
158
158
|
|
|
159
|
-
logger.
|
|
160
|
-
logger.
|
|
159
|
+
logger.info();
|
|
160
|
+
logger.info(`${info('TIP:')} Run with cloud bindings: ${bold(runCmd(options, isJavaProject))}`);
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
async function cds_bind_exec(command) { // eslint-disable-lint
|
package/bin/deploy/to-sqlite.js
CHANGED
|
@@ -6,10 +6,12 @@ exports.deploy = function deploy_to_sqlite (_model, _db, options) {
|
|
|
6
6
|
const model = _model || conf.model || cds.resolve(!fts ? '*' : ['*',fts], false)
|
|
7
7
|
const file = _database4 (_db, _dbrl, conf)
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
// current 'sqlite' default is bad, as we want to default to 'plain', so ignore it
|
|
10
|
+
const env_dialect = cds.env.sql.dialect !== 'sqlite' ? cds.env.sql.dialect : 'plain'
|
|
11
|
+
const to_dialect = _dbrl && _dbrl.match(/^(\w+)/) && RegExp.$1
|
|
12
|
+
if (to_dialect) options.dialect = (to_dialect === 'sql')
|
|
13
|
+
? conf.dialect || env_dialect
|
|
14
|
+
: to_dialect
|
|
13
15
|
if (options.dialect === 'postgres' && !options.dry) console.error('\nDeploying to PostgreSQL is not yet supported\n')
|
|
14
16
|
|
|
15
17
|
return cds.deploy(model) .to ('sqlite:'+file, options)
|
package/bin/help.js
CHANGED
|
@@ -10,7 +10,7 @@ module.exports = exports = Object.assign (help, {help:`
|
|
|
10
10
|
|
|
11
11
|
*i* | *init* jump-start cds-based projects
|
|
12
12
|
*a* | *add* add a feature to an existing project
|
|
13
|
-
*y* | *bind*
|
|
13
|
+
*y* | *bind* bind application to remote services
|
|
14
14
|
*m* | *import* add models from external sources
|
|
15
15
|
*c* | *compile* compile cds models to different outputs
|
|
16
16
|
*p* | *parse* parses given cds models
|
package/bin/login.js
CHANGED
|
@@ -82,6 +82,7 @@ module.exports = Object.assign(login, {
|
|
|
82
82
|
|
|
83
83
|
For security reasons, username will only be saved for app URLs that look like localhost.
|
|
84
84
|
The same is true for an empty password. Non-empty passwords will never be saved.
|
|
85
|
+
If saved, a username will be associated with the project folder, not with the (potentially ambiguous) URL.
|
|
85
86
|
|
|
86
87
|
Note: Productive secrets should not be specified on a command line, since this might expose them to
|
|
87
88
|
unauthorized users on the same machine!
|
package/bin/mock.js
CHANGED
package/bin/push.js
CHANGED
|
@@ -5,25 +5,29 @@ module.exports = Object.assign(push, {
|
|
|
5
5
|
help: `
|
|
6
6
|
# SYNOPSIS
|
|
7
7
|
|
|
8
|
-
*cds push* [ <extension-project-directory> ]
|
|
8
|
+
*cds push* [ <extension-project-directory> | <extension-archive> | <extension-url> ]
|
|
9
9
|
|
|
10
10
|
Push your extension to the SaaS app in order to enable or update it.
|
|
11
11
|
|
|
12
|
+
You may specify a project directory or a custom extension archive, either as a
|
|
13
|
+
local file or as a URL to download it from.
|
|
14
|
+
A download URL may contain Basic-Auth credentials as in 'https://user:pass@host'.
|
|
15
|
+
|
|
12
16
|
By default, the current working directory is used as project directory.
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+
Any authentication data will not be saved.
|
|
15
19
|
|
|
16
20
|
|
|
17
21
|
# OPTIONS
|
|
18
22
|
|
|
19
23
|
*--to* <url>
|
|
20
24
|
|
|
21
|
-
Specify the URL of the extensible SaaS app.
|
|
22
|
-
|
|
25
|
+
Specify the URL of the extensible SaaS app. Determined from the current user's active
|
|
26
|
+
CDS or Cloud Foundry login by default.
|
|
23
27
|
|
|
24
28
|
*-s* | *--subdomain* <tenant-subdomain>
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
The tenant subdomain. Determined from project settings by default.
|
|
27
31
|
|
|
28
32
|
*-p* | *--passcode* <passcode>
|
|
29
33
|
|
|
@@ -37,6 +41,7 @@ module.exports = Object.assign(push, {
|
|
|
37
41
|
|
|
38
42
|
Client ID and optionally Client Secret for authentication.
|
|
39
43
|
|
|
44
|
+
|
|
40
45
|
# SEE ALSO
|
|
41
46
|
|
|
42
47
|
*cds pull* to get the CDS model of the SaaS app.
|
|
@@ -47,17 +52,34 @@ module.exports = Object.assign(push, {
|
|
|
47
52
|
|
|
48
53
|
|
|
49
54
|
async function push([projectFolder], options = {}) {
|
|
55
|
+
const { statSync } = require('fs');
|
|
56
|
+
const { resolve } = require('path');
|
|
57
|
+
function isDir(dir) {
|
|
58
|
+
return statSync(resolve(dir), { throwIfNoEntry: false })?.isDirectory();
|
|
59
|
+
}
|
|
60
|
+
|
|
50
61
|
const [username, password] = options.user?.split(':') ?? [];
|
|
51
62
|
delete options.user;
|
|
52
63
|
const [clientid, clientsecret] = options.client?.split(':') ?? [];
|
|
53
64
|
delete options.client;
|
|
54
65
|
|
|
55
|
-
|
|
66
|
+
let extArchive;
|
|
67
|
+
if (projectFolder && !isDir(projectFolder)) {
|
|
68
|
+
extArchive = projectFolder;
|
|
69
|
+
projectFolder = undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const args = [{
|
|
56
73
|
...options,
|
|
57
74
|
projectFolder,
|
|
58
75
|
username,
|
|
59
76
|
password,
|
|
60
77
|
clientid,
|
|
61
78
|
clientsecret
|
|
62
|
-
}
|
|
79
|
+
}];
|
|
80
|
+
if (extArchive) {
|
|
81
|
+
args.push({ extArchive });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
await require('../lib/client/push').run(...args);
|
|
63
85
|
}
|
|
@@ -47,7 +47,7 @@ class UserCdsrcJsonStorageProvider {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
logger.
|
|
50
|
+
logger.info(`adding entry '${file}' to ${GIT_IGNORE_FILE}.`);
|
|
51
51
|
let gitignore = await this.readFileSafely(GIT_IGNORE_FILE);
|
|
52
52
|
gitignore = gitignore + `
|
|
53
53
|
# added by cds bind
|
|
@@ -80,7 +80,7 @@ ${file}
|
|
|
80
80
|
// if (options['save-credentials'] && services.find(service => service.credentials) && process.stdin.isTTY) {
|
|
81
81
|
// const answer = await this.askBooleanQuestion(warn(`IF YOU CONTINUE, SENSITIVE DATA WILL BE WRITTEN TO AN UNENCRYPTED FILE [${jsonPath}]! Do you want to continue [y/N] ?`));
|
|
82
82
|
// if (!answer) {
|
|
83
|
-
// options.logger.
|
|
83
|
+
// options.logger.info(`aborting cds bind; not writing any sensitive data to file [${jsonPath}]`);
|
|
84
84
|
// return;
|
|
85
85
|
// }
|
|
86
86
|
// }
|
|
@@ -115,9 +115,9 @@ ${file}
|
|
|
115
115
|
|
|
116
116
|
await this.writeJsonFile(jsonPath, jsonObject, options.logger);
|
|
117
117
|
if (options.profile) {
|
|
118
|
-
options.logger.
|
|
118
|
+
options.logger.info(`Saving bindings to ${highlight(jsonPath)} in profile ${highlight(options.profile)}`);
|
|
119
119
|
} else {
|
|
120
|
-
options.logger.
|
|
120
|
+
options.logger.info(`Saving bindings to ${highlight(jsonPath)}`);
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
|
package/lib/cds.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
const _local = id => require(_resolve(id))
|
|
2
|
+
|
|
1
3
|
/** @type { import('@sap/cds/lib') } */
|
|
2
|
-
const cds = module.exports =
|
|
4
|
+
const cds = module.exports = _local ('@sap/cds')
|
|
3
5
|
|
|
4
6
|
// for internal use in cds-dk only
|
|
5
|
-
cds._log =
|
|
7
|
+
cds._log = _local ('@sap/cds/bin/utils/log')
|
|
8
|
+
cds._local = _local
|
|
6
9
|
|
|
7
|
-
function
|
|
10
|
+
function _resolve (id) {
|
|
8
11
|
return require.resolve (id, {paths:[
|
|
9
12
|
process.cwd(), // project-local module is preferred
|
|
10
13
|
__dirname // otherwise from our own dependencies
|
package/lib/client/activate.js
CHANGED
|
@@ -15,20 +15,20 @@ module.exports = class BaseCommand {
|
|
|
15
15
|
return exists;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
static
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
switch (error.response && error.response.status) {
|
|
18
|
+
static handleHttpError(error, params, url) {
|
|
19
|
+
url = url ?? params.get('appUrl');
|
|
20
|
+
const prefix = `Request to ${url} failed`;
|
|
21
|
+
switch (error.response?.status) {
|
|
24
22
|
case 401:
|
|
25
|
-
throw new CliError('
|
|
26
|
-
|
|
23
|
+
throw new CliError(prefix + ': invalid authentication. Retry with valid passcode' +
|
|
24
|
+
params.has('passcodeUrl') ? ` from ${params.get('passcodeUrl')}` : '', error);
|
|
25
|
+
case 403:
|
|
26
|
+
throw new CliError(prefix + ': insufficient authorization. Ensure client has all necessary roles', error);
|
|
27
27
|
case 404:
|
|
28
|
-
throw new CliError(
|
|
28
|
+
throw new CliError(prefix + ': resource not found.');
|
|
29
29
|
case 422:
|
|
30
30
|
// Compilation or native-extension error.
|
|
31
|
-
throw new CliError(
|
|
31
|
+
throw new CliError(prefix, error);
|
|
32
32
|
default:
|
|
33
33
|
// Unexpected error should be transparent to user.
|
|
34
34
|
throw error;
|
|
@@ -15,7 +15,7 @@ function globalModulesDir() {
|
|
|
15
15
|
return cachedDir;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
function requireGlobal(packageId) {
|
|
19
19
|
try {
|
|
20
20
|
const packagePath = require.resolve(packageId, { paths: [globalModulesDir()] });
|
|
21
21
|
return require(packagePath);
|
|
@@ -24,3 +24,12 @@ module.exports = function requireGlobal(packageId) {
|
|
|
24
24
|
log.debug(`Failed to find module '${packageId}' in ${globalModulesDir()}`, error);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
+
|
|
28
|
+
function resetCache() {
|
|
29
|
+
cachedDir = undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = {
|
|
33
|
+
requireGlobal,
|
|
34
|
+
resetCache
|
|
35
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
function debug(debugText, error) {
|
|
4
4
|
if (process.env.DEBUG) {
|
|
5
|
-
console.
|
|
5
|
+
console.log(getMessage(debugText, error));
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
8
|
|
|
@@ -11,7 +11,11 @@ function getMessage(errorText, error) {
|
|
|
11
11
|
if (!error) {
|
|
12
12
|
return message;
|
|
13
13
|
}
|
|
14
|
-
message += '\n' + (
|
|
14
|
+
message += '\n' + (
|
|
15
|
+
error.response?.data?.message // response body == error
|
|
16
|
+
|| error.response?.data?.error?.message // response body == { error }
|
|
17
|
+
|| error.message // no response attached
|
|
18
|
+
);
|
|
15
19
|
if (process.env.DEBUG && error.stack) {
|
|
16
20
|
message += '\n' + error.stack;
|
|
17
21
|
}
|
package/lib/client/pull.js
CHANGED
|
@@ -7,13 +7,13 @@ const axios = require('axios');
|
|
|
7
7
|
|
|
8
8
|
const { login } = require('./auth_manager');
|
|
9
9
|
const BaseCommand = require('./base_command');
|
|
10
|
+
const { CliError } = require('./helper/errors');
|
|
10
11
|
|
|
11
12
|
module.exports = class Pull extends BaseCommand {
|
|
12
13
|
|
|
13
14
|
static async run(paramValues) {
|
|
14
15
|
if (!tar) {
|
|
15
|
-
|
|
16
|
-
return process.exitCode = 1;
|
|
16
|
+
throw new CliError(`cds pull requires @sap/cds version >= 6.2. Current version is ${cds.version}\n`);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
const params = await login(paramValues);
|
|
@@ -26,8 +26,8 @@ module.exports = class Pull extends BaseCommand {
|
|
|
26
26
|
fs.rmSync(target, { force: true, recursive: true });
|
|
27
27
|
fs.mkdirSync(target, { recursive: true });
|
|
28
28
|
|
|
29
|
-
const
|
|
30
|
-
return tar.xz(
|
|
29
|
+
const baseModelTgz = await this.getTgz(params);
|
|
30
|
+
return tar.xz(baseModelTgz).to(target);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
static getAppPackageName(projectFolder) {
|
|
@@ -40,7 +40,7 @@ module.exports = class Pull extends BaseCommand {
|
|
|
40
40
|
const options = { ...params.get('reqAuth'), responseType: 'arraybuffer' };
|
|
41
41
|
|
|
42
42
|
return axios.post(pullUrl, {}, options)
|
|
43
|
-
.then(response => response.data)
|
|
44
|
-
.catch(error => this.handleHttpError(error, params));
|
|
43
|
+
.then(response => Buffer.from(response.data))
|
|
44
|
+
.catch(error => this.handleHttpError(error, params, pullUrl));
|
|
45
45
|
}
|
|
46
46
|
}
|
package/lib/client/push.js
CHANGED
|
@@ -5,26 +5,45 @@ const axios = require('axios');
|
|
|
5
5
|
|
|
6
6
|
const { login } = require('./auth_manager');
|
|
7
7
|
const BaseCommand = require('./base_command');
|
|
8
|
-
const { CliError } = require('./helper/errors');
|
|
8
|
+
const { CliError } = require('./helper/errors');
|
|
9
|
+
const { schemaRegex } = require('./helper/url_helper');
|
|
9
10
|
|
|
10
11
|
module.exports = class Push extends BaseCommand {
|
|
11
12
|
|
|
12
|
-
static async run(paramValues) {
|
|
13
|
+
static async run(paramValues, pushOptions) {
|
|
13
14
|
const params = await login(paramValues);
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
await this.pushTgz(params, tgz, extensionName);
|
|
15
|
+
|
|
16
|
+
const { src, content } = await this.readSource(params, pushOptions);
|
|
17
|
+
|
|
18
|
+
const extensionName = this.getExtensionName(params.get('projectFolder'));
|
|
19
|
+
const url = params.get('appUrl');
|
|
20
|
+
const subdomain = params.get('subdomain');
|
|
21
|
+
const target = Object.assign({ url }, subdomain && { subdomain });
|
|
22
|
+
console.log(`\nPushing extension '${extensionName}' from`, { src }, 'to', target);
|
|
23
|
+
|
|
24
|
+
await this.pushTgz(params, content, extensionName);
|
|
25
25
|
console.log ('Activation succeeded.\n')
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
static async readSource(params, pushOptions) {
|
|
29
|
+
let src = pushOptions?.extArchive ?? join(params.get('projectFolder'), 'gen', 'extension.tgz');
|
|
30
|
+
let content;
|
|
31
|
+
if (fs.existsSync(src)) {
|
|
32
|
+
content = fs.readFileSync(src, { encoding: 'base64' });
|
|
33
|
+
src = local(src);
|
|
34
|
+
} else {
|
|
35
|
+
if (!schemaRegex.test(src)) {
|
|
36
|
+
throw new CliError(`Nonexistent path: ${src}`);
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
content = (await axios.get(src, { responseEncoding: 'base64' })).data;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
this.handleHttpError(error, params, src);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { src, content };
|
|
45
|
+
}
|
|
46
|
+
|
|
28
47
|
static getExtensionName(projectFolder) {
|
|
29
48
|
try {
|
|
30
49
|
return require(join(projectFolder, 'package.json')).name;
|
|
@@ -41,6 +60,6 @@ module.exports = class Push extends BaseCommand {
|
|
|
41
60
|
extension: tgz,
|
|
42
61
|
tag: extensionName
|
|
43
62
|
}, options)
|
|
44
|
-
.catch(error => this.handleHttpError(error, params));
|
|
63
|
+
.catch(error => this.handleHttpError(error, params, pushUrl));
|
|
45
64
|
}
|
|
46
65
|
}
|
|
@@ -3,8 +3,8 @@ const os = require('os');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const axios = require('axios');
|
|
5
5
|
|
|
6
|
-
const requireGlobal = require('./helper/dependency_helper');
|
|
7
|
-
|
|
6
|
+
const { requireGlobal } = require('./helper/dependency_helper');
|
|
7
|
+
let keytar;
|
|
8
8
|
|
|
9
9
|
const log = require('./helper/logging');
|
|
10
10
|
const { getAppFromSuggestions, getAppName, getSubdomain } = require('./helper/cf_helper');
|
|
@@ -159,13 +159,15 @@ class SettingsManager {
|
|
|
159
159
|
log.debug(`Saving settings for project ${params.get('projectFolder')}`);
|
|
160
160
|
|
|
161
161
|
const paramsMap = params.toValueMap(Persistence.setting);
|
|
162
|
-
if (
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
162
|
+
if (params.has('username')) {
|
|
163
|
+
// Save username only for localhost for security reasons.
|
|
164
|
+
if (localhostRegex.test(params.get('appUrl'))) {
|
|
165
|
+
console.log(`Saving username${(params.get('isEmptyPassword') ? ' and empty-password hint' : '')} with project settings.`);
|
|
166
|
+
} else {
|
|
167
|
+
log.debug('Not saving username because app is not recognized to run on localhost.');
|
|
168
|
+
delete paramsMap.username;
|
|
169
|
+
delete paramsMap.isEmptyPassword;
|
|
170
|
+
}
|
|
169
171
|
}
|
|
170
172
|
await this._saveToFile(params.get('projectFolder'), paramsMap);
|
|
171
173
|
|
|
@@ -196,6 +198,12 @@ class SettingsManager {
|
|
|
196
198
|
log.debug(`Loaded project settings for ${params.get('projectFolder')}: ${loadedSettings.format()}`);
|
|
197
199
|
const loadedAppUrl = loadedSettings.get('appUrl');
|
|
198
200
|
|
|
201
|
+
if (params.has('passcode') && loadedSettings.has('username')) {
|
|
202
|
+
console.log(`Unsetting username${(loadedSettings.has('isEmptyPassword') ? ' and empty-password hint' : '')}`);
|
|
203
|
+
loadedSettings.delete('username');
|
|
204
|
+
loadedSettings.delete('isEmptyPassword');
|
|
205
|
+
}
|
|
206
|
+
|
|
199
207
|
params.mergeLower(loadedSettings);
|
|
200
208
|
|
|
201
209
|
await this.updateUrls(params, logout, loadedAppUrl);
|
|
@@ -206,6 +214,12 @@ class SettingsManager {
|
|
|
206
214
|
|
|
207
215
|
static async updateUrls(params, logout, loadedAppUrl) {
|
|
208
216
|
async function addAppUrlAndSubdomain() {
|
|
217
|
+
function needsSubdomain() {
|
|
218
|
+
// Subdomain needed for token retrieval.
|
|
219
|
+
return params.has('appUrl') && !localhostRegex.test(params.get('appUrl')) && !params.has('username');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Prefix URL schema
|
|
209
223
|
if (params.has('appUrl')) {
|
|
210
224
|
const appUrl = params.get('appUrl');
|
|
211
225
|
if (!schemaRegex.test(appUrl)) {
|
|
@@ -217,11 +231,16 @@ class SettingsManager {
|
|
|
217
231
|
log.debug(`Added schema to app URL: ${params.get('appUrl')}`);
|
|
218
232
|
}
|
|
219
233
|
}
|
|
234
|
+
|
|
220
235
|
if (!logout) {
|
|
236
|
+
// Get appUrl from CF
|
|
221
237
|
if (!params.has('appUrl')) {
|
|
222
238
|
log.debug('App URL not given');
|
|
223
239
|
await getAppUrlAndSubdomainFromSuggestions(params);
|
|
224
|
-
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Get subdomain from CF
|
|
243
|
+
if (!params.has('subdomain') && needsSubdomain()) {
|
|
225
244
|
log.debug('Subdomain not given');
|
|
226
245
|
const appName = await getAppName(params.get('appUrl'));
|
|
227
246
|
if (appName) {
|
|
@@ -229,10 +248,14 @@ class SettingsManager {
|
|
|
229
248
|
}
|
|
230
249
|
}
|
|
231
250
|
}
|
|
251
|
+
|
|
252
|
+
// Check required param: appUrl
|
|
232
253
|
if (!params.has('appUrl')) {
|
|
233
254
|
throw new CliError('App URL not given. Please specify it or log in to Cloud Foundry and repeat this command.');
|
|
234
255
|
}
|
|
235
|
-
|
|
256
|
+
|
|
257
|
+
// Check required param: subdomain
|
|
258
|
+
if (!params.has('subdomain') && needsSubdomain()) {
|
|
236
259
|
const subdomain = extractSubdomain(params.get('appUrl'));
|
|
237
260
|
if (!subdomain) {
|
|
238
261
|
throw new CliError('Subdomain not given');
|
|
@@ -263,16 +286,25 @@ class SettingsManager {
|
|
|
263
286
|
}
|
|
264
287
|
return;
|
|
265
288
|
}
|
|
266
|
-
if (params.get('username')
|
|
289
|
+
if (params.get('username')) {
|
|
267
290
|
return;
|
|
268
291
|
}
|
|
269
|
-
|
|
292
|
+
keytar = requireGlobal('keytar');
|
|
293
|
+
if (keytar) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
// keytar missing
|
|
297
|
+
|
|
298
|
+
if (params.get('tokenStorage') === 'keyring' && params.get('saveData')) {
|
|
299
|
+
// Explicit login w/o plain-text storage enabled
|
|
270
300
|
throw new CliError(capitalize(TOKEN_STORAGE_DESC.keyring) + ' requested but keytar not installed. ' +
|
|
271
301
|
'Run `npm install -g keytar` or switch to ' + TOKEN_STORAGE_DESC.plain + ' by adding `--plain` (discouraged).');
|
|
272
302
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
303
|
+
|
|
304
|
+
const doLog = params.get('saveData') || logout
|
|
305
|
+
? console.log.bind(console) // Explicit login/logout call
|
|
306
|
+
: log.debug;
|
|
307
|
+
doLog(`Disabling ${TOKEN_STORAGE_DESC.keyring} functionality: keytar not found. Run \`npm install -g keytar\` to install it.`);
|
|
276
308
|
}
|
|
277
309
|
|
|
278
310
|
static async addAuth(params, logout) {
|
|
@@ -293,7 +325,7 @@ class SettingsManager {
|
|
|
293
325
|
}
|
|
294
326
|
async function addPasscode() {
|
|
295
327
|
const prompt = `Passcode${params.get('passcodeUrl') ? ' (visit ' + params.get('passcodeUrl') + ' to generate)' : ''}: `;
|
|
296
|
-
params.set('passcode', await Question.askQuestion(prompt, undefined, true));
|
|
328
|
+
params.set('passcode', (await Question.askQuestion(prompt, undefined, true)).trim());
|
|
297
329
|
console.log();
|
|
298
330
|
if (params.get('passcode') === '') {
|
|
299
331
|
throw new CliError('Passcode cannot be empty');
|
|
@@ -337,11 +369,10 @@ class SettingsManager {
|
|
|
337
369
|
}
|
|
338
370
|
if (params.get('password') === '') {
|
|
339
371
|
params.set('isEmptyPassword', true);
|
|
340
|
-
// TODO check password only in production env
|
|
341
|
-
// throw new CliError('Password cannot be empty');
|
|
342
372
|
} else {
|
|
343
373
|
params.delete('isEmptyPassword');
|
|
344
374
|
}
|
|
375
|
+
log.debug('Ignoring any saved authentication data because username is given');
|
|
345
376
|
return;
|
|
346
377
|
} else {
|
|
347
378
|
params.delete('isEmptyPassword');
|
|
@@ -358,7 +389,7 @@ class SettingsManager {
|
|
|
358
389
|
|
|
359
390
|
setRelevantAuth(auth);
|
|
360
391
|
|
|
361
|
-
if (!params.has('token') && !params.has('passcode') && !params.has('clientsecret')
|
|
392
|
+
if (!logout && !params.has('token') && !params.has('passcode') && !params.has('clientsecret')) {
|
|
362
393
|
await addPasscode();
|
|
363
394
|
}
|
|
364
395
|
}
|
|
@@ -368,7 +399,7 @@ class SettingsManager {
|
|
|
368
399
|
await this.loadAndMergeSettings(allParams, true);
|
|
369
400
|
|
|
370
401
|
const target = `URL ${allParams.get('appUrl')}, subdomain '${allParams.get('subdomain')}'`;
|
|
371
|
-
let fromStorage = fromOtherStorage && other(params.get('tokenStorage'))
|
|
402
|
+
let fromStorage = fromOtherStorage && other(params.get('tokenStorage'));
|
|
372
403
|
const deleteBoth = !fromStorage && allParams.get('tokenStorage') && allParams.get('clearOtherTokenStorage');
|
|
373
404
|
|
|
374
405
|
log.debug(`Deleting authentication data${fromOtherStorage ? ' from other storage' : ''} for ${target}`);
|
|
@@ -1830,7 +1830,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
|
|
|
1830
1830
|
schemas[schemaName].properties = schemaProperties;
|
|
1831
1831
|
|
|
1832
1832
|
if (suffix === SUFFIX.create && required.length > 0)
|
|
1833
|
-
schemas[schemaName].required = required;
|
|
1833
|
+
schemas[schemaName].required = [...new Set(required)];
|
|
1834
1834
|
|
|
1835
1835
|
const description = type[voc.Core.Description];
|
|
1836
1836
|
if (description) {
|
package/lib/compile/to-xsuaa.js
CHANGED
|
@@ -3,7 +3,7 @@ const fs = require('fs')
|
|
|
3
3
|
const path = require('path')
|
|
4
4
|
|
|
5
5
|
const hardcoded = {
|
|
6
|
-
Scopes: ['system-user', 'authenticated-user', 'registered-user', 'identified-user', 'internal-user', 'any'],
|
|
6
|
+
Scopes: ['system-user', 'authenticated-user', 'registered-user', 'identified-user', 'internal-user', 'privileged-user', 'any'],
|
|
7
7
|
Attributes: ['id', 'tenant', 'ext_attr']
|
|
8
8
|
}
|
|
9
9
|
|