@sap/cds-dk 9.4.2 → 9.5.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 (151) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/bin/compile/index.js +1 -1
  3. package/bin/data-products/index.js +10 -2
  4. package/bin/data-products/prepare-csn.js +63 -26
  5. package/bin/export.js +1 -1
  6. package/bin/help.js +2 -3
  7. package/bin/import.js +13 -3
  8. package/bin/repl.js +7 -11
  9. package/bin/up.js +164 -13
  10. package/bin/util/containerize.js +183 -0
  11. package/bin/version.js +4 -4
  12. package/lib/bind/k8s.js +1 -1
  13. package/lib/build/buildTaskEngine.js +5 -2
  14. package/lib/build/plugin.js +1 -1
  15. package/lib/build/plugins/helm/files/templates/_deployment_helpers.tpl +1 -1
  16. package/lib/build/plugins/helm/index.js +7 -7
  17. package/lib/build/provider/hana/2migration.js +16 -24
  18. package/lib/build/provider/hana/index.js +11 -3
  19. package/lib/build/provider/hana/template/.hdiconfig-all +7 -1
  20. package/lib/build/provider/hana/template/package.json +1 -1
  21. package/lib/build/provider/internalBuildPlugin.js +15 -1
  22. package/lib/build/provider/java/index.js +12 -1
  23. package/lib/build/provider/mtx-sidecar/index.js +2 -1
  24. package/lib/build/provider/nodejs/index.js +2 -1
  25. package/lib/build/util.js +3 -3
  26. package/lib/compile/to_openapi.js +1 -5
  27. package/lib/import/common.js +5 -4
  28. package/lib/import/message.js +1 -1
  29. package/lib/import/odata/ParserContext.js +1 -0
  30. package/lib/import/odata/v4parser.js +48 -3
  31. package/lib/import/openapi/importOpenAPI.js +6 -1
  32. package/lib/init/constants.js +1 -1
  33. package/lib/init/index.js +15 -6
  34. package/lib/init/merge.js +16 -0
  35. package/lib/init/projectReader.js +54 -24
  36. package/lib/init/registries/mta.js +5 -2
  37. package/lib/init/registries/xs-app.js +7 -1
  38. package/lib/init/template/ams/index.js +1 -1
  39. package/lib/init/template/app-frontend/files/app-package.json.hbs +1 -7
  40. package/lib/init/template/app-frontend/files/ui5.yaml.hbs +20 -1
  41. package/lib/init/template/app-frontend/index.js +0 -4
  42. package/lib/init/template/application-logging/index.js +9 -14
  43. package/lib/init/template/approuter/files/mta.yaml.hbs +7 -2
  44. package/lib/init/template/approuter/index.js +21 -16
  45. package/lib/init/template/audit-logging/index.js +2 -2
  46. package/lib/init/template/cloud-logging/index.js +2 -3
  47. package/lib/init/template/containerize/files/containerize.yaml.hbs +17 -3
  48. package/lib/init/template/containerize/index.js +0 -4
  49. package/lib/init/template/data/as-json.js +18 -20
  50. package/lib/init/template/destination/index.js +0 -4
  51. package/lib/init/template/dynatrace/index.js +0 -2
  52. package/lib/init/template/github-actions/files/kyma-setup/action.yaml +0 -2
  53. package/lib/init/template/github-actions/files/workflows/cf.yaml.hbs +16 -1
  54. package/lib/init/template/github-actions/files/workflows/test.yaml.hbs +20 -0
  55. package/lib/init/template/github-actions/index.js +5 -8
  56. package/lib/init/template/hana/files/db/package.json +2 -2
  57. package/lib/init/template/hana/files/mta.yaml.hbs +0 -4
  58. package/lib/init/template/hana/index.js +5 -7
  59. package/lib/init/template/helm/index.js +16 -33
  60. package/lib/init/template/helm-unified-runtime/index.js +0 -4
  61. package/lib/init/template/html5-repo/files/app-package.json.hbs +1 -7
  62. package/lib/init/template/html5-repo/files/containerize.yaml.hbs +0 -9
  63. package/lib/init/template/html5-repo/files/mta.yaml.hbs +0 -7
  64. package/lib/init/template/html5-repo/files/ui5.yaml.hbs +20 -1
  65. package/lib/init/template/html5-repo/files/values.yaml.hbs +6 -0
  66. package/lib/init/template/html5-repo/files/xs-app.json.hbs +5 -3
  67. package/lib/init/template/html5-repo/index.js +8 -14
  68. package/lib/init/template/ias/files/values.yaml.hbs +6 -6
  69. package/lib/init/template/ias/index.js +14 -23
  70. package/lib/init/template/kyma/index.js +14 -1
  71. package/lib/init/template/malware-scanner/files/mta.yaml.hbs +3 -1
  72. package/lib/init/template/malware-scanner/files/values.yaml +3 -1
  73. package/lib/init/template/multitenancy/files/mta.yaml.hbs +1 -3
  74. package/lib/init/template/multitenancy/files/values.yaml.hbs +8 -0
  75. package/lib/init/template/multitenancy/index.js +10 -5
  76. package/lib/init/template/nodejs/files/package.json.hbs +1 -1
  77. package/lib/init/template/pipeline/files/app-package.json.hbs +1 -7
  78. package/lib/init/template/pipeline/files/ui5.yaml.hbs +20 -1
  79. package/lib/init/template/pipeline/index.js +6 -3
  80. package/lib/init/template/portal/files/mta.yaml.hbs +5 -3
  81. package/lib/init/template/portal/index.js +3 -6
  82. package/lib/init/template/sample/files/_i18n/i18n_de.properties +12 -0
  83. package/lib/init/template/sample/files/_i18n/i18n_en.properties +21 -0
  84. package/lib/init/template/sample/files/_i18n/i18n_fr.properties +20 -0
  85. package/lib/init/template/sample/files/_i18n/messages_de.properties +34 -0
  86. package/lib/init/template/sample/files/_i18n/messages_en.properties +34 -0
  87. package/lib/init/template/sample/files/_i18n/messages_fr.properties +34 -0
  88. package/lib/init/template/sample/files/app/_i18n/i18n_de.properties +0 -26
  89. package/lib/init/template/sample/files/app/_i18n/i18n_en.properties +8 -0
  90. package/lib/init/template/sample/files/app/admin-authors/fiori-service.cds +52 -0
  91. package/lib/init/template/sample/files/app/admin-authors/webapp/Component.js.hbs +3 -0
  92. package/lib/init/template/sample/files/app/admin-authors/webapp/i18n/i18n.properties +11 -0
  93. package/lib/init/template/sample/files/app/admin-authors/webapp/manifest.json +130 -0
  94. package/lib/init/template/sample/files/app/admin-books/fiori-service.cds +81 -88
  95. package/lib/init/template/sample/files/app/admin-books/webapp/Component.js.hbs +3 -0
  96. package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n.properties +10 -2
  97. package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +24 -48
  98. package/lib/init/template/sample/files/app/appconfig/fioriSandboxConfig.json +54 -3
  99. package/lib/init/template/sample/files/app/browse/fiori-service.cds +30 -30
  100. package/lib/init/template/sample/files/app/browse/webapp/Component.js.hbs +3 -0
  101. package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n.properties +10 -3
  102. package/lib/init/template/sample/files/app/browse/webapp/manifest.json +18 -23
  103. package/lib/init/template/sample/files/app/common.cds +126 -158
  104. package/lib/init/template/sample/files/app/fiori-apps.html +30 -0
  105. package/lib/init/template/sample/files/app/genres/fiori-service.cds +33 -0
  106. package/lib/init/template/sample/files/app/genres/tree-view.cds +42 -0
  107. package/lib/init/template/sample/files/app/genres/value-help.cds +6 -0
  108. package/lib/init/template/sample/files/app/genres/webapp/Component.js.hbs +3 -0
  109. package/lib/init/template/sample/files/app/genres/webapp/i18n/i18n.properties +4 -0
  110. package/lib/init/template/sample/files/app/genres/webapp/i18n/i18n_de.properties +2 -0
  111. package/lib/init/template/sample/files/app/genres/webapp/manifest.json +126 -0
  112. package/lib/init/template/sample/files/app/services.cds +6 -2
  113. package/lib/init/template/sample/files/db/currencies.cds +13 -0
  114. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Books.csv +5 -5
  115. package/lib/init/template/sample/files/db/data/{sap.capire.bookshop-Books_texts.csv → sap.capire.bookshop-Books.texts.csv} +4 -4
  116. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Genres.csv +42 -15
  117. package/lib/init/template/sample/files/db/data/sap.common-Currencies.csv +12 -0
  118. package/lib/init/template/sample/files/db/data/sap.common-Currencies.texts.csv +15 -0
  119. package/lib/init/template/sample/files/db/hana/index.cds +10 -0
  120. package/lib/init/template/sample/files/db/schema.cds +35 -22
  121. package/lib/init/template/sample/files/db/sqlite/index.cds +10 -0
  122. package/lib/init/template/sample/files/package.json.hbs +14 -1
  123. package/lib/init/template/sample/files/srv/access-control.cds +3 -0
  124. package/lib/init/template/sample/files/srv/admin-service.cds +3 -2
  125. package/lib/init/template/sample/files/srv/cat-service.cds +7 -11
  126. package/lib/init/template/sample/index.js +26 -17
  127. package/lib/init/template/telemetry/index.js +1 -3
  128. package/lib/init/template/typescript/files/package.json +1 -1
  129. package/lib/init/template/workzone-standard/files/mta.yaml.hbs +0 -5
  130. package/lib/init/template/workzone-standard/index.js +0 -1
  131. package/lib/init/template/xsuaa/files/mta.yaml.hbs +4 -0
  132. package/lib/init/template/xsuaa/index.js +5 -7
  133. package/lib/lint/Linter.js +10 -4
  134. package/lib/lint/eslintHelp.json +1 -1
  135. package/lib/mtx/tenant_action.js +12 -4
  136. package/lib/util/checksum.js +32 -0
  137. package/lib/util/question.js +60 -7
  138. package/npm-shrinkwrap.json +61 -37
  139. package/package.json +2 -2
  140. package/lib/init/template/app-frontend/files/ui5-deploy.yaml.hbs +0 -25
  141. package/lib/init/template/github-actions/files/cf-setup/action.yaml +0 -53
  142. package/lib/init/template/html5-repo/files/ui5-deploy.yaml.hbs +0 -25
  143. package/lib/init/template/pipeline/files/ui5-deploy.yaml.hbs +0 -25
  144. package/lib/init/template/sample/files/app/_i18n/i18n.properties +0 -29
  145. package/lib/init/template/sample/files/app/admin-books/webapp/Component.js +0 -8
  146. package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n_de.properties +0 -3
  147. package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n_en.properties +0 -3
  148. package/lib/init/template/sample/files/app/browse/webapp/Component.js +0 -7
  149. package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n_de.properties +0 -3
  150. package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n_en.properties +0 -4
  151. package/lib/init/template/sample/files/app/index.html +0 -32
