@sap/cds-dk 9.8.4 → 9.9.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.
Files changed (74) hide show
  1. package/CHANGELOG.md +18 -2
  2. package/bin/add.js +3 -3
  3. package/bin/cds.js +16 -2
  4. package/bin/import.js +6 -2
  5. package/bin/logout.js +9 -12
  6. package/lib/bind/shared.js +2 -2
  7. package/lib/build/constants.js +0 -1
  8. package/lib/build/index.js +420 -463
  9. package/lib/build/plugins/hana/index.js +121 -2
  10. package/lib/build/plugins/helm/files/subcharts/content-deployment/Chart.yaml +1 -1
  11. package/lib/build/plugins/helm/files/subcharts/content-deployment/templates/job.yaml +1 -1
  12. package/lib/build/plugins/helm/files/subcharts/content-deployment/values.schema.json +4 -0
  13. package/lib/build/plugins/helm/files/subcharts/content-deployment/values.yaml +5 -2
  14. package/lib/build/plugins/internal.js +1 -1
  15. package/lib/build/plugins/mtx-extension/index.js +0 -2
  16. package/lib/deploy/index.js +23 -3
  17. package/lib/deploy/to-hana/hana.js +24 -15
  18. package/lib/import/common.js +6 -3
  19. package/lib/init/constants.js +3 -1
  20. package/lib/init/indent.js +0 -1
  21. package/lib/init/merge.js +0 -1
  22. package/lib/init/mvn.js +4 -2
  23. package/lib/init/projectReader.js +0 -1
  24. package/lib/init/registries/mta.js +4 -2
  25. package/lib/init/template/app-frontend/index.js +39 -4
  26. package/lib/init/template/data/as-csv.js +2 -0
  27. package/lib/init/template/esm/index.js +5 -0
  28. package/lib/init/template/extensibility/index.js +1 -1
  29. package/lib/init/template/hana/index.js +1 -1
  30. package/lib/init/template/html5-repo/files/mta.yaml.hbs +9 -20
  31. package/lib/init/template/http/index.js +34 -6
  32. package/lib/init/template/kyma/index.js +1 -0
  33. package/lib/init/template/postgres/files/values.yaml.hbs +1 -1
  34. package/lib/init/template/postgres/index.js +1 -1
  35. package/lib/init/template/react/files/index.html +13 -0
  36. package/lib/init/template/react/files/package.json.hbs +22 -0
  37. package/lib/init/template/react/files/public/favicon.svg +1 -0
  38. package/lib/init/template/react/files/src/App.jsx.hbs +29 -0
  39. package/lib/init/template/react/files/src/index.css +28 -0
  40. package/lib/init/template/react/files/src/main.jsx +10 -0
  41. package/lib/init/template/react/files/vite.config.js +19 -0
  42. package/lib/init/template/react/index.js +29 -0
  43. package/lib/init/template/sample/index.js +1 -4
  44. package/lib/init/template/typer/index.js +1 -1
  45. package/lib/init/template/typescript/files/package.json +1 -1
  46. package/lib/init/template/typescript/index.js +1 -1
  47. package/lib/init/template/ui5/index.js +2 -1
  48. package/lib/init/template/vue/files/index.html +13 -0
  49. package/lib/init/template/vue/files/package.json.hbs +19 -0
  50. package/lib/init/template/vue/files/public/favicon.svg +1 -0
  51. package/lib/init/template/vue/files/src/App.vue.hbs +34 -0
  52. package/lib/init/template/vue/files/src/main.js +5 -0
  53. package/lib/init/template/vue/files/src/style.css +20 -0
  54. package/lib/init/template/vue/files/vite.config.js +19 -0
  55. package/lib/init/template/vue/index.js +29 -0
  56. package/lib/init/template/workzone-standard/files/mta.yaml.hbs +2 -13
  57. package/lib/init/xml.js +0 -2
  58. package/lib/mtx/auth_manager.js +21 -11
  59. package/lib/mtx/download_legacy.js +9 -8
  60. package/lib/mtx/pull.js +9 -8
  61. package/lib/mtx/push.js +42 -28
  62. package/lib/mtx/settings_manager.js +17 -10
  63. package/lib/mtx/tenant_action.js +9 -9
  64. package/lib/mtx/util/request.js +158 -0
  65. package/lib/util/cf.js +30 -29
  66. package/lib/watch/node-watch.js +1 -1
  67. package/lib/watch/vite-watch.js +76 -0
  68. package/lib/watch/watched.js +5 -4
  69. package/npm-shrinkwrap.json +14 -154
  70. package/package.json +1 -2
  71. package/lib/init/template/sample/files/db/hana/index.cds +0 -10
  72. package/lib/init/template/sample/files/db/sqlite/index.cds +0 -10
  73. package/lib/init/template/sample/files/package.json.hbs +0 -14
  74. package/lib/util/axios.js +0 -118
