@sap/cds-dk 6.8.2 → 7.0.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 (218) hide show
  1. package/bin/activate.js +15 -7
  2. package/bin/add.js +29 -15
  3. package/bin/bind.js +49 -116
  4. package/bin/build.js +114 -0
  5. package/bin/cds.js +67 -24
  6. package/bin/compile/index.js +2 -2
  7. package/bin/deploy/index.js +46 -39
  8. package/bin/deploy/to-hana.js +11 -8
  9. package/bin/deploy/to-postgres.js +8 -2
  10. package/bin/deploy/to-sqlite.js +0 -1
  11. package/bin/env.js +75 -41
  12. package/bin/eval.js +3 -3
  13. package/bin/extend.js +17 -10
  14. package/bin/help.js +22 -21
  15. package/bin/import.js +10 -7
  16. package/bin/init.js +38 -17
  17. package/bin/login.js +69 -55
  18. package/bin/logout.js +12 -9
  19. package/bin/migrate.js +10 -4
  20. package/bin/pull.js +12 -5
  21. package/bin/push.js +24 -13
  22. package/bin/repl.js +1 -0
  23. package/bin/run.js +25 -0
  24. package/bin/subscribe.js +13 -8
  25. package/bin/unsubscribe.js +15 -8
  26. package/bin/upgrade.js +15 -9
  27. package/bin/util/log.js +24 -0
  28. package/bin/version.js +124 -0
  29. package/bin/watch.js +1 -35
  30. package/bin/watched.js +39 -0
  31. package/lib/bind/KubernetesApiAccessor.js +2 -2
  32. package/lib/bind/bindingManager.js +1 -1
  33. package/lib/bind/bindingUtil.js +0 -9
  34. package/lib/bind/cfKeyProvider.js +10 -72
  35. package/lib/bind/index.js +114 -0
  36. package/lib/bind/kubernetesCredentialsProvider.js +2 -2
  37. package/lib/bind/userCdsrcJsonStorageProvider.js +7 -44
  38. package/lib/build/buildTaskEngine.js +355 -0
  39. package/lib/build/buildTaskFactory.js +284 -0
  40. package/lib/build/buildTaskHandler.js +240 -0
  41. package/lib/build/buildTaskProvider.js +22 -0
  42. package/lib/build/buildTaskProviderFactory.js +182 -0
  43. package/lib/build/constants.js +56 -0
  44. package/lib/build/index.js +21 -0
  45. package/lib/build/provider/buildTaskHandlerEdmx.js +83 -0
  46. package/lib/build/provider/buildTaskHandlerFeatureToggles.js +132 -0
  47. package/lib/build/provider/buildTaskHandlerInternal.js +228 -0
  48. package/lib/build/provider/buildTaskProviderInternal.js +370 -0
  49. package/lib/build/provider/fiori/index.js +167 -0
  50. package/lib/build/provider/hana/2migration.js +179 -0
  51. package/lib/build/provider/hana/index.js +522 -0
  52. package/lib/build/provider/hana/migrationtable.js +472 -0
  53. package/lib/build/provider/hana/template/.hdiconfig-all +163 -0
  54. package/lib/build/provider/hana/template/.hdiconfig-cloud +137 -0
  55. package/lib/build/provider/hana/template/.hdinamespace +4 -0
  56. package/lib/build/provider/hana/template/package.json +12 -0
  57. package/lib/build/provider/hana/template/undeploy.json +5 -0
  58. package/lib/build/provider/java/index.js +93 -0
  59. package/lib/build/provider/java-cf/index.js +1 -0
  60. package/lib/build/provider/mtx/index.js +37 -0
  61. package/lib/build/provider/mtx/resourcesTarBuilder.js +94 -0
  62. package/lib/build/provider/mtx-extension/index.js +131 -0
  63. package/lib/build/provider/mtx-sidecar/index.js +138 -0
  64. package/lib/build/provider/node-cf/index.js +1 -0
  65. package/lib/build/provider/nodejs/index.js +178 -0
  66. package/lib/build/util.js +263 -0
  67. package/lib/cds.js +9 -21
  68. package/lib/client/auth_manager.js +39 -16
  69. package/lib/client/helper/dependency_helper.js +7 -6
  70. package/lib/client/push.js +2 -1
  71. package/lib/client/settings_manager.js +10 -39
  72. package/lib/deploy/to-hana/hana.js +273 -0
  73. package/lib/deploy/to-hana/hdiDeployUtil.js +68 -0
  74. package/lib/deploy/to-hana/mtaUtil.js +171 -0
  75. package/lib/import/asyncapi/importAsyncAPI.js +3 -2
  76. package/lib/import/asyncapi/index.js +5 -2
  77. package/lib/import/asyncapi/parserContext.js +67 -26
  78. package/lib/import/asyncapi/utils.js +34 -3
  79. package/lib/import/common.js +40 -30
  80. package/lib/import/importapi.js +36 -35
  81. package/lib/index.js +12 -17
  82. package/lib/init/constants.js +36 -71
  83. package/lib/init/index.js +18 -52
  84. package/lib/init/template/_merging/registry-mta.js +21 -3
  85. package/lib/init/template/approuter/files/mta.yaml.hbs +29 -29
  86. package/lib/init/template/approuter/files/values.yaml.hbs +5 -5
  87. package/lib/init/template/approuter/files/xs-app.json.hbs +8 -2
  88. package/lib/init/template/approuter/index.js +7 -8
  89. package/lib/init/template/auditlog/files/mta.yaml.hbs +5 -5
  90. package/lib/init/template/auditlog/files/package.json.hbs +13 -13
  91. package/lib/init/template/auditlog/index.js +3 -3
  92. package/lib/init/template/cf-manifest/files/manifest.yml.hbs +22 -22
  93. package/lib/init/template/cf-manifest/files/services-manifest.yml.hbs +3 -3
  94. package/lib/init/template/connectivity/files/mta.yaml.hbs +14 -0
  95. package/lib/init/template/connectivity/files/package.json.hbs +17 -0
  96. package/lib/init/template/connectivity/index.js +21 -4
  97. package/lib/init/template/data/index.js +5 -1
  98. package/lib/init/template/destination/files/mta.yaml.hbs +14 -0
  99. package/lib/init/template/destination/files/package.json.hbs +17 -0
  100. package/lib/init/template/{destinations → destination}/index.js +19 -13
  101. package/lib/init/template/enterprise-messaging/files/event-mesh.json.hbs +3 -3
  102. package/lib/init/template/enterprise-messaging/files/mta.yaml.hbs +5 -5
  103. package/lib/init/template/enterprise-messaging/files/package.json.hbs +9 -9
  104. package/lib/init/template/enterprise-messaging/files/xs-security.json.hbs +19 -0
  105. package/lib/init/template/enterprise-messaging/index.js +27 -6
  106. package/lib/init/template/enterprise-messaging-shared/files/event-mesh.json.hbs +3 -3
  107. package/lib/init/template/enterprise-messaging-shared/files/mta.yaml.hbs +5 -5
  108. package/lib/init/template/enterprise-messaging-shared/files/package.json.hbs +9 -9
  109. package/lib/init/template/enterprise-messaging-shared/index.js +4 -5
  110. package/lib/init/template/extensibility/files/package.json.hbs +10 -10
  111. package/lib/init/template/extensibility/index.js +8 -9
  112. package/lib/init/template/file-based-messaging/files/package.json.hbs +9 -9
  113. package/lib/init/template/file-based-messaging/index.js +3 -4
  114. package/lib/init/template/hana/files/db/package.json +1 -1
  115. package/lib/init/template/hana/files/mta.yaml.hbs +21 -21
  116. package/lib/init/template/hana/files/package.json.hbs +16 -22
  117. package/lib/init/template/hana/files/values.yaml.hbs +1 -1
  118. package/lib/init/template/hana/index.js +5 -15
  119. package/lib/init/template/helm/files/Chart.yaml.hbs +4 -4
  120. package/lib/init/template/helm/files/values.yaml.hbs +6 -6
  121. package/lib/init/template/helm/index.js +5 -10
  122. package/lib/init/template/helm/subcharts/content-deployment/Chart.yaml +2 -0
  123. package/lib/init/template/helm/subcharts/content-deployment/templates/_helpers.tpl +37 -31
  124. package/lib/init/template/helm/subcharts/content-deployment/templates/job.yaml +13 -14
  125. package/lib/init/template/helm/subcharts/content-deployment/templates/secret.yaml +2 -2
  126. package/lib/init/template/helm/subcharts/content-deployment/templates/service-account.yaml +1 -1
  127. package/lib/init/template/helm/subcharts/content-deployment/templates/service-binding.yaml +3 -3
  128. package/lib/init/template/helm/subcharts/content-deployment/values.md +116 -116
  129. package/lib/init/template/helm/subcharts/service-instance/Chart.yaml +2 -0
  130. package/lib/init/template/helm/subcharts/service-instance/templates/_helpers.tpl +9 -9
  131. package/lib/init/template/helm/subcharts/service-instance/templates/serviceinstance.yaml +2 -2
  132. package/lib/init/template/helm/subcharts/service-instance/values.md +15 -15
  133. package/lib/init/template/helm/subcharts/web-application/Chart.yaml +2 -0
  134. package/lib/init/template/helm/subcharts/web-application/templates/NOTES.txt +15 -0
  135. package/lib/init/template/helm/subcharts/web-application/templates/_helpers.tpl +23 -23
  136. package/lib/init/template/helm/subcharts/web-application/templates/api-rule.yaml +4 -4
  137. package/lib/init/template/helm/subcharts/web-application/templates/deployment.yaml +17 -17
  138. package/lib/init/template/helm/subcharts/web-application/templates/network-policy.yaml +24 -3
  139. package/lib/init/template/helm/subcharts/web-application/templates/pod-disruption-budget.yaml +3 -3
  140. package/lib/init/template/helm/subcharts/web-application/templates/secret.yaml +2 -2
  141. package/lib/init/template/helm/subcharts/web-application/templates/service-account.yaml +1 -1
  142. package/lib/init/template/helm/subcharts/web-application/templates/service-binding.yaml +3 -3
  143. package/lib/init/template/helm/subcharts/web-application/templates/service.yaml +3 -3
  144. package/lib/init/template/helm/subcharts/web-application/values.md +141 -123
  145. package/lib/init/template/helm/subcharts/web-application/values.schema.json +7 -0
  146. package/lib/init/template/helm/subcharts/web-application/values.yaml +1 -0
  147. package/lib/init/template/html5-repo/index.js +4 -5
  148. package/lib/init/template/java/index.js +5 -5
  149. package/lib/init/template/kibana-logging/files/mta.yaml.hbs +5 -5
  150. package/lib/init/template/kibana-logging/files/package.json.hbs +9 -9
  151. package/lib/init/template/kibana-logging/index.js +3 -4
  152. package/lib/init/template/lint/index.js +3 -3
  153. package/lib/init/template/mta/files/mta.yaml.hbs +14 -14
  154. package/lib/init/template/mta/files/package.json +5 -0
  155. package/lib/init/template/mta/index.js +12 -13
  156. package/lib/init/template/mtx/index.js +2 -2
  157. package/lib/init/template/multitenancy/files/mta.yaml.hbs +29 -27
  158. package/lib/init/template/multitenancy/files/package.json.hbs +17 -14
  159. package/lib/init/template/multitenancy/files/package.sidecar.json.hbs +7 -7
  160. package/lib/init/template/multitenancy/files/values.yaml.hbs +19 -19
  161. package/lib/init/template/multitenancy/index.js +14 -16
  162. package/lib/init/template/nodejs/files/package.json.hbs +1 -1
  163. package/lib/init/template/nodejs/index.js +5 -8
  164. package/lib/init/template/redis-messaging/files/mta.yaml.hbs +5 -5
  165. package/lib/init/template/redis-messaging/files/package.json.hbs +9 -9
  166. package/lib/init/template/redis-messaging/index.js +4 -5
  167. package/lib/init/template/sample/files/app/_i18n/i18n.properties +43 -0
  168. package/lib/init/template/sample/files/app/_i18n/i18n_de.properties +14 -0
  169. package/lib/init/template/sample/files/app/admin-books/fiori-service.cds +114 -0
  170. package/lib/init/template/sample/files/app/admin-books/webapp/Component.js +8 -0
  171. package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n.properties +11 -0
  172. package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +129 -0
  173. package/lib/init/template/sample/files/app/appconfig/fioriSandboxConfig.json +57 -0
  174. package/lib/init/template/sample/files/app/common.cds +263 -0
  175. package/lib/init/template/sample/files/app/index.html +30 -0
  176. package/lib/init/template/sample/files/app/services.cds +5 -0
  177. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Authors.csv +5 -0
  178. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Books.csv +6 -0
  179. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Books_texts.csv +5 -0
  180. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Genres.csv +16 -0
  181. package/lib/init/template/sample/files/db/schema.cds +31 -0
  182. package/lib/init/template/sample/files/nodejs/admin-service.js +12 -0
  183. package/lib/init/template/sample/files/nodejs/cat-service.js +26 -0
  184. package/lib/init/template/sample/files/srv/admin-service.cds +5 -0
  185. package/lib/init/template/sample/files/srv/cat-service.cds +16 -0
  186. package/lib/init/template/sample/index.js +24 -0
  187. package/lib/init/template/sample-tiny/index.js +39 -0
  188. package/lib/init/template/samples/index.js +5 -56
  189. package/lib/init/template/sqlite/files/package.json.hbs +13 -13
  190. package/lib/init/template/sqlite/index.js +2 -2
  191. package/lib/init/template/templateBase.js +24 -1
  192. package/lib/init/template/toggles/files/package.json.hbs +10 -10
  193. package/lib/init/template/toggles/index.js +3 -3
  194. package/lib/init/template/typer/files/gitignore +1 -0
  195. package/lib/init/template/typer/files/package.json +8 -0
  196. package/lib/init/template/typer/files/xsconfig.json +9 -0
  197. package/lib/init/template/typer/index.js +36 -0
  198. package/lib/init/template/xsuaa/files/mta.yaml.hbs +12 -12
  199. package/lib/init/template/xsuaa/files/package.json.hbs +13 -13
  200. package/lib/init/template/xsuaa/files/values.yaml.hbs +8 -8
  201. package/lib/init/template/xsuaa/index.js +8 -19
  202. package/lib/init/util/merge.js +73 -59
  203. package/lib/init/util/mvn.js +21 -17
  204. package/lib/init/util/projectReader.js +49 -87
  205. package/lib/init/util/xml.js +14 -23
  206. package/lib/util/cfUtil.js +366 -0
  207. package/lib/util/command.js +3 -3
  208. package/lib/util/gitUtil.js +55 -0
  209. package/lib/util/version.js +27 -0
  210. package/npm-shrinkwrap.json +339 -277
  211. package/package.json +6 -6
  212. package/CHANGELOG.md +0 -1453
  213. package/bin/build/all.js +0 -88
  214. package/bin/build/index.js +0 -82
  215. package/lib/compile/index.js +0 -14
  216. /package/lib/init/template/{samples → sample-tiny}/files/nodejs/db/data/my.bookshop-Books.csv +0 -0
  217. /package/lib/init/template/{samples → sample-tiny}/files/nodejs/db/data-model.cds +0 -0
  218. /package/lib/init/template/{samples → sample-tiny}/files/nodejs/srv/cat-service.cds.hbs +0 -0