@@ -0,0 +1,183 @@
1
+ const cds = require('../../lib/cds')
2
+ const { path } = cds.utils
3
+ const { join } = path
4
+ const { execSync } = require('node:child_process')
5
+ const { highlight, info } = require('../../lib/util/term')
6
+ const { calcDirChecksum } = require('../../lib/util/checksum')
7
+
8
+ const buildpacks = new Set([
9
+ 'java', 'nodejs', 'sap-machine', 'executable-jar', 'spring-boot', 'syft'
10
+ ])
11
+ const builders = new Set([
12
+ 'builder-jammy-base', 'builder-jammy-buildpackless-base', 'builder-jammy-full', 'builder-jammy-buildpackless-full'
13
+ ])
14
+
15
+ const keyProcessorMap = {
16
+ dockerfile: processDockerfile,
17
+ commands: processCommands,
18
+ buildpack: processBuildpack
19
+ }
20
+
21
+ const run = (cmd, { continueOnFail = false, silent = false } = {}) => {
22
+ cmd = Array.isArray(cmd) ? cmd.join(' ') : cmd
23
+ console.log(highlight(cmd))
24
+ try {
25
+ return execSync(cmd, {
26
+ stdio: silent ? 'pipe' : 'inherit',
27
+ env: { ...process.env, FORCE_COLOR: cds.utils.colors.enabled }
28
+ })
29
+ } catch (error) {
30
+ if (continueOnFail) {
31
+ if (!silent)
32
+ console.error(error.message)
33
+ return
34
+ }
35
+ throw error.message
36
+ }
37
+ }
38
+
39
+ function appendSubcommand(param, subcommand) {
40
+ if (!param) return []
41
+ if (subcommand === '--env') {
42
+ return Object.entries(param).flatMap(([k, v]) =>
43
+ String(v).split(',').map(env => [subcommand, `${k}=${env.trim() || '""'}`])
44
+ ).flat()
45
+ }
46
+ return param.split(',').map(e => [subcommand, e.trim()]).flat()
47
+ }
48
+
49
+ function processDockerfile(bp, name, tag) {
50
+ if (!bp.dockerfile) return []
51
+ return [['docker', 'build',
52
+ ...appendSubcommand(`${name}:${tag ?? 'latest'}`, '-t'),
53
+ ...appendSubcommand(bp.dockerfile, '-f'),
54
+ '.']]
55
+ }
56
+
57
+ function processCommands(bp) {
58
+ return bp.commands ? bp.commands.map(c => c.trim().split(' ')) : []
59
+ }
60
+ function processBuildpack(bp, name, tag, clearCache = false) {
61
+ let { type, path, env, builder } = bp.buildpack
62
+ type = type ? type.split(',').map(t => buildpacks.has(t.trim()) ? `paketo-buildpacks/${t.trim()}` : t.trim()) : []
63
+ if (builders.has(builder)) builder = `paketobuildpacks/${builder}`
64
+ const cacheArgs = clearCache ? ['--clear-cache'] : []
65
+ return [['pack',
66
+ ...appendSubcommand(`${name}:${tag}`, 'build'),
67
+ ...appendSubcommand(path, '--path'),
68
+ ...type.flatMap(t => appendSubcommand(t, '--buildpack')),
69
+ ...appendSubcommand(builder, '--builder'),
70
+ ...appendSubcommand(env, '--env'),
71
+ ...cacheArgs
72
+ ]]
73
+ }
74
+
75
+ /**
76
+ * Parses a YAML configuration file to extract Docker build and push commands for modules.
77
+ *
78
+ * @param {string} filename - The path to the YAML file to parse.
79
+ * @param {string} [repoOpt=''] - Optional repository override. If not provided, uses the repository defined in the YAML file.
80
+ * @param {boolean} [clearCache=false] - Whether to clear cache for buildpack builds.
81
+ * @returns {Promise<{
82
+ * commands: Array<{
83
+ * name: string,
84
+ * tag: string,
85
+ * buildCmd: Array<string>,
86
+ * tagCmd: Array<string>,
87
+ * pushCmd: Array<string>,
88
+ * image: string,
89
+ * checksum: string|null
90
+ * path: string
91
+ * }>,
92
+ * before_all: Array<Array<string>>,
93
+ * repository: string
94
+ * }>} An object containing the commands for each module, any global pre-commands, and the repository name.
95
+ * @throws {Error} If modules are not defined in the YAML file or if build-parameters are invalid.
96
+ */
97
+ async function parseYAML(filename, repoOpt = '', options) {
98
+ const file = cds.load.yaml(join(cds.root, filename))
99
+ const { repository: repoFile, modules, tag: globalTag = 'latest' } = file
100
+ const repository = repoOpt || repoFile
101
+ const before_all = file['before-all'] ? file['before-all'].map(c => c.trim().split(' ')) : []
102
+ if (!modules) throw `no modules defined in ${filename}`
103
+ const commands = await Promise.all(modules.map(async (m) => {
104
+ const name = m.name, tag = m.tag || globalTag, bp = m['build-parameters']
105
+ const rawImg = `${repository}/${name}`, source = `${name}:${tag}`, repoImg = `${repository}/${source}`
106
+ const key = Object.keys(bp)[0]
107
+ let checksum = null
108
+ if (bp.buildpack?.path)
109
+ checksum = await calcDirChecksum(
110
+ join(cds.root, bp.buildpack.path),
111
+ (dir) => !dir.includes('node_modules'),
112
+ 'sha256',
113
+ [
114
+ join(cds.root, filename),
115
+ join(cds.root, 'package.json')
116
+ ]
117
+ )
118
+ const buildType = keyProcessorMap[key]
119
+
120
+ if (!buildType) throw `'build-parameters' ${Object.keys(bp)} in ${filename} is invalid`
121
+ return {
122
+ name,
123
+ tag,
124
+ buildCmd: buildType(bp, name, tag, key === 'buildpack' && options.clearCache),
125
+ tagCmd: [['docker', 'tag', source, repoImg]].concat(checksum ? [['docker', 'tag', source, `${rawImg}:${checksum}`]] : []),
126
+ pushCmd: ['docker', 'push', repoImg],
127
+ image: rawImg,
128
+ checksum,
129
+ path: bp.buildpack?.path
130
+ }
131
+ }))
132
+ return { commands, before_all, repository }
133
+ }
134
+
135
+ /**
136
+ * Executes the containerization commands for each module.
137
+ * @param {*} modules - The list of modules with their respective build, tag, and push commands.
138
+ * @param {*} options - Options object, e.g., { push: true } to push images after building.
139
+ */
140
+ async function executeModules(modules, options) {
141
+ for (const m of modules) {
142
+ if (m.checksum) {
143
+ try {
144
+ run(`docker inspect --format='{{index .RepoTags 0}}' ${m.image}:${m.checksum}`, { silent: true })
145
+ console.log(info(`image ${m.image}:${m.checksum} already exists – skipping build and push`))
146
+ continue
147
+ } catch (error) {
148
+ // ignore
149
+ }
150
+ }
151
+
152
+ for (const cmd of m.buildCmd) run(cmd)
153
+ if (options.push) {
154
+ for (const cmd of m.tagCmd) run(cmd)
155
+ run(m.pushCmd)
156
+ }
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Main function to containerize modules based on a YAML configuration file.
162
+ * @param {string} filename - The path to the YAML configuration file.
163
+ * @param {{ push: boolean, clearCache: boolean }} options - Options object, e.g., { push: true } to push images after building.
164
+ * @returns {Promise<Array<{
165
+ * name: string,
166
+ * tag: string,
167
+ * buildCmd: Array<string>,
168
+ * tagCmd: Array<string>,
169
+ * pushCmd: Array<string>,
170
+ * image: string,
171
+ * checksum: string|null
172
+ * path: string
173
+ * }>} - The list of modules that were containerized.
174
+ */
175
+ async function containerize(filename = 'containerize.yaml', options = { clearCache: !!process.env.CLEAR_CACHE, push: true }) {
176
+ const { repository, before_all, commands: modules } = await parseYAML(filename, '', options)
177
+ if (!repository || !modules) return
178
+ for (const cmd of before_all) run(cmd)
179
+ await executeModules(modules, options)
180
+ return modules
181
+ }
182
+
183
+ module.exports = { containerize }
package/bin/version.js CHANGED
@@ -52,7 +52,7 @@ function list_versions (args, options) {
52
52
 
53
53
  process.stdout.isTTY && console.log()
54
54
  if (options.markdown || options.info) {
55
- _markdown (versions)
55
+ _markdown (versions, options)
56
56
  } else {
57
57
  const mark = !colors ? s => s : require('../lib/util/term').info
58
58
  for (let each of Object.keys(versions)) console.log(`${mark(each)}: ${versions[each]}`)
@@ -81,7 +81,7 @@ function info({ root: cwd = process.cwd() }) {
81
81
 
82
82
  // Sort entries, with @sap/cds packages on top, followed by @cap-js packages
83
83
  const keys = Object.keys(versions).sort(), sorted={}
84
- for (let k of ['@sap/cds','@sap/cds-dk']) if (!(k in sorted)) sorted[k] = versions[k]
84
+ for (let k of ['@sap/cds']) if (!(k in sorted)) sorted[k] = versions[k]
85
85
  for (let k of keys) if (!(k in sorted) && k.startsWith('@sap/cds')) sorted[k] = versions[k]
86
86
  for (let k of keys) if (!(k in sorted) && k.startsWith('@cap-js')) sorted[k] = versions[k]
87
87
  for (let k of keys) if (!(k in sorted)) sorted[k] = versions[k]
@@ -115,9 +115,9 @@ function _versions_from(pkg_path, pkg_label) {
115
115
  return versions
116
116
  }
117
117
 
118
- function _markdown (versions) {
118
+ function _markdown (versions, { root: cwd = process.cwd() }) {
119
119
  const pkg = { name:'', repository:'', version:'' }; try {
120
- Object.assign (pkg, require (resolve('package.json')))
120
+ Object.assign (pkg, require (require.resolve('./package.json', { paths:[cwd] })))
121
121
  } catch (e) {/* ignored */}
122
122
  const repo = pkg.repository.url || pkg.repository || 'https://github.com/<your/repo>'
123
123
  const valLength = Math.max(repo.length, versions.home.length)
package/lib/bind/k8s.js CHANGED
@@ -32,7 +32,7 @@ module.exports = new class K8SBind extends require('./plugin') {
32
32
 
33
33
  services4(app) {
34
34
  const { execSync } = require('node:child_process')
35
- const json = cmd => JSON.parse(execSync(cmd, { encoding: 'utf8' }))
35
+ const json = cmd => JSON.parse(execSync(cmd, { encoding: 'utf8', maxBuffer: 10 * 1024 * 1024 }))
36
36
 
37
37
  const bindings = json('kubectl get servicebindings -o json').items
38
38
  .filter(b => b.metadata.name.startsWith(app))
@@ -179,7 +179,7 @@ class BuildTaskEngine {
179
179
 
180
180
  /** @private */
181
181
  _resolveHandlerResponse(results, pluginMessages = []) {
182
- /** @type {BuildError[]}*/
182
+ /** @type {(BuildError | Error)[]}*/
183
183
  const errors = []
184
184
  const resolvedResults = results.reduce((acc, r) => {
185
185
  if (r.status === 'fulfilled') {
@@ -195,7 +195,10 @@ class BuildTaskEngine {
195
195
  const error = errors.find(e => e.constructor.name !== COMPILATION_ERROR)
196
196
  if (error) {
197
197
  // throw original BuildError including existing BuildMessages
198
- error.messages = error.messages.map(m => typeof m === 'string' ? new BuildMessage(m, SEVERITY_ERROR) : m)
198
+ if (!(error instanceof BuildError)) {
199
+ throw error
200
+ }
201
+ error.messages = error.messages?.map(m => typeof m === 'string' ? new BuildMessage(m, SEVERITY_ERROR) : m)
199
202
  error.messages = this._sortMessagesUnique(pluginMessages.filter(m => m instanceof BuildMessage), error.messages)
200
203
  throw error
201
204
  }
@@ -126,7 +126,7 @@ class Plugin {
126
126
  * @abstract
127
127
  * Called by the framework to create the artifacts of this build plugin.
128
128
  */
129
- async build() { /* abstract */}
129
+ async build() { /* abstract */ }
130
130
 
131
131
  /**
132
132
  * Called by the framework immediately before 'build' to delete any output created by this build plugin.
@@ -23,7 +23,7 @@ copied from web-application: web-application.fullname
23
23
  */}}
24
24
  {{- define "cap.deploymentHost" -}}
25
25
  {{- if .deployment.expose.host }}
26
- {{- .deployment.expose.host }}
26
+ {{- tpl .deployment.expose.host . }}
27
27
  {{- else }}
28
28
  {{- $name := (include "cap.web-application.fullname" .) }}
29
29
  {{- if hasPrefix $name .Release.Namespace }}
@@ -1,6 +1,5 @@
1
1
  const cds = require('../../../cds')
2
- const yaml = require('yaml')
3
- const { exists, path, readFileSync } = cds.utils
2
+ const { exists, path, readFileSync, yaml } = cds.utils
4
3
  const { Plugin, BuildError } = require('../..')
5
4
  const { join } = path
6
5
  const { readProject } = require('../../../init/projectReader')
@@ -11,7 +10,7 @@ module.exports = class HelmBuildPlugin extends Plugin {
11
10
  static hasTask() {
12
11
  if (exists('chart/Chart.yaml')) {
13
12
  const chartFile = readFileSync(join(cds.root, 'chart', 'Chart.yaml'), 'utf8')
14
- const chartYaml = yaml.parseDocument(chartFile).toJS()
13
+ const chartYaml = yaml.parse(chartFile)
15
14
  return chartYaml.annotations?.["app.kubernetes.io/managed-by"] === 'cds-dk/helm'
16
15
  }
17
16
  return false
@@ -27,15 +26,16 @@ module.exports = class HelmBuildPlugin extends Plugin {
27
26
  }
28
27
 
29
28
  async build() {
30
- if(!HelmBuildPlugin.hasTask()) {
31
- this.pushMessage("Helm Chart not found. Execute `cds add helm` to add the helm chart.", Plugin.WARNING)
29
+ if (!HelmBuildPlugin.hasTask()) {
30
+ this.pushMessage("Helm Chart not found. Execute `cds add kyma` to add the helm chart.", Plugin.WARNING)
32
31
  return
33
32
  }
34
33
 
35
- const { hasApprouter, hasConnectivity, hasHtml5Repo } = readProject()
34
+ const project = readProject()
35
+ const { hasApprouter, hasConnectivity, hasHtml5Repo } = project
36
36
 
37
37
  const chartFile = readFileSync(join(cds.root, 'chart', 'Chart.yaml'), 'utf8')
38
- const chartYaml = yaml.parseDocument(chartFile).toJS()
38
+ const chartYaml = yaml.parse(chartFile)
39
39
  const isInternal = chartYaml.dependencies?.find(dep => dep.name === 'web-application')?.repository !== undefined
40
40
 
41
41
  if (!isInternal) {
@@ -48,31 +48,23 @@ function _compileToHana(model, options, beforeImage) {
48
48
  let migrations = []
49
49
  let afterImage
50
50
 
51
- if (cds.compile.to.hana) {
52
- const result = cds.compile.to.hana(model, options, beforeImage);
53
-
54
- for (const [content, { file }] of result) {
55
- switch (file) {
56
- case 'deletions.json':
57
- break
58
- case 'migrations.json':
59
- migrations = content
60
- break
61
- case 'afterImage.json':
62
- afterImage = content
63
- break
64
- default: {
65
- const { name, ext: suffix } = path.parse(file)
66
- definitions.push({ name, suffix, sql: content })
67
- }
51
+ const result = cds.compile.to.hana(model, options, beforeImage);
52
+
53
+ for (const [content, { file }] of result) {
54
+ switch (file) {
55
+ case 'deletions.json':
56
+ break
57
+ case 'migrations.json':
58
+ migrations = content
59
+ break
60
+ case 'afterImage.json':
61
+ afterImage = content
62
+ break
63
+ default: {
64
+ const { name, ext: suffix } = path.parse(file)
65
+ definitions.push({ name, suffix, sql: content })
68
66
  }
69
67
  }
70
- } else {
71
- // compatibility with cds < 8.0.0
72
- const result = cdsc.to.hdi.migration(cds.minify(model), options, beforeImage)
73
- definitions = result.definitions
74
- migrations = result.migrations
75
- afterImage = result.afterImage
76
68
  }
77
69
  let migrationTableNames = new Set()
78
70
  if (afterImage) {
@@ -97,7 +89,7 @@ function _compileToHana(model, options, beforeImage) {
97
89
  * @param {String} srcPath Fully qualified path of the directory containing .hdbmigrationtable files
98
90
  * @param {String} migration the migration descriptor
99
91
  * @param {String} tableSql SQL TABLE definition
100
- * @returns {Object} Providing access to 'content' and 'fileName'.
92
+ * @returns {Promise<Object>} Providing access to 'content' and 'fileName'.
101
93
  */
102
94
  async function _2migrationtable(srcPath, migration, tableSql) {
103
95
  let migrationTableModel = null
@@ -21,6 +21,10 @@ const FILE_NAME_UNDEPLOY_JSON = "undeploy.json"
21
21
  const DEPLOY_FORMAT = "deploy-format"
22
22
  const DEBUG = cds.debug('cli|build')
23
23
 
24
+ // Some HDI artifacts do not require explicit plugin specification in _.hdiconfig_.
25
+ // This list contains exceptions which will be ignored during the consistency check.
26
+ const PLUGINLESS_HDB_FILES = new Set([".hdbgrants", ".hdbrevokes"])
27
+
24
28
  class HanaBuildPlugin extends InternalBuildPlugin {
25
29
  init() {
26
30
  if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
@@ -473,8 +477,12 @@ class HanaBuildPlugin extends InternalBuildPlugin {
473
477
  }
474
478
  }
475
479
  if (Object.keys(content['file_suffixes']).length < hdiPlugins.size) {
476
- const missingPlugins = Array.from(hdiPlugins).filter(key => !content['file_suffixes'][key.slice(1)])
477
- this.pushMessage(`'HANA database plugin not found for following file suffix [${missingPlugins.join(', ')}]`)
480
+ const missingPlugins = Array.from(hdiPlugins)
481
+ .filter(key => !content['file_suffixes'][key.slice(1)])
482
+ .filter(key => !PLUGINLESS_HDB_FILES.has(key))
483
+ if (missingPlugins.length) {
484
+ this.pushMessage(`'HANA database plugin not found for following file suffix [${missingPlugins.join(', ')}]`)
485
+ }
478
486
  }
479
487
  await this.write(content).to(hdiConfigPath)
480
488
  }
@@ -578,7 +586,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
578
586
  /**
579
587
  * Returns all hana native artifacts located in db/src/** workspace folders.
580
588
  * @param {Array} workspacePaths Array of workspace paths, 'cds.root' is excluded from the list
581
- * @returns {Array} Array of workspace files, each entry contains the db src directory,
589
+ * @returns {Promise<Array>} Array of workspace files, each entry contains the db src directory,
582
590
  * the relative file path and the file name.
583
591
  */
584
592
  async _getWorkspaceFiles(workspacePaths) {
@@ -167,6 +167,12 @@
167
167
  },
168
168
  "hdbschedulerjob" : {
169
169
  "plugin_name" : "com.sap.hana.di.schedulerjob"
170
+ },
171
+ "hdblogicalschema" : {
172
+ "plugin_name" : "com.sap.hana.di.logicalschema"
173
+ },
174
+ "hdblogicalschemaconfig" : {
175
+ "plugin_name" : "com.sap.hana.di.logicalschema.config"
170
176
  }
171
177
  }
172
- }
178
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "deploy",
3
3
  "dependencies": {
4
- "hdb": "^0",
4
+ "hdb": "^2.0.0",
5
5
  "@sap/hdi-deploy": "^5"
6
6
  },
7
7
  "engines": {
@@ -4,6 +4,7 @@ const cds = require('../../cds')
4
4
  const Plugin = require('../plugin')
5
5
  const { hasOptionValue, getFiles } = require('../util')
6
6
  const { FOLDER_GEN, DEFAULT_BUILTIN_PRIORITY_MAX } = require('../constants')
7
+ const TRACE = cds.debug('trace')
7
8
 
8
9
  class InternalBuildPlugin extends Plugin {
9
10
  /**
@@ -116,7 +117,7 @@ class InternalBuildPlugin extends Plugin {
116
117
  cwd: cds.root,
117
118
  src: this.hasBuildOption('flatDeployLayout', true) || this.task.src === this.task.dest ? this.task.src : cds.root
118
119
  }
119
- const csnStr = cds.compile.to.json(model, jsonOptions)
120
+ const csnStr = cds_compile_to_json(model, jsonOptions)
120
121
  await this.write(csnStr).to(csnFile)
121
122
  return csnStr
122
123
  }
@@ -156,4 +157,17 @@ class InternalBuildPlugin extends Plugin {
156
157
  return null
157
158
  }
158
159
  }
160
+
161
+ // Emits the 'compile.for.runtime' event, as the produced CSN is for runtime usage.
162
+ // Plugins use it to modify the CSN before it is stringified.
163
+ function cds_compile_to_json (csn,o) {
164
+ TRACE?.time('cds.compile 2json'.padEnd(22)); try {
165
+ let result, next = ()=> result ??= cds.compile.to.json (csn,o)
166
+ cds.emit ('compile.for.runtime', csn, o, next)
167
+ return next() //> in case no handler called next
168
+ }
169
+ finally { TRACE?.timeEnd('cds.compile 2json'.padEnd(22)) }
170
+ }
171
+
172
+
159
173
  module.exports = InternalBuildPlugin
@@ -7,6 +7,7 @@ const { OUTPUT_MODE, OUTPUT_MODE_RESULT, FILE_EXT_CDS, CONTENT_LANGUAGE_BUNDLES,
7
7
  const { getI18nDefaultFolder } = require('../../util')
8
8
  const DEFAULT_COMPILE_DEST_FOLDER = path.normalize('src/main/resources/edmx')
9
9
  const DEBUG = cds.debug('cli|build')
10
+ const LOG = cds.log('cli|build')
10
11
 
11
12
  class JavaBuildPlugin extends EdmxBuildPlugin {
12
13
  init() {
@@ -25,7 +26,17 @@ class JavaBuildPlugin extends EdmxBuildPlugin {
25
26
 
26
27
  if (!this.hasBuildOption(CONTENT_EDMX, false)) {
27
28
  // generate edmx files containing all features - localized edmx for backward compatibility
28
- if (this.hasBuildOption(CONTENT_LOCALIZED_EDMX, true)) {
29
+ let localized = this.hasBuildOption(CONTENT_LOCALIZED_EDMX, true)
30
+
31
+ // Force localized to true for OData v2
32
+ if (cds.env.odata.version === 'v2') {
33
+ if (!localized) {
34
+ LOG.info('Forcing localized EDMX generation for OData v2')
35
+ localized = true
36
+ }
37
+ }
38
+
39
+ if (localized) {
29
40
  await this.compileToEdmx(model, compileDest, { [FLAVOR_LOCALIZED_EDMX]: true })
30
41
  }
31
42
  // non-localized edmx
@@ -86,7 +86,8 @@ class MtxSidecarBuildPlugin extends NodejsBuildPlugin {
86
86
  return
87
87
  }
88
88
  const { dictionary, sources } = await this.compileAll(model, destMainSrv, destMain)
89
- await this.collectAllLanguageBundles(dictionary, sources, destMainSrv, destMain)
89
+ // await this.collectAllLanguageBundles(dictionary, sources, destMainSrv, destMain)
90
+ await this.collectAllLanguageBundles(dictionary, sources, destMain, destMain)
90
91
 
91
92
  if (!this.hasBuildOption(CONTENT_EDMX, false)) {
92
93
  //inferred model expected by cds.compile.to.edmx()
@@ -53,7 +53,8 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
53
53
  if (model) {
54
54
  const { dictionary, sources } = await this.compileAll(model, destSrv, destRoot)
55
55
  // collect and write language bundles into single i18n.json file
56
- await this.collectAllLanguageBundles(dictionary, sources, destSrv, destRoot)
56
+ await this.collectAllLanguageBundles(dictionary, sources, destRoot, destRoot)
57
+ // await this.collectAllLanguageBundles(dictionary, sources, destSrv, destRoot)
57
58
 
58
59
  if (!this.hasBuildOption(CONTENT_EDMX, false)) {
59
60
  const compileOptions = { [FLAVOR_LOCALIZED_EDMX]: this.hasBuildOption(FLAVOR_LOCALIZED_EDMX, true) }
package/lib/build/util.js CHANGED
@@ -165,9 +165,9 @@ function flatten(modelPaths) {
165
165
 
166
166
  function getI18nDefaultFolder() {
167
167
  // keep '_i18n' as preferred folder for compatibility reasons
168
- if (cds.env.i18n.folders.includes('_i18n')) return '_i18n'
169
- if (cds.env.i18n.folders.includes('i18n')) return 'i18n'
170
- return cds.env.i18n.folders[0] ?? 'i18n'
168
+ // if (cds.env.i18n.folders.includes('_i18n')) return '_i18n'
169
+ // if (cds.env.i18n.folders.includes('i18n')) return 'i18n'
170
+ return cds.env.i18n.folders?.[0] ?? '_i18n'
171
171
  }
172
172
 
173
173
  async function pathExists(path) {
@@ -1,8 +1,4 @@
1
1
  const cds = require('../cds')
2
- const { compile } = cds._local('@cap-js/openapi') // load a local cap-js/openapi (fallback to our own)
3
-
4
- function cds_compile_to_openapi (csn, options) {
5
- return compile(csn, options)
6
- }
2
+ const { compile: cds_compile_to_openapi } = cds._local('@cap-js/openapi') // load a local cap-js/openapi (fallback to our own)
7
3
 
8
4
  module.exports = cds_compile_to_openapi
@@ -336,12 +336,12 @@ function _printUsageHint(dest, service, cwd) {
336
336
  dest = dest.replace(extension, '');
337
337
  let using
338
338
  if (service.length > 1){
339
- using = `using { ${service.join(', ')}} from './${path.relative(cds.env.folders.srv, dest).replace(/\\/g, '/')}'`;
339
+ using = `using { ${service.join(', ')}} from './${path.relative(cds.env.folders.srv, dest).replace(/\\/g, '/')}';`;
340
340
  } else {
341
- using = `using { ${service} as external } from './${path.relative(cds.env.folders.srv, dest).replace(/\\/g, '/')}'`;
341
+ using = `using { ${service} as external } from './${path.relative(cds.env.folders.srv, dest).replace(/\\/g, '/')}';`;
342
342
  }
343
343
  const message = `\n[cds] - imported API to ${path.relative(cwd, dest)}
344
- > use it in your CDS models through the like of:
344
+ > use it in your CDS models through the likes of:
345
345
 
346
346
  ${require('../util/term').info(using)}
347
347
  `;
@@ -510,7 +510,8 @@ async function postProcess(filePath, options, csn, cwd) {
510
510
  usingStatements.push(`using from './${path.basename(csnFile.replace(`'`, `''`))}';`);
511
511
  }
512
512
 
513
- _printUsageHint(csnFile, services, cwd);
513
+ if (!options.dry)
514
+ _printUsageHint(csnFile, services, cwd);
514
515
 
515
516
  if (!options.dry && !options.no_copy)
516
517
  await _copyToSrvExternal(filePath, destFilePath, cwd);
@@ -12,7 +12,7 @@ const messages = {
12
12
  FILE_MODIFIED: 'The file with the same name exists and is been modified. Use --force flag to forcefully overwrite the file.',
13
13
  OUTPUT_FILE_MISMATCH: 'Specify the file extension that --as option supports.',
14
14
  INVALID_AS_OPTION: 'For --as option, please specify one of these output file format [cds, csn, json].',
15
- UNSUPPORTED_FROM_OPTION: 'For --from option, please specify one of these types [edmx, openapi, asyncapi, rfc]. Make sure the required importer plugin is installed.',
15
+ UNSUPPORTED_FROM_OPTION: `No importer is installed for type '{0}'. Known import kinds are: {1}. Make sure the required importer plugin is installed.`,
16
16
  INCORRECT_EXTENSION: 'If --as option is not provided, then please provide either .json or .csn output file extension.',
17
17
  INCORRECT_INPUT_NAMESPACES: 'Invalid value for --include-namespaces option. Specify namespaces as comma-separated list or "*"',
18
18
  MISSING_ASSOCIATION: 'Association is missing in the input file provided. Please check the edmx file.',
@@ -38,6 +38,7 @@ module.exports = class ParserContext {
38
38
  this.schemaAliasToNamespace = {};
39
39
  this.schemaToSchemaDataIndex = {};
40
40
  this.schemaToPrimarySchema = {};
41
+ this.schemaDetails = {};
41
42
  this.vocabNamespaceToAlias = {};
42
43
  this.entityToBoundActions = {};
43
44
  this.entityToBoundFunctions = {};