@@ -21,7 +21,7 @@ module.exports = class PostgresTemplate extends require('../../plugin') {
21
21
  const project = readProject()
22
22
  const { configFile, isJava } = project
23
23
  project.shortcut = !exists(configFile) || typeof (await read(configFile)).cds?.requires?.db !== 'object'
24
- await merge(__dirname, 'files/package.json.hbs').into(configFile, { with: project, forceOverwrite: true }) // REVISIT: forceOverwrite: true as default?
24
+ await merge(__dirname, 'files/package.json.hbs').into(configFile, { with: project, forceOverwrite: true })
25
25
  if (isJava) await mvn.add('postgresql')
26
26
 
27
27
  await sort('package.json', 'dependencies')
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>catalog</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.jsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "{{app}}",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "react": "^19",
13
+ "react-dom": "^19"
14
+ },
15
+ "devDependencies": {
16
+ "@types/react": "^19",
17
+ "@types/react-dom": "^19",
18
+ "@vitejs/plugin-react": "^6",
19
+ "adm-zip": "^0",
20
+ "vite": "^8"
21
+ }
22
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 56 56" aria-hidden="true"><path fill="#5A7A94" d="M45.84 21.462a8.489 8.489 0 0 0-12.783-5.713A10.747 10.747 0 0 0 22.41 8.5C10 8.5 11.09 21.488 11.09 21.488A10.3 10.3 0 0 0 13 41.5v-3a7.477 7.477 0 0 1-5.639-5 7.247 7.247 0 0 1 4.666-9.164l2.279-.768-.286-1.5A7.521 7.521 0 0 1 13.9 20a10.535 10.535 0 0 1 2.188-5.828 7.375 7.375 0 0 1 6.4-2.824c3.264 0 7.286 2.9 8.221 5.636l.934 2.732 2.424-1.318a7.6 7.6 0 0 1 3.39-.894 5.546 5.546 0 0 1 5.443 4.518l.288 1.528 1.125.514A7.809 7.809 0 0 1 48.891 29.7 7.809 7.809 0 0 1 43 38.5v3.02A10.793 10.793 0 0 0 52 31a10.483 10.483 0 0 0-6.16-9.538"/><g fill="#0092D1" transform="scale(1.05 1.2)"><g transform="translate(-2 -7)"><path d="M24.561 38.939l-6-6a1.5 1.5 0 0 0-2.122 2.122L21.379 40l-4.94 4.939a1.5 1.5 0 0 0 2.122 2.122l6-6a1.5 1.5 0 0 0 0-2.122"/><path d="M26.5 35.5h12a1.5 1.5 0 0 0 0-3h-12a1.5 1.5 0 0 0 0 3"/><path d="M38.5 38.5h-9a1.5 1.5 0 0 0 0 3h9a1.5 1.5 0 0 0 0-3"/><path d="M38.5 44.5h-12a1.5 1.5 0 0 0 0 3h12a1.5 1.5 0 0 0 0-3"/></g></g></svg>
@@ -0,0 +1,29 @@
1
+ import { useEffect, useState } from 'react'
2
+
3
+ export default function App() {
4
+ const [books, setBooks] = useState([])
5
+
6
+ useEffect(() => {
7
+ fetch('/odata/v4/{{#isJava}}CatalogService{{/isJava}}{{^isJava}}catalog{{/isJava}}/Books')
8
+ .then(r => r.json())
9
+ .then(r => setBooks(r.value))
10
+ }, [])
11
+
12
+ return (
13
+ <>
14
+ <h1>Books</h1>
15
+ <table>
16
+ <tbody>
17
+ {books.map(b => (
18
+ <tr key={b.ID}>
19
+ <td>
20
+ <span className="title">{b.title}</span>{{^isJava}}
21
+ <span className="author"><i> by {b.author}</i></span>{{/isJava}}
22
+ </td>
23
+ </tr>
24
+ ))}
25
+ </tbody>
26
+ </table>
27
+ </>
28
+ )
29
+ }
@@ -0,0 +1,28 @@
1
+ :root {
2
+ --text: #111;
3
+ --text-muted: #777;
4
+ --bg: #fff;
5
+ --sans: system-ui, 'Segoe UI', Roboto, sans-serif;
6
+ font-family: var(--sans);
7
+ }
8
+
9
+ @media (prefers-color-scheme: dark) {
10
+ :root {
11
+ --text: #fff;
12
+ --text-muted: #a3a3a3;
13
+ --bg: #16171d;
14
+ }
15
+ }
16
+
17
+ body {
18
+ color: var(--text);
19
+ background: var(--bg);
20
+ }
21
+
22
+ .title {
23
+ color: var(--text);
24
+ }
25
+
26
+ .author {
27
+ color: var(--text-muted);
28
+ }
@@ -0,0 +1,10 @@
1
+ import { StrictMode } from 'react'
2
+ import { createRoot } from 'react-dom/client'
3
+ import './index.css'
4
+ import App from './App.jsx'
5
+
6
+ createRoot(document.getElementById('root')).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>,
10
+ )
@@ -0,0 +1,19 @@
1
+ import { defineConfig } from 'vite'
2
+ import AdmZip from 'adm-zip'
3
+ import react from '@vitejs/plugin-react'
4
+
5
+ export default defineConfig({
6
+ plugins: [react(), {
7
+ name: 'zip-dist', // zipping ./dist is required for BTP HTML5 repo or Application Frontend deployment
8
+ closeBundle() {
9
+ const zip = new AdmZip()
10
+ zip.addLocalFolder('dist')
11
+ zip.writeZip('dist/catalog.zip')
12
+ }
13
+ }],
14
+ server: {
15
+ proxy: {
16
+ '/odata': 'http://localhost:4004'
17
+ }
18
+ }
19
+ })
@@ -0,0 +1,29 @@
1
+ const cds = require('../../../cds')
2
+ const { path, sh: $ } = cds.utils
3
+ const { ask4 } = require('../../../util/question')
4
+ const { renderAndCopy } = require('../../template')
5
+
6
+ module.exports = class ReactTemplate extends require('../../plugin') {
7
+
8
+ options() {
9
+ return {
10
+ 'into': {
11
+ type: 'string',
12
+ short: 'i',
13
+ help: `Name of the app folder in 'app/'. Example: 'catalog' → 'app/catalog'.`
14
+ }
15
+ }
16
+ }
17
+
18
+ static help() {
19
+ return 'user interface for React'
20
+ }
21
+
22
+ async run() {
23
+ const app = cds.cli.options.into ?? await ask4([`app name: `])
24
+ let into = path.join(cds.env.folders.app, app)
25
+ await renderAndCopy(path.join(__dirname, 'files'), into, { app, isJava: cds.env['project-nature'] === 'java' })
26
+ if (cds.cli.command === 'init') into = path.join(cds.cli.argv[0], into)
27
+ await $`npm i --prefix ${into}`
28
+ }
29
+ }
@@ -2,7 +2,6 @@ const { join } = require('path')
2
2
  const cds = require('../../../cds')
