@sap/cds-dk 6.0.4 → 6.1.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.
Files changed (97) hide show
  1. package/CHANGELOG.md +40 -1
  2. package/bin/add.js +29 -16
  3. package/bin/compile/index.js +6 -1
  4. package/bin/fix-redirects.js +4 -2
  5. package/bin/init.js +5 -0
  6. package/bin/login.js +8 -2
  7. package/bin/pull.js +58 -0
  8. package/bin/push.js +52 -0
  9. package/bin/subscribe.js +45 -0
  10. package/lib/bind/bindingUtil.js +1 -2
  11. package/lib/client/activate.js +1 -1
  12. package/lib/client/auth_manager.js +7 -3
  13. package/lib/client/extend.js +1 -1
  14. package/lib/client/helper/cf_helper.js +1 -0
  15. package/lib/client/helper/url_helper.js +7 -3
  16. package/lib/client/index.js +1 -1
  17. package/lib/client/params.js +4 -2
  18. package/lib/client/pull.js +51 -0
  19. package/lib/client/push.js +49 -0
  20. package/lib/client/settings_manager.js +74 -21
  21. package/lib/compile/openapi/csdl2openapi.js +135 -163
  22. package/lib/compile/openapi/index.js +0 -1
  23. package/lib/import/common.js +9 -1
  24. package/lib/import/odata/metadataconverter/MetadataConverter.js +17 -4
  25. package/lib/import/odata/v2parser.js +0 -1
  26. package/lib/import/odata/v4parser.js +103 -29
  27. package/lib/import/openapi/importOpenAPI.js +3 -3
  28. package/lib/init/constants.js +9 -3
  29. package/lib/init/template/_merging/registry-mta.js +290 -0
  30. package/lib/init/template/approuter/index.js +22 -32
  31. package/lib/init/template/auditlog/index.js +7 -9
  32. package/lib/init/template/extensibility/files/cds.package.json +7 -0
  33. package/lib/init/template/extensibility/files/cds.package.json.hbs +9 -0
  34. package/lib/init/template/extensibility/files/xs-security.json.hbs +26 -0
  35. package/lib/init/template/extensibility/index.js +48 -0
  36. package/lib/init/template/hana/files/java/cdsrc.json.hbs +1 -1
  37. package/lib/init/template/hana/files/java/profile.cdsrc.json.hbs +2 -2
  38. package/lib/init/template/hana/files/mta.yaml.hbs +10 -0
  39. package/lib/init/template/hana/files/nodejs/cds.package.json.hbs +1 -1
  40. package/lib/init/template/hana/files/nodejs/cds.profile.package.json.hbs +2 -2
  41. package/lib/init/template/hana/index.js +74 -61
  42. package/lib/init/template/kibana-logging/index.js +31 -37
  43. package/lib/init/template/lint/files/.eslint/rules/no-entity-moo.js +14 -14
  44. package/lib/init/template/mta/files/mta.yaml.hbs +1 -1
  45. package/lib/init/template/mta/index.js +13 -7
  46. package/lib/init/template/mtx/index.js +2 -142
  47. package/lib/init/template/multitenancy/files/cds.package.json +7 -0
  48. package/lib/init/template/multitenancy/files/cds.package.json.hbs +9 -0
  49. package/lib/init/template/{mtx → multitenancy}/files/dependencies.package.json +1 -1
  50. package/lib/init/template/multitenancy/files/java/.cdsrc.json +19 -0
  51. package/lib/init/template/multitenancy/files/java/package.json +31 -0
  52. package/lib/init/template/{mtx → multitenancy}/files/mta.yaml.hbs +13 -6
  53. package/lib/init/template/multitenancy/files/xs-security.json.hbs +18 -0
  54. package/lib/init/template/multitenancy/index.js +109 -0
  55. package/lib/init/template/nodejs/files/package.json.hbs +3 -0
  56. package/lib/init/template/templateBase.js +2 -1
  57. package/lib/init/template/toggles/files/cds.package.json +7 -0
  58. package/lib/init/template/toggles/files/cds.package.json.hbs +9 -0
  59. package/lib/init/template/toggles/index.js +24 -0
  60. package/lib/init/template/xsuaa/files/mta.yaml.hbs +10 -0
  61. package/lib/init/template/xsuaa/index.js +44 -33
  62. package/lib/init/util/projectReader.js +12 -7
  63. package/lib/init/util/templateUtil.js +112 -110
  64. package/lib/k8s/ChartAdder.js +11 -3
  65. package/lib/k8s/ChartBuilder.js +6 -0
  66. package/lib/k8s/buildModel.js +1 -3
  67. package/lib/k8s/connectivity/index.js +5 -5
  68. package/lib/k8s/deployment/index.js +2 -4
  69. package/lib/k8s/deployment/subchart/Chart.yaml +1 -1
  70. package/lib/k8s/deployment/subchart/templates/_helpers.tpl +22 -11
  71. package/lib/k8s/deployment/subchart/templates/deployment.yaml +12 -5
  72. package/lib/k8s/deployment/subchart/templates/network-policy.yaml +3 -1
  73. package/lib/k8s/deployment/subchart/templates/pod-disruption-budget.yaml +1 -1
  74. package/lib/k8s/deployment/subchart/values.md +812 -0
  75. package/lib/k8s/deployment/subchart/values.schema.json +125 -30
  76. package/lib/k8s/deployment/subchart/values.yaml +7 -3
  77. package/lib/k8s/hana-deployer/index.js +10 -0
  78. package/lib/k8s/hana-deployer/templates/hana-deployer-job.yaml +8 -0
  79. package/lib/k8s/html5-apps-deployer/index.js +10 -0
  80. package/lib/k8s/html5-apps-deployer/templates/html5-apps-deployer-job.yaml +8 -0
  81. package/lib/k8s/index.js +2 -2
  82. package/lib/k8s/resources/helm/chart/templates/_deployment_helpers.tpl +1 -159
  83. package/lib/k8s/resources/helm/chart/templates/_helpers.tpl +0 -33
  84. package/lib/k8s/resources/helm/chart/templates/_sapcp_helpers.tpl +1 -1
  85. package/lib/k8s/resources/helm/chart/values.schema.json +449 -0
  86. package/lib/lint/Linter.js +155 -158
  87. package/lib/lint/io.js +13 -5
  88. package/npm-shrinkwrap.json +510 -86
  89. package/package.json +5 -4
  90. package/lib/init/template/_merging/existences.js +0 -297
  91. package/lib/init/template/mtx/files/cds.package.json +0 -12
  92. package/lib/init/template/mtx/files/cds.package.json.hbs +0 -14
  93. package/lib/init/template/mtx/files/java/.cdsrc.json +0 -17
  94. package/lib/init/template/mtx/files/java/package.json +0 -43
  95. package/lib/init/template/mtx/files/java/server.js +0 -30
  96. package/lib/init/template/mtx/files/xs-security.json.hbs +0 -48
  97. package/lib/lint/.eslintrc.js +0 -3