@@ -0,0 +1,370 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+ const cds = require('../../cds')
4
+ const { hasJavaNature, getDefaultModelOptions, BuildError, hasOptionValue } = require('../util')
5
+ const BuildTaskProvider = require('../buildTaskProvider')
6
+
7
+ const { FILE_EXT_CDS, BUILD_TASK_HANA, BUILD_TASK_FIORI, BUILD_TASK_JAVA, BUILD_TASK_JAVA_CF, BUILD_TASK_NODEJS, BUILD_TASK_NODE_CF, BUILD_TASK_MTX,
8
+ BUILD_TASK_PREFIX, BUILD_TASKS, BUILD_TASK_MTX_SIDECAR, MTX_SIDECAR_FOLDER, BUILD_TASK_MTX_EXTENSION, NODEJS_MODEL_EXCLUDE_LIST,
9
+ DEFAULT_BUILT_IN_MODELS } = require("../constants")
10
+
11
+ class BuildTaskProviderInternal extends BuildTaskProvider {
12
+ constructor(logger) {
13
+ super()
14
+ this._logger = logger
15
+ }
16
+ get logger() {
17
+ return this._logger
18
+ }
19
+
20
+ canHandleTask(task) {
21
+ return BUILD_TASKS.includes(task.for)
22
+ || task.use && task.use.startsWith(BUILD_TASK_PREFIX)
23
+ // support different build task implementations of the same build task ID defined by 'for'
24
+ //return (BUILD_TASKS.includes(task.for) && !task.use) || (task.use && task.use.startsWith(BUILD_TASK_PREFIX))
25
+ }
26
+
27
+ loadHandler(task) {
28
+ return require(`./${BuildTaskProviderInternal._getForValueFromTask(task)}`)
29
+ }
30
+
31
+ async lookupTasks(tasks) {
32
+ return this._createTasks(tasks, tasks.length > 0)
33
+ }
34
+
35
+ async applyTaskDefaults(task) {
36
+ const taskFor = BuildTaskProviderInternal._getForValueFromTask(task)
37
+ task.for = task.for || taskFor
38
+ task.use = task.use || `${BUILD_TASK_PREFIX}/${taskFor}`
39
+
40
+ // src folder needs to be initialized first
41
+ BuildTaskProviderInternal._setDefaultSrcFolder(task)
42
+
43
+ BuildTaskProviderInternal._setDefaultModel(task)
44
+ }
45
+
46
+ async _createTasks(tasks, addRequiredTasks) {
47
+ let db = typeof cds.env.folders.db === "string" ? [BuildTaskProviderInternal._normalizePath(cds.env.folders.db)] : cds.env.folders.db
48
+ let srv = typeof cds.env.folders.srv === "string" ? [BuildTaskProviderInternal._normalizePath(cds.env.folders.srv)] : cds.env.folders.srv
49
+
50
+ const dbOptions = {
51
+ model: []
52
+ }
53
+ const srvOptions = {
54
+ model: []
55
+ }
56
+ if (Array.isArray(db) && db.length > 0) {
57
+ db = BuildTaskProviderInternal._getModuleFolder(db) || null
58
+ if (!db && !addRequiredTasks) { // log once
59
+ this.logger.debug("No database module found")
60
+ }
61
+ }
62
+ if (Array.isArray(srv) && srv.length > 0) {
63
+ srv = BuildTaskProviderInternal._getModuleFolder(srv) || null
64
+ if (!srv && !addRequiredTasks) { // log once
65
+ this.logger.debug("No service module found")
66
+ }
67
+ }
68
+ // create required build tasks
69
+ if (addRequiredTasks) {
70
+ const mtxTask = tasks.find(task => task.for === BUILD_TASK_MTX || task.for === BUILD_TASK_MTX_SIDECAR)
71
+ if (mtxTask) {
72
+ //restore used tasks as they might have been filtered by 'cds build --for mtx'
73
+ if (mtxTask._uses) {
74
+ mtxTask._uses.map(use => use.task).forEach(task => {
75
+ if (!tasks.includes(task)) {
76
+ tasks.push(task)
77
+ }
78
+ })
79
+ }
80
+ let dbTask = tasks.find(task => task.for === BUILD_TASK_HANA)
81
+ if (!dbTask) {
82
+ // db task might be missing if mtx task is enforced by 'cds build --for mtx'
83
+ dbTask = this._createDbTask(db, dbOptions)
84
+ if (dbTask) {
85
+ tasks.push(dbTask)
86
+ }
87
+ }
88
+ if (dbTask && (!mtxTask._uses || !mtxTask._uses.map(use => use.task).includes(dbTask))) {
89
+ const use = { task: dbTask }
90
+ mtxTask._uses ? mtxTask._uses.push(use) : mtxTask._uses = [use]
91
+ }
92
+ }
93
+ } else {
94
+ !db && this.logger._debug && this.logger.debug(`project doesn't have a database module '${cds.env.folders.db}'`)
95
+ !srv && this.logger._debug && this.logger.debug(`project doesn't have a service module '${cds.env.folders.srv}'`)
96
+
97
+ // create hana build task
98
+ const dbTask = this._createDbTask(db, dbOptions)
99
+ if (dbTask) {
100
+ tasks.push(dbTask)
101
+ }
102
+ // create java or node build task
103
+ const srvTask = this._createSrvTask(srv, srvOptions)
104
+ if (srvTask) {
105
+ tasks.push(srvTask)
106
+ }
107
+
108
+ // create mtx build task
109
+ const mtxTask = this._createMtxTask(srv, tasks)
110
+ if (mtxTask) {
111
+ tasks.push(mtxTask)
112
+ }
113
+ }
114
+ }
115
+
116
+ _createDbTask(src, taskOptions) {
117
+ this.logger.debug("determining database kind")
118
+ if (!src || BuildTaskProviderInternal._isMtxExtension()) {
119
+ return null
120
+ }
121
+ let task = null
122
+ if (this._useHana()) {
123
+ this.logger.debug("found HANA database")
124
+ // legacy build supports dest property
125
+ const compileDest = cds.env.data?.dest
126
+ if (compileDest) {
127
+ //../db/src/gen
128
+ // compileDest is relative to src folder in modular build - resolve correctly
129
+ taskOptions.compileDest = path.relative(path.resolve(cds.root, src), path.resolve(cds.root, compileDest))
130
+ }
131
+ task = {
132
+ src: src,
133
+ for: BUILD_TASK_HANA,
134
+ options: taskOptions
135
+ }
136
+ } else {
137
+ this.logger.debug("found sqlite database - skipping HANA build task")
138
+ }
139
+ return task
140
+ }
141
+
142
+ _useHana() {
143
+ if (cds.env.requires.db?.kind === "hana" || cds.env.requires.db?.dialect === "hana") {
144
+ return true
145
+ }
146
+ // false if other db has been defined
147
+ if (cds.env.requires.db?.kind) {
148
+ return false
149
+ }
150
+ // check whether cds config represents a legacy build system config for which requires.db was not configured
151
+ // Note: compat layer sets requires.db: {}
152
+ const userEnv = cds.env.for("cds", cds.root, false)
153
+ return userEnv && (userEnv.data?.model || userEnv.service?.model)
154
+ }
155
+
156
+ _createMtxTask(src, tasks) {
157
+ this.logger.debug("determining MTX architecture")
158
+
159
+ // preserve order of creation
160
+ if (BuildTaskProviderInternal._isMtxExtension()) {
161
+ this.logger.debug("MTX extension app")
162
+ return { for: BUILD_TASK_MTX_EXTENSION }
163
+ }
164
+
165
+ if (BuildTaskProviderInternal._isMtxs()) {
166
+ const sidecarPath = path.join(cds.root, MTX_SIDECAR_FOLDER)
167
+ let sidecarEnv
168
+ if (fs.existsSync(sidecarPath)) {
169
+ sidecarEnv = cds.env.for("cds", sidecarPath)
170
+ }
171
+ if (
172
+ cds.env.profiles?.includes('with-mtx-sidecar') || // new presets
173
+ cds.env.requires["cds.xt.ModelProviderService"]?.external // for compatibility with former mtxs presets
174
+ ) {
175
+ this.logger.debug("MTX app with sidecar")
176
+ if (!sidecarEnv) {
177
+ throw new BuildError(`MTX sidecar directory '${sidecarPath}' not existing. Custom build task configuration required if the folder is named differently.`)
178
+ }
179
+ if (!sidecarEnv.requires["cds.xt.ModelProviderService"]?._in_sidecar) {
180
+ // correct profile might have been set, but couldn't be resolved
181
+ if (sidecarEnv.profiles?.includes('mtx-sidecar')) {
182
+ throw new BuildError('MTX configuration cannot be resolved. Make sure an up-to-date version of @sap/cds-mtxs is installed.')
183
+ }
184
+ throw new BuildError(`Invalid MTX sidecar configuration - profile 'mtx-sidecar' not set.`)
185
+ }
186
+ return { for: BUILD_TASK_MTX_SIDECAR }
187
+ }
188
+ if (sidecarEnv?.requires["cds.xt.ModelProviderService"]?._in_sidecar || sidecarEnv?.profiles?.includes('mtx-sidecar')) {
189
+ throw new BuildError(`MTX sidecar requires profile 'with-mtx-sidecar' in project root configuration.`)
190
+ }
191
+
192
+ if (cds.env.requires["cds.xt.ModelProviderService"]?._in_sidecar || cds.env.profiles?.includes('mtx-sidecar')) {
193
+ // cds build is executed in sidecar folder
194
+ throw new BuildError(`Seems that you are executing 'cds build' in the 'mtx/sidecar' folder. Execute 'cds build' in the project root folder instead.`)
195
+ }
196
+
197
+ // mtxs with nodejs, but without sidecar
198
+ if (!tasks.some(task => task.for === BUILD_TASK_JAVA || task.for === BUILD_TASK_JAVA_CF)) {
199
+ this.logger.debug("MTX app without sidecar")
200
+ return {
201
+ for: BUILD_TASK_MTX,
202
+ src
203
+ }
204
+ }
205
+ }
206
+ }
207
+
208
+ _createSrvTask(src, taskOptions) {
209
+ this.logger.debug("determining implementation technology")
210
+ if (!src || BuildTaskProviderInternal._isMtxExtension()) {
211
+ return null
212
+ }
213
+ let task = null
214
+ if (BuildTaskProviderInternal._hasJavaNature(src)) {
215
+ task = this._createJavaTask(src, taskOptions)
216
+ } else {
217
+ task = this._createNodeTask(src, taskOptions)
218
+ }
219
+ return task
220
+ }
221
+
222
+ _createJavaTask(src, taskOptions) {
223
+ this.logger.debug("found implementation technology Java")
224
+ // legacy build supports dest property
225
+ const compileDest = cds.env.service?.dest
226
+ if (compileDest) {
227
+ // compileDest is relative to src folder in modular build - resolve correctly
228
+ taskOptions.compileDest = path.relative(path.resolve(cds.root, src), path.resolve(cds.root, compileDest))
229
+ }
230
+ return {
231
+ src: src,
232
+ for: BUILD_TASK_JAVA,
233
+ options: taskOptions
234
+ }
235
+ }
236
+
237
+ _createNodeTask(src, taskOptions) {
238
+ this.logger.debug("found implementation technology Nodejs")
239
+ return {
240
+ src: src,
241
+ for: BUILD_TASK_NODEJS,
242
+ options: taskOptions
243
+ }
244
+ }
245
+
246
+ static _isMtxExtension() {
247
+ return !!cds.env.extends
248
+ }
249
+
250
+ /**
251
+ * Distinguishes whether the Nodejs project is a Streamlined MTX (cds >=6) or an old MTX project.
252
+ */
253
+ static _isMtxs() {
254
+ return cds.env.requires.toggles
255
+ || cds.env.profiles.includes('with-mtx-sidecar')
256
+ || cds.env.requires['cds.xt.ModelProviderService']
257
+ || (typeof cds.env.requires.multitenancy === "object")
258
+ }
259
+
260
+ static _setDefaultSrcFolder(task) {
261
+ switch (task.for) {
262
+ case BUILD_TASK_HANA:
263
+ task.src = task.src || this._normalizePath(cds.env.folders.db)
264
+ break
265
+ case BUILD_TASK_JAVA:
266
+ case BUILD_TASK_JAVA_CF:
267
+ case BUILD_TASK_NODEJS:
268
+ case BUILD_TASK_NODE_CF:
269
+ task.src = task.src || this._normalizePath(cds.env.folders.srv)
270
+ break
271
+ case BUILD_TASK_FIORI:
272
+ task.src = task.src || this._normalizePath(cds.env.folders.app)
273
+ break
274
+ case BUILD_TASK_MTX_SIDECAR:
275
+ task.src = task.src || MTX_SIDECAR_FOLDER
276
+ break
277
+ case BUILD_TASK_MTX_EXTENSION:
278
+ task.src = task.src || "."
279
+ break
280
+ case BUILD_TASK_MTX:
281
+ require('../../util/version').checkMtx(cds)
282
+ // mtxs with nodejs, but without sidecar
283
+ task.src = task.src || this._normalizePath(cds.env.folders.srv)
284
+ break
285
+ default:
286
+ throw new Error(`Unknown build task '${task.use || task.for}'`)
287
+ }
288
+ }
289
+
290
+ static _setDefaultModel(task) {
291
+ let taskModelPaths = task.options?.model
292
+ if (taskModelPaths && !Array.isArray(taskModelPaths)) {
293
+ taskModelPaths = [taskModelPaths]
294
+ }
295
+ task.options = task.options || {}
296
+ let defaultModelPaths = []
297
+ // add default models to custom build tasks if this hasn't been disabled
298
+ if (taskModelPaths?.length) {
299
+ if (!hasOptionValue(task.options?.[DEFAULT_BUILT_IN_MODELS], false)) {
300
+ // get the built-in models
301
+ defaultModelPaths = getDefaultModelOptions().filter(p => p.match('@sap/cds'))
302
+ }
303
+ } else {
304
+ defaultModelPaths = getDefaultModelOptions()
305
+ defaultModelPaths.push(task.src)
306
+ if (hasOptionValue(task.options?.[DEFAULT_BUILT_IN_MODELS], false)) {
307
+ // all default models except the built-in models
308
+ defaultModelPaths = defaultModelPaths.filter(p => !p.match('@sap/cds'))
309
+ }
310
+ }
311
+
312
+ // REVISIT: filter nodejs bootstrap service models for Java - issues/12770#issuecomment-1805719
313
+ if (this._hasJavaNature(cds.env.folders.srv)) {
314
+ defaultModelPaths = defaultModelPaths.filter(p => !NODEJS_MODEL_EXCLUDE_LIST.includes(p))
315
+ }
316
+ // also add built-in models to custom build tasks already containing model options
317
+ if (!taskModelPaths || defaultModelPaths.length) {
318
+ task.options.model = [...new Set(taskModelPaths?.length ? taskModelPaths.concat(defaultModelPaths) : defaultModelPaths)]
319
+ }
320
+ }
321
+
322
+ /**
323
+ * Returns whether this project is a java project or not.
324
+ * @param {string} srv - the folder name of the service module
325
+ */
326
+ static _hasJavaNature(srv) {
327
+ srv = this._getModuleFolder(Array.isArray(srv) ? srv : [srv])
328
+ return hasJavaNature([cds.root, srv && path.join(cds.root, srv)])
329
+ }
330
+
331
+ static _getForValueFromTask(task) {
332
+ return task.for ? task.for : task.use && task.use.substring(BUILD_TASK_PREFIX.length + 1)
333
+ }
334
+
335
+ /**
336
+ * For valid paths remove trailing '/'. Otherwise return as is - important!!
337
+ * @param {*} dir
338
+ */
339
+ static _normalizePath(dir) {
340
+ return typeof dir === "string" ? dir.replace(/\/$/, '') : dir
341
+ }
342
+
343
+ /**
344
+ * Determines the module folder from the past list that may represent files or folders w or w/o .cds file extension.
345
+ * @param {Array} filesOrFolders
346
+ */
347
+ static _getModuleFolder(filesOrFolders) {
348
+ const resources = [...filesOrFolders]
349
+ filesOrFolders.forEach(fileOrFolder => {
350
+ if (path.extname(fileOrFolder) !== FILE_EXT_CDS) {
351
+ resources.push(fileOrFolder + FILE_EXT_CDS)
352
+ }
353
+ })
354
+ return resources.reduce((acc, resource) => {
355
+ if (!acc) {
356
+ let resourcePath = path.resolve(cds.root, resource)
357
+ if (fs.existsSync(resourcePath)) {
358
+ if (fs.lstatSync(resourcePath).isDirectory()) {
359
+ acc = resource
360
+ } else {
361
+ // represents file
362
+ acc = path.dirname(resource)
363
+ }
364
+ }
365
+ }
366
+ return acc
367
+ }, null)
368
+ }
369
+ }
370
+ module.exports = BuildTaskProviderInternal
@@ -0,0 +1,167 @@
1
+ const path = require('path')
2
+ const cds = require('../../../cds')
3
+ const BuildTaskHandlerEdmx = require('../buildTaskHandlerEdmx')
4
+ const URL = require('url')
5
+ const { getProperty, relativePaths } = require('../../util')
6
+
7
+ const { OUTPUT_MODE, OUTPUT_MODE_RESULT, BUILD_TASK_FIORI, CDS_CONFIG_PATH_SEP, OUTPUT_MODE_FILESYSTEM } = require('../../constants')
8
+
9
+ /**
10
+ * With cds 4 service metadata for the UI service binding is no longer created by default.
11
+ * For SAP Web IDE Full-Stack compatibility a corresponding metadata.xml is still generated.
12
+ * Though, 'fiori' build tasks can still be configured for UI5 mock server usage - see https://github.wdf.sap.corp/cap/issues/issues/8673
13
+ */
14
+ class FioriAppModuleBuilder extends BuildTaskHandlerEdmx {
15
+ init() {
16
+ // enforce
17
+ this._result = {
18
+ dest: this.task.dest,
19
+ edmx: new Map(),
20
+ languages: new Set(),
21
+ services: new Set()
22
+ }
23
+ }
24
+
25
+ async prepare() {
26
+ this.context.for = this.context.for || {}
27
+ this.context.for[BUILD_TASK_FIORI] = this.context.for[BUILD_TASK_FIORI] || {}
28
+
29
+ // cache for later use across multiple FioriAppModuleBuilder instances
30
+ const fioriBuildOptions = this.context.for[BUILD_TASK_FIORI]
31
+ fioriBuildOptions.appModel = new Map()
32
+ fioriBuildOptions.appEdmx = new Map()
33
+
34
+ // group tasks that have a common application root folder
35
+ const appTaskGroups = new Map()
36
+ this.context.tasks.forEach(task => {
37
+ if (task.for === BUILD_TASK_FIORI) {
38
+ const appFolder = path.relative(cds.root, task.src).split(CDS_CONFIG_PATH_SEP)[0]
39
+ appTaskGroups.has(appFolder) ? appTaskGroups.get(appFolder).push(task) : appTaskGroups.set(appFolder, [task])
40
+ }
41
+ })
42
+
43
+ // merge all model references to resolve the model later on
44
+ const appModelGroups = new Map()
45
+ for (let [appFolder, appTaskGroup] of appTaskGroups.entries()) {
46
+ const appModels = new Set()
47
+ appTaskGroup.forEach(task => {
48
+ if (Array.isArray(task.options.model)) {
49
+ task.options.model.forEach(model => appModels.add(model))
50
+ }
51
+ })
52
+ appModelGroups.set(appFolder, appModels)
53
+ }
54
+
55
+ const originFioriBuildOption = fioriBuildOptions[OUTPUT_MODE]
56
+ fioriBuildOptions[OUTPUT_MODE] = OUTPUT_MODE_RESULT
57
+
58
+ try {
59
+ const edmxOptions = { version: cds.env.odata?.version }
60
+ for (let [appFolder, appModelGroup] of appModelGroups.entries()) {
61
+ this.logger.debug(`building module [${appFolder}] using [${this.constructor.name}]`)
62
+ const modelPaths = cds.resolve(Array.from(appModelGroup.values()))
63
+ if (!modelPaths || modelPaths.length === 0) {
64
+ continue
65
+ }
66
+ this.logger._debug && this.logger.debug(`model: ${relativePaths(cds.root, modelPaths).join(", ")}`)
67
+
68
+ //cache model per fiori app root folder
69
+ const options = this.options()
70
+ const model = await cds.load(modelPaths, options)
71
+ fioriBuildOptions.appModel.set(appFolder, model)
72
+
73
+ await this.compileToEdmx(model, null, edmxOptions)
74
+
75
+ // cache edmx per fiori app root folder
76
+ fioriBuildOptions.appEdmx.set(appFolder, this._result.edmx)
77
+ }
78
+ } finally {
79
+ fioriBuildOptions[OUTPUT_MODE] = originFioriBuildOption
80
+ }
81
+ return false
82
+ }
83
+
84
+ /**
85
+ * This version only creates a odata representation for the 'mainService' data source
86
+ * as defined by the fiori wizard - everything else is currently not supported.
87
+ * Therefore errors are only logged, the build does not fail in case a service
88
+ * cannot be resolved based on the defined service URI
89
+ */
90
+ async build() {
91
+ const model = await this.model()
92
+ if (!model) {
93
+ return
94
+ }
95
+ await this._writeEdmxToWebapp(this.task.src, this.task.dest)
96
+ }
97
+
98
+ async _writeEdmxToWebapp(src, dest) {
99
+ const manifestPath = path.join(src, 'webapp', 'manifest.json')
100
+ let manifest
101
+
102
+ try {
103
+ manifest = require(manifestPath)
104
+ } catch (error) {
105
+ this.logger.log(`UI module does not contain a manifest.json [${relativePaths(cds.root, manifestPath)}], skipping build`)
106
+ return
107
+ }
108
+
109
+ const mainService = getProperty(manifest, ['sap.app', 'dataSources', 'mainService'])
110
+ if (!mainService) {
111
+ // no mainService defined - not supported
112
+ this.logger._debug && this.logger.debug(`UI module does not have a datasource [mainService], [${relativePaths(cds.root, manifestPath)}], skipping build`)
113
+ return
114
+ }
115
+
116
+ const localUri = getProperty(mainService, ['settings', 'localUri'])
117
+ const uri = mainService.uri
118
+
119
+ if (!localUri || !uri) {
120
+ this.logger._debug && this.logger.warn(`local uri setting missing for data source [mainService], [${relativePaths(cds.root, manifestPath)}]`)
121
+ return
122
+ }
123
+
124
+ const appFolder = path.relative(cds.root, src).split(CDS_CONFIG_PATH_SEP)[0]
125
+ const model = this.context.for[BUILD_TASK_FIORI].appModel.get(appFolder)
126
+ if (!model) {
127
+ this.logger.error(`failed to load model for service uri ${uri}, data source [mainService]`)
128
+ return
129
+ }
130
+
131
+ const edmx = this._getEdmxForUri(model, appFolder, uri)
132
+ if (!edmx) {
133
+ this.logger.error(`failed to resolve service definition for service uri ${uri}, data source [mainService]`)
134
+ return
135
+ }
136
+
137
+ if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_FILESYSTEM)) {
138
+ const edmxPath = path.resolve(path.join(dest, 'webapp'), this._strippedUrlPath(localUri))
139
+ return this.write(edmx).to(edmxPath)
140
+ }
141
+ }
142
+
143
+ _getEdmxForUri(model, appFolder, uri) {
144
+ const uriSegments = this._strippedUrlPath(uri).split('/')
145
+
146
+ // one segment of the URI has to match a service name
147
+ // NOTE: assumption is that the service definition can be resolved - either by
148
+ // - defining corresponding using statement in annotations model or
149
+ // - adding the service module folder to the model option
150
+ let service = cds.reflect(model).find(service => uriSegments.find(segment => service.name === segment))
151
+
152
+ if (service) {
153
+ const allServices = this.context.for[BUILD_TASK_FIORI].appEdmx.get(appFolder)
154
+ if (allServices) {
155
+ return allServices.get(service.name + ".xml")
156
+ }
157
+ }
158
+ return null
159
+ }
160
+
161
+ _strippedUrlPath(urlString) {
162
+ const url = URL.parse(urlString)
163
+ return url.pathname.replace(/^(\/|\\)/, '').replace(/(\/|\\)$/, '') // strip leading and trailing slash or backslash)
164
+ }
165
+ }
166
+
167
+ module.exports = FioriAppModuleBuilder