3
3
  const { read, write, copy } = cds.utils
4
4
  const { readProject } = require('../../projectReader')
5
- const { merge } = require('../../merge')
6
5
  const { renderAndCopy } = require('../../template')
7
6
  const mvn = require('../../mvn')
8
7
 
@@ -46,8 +45,6 @@ module.exports = class SampleTemplate extends require('../../plugin') {
46
45
  ])
47
46
  }
48
47
 
49
- await merge(__dirname, 'files/package.json.hbs').into('package.json', { with: { appPath }})
50
-
51
48
  // manifest.json IDs must be unique -> otherwise deployment failures on conflicts
52
49
  await Promise.all(project.appUIPaths.map(async p => {
53
50
  const manifest = await read(join(appPath, p, 'webapp/manifest.json'))
@@ -55,7 +52,7 @@ module.exports = class SampleTemplate extends require('../../plugin') {
55
52
  await write(join(appPath, p, 'webapp/manifest.json'), manifest, { spaces: 2 })
56
53
  }))
57
54
 
58
- if (project.isNodejs) { // REVISIT: align with Java samples
55
+ if (project.isNodejs) {
59
56
  const fioriSandboxConfigPath = join(appPath, 'appconfig/fioriSandboxConfig.json')
60
57
  const fioriSandboxConfig = await read(fioriSandboxConfigPath)
61
58
  const sampleInbounds = { BrowseBooks: 'browse', ManageAuthors: 'admin-authors', BrowseGenres: 'genres', ManageBooks: 'admin-books' }
@@ -28,7 +28,7 @@ module.exports = class TyperTemplate extends require('../../plugin') {
28
28
  }
29
29
  await merge(template('package.json')).into('package.json')
30
30
  await mergeGitignore('.gitignore', template('gitignore'))
31
- console.log(dim(` adding '@cds-models' to .gitignore to ignore generated types`)) // REVISIT: other facets are silent wrt. what they change
31
+ console.log(dim(` adding '@cds-models' to .gitignore to ignore generated types`))
32
32
  console.log(dim(' added new dependencies to the project. Run ') + bold('npm i') + dim(' to install.'))
33
33
  try {
34
34
  await merge(source).into(target)
@@ -3,6 +3,6 @@
3
3
  "@cap-js/cds-types": "^0.16.0",
4
4
  "@types/node": "^24.0.0",
5
5
  "tsx": "^4",
6
- "typescript": "^5"
6
+ "typescript": "^6.0.0"
7
7
  }
8
8
  }
@@ -34,6 +34,6 @@ module.exports = class TypescriptTemplate extends require('../../plugin') {
34
34
  : e
35
35
  }
36
36
  await merge(join(__dirname, 'files', 'package.json')).into('package.json')
37
- await new TyperTemplate().run() // REVISIT: we should avoid referencing other plugins
37
+ await new TyperTemplate().run()
38
38
  }
39
39
  }