package/CHANGELOG.md CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  # Change Log
2
3
 
3
4
  All notable changes to this project will be documented in this file.
@@ -6,6 +7,43 @@ This project adheres to [Semantic Versioning](http://semver.org/).
6
7
 
7
8
  The format is based on [Keep a Changelog](http://keepachangelog.com/).
8
9
 
10
+ ## Version 6.1.1 - 2022-08-11
11
+
12
+ ### Added
13
+
14
+ - `cds import` now supports OData and SAP annotations for OData V4 imports.
15
+ - `cds compile --to openapi` defines operation-specific HTTP error response status codes with descriptions via `ErrorResponses` property of certain annotations.
16
+ - `cds compile --to openapi` now supports `--openapi:servers` option.
17
+ - `cds add multitenancy` will add feature multitenancy-specific configuration, without extensibility.
18
+ - `cds add toggles` will add feature toggle-specific configuration.
19
+ - `cds add extensibility` will add configuration for project extensibility.
20
+ - `cds add helm` now supports resource configuration for HANA deployment job and HTML5 app deployment job.
21
+ - `cds add helm` added JSON Schema for values.yaml
22
+ - `cds pull` will download the current CDS model of an extended SaaS app running with @sap/cds-mtxs.
23
+ - `cds push` will upload an extension to a SaaS app running with @sap/cds-mtxs.
24
+
25
+ ### Changed
26
+
27
+ - `cds add helm` updated default resource requirements for both `java` and `nodejs` projects.
28
+ - `cds add helm` uses servicebinding.io bindings for CAP Java services, HANA and HTML5 app deployment jobs.
29
+ - `cds compile --to openapi` creates only component schemas for schemas referenced in operations and in other schemas.
30
+ - `cds import` switch from `@openapi.schema` to `@JSON.Schema`.
31
+ - `cds add mtx` will now add configuration for streamlined MTX. It effectively acts as a shortcut for `cds add multitenancy,toggles,extensibility`
32
+ - `cds add mtx` no longer includes `hana` and `xsuaa`. To achieve the same effect as before, run `cds add mtx && cds add hana,xsuaa --for production`.
33
+ - `cds bind -2 <xsuaa service instance>` binds the CDS `auth` service to the xsuaa instance. Previously `uaa` was used. This requires `@sap/cds` 6 or higher.
34
+ - `cds add lint:dev` updated to adjust to new api structure of `@sap/eslint-plugin-cds` v2.5.0
35
+ - `cds init` uses latest Maven Java archetype version 1.27.0 for creating Java projects.
36
+ - `cds login localhost:<port> -u <username>` now saves username (and empty password, if applicable) with project settings for convenience.
37
+
38
+ ### Fixed
39
+
40
+ - `cds add helm:connectivity`: `connectivity.configMapName` was not used for the `connectivity-proxy-info`.
41
+ - `cds import` replaced occurrences of `\\` with `/` in the `package.json` for Linux platforms.
42
+ - `cds import` fixed `@Core.Description` and `doc` property duplication.
43
+ - `cds extend` and `cds activate` no longer save any data (this is reserved to `cds login`).
44
+ - Extensibility commands now add http (not https) to local app URLs without schema.
45
+ - Extensibility commands don't query CF any longer when run against local apps.
46
+
9
47
  ## Version 6.0.4 - 2022-08-02
10
48
 
11
49
  ### Changed
@@ -14,7 +52,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
14
52
  - Use `@sap/cds` 6.0.4
15
53
  - `cds bind` handles orgs and spaces containing commas correctly.
16
54
 
17
-
18
55
  ## Version 6.0.3 - 2022-07-14
19
56
 
20
57
  ### Changed
@@ -24,6 +61,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
24
61
 
25
62
  ### Fixed
26
63
 
64
+ - `--vap-file` parameter of `cds deploy` is available again (removed in 6.0.0)
65
+ - `cds add helm:connectivity`: `connectivity.configMapName` was not used for the `connectivity-proxy-info`.
27
66
  - `--vcap-file` parameter of `cds deploy` is available again (removed in 6.0.0)
28
67
  - `cds add helm:connectivity`: `connectivity.configMapName` was not used for the `connectivity-proxy-info`.
29
68
  - `cds add helm:connectivity`: Environment variables added for connectivity service for Java.
package/bin/add.js CHANGED
@@ -13,30 +13,40 @@ module.exports = Object.assign(add, {
13
13
 
14
14
  The following <features> are supported so far:
15
15
 
16
- *hana* - adds configuration for SAP HANA deployment
16
+ *hana* - adds configuration for SAP HANA deployment
17
17
 
18
- *xsuaa* - adds configuration for authentication via XSUAA
18
+ *xsuaa* - adds configuration for authentication via XSUAA
19
19
 
20
- *mtx* - adds configuration for multitenancy and extensibility
20
+ *multitenancy* - adds configuration for multitenancy
21
21
 
22
- *approuter* - adds configuration for the Cloud Foundry approuter
22
+ *toggles* - adds configuration for feature toggles
23
23
 
24
- *mta* - adds an _mta.yaml_ file for MTA-based SAP Business Technology Platform deployment
24
+ *extensibility* - adds configuration for extensibility
25
25
 
26
- *cf-manifest* - adds _manifest.yml_ and _services-manifest.yml_ files for Cloud Foundry native deployment
26
+ *mtx* - adds configuration for multitenancy, feature toggles and extensibility
27
27
 
28
- This approach makes use of the 'Create-Service-Push Plugin' that has to be installed
29
- separately using _cf install-plugin Create-Service-Push_.
30
- _cf create-service-push_ creates the service(s) and pushes the application(s) to the
31
- SAP Business Technology Platform.
28
+ *approuter* - adds configuration for the Cloud Foundry approuter
32
29
 
33
- *pipeline* - adds files for CI/CD pipeline integration
30
+ *mta* - adds an _mta.yaml_ file for MTA-based SAP Business Technology Platform deployment
34
31
 
35
- *samples* - adds simple sample files
32
+ *cf-manifest* - adds _manifest.yml_ and _services-manifest.yml_ files for Cloud Foundry native deployment
36
33
 
37
- *notebook* - adds a Jupyter Notebook (beta)
34
+ This approach makes use of the 'Create-Service-Push Plugin' that has to be installed
35
+ separately using _cf install-plugin Create-Service-Push_.
36
+ _cf create-service-push_ creates the service(s) and pushes the application(s) to the
37
+ SAP Business Technology Platform.
38
+
39
+ *pipeline* - adds files for CI/CD pipeline integration
40
+
41
+ *samples* - adds simple sample files
42
+
43
+ *data* - adds data content for CSN model entities (as csv files)
44
+
45
+ *helm* - adds helm chart folder that contains the files needed to deploy the application using Kyma.
46
+
47
+ To add a specific feature in the helm chart you can use the command 'cds add helm:<featureName>'.
48
+ For example cds add helm:xsuaa and cds add helm:html5_apps_deployer.
38
49
 
39
- *data* - adds data content for CSN model entities (as csv files)
40
50
 
41
51
  # OPTIONS
42
52
 
@@ -56,10 +66,13 @@ module.exports = Object.assign(add, {
56
66
 
57
67
  # EXAMPLES
58
68
 
59
- *cds add* hana,mta,pipeline
60
- *cds add* mtx --for production
69
+ *cds add* mta,pipeline
70
+ *cds add* mtx
71
+ *cds add* hana,xsuaa --for production
61
72
  *cds add* cf-manifest
62
73
  *cds add* data --for my.namspace.MyEntity
74
+ *cds add* helm
75
+
63
76
 
64
77
  # SEE ALSO
65
78
 
@@ -1,6 +1,6 @@
1
1
  module.exports = Object.assign ( compile, {
2
2
  options: [
3
- '--from', '--service', '--lang', '--for', '--to', '--dest', '--log-level', '--flavor', '--dialect', '--sql-dialect', '--openapi:url'
3
+ '--from', '--service', '--lang', '--for', '--to', '--dest', '--log-level', '--flavor', '--dialect', '--sql-dialect', '--openapi:url', '--openapi:servers'
4
4
  ],
5
5
  shortcuts: [
6
6
  undefined, '-s', '-l', '-4', '-2', '-o', undefined, '-f'
@@ -105,6 +105,11 @@ module.exports = Object.assign ( compile, {
105
105
  base path as declared in the CDS source.
106
106
  Use the \${service-path} variable to have the service path included in the URL.
107
107
 
108
+ *--openapi:servers* <Stringified JSON Object for Open API export>
109
+
110
+ The servers definition used in the generated OpenAPI document. *--openapi:url* is
111
+ ignored when this option is specified.
112
+
108
113
  *--openapi:diagram*
109
114
 
110
115
  Include YUML diagram in the generated OpenAPI document, default: *false*.
@@ -1,5 +1,6 @@
1
1
  const [arg0, arg1, _, ...args] = process.argv // eslint-disable-line no-unused-vars
2
- const files = require('../lib/cds').resolve(args.length ? args : '*')
2
+ const cds = require('../lib/cds')
3
+ const files = cds.resolve(args.length ? args : '*')
3
4
  process.argv = [ arg0, arg1, 'ria', ...files ]
4
5
 
5
6
  console.log ('[cds] - searching for ambiguous redirects in', files)
@@ -7,6 +8,7 @@ const log = console.log; console.log = (...args) => {
7
8
  if (!log.found) log (log.found = '> found ambiguous redirects in these models -> please fix as follows:\n')
8
9
  log (...args)
9
10
  }
10
- require ('@sap/cds-compiler/bin/cdsv2m')
11
+
12
+ require(require.resolve ('@sap/cds-compiler/bin/cdsv2m', {paths: [ cds.home, process.cwd(), __dirname ]}))
11
13
 
12
14
  module.exports = ()=>{}
package/bin/init.js CHANGED
@@ -50,6 +50,11 @@ module.exports = Object.assign(init, {
50
50
 
51
51
  *notebook* - adds a Jupyter Notebook (beta)
52
52
 
53
+ *helm* - adds helm chart folder that contains the files needed to deploy the application using Kyma.
54
+
55
+ To add a specific feature in the helm chart you can use the command 'cds add helm:<featureName>'.
56
+ For example cds add helm:xsuaa and cds add helm:html5_apps_deployer.
57
+
53
58
  *--java:mvn* <Comma separated maven archetype specific parameters>
54
59
 
55
60
  Add the given parameters to the archetype call.
package/bin/login.js CHANGED
@@ -71,6 +71,11 @@ module.exports = Object.assign(login, {
71
71
 
72
72
  Tenant-specific passcode for authentication.
73
73
 
74
+ *-u* | *--username* <name>
75
+
76
+ Username for authentication with Basic Auth in test scenarios. For security reasons,
77
+ username will only be saved for app URLs that look like localhost.
78
+
74
79
  *-c* | *--clientid* <clientid>
75
80
 
76
81
  Client ID for authentication. This will override the default Client ID from environment.
@@ -103,11 +108,12 @@ async function login ([url], options = {}) {
103
108
  Object.assign(options, {
104
109
  url,
105
110
  tokenStorage: options.plain ? 'plain' : 'keyring',
106
- renewLogin: true
111
+ renewLogin: true,
112
+ saveData: true
107
113
  })
108
114
  delete options.plain
109
115
 
110
- await require('../lib/client/auth_manager').loginAndUpdateSettings(options)
116
+ await require('../lib/client/auth_manager').login(options)
111
117
  }
112
118
 
113
119
 
package/bin/pull.js ADDED
@@ -0,0 +1,58 @@
1
+ module.exports = Object.assign(pull, {
2
+ options: ['--directory', '--subdomain', '--passcode', '--username', '--clientid'],
3
+ flags: [],
4
+ shortcuts: ['-d', '-s', '-p', '-u', '-c'],
5
+ help: `
6
+ # SYNOPSIS
7
+
8
+ *cds pull* [ <app-url> ]
9
+
10
+ Pull the base model for your SaaS app extension.
11
+
12
+ To run this command, you need to obtain from the SaaS application provider:
13
+ - the app URL
14
+ - a template for your extension project.
15
+
16
+ Then create your extension project from the template and run *cds pull* to update your copy of the CDS model of the
17
+ SaaS app (also known as the base model).
18
+
19
+ To facilitate running this multiple times against the same app, refer to the *cds login* command.
20
+
21
+ If the app URL is omitted, it is determined from the current user's active CDS or Cloud Foundry login.
22
+
23
+
24
+ # OPTIONS
25
+
26
+ *-d* | *--directory* <project-extension-directory>
27
+
28
+ Specify the target directory of your extension project. By default, the current working directory is used.
29
+
30
+ *-s* | *--subdomain* <tenant-subdomain>
31
+
32
+ Specify your tenant's subdomain. By default, it's determined from the app URL.
33
+
34
+ *-p* | *--passcode* <passcode>
35
+
36
+ Tenant-specific passcode for authentication.
37
+
38
+ *-u* | *--username* <name>
39
+
40
+ Username for authentication with Basic Auth in test scenarios.
41
+
42
+ *-c* | *--clientid* <clientid>
43
+
44
+ Client ID for authentication. This will override the default Client ID from environment.
45
+
46
+ # SEE ALSO
47
+
48
+ *cds push* to push an extension to the SaaS app.
49
+
50
+ `})
51
+
52
+
53
+ async function pull([url], options = {}) {
54
+ await require('../lib/client/pull').run({
55
+ ...options,
56
+ url
57
+ })
58
+ }
package/bin/push.js ADDED
@@ -0,0 +1,52 @@
1
+ module.exports = Object.assign(push, {
2
+ options: ['--subdomain', '--passcode', '--username', '--clientid', '--to'],
3
+ flags: [],
4
+ shortcuts: ['-s', '-p', '-u', '-c'],
5
+ help: `
6
+ # SYNOPSIS
7
+
8
+ *cds push* [ <extension-project-directory> ]
9
+
10
+ Push your extension to the SaaS app in order to enable or update it.
11
+
12
+ By default, the current working directory is used as project directory.
13
+
14
+ To facilitate running this multiple times against the same app, refer to the *cds login* command.
15
+
16
+
17
+ # OPTIONS
18
+
19
+ *--to* <url>
20
+
21
+ Specify the URL of the extensible SaaS app. By default, it is determined from
22
+ the current user's active CDS or Cloud Foundry login.
23
+
24
+ *-s* | *--subdomain* <tenant-subdomain>
25
+
26
+ Specify your tenant's subdomain. By default, it's determined from project settings.
27
+
28
+ *-p* | *--passcode* <passcode>
29
+
30
+ Tenant-specific passcode for authentication.
31
+
32
+ *-u* | *--username* <name>
33
+
34
+ Username for authentication with Basic Auth in test scenarios.
35
+
36
+ *-c* | *--clientid* <clientid>
37
+
38
+ Client ID for authentication. This will override the default Client ID from environment.
39
+
40
+ # SEE ALSO
41
+
42
+ *cds pull* to get the CDS model of the SaaS app.
43
+
44
+ `})
45
+
46
+
47
+ async function push([projectFolder], options = {}) {
48
+ await require('../lib/client/push').run({
49
+ ...options,
50
+ projectFolder
51
+ })
52
+ }
@@ -0,0 +1,45 @@
1
+ module.exports = Object.assign(subscribe, {
2
+ options: ['--to', '--user'],
3
+ flags: [],
4
+ shortcuts: ['-2', '-u'],
5
+ help: `
6
+ # SYNOPSIS
7
+
8
+ *cds subscribe* <tenant>
9
+
10
+ Subscribe a tenant to a multi-tenant SaaS app.
11
+
12
+ *cds subscribe* only supports Basic Authentication and is intended for non-productive use,
13
+ including SaaS apps running on localhost with mock authentication.
14
+
15
+ # OPTIONS
16
+
17
+ *-2* | *--to* <url>
18
+
19
+ Specify the URL of the extensible SaaS app. Required setting.
20
+
21
+ *-u* | *--user* <name>[:<password>]
22
+
23
+ Username and optionally password for authentication. Required setting.
24
+ See https://cap.cloud.sap/docs/guides/multitenancy/#send-subscribe-events for information on suitable mock users.
25
+
26
+ If password is omitted, it is assumed empty.
27
+ Note: productive passwords should not be specified on the command line.
28
+
29
+ `})
30
+
31
+
32
+
33
+ async function subscribe([tenant], { to: url, user } = {}) {
34
+ const Logger = require('../lib/client/helper/logging');
35
+
36
+ if (!tenant) throw new Error('Must provide a tenant')
37
+ if (!url) throw new Error('Must provide an app URL via `--to`') // for now, if we don't infer it from the project
38
+ if (typeof user !== 'string') throw new Error('Must provide a username')
39
+ const [, username, , password = ''] = /([^:]+)(:(.*))?/.exec(user);
40
+ Logger.log(`Subscribing to ${url} for tenant '${tenant}' with user '${username}'${password === '' ? ' and empty password' : ''}`);
41
+ const options = { auth: { username, password } };
42
+ return require('axios').post (url+'/-/cds/deployment/subscribe', { tenant }, options);
43
+ }
44
+
45
+ // cds subscribe t1 --to http://localhost:4004 -u yves
@@ -3,8 +3,7 @@ const os = require('os');
3
3
  const IS_WIN = os.platform() === 'win32';
4
4
 
5
5
  const KindToRequiresNameMap = {
6
- xsuaa: 'uaa',
7
- 'xsuaa-auth': 'uaa',
6
+ xsuaa: 'auth',
8
7
  'hana-cloud': 'db',
9
8
  hana: 'db',
10
9
  destinations: 'destinations',
@@ -42,7 +42,7 @@ module.exports = class Activate extends BaseCommand {
42
42
 
43
43
  static async run(injection, paramValues) {
44
44
  try {
45
- const params = await AuthManager.loginAndUpdateSettings(paramValues);
45
+ const params = await AuthManager.login(paramValues);
46
46
 
47
47
  if (process.env.DEBUG) {
48
48
  Logger.log('Activating extension with parameters ' + JSON.stringify({
@@ -77,7 +77,7 @@ async function retrieveTokenOrPasscodeUrl(params) {
77
77
 
78
78
  module.exports = class AuthManager {
79
79
 
80
- static async loginAndUpdateSettings(paramValues) {
80
+ static async login(paramValues) {
81
81
  SettingsManager.init();
82
82
  const params = new ParamCollection(paramValues);
83
83
  await SettingsManager.loadAndMergeSettings(params);
@@ -86,7 +86,9 @@ module.exports = class AuthManager {
86
86
  } else {
87
87
  await retrieveTokenOrPasscodeUrl(params);
88
88
  if (!params.has('token')) {
89
- await SettingsManager.saveSettings(params);
89
+ if (params.get('saveData')) {
90
+ await SettingsManager.saveSettings(params);
91
+ }
90
92
  throw new Error('Failed to login: no valid token or passcode provided. ' +
91
93
  'Get a passcode' + (params.get('passcodeUrl') ? ' from ' + params.get('passcodeUrl') : '') +
92
94
  ' and supply the code when re-running the command');
@@ -94,7 +96,9 @@ module.exports = class AuthManager {
94
96
  params.set('reqAuth', { headers: { Authorization: 'Bearer ' + params.get('token') } });
95
97
  }
96
98
 
97
- await SettingsManager.saveSettings(params); // saves token conditionally
99
+ if (params.get('saveData')) {
100
+ await SettingsManager.saveSettings(params); // saves token conditionally
101
+ }
98
102
 
99
103
  return params;
100
104
  }
@@ -71,7 +71,7 @@ module.exports = class Extend extends BaseCommand {
71
71
 
72
72
  static async run(paramValues) {
73
73
  try {
74
- const params = await AuthManager.loginAndUpdateSettings(paramValues);
74
+ const params = await AuthManager.login(paramValues);
75
75
 
76
76
  if (process.env.DEBUG) {
77
77
  Logger.log(`\n\nCreating extension project with parameters ${JSON.stringify({
@@ -33,6 +33,7 @@ select one by entering a number or leave empty to quit:
33
33
  ${formatObjectsFixedWidth(apps, ['name', 'url'], 3, { numbering: true })}
34
34
 
35
35
  Your choice: `);
36
+ console.log();
36
37
 
37
38
  if (!choice) {
38
39
  Logger.debug('No app URL selected');
@@ -1,10 +1,12 @@
1
1
  const IP_ADDR = /^[1-9][0-9]{0,2}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/;
2
2
  const schemaRegex = /^https?:\/\//;
3
- const defaultSchema = 'https://';
3
+ const localhostRegex = /^(https?:\/\/)?(localhost|127\.0\.0\.1|\[?::1]?)\b/;
4
+ const httpSchema = 'http://';
5
+ const httpsSchema = 'https://';
4
6
 
5
7
  function extractSubdomain(url) {
6
8
  const hostname = new URL(url).hostname;
7
- if (hostname === 'localhost' || IP_ADDR.test(hostname)) {
9
+ if (localhostRegex.test(hostname) || IP_ADDR.test(hostname)) {
8
10
  return undefined;
9
11
  }
10
12
  return hostname.split('.')[0];
@@ -12,6 +14,8 @@ function extractSubdomain(url) {
12
14
 
13
15
  module.exports = {
14
16
  schemaRegex,
15
- defaultSchema,
17
+ localhostRegex,
18
+ httpSchema,
19
+ httpsSchema,
16
20
  extractSubdomain
17
21
  };
@@ -7,7 +7,7 @@ const { SettingsManager } = require('./settings_manager');
7
7
  module.exports = {
8
8
  extend: Extend.run,
9
9
  activate: Activate.run,
10
- login: AuthManager.loginAndUpdateSettings,
10
+ login: AuthManager.login,
11
11
  logout: AuthManager.logout,
12
12
  question: Question,
13
13
  config: SettingsManager.config
@@ -163,11 +163,13 @@ class BasicParams {
163
163
 
164
164
  // - auth-related
165
165
  this.passcode = new Param('passcode', '', { obfuscate: ObfuscationLevel.partial });
166
- this.tokenStorage = new Param('tokenStorage', undefined, { allowedValues: ['plain', 'keyring'] });
166
+ this.tokenStorage = new Param('tokenStorage', undefined, { allowedValues: ['plain', 'keyring'] });
167
167
  this.plain = new Param('plain', false, { internal: true }); // for backward compatibility - ignored
168
+ this.saveData = new Param('saveData', false, { internal: true });
168
169
  this.renewLogin = new Param('renewLogin', false);
169
- this.username = new Param('username', '');
170
+ this.username = new Param('username', '', { persist: Persistence.setting }); // only saved against localhost (non-productive); as setting due to ambiguous app URL
170
171
  this.password = new Param('password', '', { obfuscate: ObfuscationLevel.full });
172
+ this.isEmptyPassword = new Param('isEmptyPassword', false, { internal: true, persist: Persistence.setting }); // extra param so we can ensure we never save a password
171
173
  this.clientid = new Param('clientid', '');
172
174
  this.clientsecret = new Param('clientsecret', '', { obfuscate: ObfuscationLevel.partial });
173
175
 
@@ -0,0 +1,51 @@
1
+ const fs = require('fs');
2
+ const { join } = require('path');
3
+ const axios = require('axios');
4
+
5
+ const { unpackTarArchive } = require('@sap/cds/lib/utils/resources');
6
+
7
+ const { login } = require('./auth_manager');
8
+ const BaseCommand = require('./base_command');
9
+ const Logger = require('./helper/logging');
10
+
11
+ module.exports = class Pull extends BaseCommand {
12
+
13
+ static async run(paramValues) {
14
+ try {
15
+ const params = await login(paramValues);
16
+
17
+ Logger.log(`Pulling base model from ${params.get('appUrl')}, for subdomain '${params.get('subdomain')}', to project folder '${params.get('projectFolder')}'`);
18
+
19
+ const projectFolder = params.get('projectFolder');
20
+ const targetDir = join(projectFolder, 'node_modules', this.getAppPackageName(projectFolder));
21
+ fs.rmSync(targetDir, { force: true, recursive: true });
22
+ fs.mkdirSync(targetDir, { recursive: true });
23
+
24
+ const tgzBuffer = Buffer.from(await this.getTgz(params));
25
+ return unpackTarArchive(tgzBuffer, targetDir, false);
26
+
27
+ } catch (mainError) {
28
+ Logger.error(undefined, mainError);
29
+ }
30
+ }
31
+
32
+ static getAppPackageName(projectFolder) {
33
+ try {
34
+ return require(join(projectFolder, 'package.json')).cds.extends;
35
+ } catch (error) {
36
+ return '_base';
37
+ }
38
+ }
39
+
40
+ static async getTgz(params) {
41
+ const pullUrl = `${(params.get('appUrl'))}/-/cds/extensibility/pull`;
42
+ const options = { ...params.get('reqAuth'), responseType: 'arraybuffer' };
43
+
44
+ try {
45
+ const response = await axios.post(pullUrl, {}, options);
46
+ return response.data;
47
+ } catch (error) {
48
+ return this.handleHttpError(error, params);
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,49 @@
1
+ const fs = require('fs');
2
+ const { join } = require('path');
3
+ const axios = require('axios');
4
+
5
+ const { login } = require('./auth_manager');
6
+ const BaseCommand = require('./base_command');
7
+ const Logger = require('./helper/logging');
8
+
9
+ module.exports = class Push extends BaseCommand {
10
+
11
+ static async run(paramValues) {
12
+ try {
13
+ const params = await login(paramValues);
14
+
15
+ const projectFolder = params.get('projectFolder');
16
+ Logger.log(`Pushing extension from project folder '${projectFolder}', for subdomain '${params.get('subdomain')}', to app URL ${params.get('appUrl')}`);
17
+
18
+ const tgzPath = join(projectFolder, 'gen', 'extension.tgz');
19
+ const tgz = fs.readFileSync(tgzPath, { encoding: 'base64' });
20
+ const extensionName = this.getExtensionName(projectFolder);
21
+ return this.pushTgz(params, tgz, extensionName);
22
+
23
+ } catch (mainError) {
24
+ Logger.error(undefined, mainError);
25
+ }
26
+ }
27
+
28
+ static getExtensionName(projectFolder) {
29
+ try {
30
+ return require(join(projectFolder, 'package.json')).name;
31
+ } catch (error) {
32
+ throw new Error(`Extension project at ${projectFolder} is missing package.json`);
33
+ }
34
+ }
35
+
36
+ static async pushTgz(params, tgz, extensionName) {
37
+ const pushUrl = `${params.get('appUrl')}/-/cds/extensibility/push`;
38
+ const options = { ...params.get('reqAuth') };
39
+
40
+ try {
41
+ return axios.post(pushUrl, {
42
+ extension: tgz,
43
+ tag: extensionName
44
+ }, options);
45
+ } catch (error) {
46
+ return this.handleHttpError(error, params);
47
+ }
48
+ }
49
+ }