@sap/cds 6.1.2 → 6.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/CHANGELOG.md +92 -8
  2. package/apis/cds.d.ts +18 -6
  3. package/apis/connect.d.ts +1 -1
  4. package/apis/cqn.d.ts +1 -1
  5. package/apis/log.d.ts +23 -5
  6. package/apis/ql.d.ts +128 -61
  7. package/apis/services.d.ts +11 -0
  8. package/apis/test.d.ts +61 -0
  9. package/apis/utils.d.ts +15 -0
  10. package/app/fiori/preview.js +1 -0
  11. package/bin/build/buildTaskEngine.js +70 -22
  12. package/bin/build/buildTaskFactory.js +18 -11
  13. package/bin/build/buildTaskHandler.js +1 -1
  14. package/bin/build/buildTaskProviderFactory.js +3 -13
  15. package/bin/build/constants.js +0 -1
  16. package/bin/build/index.js +14 -6
  17. package/bin/build/provider/buildTaskHandlerEdmx.js +2 -3
  18. package/bin/build/provider/buildTaskHandlerFeatureToggles.js +2 -2
  19. package/bin/build/provider/buildTaskHandlerInternal.js +3 -6
  20. package/bin/build/provider/buildTaskProviderInternal.js +51 -39
  21. package/bin/build/provider/fiori/index.js +3 -3
  22. package/bin/build/provider/hana/2migration.js +1 -1
  23. package/bin/build/provider/hana/index.js +34 -27
  24. package/bin/build/provider/java/index.js +6 -7
  25. package/bin/build/provider/mtx/index.js +20 -18
  26. package/bin/build/provider/mtx/resourcesTarBuilder.js +8 -11
  27. package/bin/build/provider/mtx-sidecar/index.js +13 -17
  28. package/bin/build/provider/nodejs/index.js +8 -7
  29. package/bin/build/util.js +22 -4
  30. package/bin/cds.js +8 -4
  31. package/bin/deploy/to-hana/cfUtil.js +53 -18
  32. package/bin/mtx/in-cds.js +1 -0
  33. package/bin/serve.js +37 -30
  34. package/lib/auth/basic-auth.js +33 -0
  35. package/lib/auth/dummy-auth.js +7 -0
  36. package/lib/auth/ias-auth.js +2 -0
  37. package/lib/auth/index.js +31 -0
  38. package/lib/auth/jwt-auth.js +3 -0
  39. package/lib/auth/mocked-users.js +72 -0
  40. package/lib/auth/passport-basic.js +12 -0
  41. package/lib/auth/passport-digest.js +14 -0
  42. package/lib/auth/xsuaa-auth.js +3 -0
  43. package/lib/compile/cds-compile.js +3 -3
  44. package/lib/compile/to/cdl.js +5 -1
  45. package/lib/compile/to/edm.js +8 -0
  46. package/lib/compile/to/gql.js +1 -0
  47. package/lib/compile/to/json.js +30 -5
  48. package/lib/compile/to/sql.js +3 -1
  49. package/lib/core/index.js +5 -1
  50. package/lib/dbs/cds-deploy.js +36 -6
  51. package/lib/env/cds-env.js +15 -5
  52. package/lib/env/cds-requires.js +51 -58
  53. package/lib/env/defaults.js +1 -0
  54. package/lib/env/schemas/cds-package.json +4 -0
  55. package/lib/env/schemas/cds-rc.json +63 -77
  56. package/lib/i18n/localize.js +16 -5
  57. package/lib/index.js +9 -4
  58. package/lib/log/cds-error.js +4 -6
  59. package/lib/log/cds-log.js +89 -53
  60. package/lib/log/service/index.js +1 -0
  61. package/lib/ql/CREATE.js +2 -5
  62. package/lib/ql/DELETE.js +1 -1
  63. package/lib/ql/DROP.js +1 -3
  64. package/lib/ql/INSERT.js +3 -3
  65. package/lib/ql/Query.js +10 -23
  66. package/lib/ql/SELECT.js +1 -2
  67. package/lib/ql/UPDATE.js +2 -2
  68. package/lib/ql/Whereable.js +7 -15
  69. package/lib/ql/cds-ql.js +9 -3
  70. package/lib/req/cds-context.js +11 -3
  71. package/lib/req/context.js +29 -23
  72. package/lib/req/locale.js +9 -5
  73. package/lib/req/request.js +1 -0
  74. package/lib/req/user.js +2 -1
  75. package/lib/srv/cds-connect.js +1 -1
  76. package/lib/srv/cds-serve.js +21 -14
  77. package/lib/srv/middlewares/cds-context.js +29 -0
  78. package/lib/srv/middlewares/ctx-model.js +24 -0
  79. package/lib/srv/middlewares/errors.js +9 -0
  80. package/lib/srv/middlewares/index.js +22 -0
  81. package/lib/srv/middlewares/sap-statistics.js +13 -0
  82. package/lib/srv/middlewares/trace.js +102 -0
  83. package/lib/srv/protocols/_legacy.js +42 -0
  84. package/lib/srv/protocols/graphql.js +39 -0
  85. package/lib/srv/protocols/hcql.js +37 -0
  86. package/lib/srv/protocols/index.js +86 -0
  87. package/lib/srv/protocols/odata-v2-proxy.js +3767 -0
  88. package/lib/srv/protocols/odata-v2.js +26 -0
  89. package/lib/srv/protocols/odata-v4.js +16 -0
  90. package/lib/srv/protocols/rest.js +13 -0
  91. package/lib/srv/srv-api.js +5 -0
  92. package/lib/srv/srv-models.js +4 -6
  93. package/lib/utils/axios.js +3 -2
  94. package/lib/utils/cds-test.js +27 -21
  95. package/lib/utils/cds-utils.js +19 -20
  96. package/lib/utils/tar.js +175 -0
  97. package/libx/_runtime/audit/generic/personal/utils.js +18 -7
  98. package/libx/_runtime/audit/utils/v2.js +1 -0
  99. package/libx/_runtime/auth/index.js +4 -0
  100. package/libx/_runtime/auth/strategies/ias-auth.js +76 -0
  101. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +8 -3
  102. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +15 -4
  103. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -1
  104. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/orderByToCQN.js +1 -1
  105. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +1 -1
  106. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/ResourcePathParser.js +9 -0
  107. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriInfo.js +5 -1
  108. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +12 -0
  109. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/utils/BufferedWriter.js +6 -2
  110. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/RequestValidator.js +47 -7
  111. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
  112. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +0 -2
  113. package/libx/_runtime/cds-services/services/utils/compareJson.js +3 -1
  114. package/libx/_runtime/cds-services/util/assert.js +7 -0
  115. package/libx/_runtime/common/aspects/relation.js +1 -1
  116. package/libx/_runtime/common/composition/data.js +61 -15
  117. package/libx/_runtime/common/composition/delete.js +0 -1
  118. package/libx/_runtime/common/composition/insert.js +0 -1
  119. package/libx/_runtime/common/composition/tree.js +4 -10
  120. package/libx/_runtime/common/composition/update.js +44 -21
  121. package/libx/_runtime/common/generic/auth/capabilities.js +8 -10
  122. package/libx/_runtime/common/generic/crud.js +1 -2
  123. package/libx/_runtime/common/generic/etag.js +4 -4
  124. package/libx/_runtime/common/generic/input.js +21 -6
  125. package/libx/_runtime/common/generic/paging.js +3 -3
  126. package/libx/_runtime/common/generic/put.js +7 -4
  127. package/libx/_runtime/common/generic/sorting.js +4 -4
  128. package/libx/_runtime/common/generic/temporal.js +3 -6
  129. package/libx/_runtime/common/i18n/messages.properties +0 -7
  130. package/libx/_runtime/common/utils/cqn2cqn4sql.js +11 -6
  131. package/libx/_runtime/common/utils/csn.js +0 -28
  132. package/libx/_runtime/common/utils/draft.js +8 -1
  133. package/libx/_runtime/common/utils/path.js +7 -1
  134. package/libx/_runtime/common/utils/propagateForeignKeys.js +122 -0
  135. package/libx/_runtime/common/utils/resolveView.js +2 -3
  136. package/libx/_runtime/common/utils/template.js +2 -3
  137. package/libx/_runtime/db/data-conversion/post-processing.js +3 -44
  138. package/libx/_runtime/db/generic/input.js +6 -6
  139. package/libx/_runtime/db/sql-builder/dataTypes.js +4 -0
  140. package/libx/_runtime/fiori/generic/activate.js +2 -2
  141. package/libx/_runtime/fiori/generic/before.js +40 -72
  142. package/libx/_runtime/fiori/generic/cancel.js +2 -2
  143. package/libx/_runtime/fiori/generic/delete.js +2 -2
  144. package/libx/_runtime/fiori/generic/edit.js +2 -2
  145. package/libx/_runtime/fiori/generic/new.js +3 -5
  146. package/libx/_runtime/fiori/generic/patch.js +49 -43
  147. package/libx/_runtime/fiori/generic/prepare.js +2 -2
  148. package/libx/_runtime/fiori/generic/read.js +27 -37
  149. package/libx/_runtime/fiori/utils/where.js +4 -2
  150. package/libx/_runtime/hana/Service.js +1 -3
  151. package/libx/_runtime/hana/conversion.js +3 -0
  152. package/libx/_runtime/hana/driver.js +33 -3
  153. package/libx/_runtime/hana/dynatrace.js +1 -0
  154. package/libx/_runtime/hana/search2Contains.js +12 -1
  155. package/libx/_runtime/hana/search2cqn4sql.js +10 -27
  156. package/libx/_runtime/hana/streaming.js +1 -0
  157. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +4 -2
  158. package/libx/_runtime/messaging/common-utils/AMQPClient.js +1 -0
  159. package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +5 -2
  160. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -0
  161. package/libx/_runtime/messaging/enterprise-messaging.js +62 -3
  162. package/libx/_runtime/messaging/outbox/utils.js +1 -1
  163. package/libx/_runtime/messaging/redis-messaging.js +1 -0
  164. package/libx/_runtime/remote/Service.js +2 -2
  165. package/libx/_runtime/remote/utils/client.js +35 -11
  166. package/libx/_runtime/remote/utils/data.js +7 -2
  167. package/libx/_runtime/sqlite/Service.js +18 -7
  168. package/libx/_runtime/sqlite/conversion.js +3 -0
  169. package/libx/_runtime/sqlite/convertAssocToOneManaged.js +3 -3
  170. package/libx/_runtime/sqlite/localized.js +8 -8
  171. package/libx/odata/afterburner.js +39 -7
  172. package/libx/odata/cqn2odata.js +6 -3
  173. package/libx/odata/grammar.pegjs +66 -18
  174. package/libx/odata/index.js +3 -2
  175. package/libx/odata/parser.js +1 -1
  176. package/libx/odata/utils.js +2 -0
  177. package/libx/rest/RestAdapter.js +62 -43
  178. package/libx/rest/middleware/input.js +2 -3
  179. package/libx/rest/middleware/parse.js +2 -1
  180. package/libx/rest/middleware/update.js +1 -1
  181. package/package.json +2 -2
  182. package/server.js +5 -4
  183. package/srv/mtx.cds +1 -1
  184. package/srv/mtx.js +4 -24
  185. package/lib/srv/adapters.js +0 -85
  186. package/lib/utils/resources/index.js +0 -48
  187. package/lib/utils/resources/tar.js +0 -49
  188. package/lib/utils/resources/utils.js +0 -11
  189. package/libx/_runtime/db/utils/propagateForeignKeys.js +0 -93
  190. package/libx/_runtime/extensibility/activate.js +0 -69
  191. package/libx/_runtime/extensibility/add.js +0 -50
  192. package/libx/_runtime/extensibility/addExtension.js +0 -72
  193. package/libx/_runtime/extensibility/defaults.js +0 -34
  194. package/libx/_runtime/extensibility/handler/transformREAD.js +0 -121
  195. package/libx/_runtime/extensibility/handler/transformRESULT.js +0 -51
  196. package/libx/_runtime/extensibility/handler/transformWRITE.js +0 -64
  197. package/libx/_runtime/extensibility/linter/allowlist_checker.js +0 -373
  198. package/libx/_runtime/extensibility/linter/annotations_checker.js +0 -113
  199. package/libx/_runtime/extensibility/linter/checker_base.js +0 -20
  200. package/libx/_runtime/extensibility/linter/namespace_checker.js +0 -180
  201. package/libx/_runtime/extensibility/linter.js +0 -32
  202. package/libx/_runtime/extensibility/push.js +0 -118
  203. package/libx/_runtime/extensibility/service.js +0 -38
  204. package/libx/_runtime/extensibility/token.js +0 -57
  205. package/libx/_runtime/extensibility/utils.js +0 -131
  206. package/libx/_runtime/extensibility/validation.js +0 -50
  207. package/libx/_runtime/extensibility/views.js +0 -12
  208. package/srv/extensibility-service.cds +0 -59
  209. package/srv/extensibility-service.js +0 -1
  210. package/srv/extensions.cds +0 -8
  211. package/srv/model-provider.cds +0 -61
  212. package/srv/model-provider.js +0 -143