@@ -8,7 +8,8 @@ module.exports = class extends require('../../plugin') {
8
8
  const startPage = path.join(cds.root, cds.env.folders.app, 'index.html')
9
9
  const packageJson = path.join(cds.root, 'package.json')
10
10
  return exists(startPage) && fs.readFileSync(startPage).includes('https://sapui5') ||
11
- exists(packageJson) && fs.readFileSync(packageJson).includes('"sapux":')
11
+ exists(packageJson) && fs.readFileSync(packageJson).includes('"sapux":') ||
12
+ exists(path.join(cds.root, cds.env.folders.app, 'fiori-apps.html'))
12
13
  }
13
14
 
14
15
  static help() {
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>catalog</title>
8
+ </head>
9
+ <body>
10
+ <div id="app"></div>
11
+ <script type="module" src="/src/main.js"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "{{app}}",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "vue": "^3"
13
+ },
14
+ "devDependencies": {
15
+ "@vitejs/plugin-vue": "^6",
16
+ "adm-zip": "^0",
17
+ "vite": "^8"
18
+ }
19
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 56 56" aria-hidden="true"><path fill="#5A7A94" d="M45.84 21.462a8.489 8.489 0 0 0-12.783-5.713A10.747 10.747 0 0 0 22.41 8.5C10 8.5 11.09 21.488 11.09 21.488A10.3 10.3 0 0 0 13 41.5v-3a7.477 7.477 0 0 1-5.639-5 7.247 7.247 0 0 1 4.666-9.164l2.279-.768-.286-1.5A7.521 7.521 0 0 1 13.9 20a10.535 10.535 0 0 1 2.188-5.828 7.375 7.375 0 0 1 6.4-2.824c3.264 0 7.286 2.9 8.221 5.636l.934 2.732 2.424-1.318a7.6 7.6 0 0 1 3.39-.894 5.546 5.546 0 0 1 5.443 4.518l.288 1.528 1.125.514A7.809 7.809 0 0 1 48.891 29.7 7.809 7.809 0 0 1 43 38.5v3.02A10.793 10.793 0 0 0 52 31a10.483 10.483 0 0 0-6.16-9.538"/><g fill="#0092D1" transform="scale(1.05 1.2)"><g transform="translate(-2 -7)"><path d="M24.561 38.939l-6-6a1.5 1.5 0 0 0-2.122 2.122L21.379 40l-4.94 4.939a1.5 1.5 0 0 0 2.122 2.122l6-6a1.5 1.5 0 0 0 0-2.122"/><path d="M26.5 35.5h12a1.5 1.5 0 0 0 0-3h-12a1.5 1.5 0 0 0 0 3"/><path d="M38.5 38.5h-9a1.5 1.5 0 0 0 0 3h9a1.5 1.5 0 0 0 0-3"/><path d="M38.5 44.5h-12a1.5 1.5 0 0 0 0 3h12a1.5 1.5 0 0 0 0-3"/></g></g></svg>
@@ -0,0 +1,34 @@
1
+ <script setup>
2
+ import { ref, onMounted } from 'vue'
3
+
4
+ const books = ref([])
5
+
6
+ onMounted(async () => {
7
+ const r = await fetch('/odata/v4/{{#isJava}}CatalogService{{/isJava}}{{^isJava}}catalog{{/isJava}}/Books')
8
+ books.value = (await r.json()).value
9
+ })
10
+ </script>
11
+
12
+ <template>
13
+ <main>
14
+ <h1>Books</h1>
15
+ <table>
16
+ <tr v-for="b in books" :key="b.ID">
17
+ <td>
18
+ <span class="title">{{ b.title }}</span>{{^isJava}}
19
+ <span class="author"><i> by {{ b.author }}</i></span>{{/isJava}}
20
+ </td>
21
+ </tr>
22
+ </table>
23
+ </main>
24
+ </template>
25
+
26
+ <style scoped>
27
+ .title {
28
+ color: var(--text);
29
+ }
30
+
31
+ .author {
32
+ color: var(--text-muted);
33
+ }
34
+ </style>
@@ -0,0 +1,5 @@
1
+ import { createApp } from 'vue'
2
+ import './style.css'
3
+ import App from './App.vue'
4
+
5
+ createApp(App).mount('#app')
@@ -0,0 +1,20 @@
1
+ :root {
2
+ --text: #111;
3
+ --text-muted: #777;
4
+ --bg: #fff;
5
+ --sans: system-ui, 'Segoe UI', Roboto, sans-serif;
6
+ font-family: var(--sans);
7
+ }
8
+
9
+ @media (prefers-color-scheme: dark) {
10
+ :root {
11
+ --text: #fff;
12
+ --text-muted: #a3a3a3;
13
+ --bg: #16171d;
14
+ }
15
+ }
16
+
17
+ body {
18
+ color: var(--text);
19
+ background: var(--bg);
20
+ }
@@ -0,0 +1,19 @@
1
+ import { defineConfig } from 'vite'
2
+ import AdmZip from 'adm-zip'
3
+ import vue from '@vitejs/plugin-vue'
4
+
5
+ export default defineConfig({
6
+ plugins: [vue(), {
7
+ name: 'zip-dist', // zipping ./dist is required for BTP HTML5 repo or Application Frontend deployment
8
+ closeBundle() {
9
+ const zip = new AdmZip()
10
+ zip.addLocalFolder('dist')
11
+ zip.writeZip('dist/catalog.zip')
12
+ }
13
+ }],
14
+ server: {
15
+ proxy: {
16
+ '/odata': 'http://localhost:4004'
17
+ }
18
+ }
19
+ })
@@ -0,0 +1,29 @@
1
+ const cds = require('../../../cds')
2
+ const { path, sh: $ } = cds.utils
3
+ const { ask4 } = require('../../../util/question')
4
+ const { renderAndCopy } = require('../../template')
5
+
6
+ module.exports = class VueTemplate extends require('../../plugin') {
7
+
8
+ options() {
9
+ return {
10
+ 'into': {
11
+ type: 'string',
12
+ short: 'i',
13
+ help: `Name of the app folder in 'app/'. Example: 'catalog' → 'app/catalog'.`
14
+ }
15
+ }
16
+ }
17
+
18
+ static help() {
19
+ return 'user interface for Vue.js'
20
+ }
21
+
22
+ async run() {
23
+ const app = cds.cli.options.into ?? await ask4([`app name: `])
24
+ let into = path.join(cds.env.folders.app, app)
25
+ await renderAndCopy(path.join(__dirname, 'files'), into, { app, isJava: cds.env['project-nature'] === 'java' })
26
+ if (cds.cli.command === 'init') into = path.join(cds.cli.argv[0], into)
27
+ await $`npm i --prefix ${into}`
28
+ }
29
+ }
@@ -56,10 +56,10 @@ resources:
56
56
  parameters:
57
57
  service: destination
58
58
  service-plan: lite
59
+ {{#hasMultitenancy}}
59
60
  config:
60
61
  HTML5Runtime_enabled: true
61
62
  init_data:
62
- {{#hasMultitenancy}}
63
63
  subaccount:
64
64
  existing_destinations_policy: update
65
65
  destinations:
@@ -69,18 +69,7 @@ resources:
69
69
  ProxyType: Internet
70
70
  URL: https://${org}.launchpad.${default-domain}
71
71
  CEP.HTML5contentprovider: true
72
- {{/hasMultitenancy}}
73
- instance:
74
- existing_destinations_policy: update
75
- destinations:
76
- - Name: srv-api
77
- URL: ~{srv-api/srv-url}
78
- Authentication: NoAuthentication
79
- Type: HTTP
80
- ProxyType: Internet
81
- HTML5.ForwardAuthToken: true
82
- HTML5.DynamicDestination: true
83
- {{#hasIas}}HTML5.IASDependencyName: {{appName}}-ias-api{{/hasIas}}
72
+ {{/hasMultitenancy}}
84
73
  requires:
85
74
  - name: srv-api
86
75
 
package/lib/init/xml.js CHANGED
@@ -16,8 +16,6 @@ function _removeTextAttributes(value, parentElement) {
16
16
  }
17
17
  }
18
18
 
19
- // REVISIT: Simplify config
20
- // REVISIT: fast-xml-parser
21
19
  /**
22
20
  * Parse a given string containing xml
23
21
  * used to remove the _text object from converted json i.e. from xml-js.
@@ -1,11 +1,8 @@
1
- const axios = require('axios');
2
-
3
1
  const cds = require('../cds');
4
2
  const { SettingsManager } = require('./settings_manager');
5
3
  const { ParamCollection } = require('./params');
6
4
  const { getMessage } = require('./util/logging');
7
-
8
- require('../util/axios').pruneErrors();
5
+ const { buildResponseError, buildNetworkError, parseResponseBody } = require('./util/request');
9
6
 
10
7
  const DEBUG = cds.debug('cli');
11
8
  const REASONS = { invalid_scope: /\binvalid_scope\b/ };
@@ -71,7 +68,7 @@ async function fetchToken(params) {
71
68
  return;
72
69
  }
73
70
 
74
- let response, error;
71
+ let responseData, error;
75
72
  const method = 'post';
76
73
  do {
77
74
  const { url, data, ignored } = reqParams(method, params);
@@ -81,11 +78,23 @@ async function fetchToken(params) {
81
78
  DEBUG?.(`Getting authentication token from ${method.toUpperCase()} ${params.obfuscateQueryParams(url)}`);
82
79
  error = undefined;
83
80
  try {
84
- response = await axios[method](url, data);
81
+ const response = await fetch(url, {
82
+ method: method.toUpperCase(),
83
+ body: data,
84
+ headers: { 'content-type': 'application/x-www-form-urlencoded' }
85
+ });
86
+ if (!response.ok) {
87
+ throw await buildResponseError(method, url, response);
88
+ }
89
+ responseData = await parseResponseBody(response);
85
90
  break;
86
91
  } catch (e) {
87
- error = e;
88
- DEBUG?.(`HTTP status ${e.status}`);
92
+ if (e.status) {
93
+ error = e;
94
+ } else {
95
+ error = buildNetworkError(method, url, e);
96
+ }
97
+ DEBUG?.(`HTTP status ${error.status}`);
89
98
  if (params.has('refreshToken')) {
90
99
  DEBUG?.('Discarding invalid refresh token');
91
100
  params.delete('refreshToken');
@@ -93,8 +102,8 @@ async function fetchToken(params) {
93
102
  break;
94
103
  }
95
104
  }
96
- } while (!response);
97
- const data = response?.data ?? error?.auth;
105
+ } while (!responseData);
106
+ const data = responseData ?? error?.auth;
98
107
  assign(params, data);
99
108
  if (error) {
100
109
  let message;
@@ -126,7 +135,8 @@ module.exports = class AuthManager {
126
135
  const params = new ParamCollection(paramValues);
127
136
  await SettingsManager.loadAndMergeSettings(params);
128
137
  if (params.has('username')) {
129
- params.set('reqAuth', { auth: { username: params.get('username'), password: params.get('password') } });
138
+ const encoded = Buffer.from(`${params.get('username')}:${params.get('password')}`).toString('base64');
139
+ params.set('reqAuth', { headers: { Authorization: `Basic ${encoded}` } });
130
140
  } else if (!params.get('skipToken')) {
131
141
  try {
132
142
  await fetchToken(params);
@@ -1,11 +1,9 @@
1
1
  const fs = require('fs');
2
2
  const { join } = require('path');
3
- const axios = require('axios');
4
3
 
5
4
  const { login } = require('./auth_manager');
6
5
 
7
- require('../util/axios').pruneErrors();
8
- const { handleHttpError } = require('./util/errors');
6
+ const { mtxFetch } = require('./util/request');
9
7
 
10
8
  const DOWNLOAD_FILE_NAME = 'migrated_projects.tgz'; // TODO try to add tenant later, if possible
11
9
 
@@ -33,10 +31,13 @@ module.exports = class Download {
33
31
  const tagRule = params.get('tagRule');
34
32
  const defaultTag = params.get('defaultTag');
35
33
  const downloadUrl = `${(params.get('appUrl'))}/-/cds/extensibility/getMigratedProjects`;
36
- const options = { ...params.get('reqAuth'), responseType: 'arraybuffer' };
37
-
38
- return axios.post(downloadUrl, { tagRule, defaultTag }, options)
39
- .then(response => Buffer.from(response.data))
40
- .catch(error => handleHttpError(error, params, { url: downloadUrl }));
34
+ const reqAuth = params.get('reqAuth') || {};
35
+ const headers = { 'content-type': 'application/json', ...reqAuth.headers };
36
+
37
+ const response = await mtxFetch('POST', downloadUrl, {
38
+ headers,
39
+ body: JSON.stringify({ tagRule, defaultTag })
40
+ }, params);
41
+ return Buffer.from(await response.arrayBuffer());
41
42
  }
42
43
  }
package/lib/mtx/pull.js CHANGED
@@ -3,13 +3,11 @@ const { local, tar } = cds.utils;
3
3
 
4
4
  const fs = require('fs');
5
5
  const { join, dirname } = require('path');
6
- const axios = require('axios');
7
6
 
8
7
  const { login } = require('./auth_manager');
8
+ const { mtxFetch } = require('./util/request');
9
9
 
10
- require('../util/axios').pruneErrors();
11
10
  const { getMessage } = require('./util/logging');
12
- const { handleHttpError } = require('./util/errors');
13
11
  const { grepQR } = require('./util/fs');
14
12
  const { escapeRegex } = require('./util/strings');
15
13
 
@@ -148,11 +146,14 @@ module.exports = class Pull {
148
146
 
149
147
  async pullTgz() {
150
148
  const pullUrl = `${(this.params.get('appUrl'))}/-/cds/extensibility/pull`;
151
- const options = { ...this.params.get('reqAuth'), responseType: 'arraybuffer' };
152
-
153
- return axios.post(pullUrl, { tag: this.getExtensionName() }, options)
154
- .then(response => Buffer.from(response.data))
155
- .catch(error => handleHttpError(error, this.params, { url: pullUrl }));
149
+ const reqAuth = this.params.get('reqAuth') || {};
150
+ const headers = { 'content-type': 'application/json', ...reqAuth.headers };
151
+
152
+ const response = await mtxFetch('POST', pullUrl, {
153
+ headers,
154
+ body: JSON.stringify({ tag: this.getExtensionName() })
155
+ }, this.params);
156
+ return Buffer.from(await response.arrayBuffer());
156
157
  }
157
158
 
158
159
  static transformBasePackage(baseModelFolder, baseModelName) {