@@ -1,10 +1,10 @@
1
1
  const fs = require('fs')
2
2
  const path = require('path')
3
3
  const _cds = require('./cds'), { log } = _cds.exec
4
- const BuildTaskProviderFactory = require('./buildTaskProviderFactory')
5
4
  const { sortMessagesSeverityAware, deduplicateMessages, CompilationError } = require('@sap/cds-compiler')
6
- const { relativePaths, BuildError, BuildMessage } = require('./util')
5
+ const { relativePaths, BuildError, BuildMessage, resolveRequiredSapModels } = require('./util')
7
6
  const { OUTPUT_MODE_DEFAULT, SEVERITIES, LOG_LEVELS, LOG_MODULE_NAMES } = require('./constants')
7
+ const BuildTaskProviderFactory = require('./buildTaskProviderFactory')
8
8
  const BuildTaskHandlerInternal = require('./provider/buildTaskHandlerInternal')
9
9
 
10
10
  const COMPILATION_ERROR = 'CompilationError'
@@ -27,7 +27,6 @@ class BuildTaskEngine {
27
27
  }
28
28
 
29
29
  async processTasks(tasks, buildOptions, clean = true) {
30
- const handlers = []
31
30
  const startTime = Date.now()
32
31
 
33
32
  if (buildOptions) {
@@ -49,6 +48,18 @@ class BuildTaskEngine {
49
48
  buildOptions.target = path.resolve(buildOptions.root, this.env.build.target)
50
49
  }
51
50
 
51
+ // validate required @sap namespace models - log only
52
+ const { unresolved, missing } = this._resolveRequiredSapServices(tasks)
53
+ const messages = []
54
+ if (unresolved.length > 0) {
55
+ messages.push(new BuildMessage(`Required CDS service models [${unresolved.join(', ')}] cannot be resolved. Make sure to install the missing npm modules.`))
56
+ }
57
+ if (missing.length > 0) {
58
+ messages.push(new BuildMessage(`Required CDS service models [${missing.join(', ')}] are missing in custom build tasks. Make sure to add the missing models.`))
59
+ }
60
+
61
+ // create build task handlers
62
+ const handlers = []
52
63
  tasks.forEach((task) => {
53
64
  if (task) {
54
65
  const handler = this._createHandler(task, buildOptions)
@@ -62,17 +73,22 @@ class BuildTaskEngine {
62
73
  try {
63
74
  await this._executePrepare(handlers, buildOptions)
64
75
  await this._executeCleanBuildTasks(handlers, buildOptions, clean)
76
+
77
+ // throwing Exception in case of compilation errors
65
78
  const buildResult = await this._executeBuildTasks(handlers, buildOptions)
66
- await this._logBuildOutput(handlers, buildOptions)
67
- this._logMessages(buildOptions, BuildTaskEngine._getHandlerMessages(handlers))
79
+
80
+ await this._writeGenerationLog(handlers, buildOptions)
81
+ this._logBuildOutput(handlers, buildOptions)
82
+ this._logMessages(buildOptions, [...BuildTaskEngine._getHandlerMessages(handlers), ...messages])
68
83
  this._logTimer(startTime, Date.now())
69
84
 
70
85
  return buildResult
71
86
  } catch (error) {
72
- await this._logBuildOutput(handlers, buildOptions, false)
73
- // cds CLI layer is doing the logging if invoked using CLI
87
+ this._logBuildOutput(handlers, buildOptions)
88
+
89
+ // cds CLI layer logs in case of an exception if invoked from CLI
74
90
  if (!buildOptions.cli) {
75
- this._logMessages(buildOptions, BuildTaskEngine._getErrorMessages([error]))
91
+ this._logMessages(buildOptions, [...BuildTaskEngine._getErrorMessages([error]), ...messages])
76
92
  }
77
93
 
78
94
  if (error.name === BuildError.name && error.errors.length > 0 && error.errors[0].constructor.name === COMPILATION_ERROR) {
@@ -128,7 +144,7 @@ class BuildTaskEngine {
128
144
  // clean entire build staging folder once
129
145
  if (buildOptions.target !== buildOptions.root) {
130
146
  this.logger._debug && this.logger.debug(`cleaning staging folder ${buildOptions.target}`)
131
- await fs.promises.rm(buildOptions.target, {force: true, recursive: true})
147
+ await fs.promises.rm(buildOptions.target, { force: true, recursive: true })
132
148
  }
133
149
 
134
150
  const results = await Promise.allSettled(handlers.map((handler) => {
@@ -249,7 +265,7 @@ class BuildTaskEngine {
249
265
  return files.map(file => path.relative(buildOptions.root, file))
250
266
  }
251
267
 
252
- async _logBuildOutput(handlers, buildOptions, writeOutputFile = true) {
268
+ _logBuildOutput(handlers, buildOptions) {
253
269
  // log all generated files
254
270
  const files = this._getBuildOutput(handlers, buildOptions)
255
271
  if (files.length > 0) {
@@ -257,18 +273,18 @@ class BuildTaskEngine {
257
273
  } else {
258
274
  this.logger.log('done >')
259
275
  }
276
+ }
260
277
 
261
- if (writeOutputFile) {
262
- const outputFile = this.env.build.outputfile || process.env.GENERATION_LOG
263
- if (outputFile) {
264
- this.logger.log(`writing generation log to [${outputFile}]\n`)
265
- await fs.promises.mkdir(path.dirname(outputFile), {recursive: true}).then(() =>
266
- fs.promises.writeFile(outputFile, files.join('\n'))
267
- ).catch((error) => {
268
- this.logger.error(`failed to write generation log`)
269
- this.logger.error(error.stack || error)
270
- return Promise.resolve()
271
- })
278
+ async _writeGenerationLog(handlers, buildOptions) {
279
+ const outputFile = this.env.build.outputfile || process.env.GENERATION_LOG
280
+ if (outputFile) {
281
+ const files = this._getBuildOutput(handlers, buildOptions)
282
+ this.logger.log(`writing generation log to [${outputFile}]\n`)
283
+ try {
284
+ await fs.promises.mkdir(path.dirname(outputFile), { recursive: true }).then(() => fs.promises.writeFile(outputFile, files.join('\n')))
285
+ } catch (error) {
286
+ this.logger.error(`failed to write generation log`)
287
+ this.logger.error(error.stack || error)
272
288
  }
273
289
  }
274
290
  }
@@ -283,7 +299,7 @@ class BuildTaskEngine {
283
299
  }
284
300
 
285
301
  _logMessages(buildOptions, messages) {
286
- if (messages && messages.length > 0) {
302
+ if (messages.length > 0) {
287
303
  const options = {
288
304
  log: this.logger.log,
289
305
  "log-level": this._getLogLevel(buildOptions) // ensures that for tests the correct cds.env is used
@@ -293,6 +309,38 @@ class BuildTaskEngine {
293
309
  }
294
310
  }
295
311
 
312
+ _resolveRequiredSapServices(tasks) {
313
+ const taskModelPaths = tasks.reduce((acc, task) => {
314
+ const model = task.options?.model
315
+ if (model) {
316
+ if (Array.isArray(model)) {
317
+ model.forEach(m => acc.add(m))
318
+ } else {
319
+ acc.add(model)
320
+ }
321
+ }
322
+ return acc
323
+ }, new Set())
324
+
325
+ const srvModelPaths = [...Object.keys(this.env.requires || {}).reduce((acc, srvName) => {
326
+ const model = this.env.requires[srvName]?.model;
327
+ if (model) {
328
+ if (Array.isArray(model)) {
329
+ model.forEach(m => acc.add(m))
330
+ } else {
331
+ acc.add(model)
332
+ }
333
+ }
334
+ return acc
335
+ }, new Set())]
336
+
337
+ const unresolved = resolveRequiredSapModels(this.cds, [...taskModelPaths])
338
+ // are the required service models contained in the task's options.model
339
+ const missing = srvModelPaths.filter(m => m.startsWith('@sap/') && !taskModelPaths.has(m) && !unresolved.find(u => u === m))
340
+
341
+ return { unresolved, missing }
342
+ }
343
+
296
344
  /**
297
345
  * Returns a sorted and flatend list of all messages extracted from the given errors.
298
346
  * @param {Array<Error>} errors
@@ -81,9 +81,15 @@ class BuildTaskFactory {
81
81
  }
82
82
 
83
83
  // 3. filters the list of build tasks and adapts according to given CLI options
84
+ // Note: A new task will be created if invoked using CLI 'cds build --for <xxx>'
85
+ // where <xxx> matches a valid task type like 'hana', 'java' etc.
84
86
  const existingTasks = tasks
85
87
  tasks = await this._applyCliTaskOptions(providerFactory, tasks)
86
88
 
89
+ if (tasks.length === 0) {
90
+ return tasks
91
+ }
92
+
87
93
  // 2. add dependencies
88
94
  await providerFactory.lookupTasks(tasks)
89
95
 
@@ -107,8 +113,10 @@ class BuildTaskFactory {
107
113
  }
108
114
 
109
115
  _applyCommonTaskDefaults(tasks, buildOptions) {
116
+ const modelPaths = this._getDefaultModelOptions(buildOptions.root)
117
+
110
118
  tasks.forEach(task => {
111
- this._setTaskOptions(task, buildOptions.root)
119
+ this._setTaskModelOptions(task, modelPaths)
112
120
  if (!task.src) {
113
121
  throw new Error(`Invalid build task definition - value of property 'src' is missing in [${task.for || task.use}].`)
114
122
  }
@@ -137,7 +145,7 @@ class BuildTaskFactory {
137
145
  _getDefaultModelOptions(projectPath) {
138
146
  const fts = this.env.features.folders
139
147
  const modelPaths = this.cds.resolve(!fts ? '*' : ['*', fts], false)
140
- return this._pushModelPaths(projectPath, [], modelPaths)
148
+ return this._pushModelPaths(projectPath, modelPaths)
141
149
  }
142
150
 
143
151
  /**
@@ -206,16 +214,15 @@ class BuildTaskFactory {
206
214
  return tasks
207
215
  }
208
216
 
209
- _setTaskOptions(task, projectPath) {
217
+ _setTaskModelOptions(task, defaultModelPaths) {
210
218
  task.options = task.options || {}
211
219
  if (!task.options.model || Array.isArray(task.options.model) && task.options.model.length === 0) {
212
- const models = new Set(this._getDefaultModelOptions(projectPath))
220
+ defaultModelPaths = new Set(defaultModelPaths)
213
221
  if (task.src) {
214
- models.add(task.src)
222
+ defaultModelPaths.add(task.src)
215
223
  }
216
- task.options.model = [...models]
217
- }
218
- else if (!Array.isArray(task.options.model)) {
224
+ task.options.model = [...defaultModelPaths]
225
+ } else if (!Array.isArray(task.options.model)) {
219
226
  task.options.model = [task.options.model]
220
227
  }
221
228
  }
@@ -251,8 +258,8 @@ class BuildTaskFactory {
251
258
  return taskOptions
252
259
  }
253
260
 
254
- _pushModelPaths(projectPath, model, ...modelPaths) {
255
- model = new Set(model)
261
+ _pushModelPaths(projectPath, ...modelPaths) {
262
+ const model = new Set()
256
263
  // may contain nested arrays
257
264
  modelPaths = BuildTaskFactory._flatten(modelPaths)
258
265
  const { roots } = this.env
@@ -272,7 +279,7 @@ class BuildTaskFactory {
272
279
  }
273
280
  }
274
281
  })
275
- return [...model]
282
+ return model
276
283
  }
277
284
 
278
285
  static _flatten(modelPaths) {
@@ -86,7 +86,7 @@ class BuildTaskHandler {
86
86
  init() { }
87
87
 
88
88
  /**
89
- * Called by the framework to create the artefacts of this build task handler.
89
+ * Called by the framework to create the artifacts of this build task handler.
90
90
  */
91
91
  async build() {
92
92
  throw new TypeError(OVERRIDE_METHOD_MSG)
@@ -55,7 +55,7 @@ class BuildTaskProviderFactory {
55
55
  }))
56
56
  }
57
57
 
58
- async lookupTasks(tasks=[]) {
58
+ async lookupTasks(tasks = []) {
59
59
  await Promise.all(this.providers.map(async (provider) => {
60
60
  const snapShot = tasks
61
61
  await this._lookupTasks(provider, tasks)
@@ -140,21 +140,11 @@ class BuildTaskProviderFactory {
140
140
  }
141
141
 
142
142
  async _lookupTasks(provider, tasks) {
143
- try {
144
- await provider.lookupTasks(tasks, this.buildOptions)
145
- } catch (e) {
146
- this.logger.error(`Build task provider ${provider.constructor.name} returned error:\n` + e)
147
- throw e
148
- }
143
+ return provider.lookupTasks(tasks, this.buildOptions)
149
144
  }
150
145
 
151
146
  async _applyTaskDefaults(provider, tasks) {
152
- try {
153
- return Promise.all(tasks.map(task => provider.applyTaskDefaults(task)))
154
- } catch (e) {
155
- this.logger.error(`Build task provider ${provider.constructor.name} returned error:\n` + e)
156
- throw e
157
- }
147
+ return Promise.all(tasks.map(task => provider.applyTaskDefaults(task)))
158
148
  }
159
149
 
160
150
  _loadProviders() {
@@ -17,7 +17,6 @@ exports.BUILD_TASK_MTX_SIDECAR = "mtx-sidecar"
17
17
  exports.BUILD_TASK_MTX_EXTENSION = "mtx-extension"
18
18
  exports.BUILD_TASKS = [this.BUILD_TASK_JAVA, this.BUILD_TASK_JAVA_CF, this.BUILD_TASK_NODEJS, this.BUILD_TASK_NODE_CF, this.BUILD_TASK_HANA, this.BUILD_TASK_FIORI, this.BUILD_TASK_MTX, this.BUILD_TASK_MTX_SIDECAR, this.BUILD_TASK_MTX_EXTENSION]
19
19
 
20
- exports.ODATA_VERSION = "odata.version"
21
20
  exports.ODATA_VERSION_V2 = "v2"
22
21
  exports.ODATA_VERSION_V4 = "v4"
23
22
 
@@ -17,7 +17,6 @@ module.exports = Object.assign(build,
17
17
  */
18
18
  async function build(options = {}) {
19
19
  const projectPath = path.resolve(options.project || '.')
20
-
21
20
  if (!fs.lstatSync(projectPath).isDirectory()) {
22
21
  return Promise.reject(`Project [${projectPath}] does not exist`)
23
22
  }
@@ -25,15 +24,24 @@ async function build(options = {}) {
25
24
  const cds = require('./cds')
26
25
  if (projectPath !== process.cwd()) {
27
26
  const env = cds.env.for('cds', projectPath);
28
- cds.env = env
29
- cds.root = projectPath
27
+ cds.env = env // REVISIT: not good / fragile
28
+ cds.root = projectPath // REVISIT: not good / fragile
30
29
  }
31
30
  const logger = options.logger || cds.log(LOG_MODULE_NAMES)
32
-
33
31
  const buildOptions = _mergeCliOptions({ root: projectPath }, options)
32
+ const buildTaskFactory = new BuildTaskFactory(logger)
33
+ const buildTaskEngine = new BuildTaskEngine(logger)
34
34
 
35
- let tasks = await new BuildTaskFactory(logger).getTasks(buildOptions)
36
- return new BuildTaskEngine(logger).processTasks(tasks, buildOptions)
35
+ try {
36
+ const tasks = await buildTaskFactory.getTasks(buildOptions)
37
+ return buildTaskEngine.processTasks(tasks, buildOptions)
38
+ } catch (e) {
39
+ // cds CLI layer logs if invoked from CLI
40
+ if (!buildOptions.cli) {
41
+ buildTaskEngine._logMessages(buildOptions, [e])
42
+ }
43
+ throw e
44
+ }
37
45
  }
38
46
 
39
47
  function _mergeCliOptions(buildOptions, options) {
@@ -50,9 +50,8 @@ class BuildTaskHandlerEdmx extends BuildTaskHandlerFeatureToggles {
50
50
  return Promise.all(promises)
51
51
  }
52
52
 
53
- _isOdataProtocol(service) {
54
- const { _protocol4Service } = this.cds.service.adapters
55
- return /^odata/.test(_protocol4Service(service))
53
+ _isOdataProtocol(srv) {
54
+ return this.cds.service.protocols.protocol4(srv).startsWith('odata')
56
55
  }
57
56
 
58
57
  _options4odata() {
@@ -72,7 +72,7 @@ class FeatureToggleBuilder extends BuildTaskHandlerInternal {
72
72
  return acc
73
73
  }, [])
74
74
 
75
- // keep existing behaviour and return paths returned by cds.resolve if features are not supported by this project
75
+ // keep existing behavior and return paths returned by cds.resolve if features are not supported by this project
76
76
  if (!fs.existsSync(path.join(this.buildOptions.root, this.ftsName))) {
77
77
  return paths
78
78
  }
@@ -106,7 +106,7 @@ class FeatureToggleBuilder extends BuildTaskHandlerInternal {
106
106
  const ftCsn = await this.cds.load(ftsPaths[ftName], options)
107
107
  const ftPath = path.join(destFts, this.ftsName, ftName)
108
108
 
109
- // replace require paths by base model path to ensure precedence of feature annos
109
+ // replace require paths by base model path to ensure precedence of feature annotations
110
110
  // see https://pages.github.tools.sap/cap/docs/cds/compiler-messages#anno-duplicate-unrelated-layer
111
111
  ftCsn.requires = [path.join(path.relative(ftPath, destBase), DEFAULT_CSN_FILE_NAME).replace(/\\/g,'/')]
112
112
 
@@ -37,7 +37,7 @@ class BuildTaskHandlerInternal extends BuildTaskHandler {
37
37
  }
38
38
  /**
39
39
  * Custom build handlers are executed before internal handlers in order
40
- * ensure and content cannot be overwriten by mistake.
40
+ * ensure and content cannot be overwritten by mistake.
41
41
  */
42
42
  get priority() {
43
43
  return BuildTaskHandlerInternal.PRIORITY_MAX_VALUE
@@ -140,19 +140,16 @@ class BuildTaskHandlerInternal extends BuildTaskHandler {
140
140
  * or in a separate staging folder which is not part of the build tasks src folder.
141
141
  */
142
142
  isStagingBuild() {
143
- return this.task.src !== this.task.dest
143
+ return !this.task.src.startsWith(this.task.dest)
144
144
  }
145
145
 
146
146
  async copyNativeContent(srcDir, destDir, customFilter) {
147
- if (!this.isStagingBuild()) {
148
- return Promise.resolve()
149
- }
150
147
  const files = BuildTaskHandlerInternal._find(srcDir, (src) => {
151
148
  // do not copy files that:
152
149
  // - are contained in the 'buildOptions.target' folder
153
150
  // - are contained in this modules 'dest' folder
154
151
  // - are contained in some generation folder
155
- // - do NOT fullfill additional specific filter criteria
152
+ // - do NOT fulfill additional specific filter criteria
156
153
  return this._commonStagingBuildFilter(src, destDir) && (!customFilter || customFilter.call(this, src))
157
154
  })
158
155
  return Promise.all(
@@ -1,6 +1,6 @@
1
1
  const fs = require('fs')
2
2
  const path = require('path')
3
- const { hasJavaNature, getProperty, isStreamlinedMtx } = require('../util')
3
+ const { hasJavaNature, getProperty, isStreamlinedMtx, BuildError } = require('../util')
4
4
  const BuildTaskProvider = require('../buildTaskProvider')
5
5
 
6
6
  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,
@@ -83,9 +83,15 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
83
83
  }
84
84
  if (Array.isArray(db) && db.length > 0) {
85
85
  db = BuildTaskProviderInternal._getModuleFolder(projectPath, db) || null
86
+ if (!db && !addRequiredTasks) { // log once
87
+ this.logger.log("No database module found")
88
+ }
86
89
  }
87
90
  if (Array.isArray(srv) && srv.length > 0) {
88
91
  srv = BuildTaskProviderInternal._getModuleFolder(projectPath, srv) || null
92
+ if (!srv && !addRequiredTasks) { // log once
93
+ this.logger.log("No service module found")
94
+ }
89
95
  }
90
96
  // create required build tasks
91
97
  if (addRequiredTasks) {
@@ -143,7 +149,7 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
143
149
  if (this._useHana(projectPath, buildOptions)) {
144
150
  this.logger.debug("found HANA database.")
145
151
  // legacy build supports dest property
146
- const compileDest = this.env.get("data.dest")
152
+ const compileDest = this.env.data?.dest
147
153
  if (compileDest) {
148
154
  //../db/src/gen
149
155
  // compileDest is relative to src folder in modular build - resolve correctly
@@ -163,62 +169,72 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
163
169
  _useHana(projectPath, buildOptions) {
164
170
  if (getProperty(buildOptions, "for.hana.skipManifestGeneration") // deprecated fallback for webide fullstack and mtx
165
171
  || getProperty(buildOptions, "for.hana.contentManifest") === false // fallback for webide fullstack and mtx
166
- || this.env.get("requires.db.kind") === "hana"
167
- || this.env.get("requires.db.dialect") === "hana") {
172
+ || this.env.requires.db?.kind === "hana"
173
+ || this.env.requires.db?.dialect === "hana") {
168
174
 
169
175
  return true
170
176
  }
171
177
  // false if other db has been defined
172
- if (this.env.get("requires.db.kind")) {
178
+ if (this.env.requires.db?.kind) {
173
179
  return false
174
180
  }
175
181
  // check whether cds config represents a legacy build system config for which requires.db was not configured
176
182
  // Note: compat layer sets requires.db: {}
177
183
  const userEnv = this.cds.env.for("cds", projectPath, false)
178
- return userEnv && (userEnv.get("data.model") || userEnv.get("service.model"))
184
+ return userEnv && (userEnv.data?.model || userEnv.service?.model)
179
185
  }
180
186
 
181
187
  _createMtxTask(projectPath, src, dest, tasks) {
182
- let task = null
183
- const isJava = tasks.some(task => task.for === BUILD_TASK_JAVA || task.for === BUILD_TASK_JAVA_CF)
184
- const isNode = tasks.some(task => task.for === BUILD_TASK_NODEJS || task.for === BUILD_TASK_NODE_CF)
185
-
188
+ // MTX build task creation is NOT supported for Java projects
189
+ if (tasks.find(task => task.for === BUILD_TASK_JAVA || task.for === BUILD_TASK_JAVA_CF)) {
190
+ return null
191
+ }
186
192
  this.logger.debug("determining mtx type for nodejs")
187
- if (isStreamlinedMtx(this.cds) && !isJava) {
188
- if (this.env.requires["cds.xt.ModelProviderService"]) {
189
- // sidecar scenario for Nodejs or Java
190
- const sidecarEnv = this.env.for("cds", path.join(projectPath, MTX_SIDECAR_FOLDER))
191
- if (BuildTaskProviderInternal._isSidecar(sidecarEnv)) {
192
- task = {
193
+
194
+ if (isStreamlinedMtx(this.cds)) {
195
+ if (this.env.requires["cds.xt.ModelProviderService"]?.kind === "rest") { // "cds.xt.ModelProviderService": "from-sidecar"
196
+ this.logger.debug("Nodejs Streamlined MTX app with sidecar")
197
+
198
+ const sidecarPath = path.join(projectPath, MTX_SIDECAR_FOLDER)
199
+ if (!fs.existsSync(sidecarPath)) {
200
+ throw new BuildError("CDS build failed", `MTX sidecar directory '${sidecarPath}' not existing. Custom build task configuration necessary if the folder is named differently.`)
201
+ }
202
+ const sidecarEnv = this.env.for("cds", sidecarPath)
203
+ if (sidecarEnv.requires["cds.xt.ModelProviderService"]?.kind === "in-sidecar") {
204
+ return {
193
205
  for: BUILD_TASK_MTX_SIDECAR
194
206
  }
195
- this.logger.debug(`${isJava ? "Java" : "Nodejs"} MTX sidecar scenario`)
196
- } else {
197
- throw Error(`MTX sidecar module '${MTX_SIDECAR_FOLDER}' not found`)
198
207
  }
199
- } else if (isNode) {
200
- task = {
201
- for: BUILD_TASK_MTX,
202
- src
203
- }
204
- this.logger.debug("Nodejs MTX scenario without sidecar")
208
+ throw new BuildError("CDS build failed", 'Invalid MTX sidecar configuration. Make sure to add required service "cds.xt.ModelProviderService": "in-sidecar".')
205
209
  }
206
- } else if (BuildTaskProviderInternal._isExtension(this.env)) {
207
- // streamlined mtx extension project
208
- task = {
210
+
211
+ if (this.env.requires["cds.xt.ModelProviderService"]?.kind === "in-sidecar") {
212
+ // cds build is executed in sidecar folder
213
+ throw new BuildError("CDS build failed", "Invalid working directory. Make sure to execute 'cds build' in CAP project root directory.")
214
+ }
215
+
216
+ this.logger.debug("Nodejs Streamlined MTX app without sidecar")
217
+ return {
218
+ for: BUILD_TASK_MTX,
219
+ src
220
+ }
221
+ }
222
+
223
+ if (BuildTaskProviderInternal._isExtension(this.env)) {
224
+ this.logger.debug("Streamlined MTX extension app")
225
+ return {
209
226
  for: BUILD_TASK_MTX_EXTENSION
210
227
  }
211
- this.logger.debug("Streamlined MTX extension")
212
- } else if (this.env.get("requires.multitenancy") && isNode) {
213
- // classic mtx
214
- task = {
228
+ }
229
+
230
+ if (this.env.requires.multitenancy) {
231
+ this.logger.debug("Nodejs Classic MTX app without sidecar")
232
+ return {
215
233
  src: ".",
216
234
  for: BUILD_TASK_MTX,
217
235
  dest
218
236
  }
219
- this.logger.debug("classic Nodejs MTX scenario")
220
237
  }
221
- return task
222
238
  }
223
239
 
224
240
  _createSrvTask(projectPath, src, taskOptions) {
@@ -238,7 +254,7 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
238
254
  _createJavaTask(projectPath, src, taskOptions) {
239
255
  this.logger.debug("found implementation technology java")
240
256
  // legacy build supports dest property
241
- const compileDest = this.env.get("service.dest")
257
+ const compileDest = this.env.service?.dest
242
258
  if (compileDest) {
243
259
  // compileDest is relative to src folder in modular build - resolve correctly
244
260
  taskOptions.compileDest = path.relative(path.resolve(projectPath, src), path.resolve(projectPath, compileDest))
@@ -259,10 +275,6 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
259
275
  }
260
276
  }
261
277
 
262
- static _isSidecar(env) {
263
- return env.requires['cds.xt.ModelProviderService']?.kind === "in-sidecar"
264
- }
265
-
266
278
  static _isExtension(env) {
267
279
  return !!env.extends
268
280
  }
@@ -3,7 +3,7 @@ const BuildTaskHandlerEdmx = require('../buildTaskHandlerEdmx')
3
3
  const URL = require('url')
4
4
  const { getProperty, isOldJavaStack, relativePaths } = require('../../util')
5
5
 
6
- const { ODATA_VERSION, ODATA_VERSION_V2, BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY, BUILD_TASK_FIORI, BUILD_TASK_JAVA_CF, CDS_CONFIG_PATH_SEP, BUILD_TASK_JAVA } = require('../../constants')
6
+ const { ODATA_VERSION_V2, BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY, BUILD_TASK_FIORI, BUILD_TASK_JAVA_CF, CDS_CONFIG_PATH_SEP, BUILD_TASK_JAVA } = require('../../constants')
7
7
 
8
8
  /**
9
9
  * With cds 4 service metadata for the UI service binding is no longer created by default.
@@ -44,14 +44,14 @@ class FioriAppModuleBuilder extends BuildTaskHandlerEdmx {
44
44
  fioriBuildOptions[BUILD_OPTION_OUTPUT_MODE] = OUTPUT_MODE_RESULT_ONLY
45
45
 
46
46
  try {
47
- const edmxOptions = { version: this.env.get(ODATA_VERSION) }
47
+ const edmxOptions = { version: this.env.odata?.version }
48
48
 
49
49
  if (edmxOptions.version !== ODATA_VERSION_V2) {
50
50
  const javaTask = this.buildOptions.tasks.find(task => task.for === BUILD_TASK_JAVA_CF || task.for === BUILD_TASK_JAVA)
51
51
 
52
52
  if (javaTask
53
53
  && await isOldJavaStack([javaTask.src, this.buildOptions.root])
54
- && !this.env.for("cds", this.buildOptions.root, false).get(ODATA_VERSION)) {
54
+ && !this.env.for("cds", this.buildOptions.root, false).odata?.version) {
55
55
 
56
56
  // old java stack
57
57
  // default is now v4 and not v2 anymore, so overwrite with v2 if using default
@@ -97,7 +97,7 @@ function _getNewMigrationEntry(changeset, currentVersion) {
97
97
  if (changeset && changeset.length > 0) {
98
98
  const dropColumns = changeset.some(e => e.drop)
99
99
  const manualChange = changeset.some(e => !e.sql)
100
- const enableDrop = cds.env.get('hana.journal.enable-drop')
100
+ const enableDrop = cds.env.hana?.journal?.['enable-drop']
101
101
  const content = changeset.reduce((acc, e) => {
102
102
  if (!acc) {
103
103
  acc = `== migration=${currentVersion + 1}\n`