@sap/cds 6.0.4 → 6.1.2

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 (139) hide show
  1. package/CHANGELOG.md +180 -18
  2. package/apis/cds.d.ts +11 -7
  3. package/apis/log.d.ts +124 -0
  4. package/apis/ql.d.ts +72 -15
  5. package/apis/services.d.ts +13 -2
  6. package/bin/build/buildTaskHandler.js +5 -2
  7. package/bin/build/constants.js +4 -1
  8. package/bin/build/provider/buildTaskHandlerEdmx.js +11 -39
  9. package/bin/build/provider/buildTaskHandlerFeatureToggles.js +14 -34
  10. package/bin/build/provider/buildTaskHandlerInternal.js +56 -4
  11. package/bin/build/provider/buildTaskProviderInternal.js +22 -14
  12. package/bin/build/provider/hana/index.js +12 -9
  13. package/bin/build/provider/java/index.js +18 -8
  14. package/bin/build/provider/mtx/index.js +7 -4
  15. package/bin/build/provider/mtx/resourcesTarBuilder.js +60 -35
  16. package/bin/build/provider/mtx-extension/index.js +57 -0
  17. package/bin/build/provider/mtx-sidecar/index.js +46 -18
  18. package/bin/build/provider/nodejs/index.js +34 -13
  19. package/bin/deploy/to-hana/cfUtil.js +7 -2
  20. package/bin/deploy/to-hana/hana.js +20 -25
  21. package/bin/deploy/to-hana/hdiDeployUtil.js +13 -2
  22. package/bin/serve.js +7 -4
  23. package/lib/compile/{index.js → cds-compile.js} +0 -0
  24. package/lib/compile/extend.js +15 -5
  25. package/lib/compile/minify.js +1 -15
  26. package/lib/compile/parse.js +1 -1
  27. package/lib/compile/resolve.js +2 -2
  28. package/lib/compile/to/srvinfo.js +6 -4
  29. package/lib/{deploy.js → dbs/cds-deploy.js} +9 -8
  30. package/lib/env/{index.js → cds-env.js} +1 -17
  31. package/lib/env/{requires.js → cds-requires.js} +24 -3
  32. package/lib/env/defaults.js +7 -1
  33. package/lib/env/schemas/cds-package.json +11 -0
  34. package/lib/env/schemas/cds-rc.json +614 -0
  35. package/lib/index.js +19 -16
  36. package/lib/log/{errors.js → cds-error.js} +1 -1
  37. package/lib/log/{index.js → cds-log.js} +0 -0
  38. package/lib/log/format/kibana.js +19 -1
  39. package/lib/ql/Query.js +9 -3
  40. package/lib/ql/SELECT.js +2 -2
  41. package/lib/ql/UPDATE.js +2 -2
  42. package/lib/ql/{index.js → cds-ql.js} +4 -10
  43. package/lib/req/context.js +49 -17
  44. package/lib/req/locale.js +5 -1
  45. package/lib/{serve → srv}/adapters.js +23 -19
  46. package/lib/{connect → srv}/bindings.js +0 -0
  47. package/lib/{connect/index.js → srv/cds-connect.js} +1 -1
  48. package/lib/{serve/index.js → srv/cds-serve.js} +0 -0
  49. package/lib/{serve → srv}/factory.js +1 -1
  50. package/lib/{serve/Service-api.js → srv/srv-api.js} +22 -6
  51. package/lib/{serve/Service-dispatch.js → srv/srv-dispatch.js} +13 -8
  52. package/lib/{serve/Service-handlers.js → srv/srv-handlers.js} +10 -0
  53. package/lib/{serve/Service-methods.js → srv/srv-methods.js} +10 -8
  54. package/lib/srv/srv-models.js +207 -0
  55. package/lib/{serve/Transaction.js → srv/srv-tx.js} +57 -40
  56. package/lib/utils/{tests.js → cds-test.js} +2 -2
  57. package/lib/utils/cds-utils.js +146 -0
  58. package/lib/utils/index.js +2 -145
  59. package/lib/utils/jest.js +43 -0
  60. package/lib/utils/resources/index.js +15 -25
  61. package/lib/utils/resources/tar.js +18 -41
  62. package/libx/_runtime/auth/index.js +14 -11
  63. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +7 -19
  64. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -4
  65. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -4
  66. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -4
  67. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +1 -4
  68. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +2 -2
  69. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +6 -19
  70. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +3 -5
  71. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +1 -1
  72. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +1 -4
  73. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/PrimitiveValueDecoder.js +0 -2
  74. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +3 -1
  75. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +38 -4
  76. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +1 -3
  77. package/libx/_runtime/cds-services/services/utils/differ.js +4 -0
  78. package/libx/_runtime/cds-services/util/errors.js +1 -29
  79. package/libx/_runtime/common/i18n/messages.properties +2 -1
  80. package/libx/_runtime/common/perf/index.js +10 -15
  81. package/libx/_runtime/common/utils/binary.js +3 -4
  82. package/libx/_runtime/common/utils/cqn2cqn4sql.js +0 -1
  83. package/libx/_runtime/common/utils/entityFromCqn.js +8 -5
  84. package/libx/_runtime/common/utils/keys.js +14 -6
  85. package/libx/_runtime/common/utils/resolveView.js +1 -1
  86. package/libx/_runtime/common/utils/template.js +1 -1
  87. package/libx/_runtime/db/Service.js +2 -14
  88. package/libx/_runtime/db/expand/expandCQNToJoin.js +28 -25
  89. package/libx/_runtime/db/expand/rawToExpanded.js +7 -6
  90. package/libx/_runtime/db/generic/input.js +8 -1
  91. package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -1
  92. package/libx/_runtime/db/sql-builder/SelectBuilder.js +37 -18
  93. package/libx/_runtime/extensibility/activate.js +47 -47
  94. package/libx/_runtime/extensibility/add.js +22 -13
  95. package/libx/_runtime/extensibility/addExtension.js +17 -13
  96. package/libx/_runtime/extensibility/defaults.js +25 -30
  97. package/libx/_runtime/extensibility/handler/transformREAD.js +20 -18
  98. package/libx/_runtime/extensibility/linter/allowlist_checker.js +373 -0
  99. package/libx/_runtime/extensibility/linter/annotations_checker.js +113 -0
  100. package/libx/_runtime/extensibility/linter/checker_base.js +20 -0
  101. package/libx/_runtime/extensibility/linter/namespace_checker.js +180 -0
  102. package/libx/_runtime/extensibility/linter.js +32 -0
  103. package/libx/_runtime/extensibility/push.js +77 -20
  104. package/libx/_runtime/extensibility/service.js +29 -12
  105. package/libx/_runtime/extensibility/token.js +57 -0
  106. package/libx/_runtime/extensibility/utils.js +8 -6
  107. package/libx/_runtime/extensibility/validation.js +6 -9
  108. package/libx/_runtime/fiori/generic/new.js +0 -11
  109. package/libx/_runtime/fiori/utils/where.js +1 -1
  110. package/libx/_runtime/hana/Service.js +0 -1
  111. package/libx/_runtime/hana/conversion.js +12 -1
  112. package/libx/_runtime/hana/customBuilder/CustomFunctionBuilder.js +4 -3
  113. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +5 -0
  114. package/libx/_runtime/hana/pool.js +6 -10
  115. package/libx/_runtime/hana/search2Contains.js +0 -5
  116. package/libx/_runtime/hana/search2cqn4sql.js +1 -0
  117. package/libx/_runtime/messaging/common-utils/authorizedRequest.js +1 -1
  118. package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +1 -2
  119. package/libx/_runtime/messaging/outbox/utils.js +1 -1
  120. package/libx/_runtime/messaging/service.js +11 -6
  121. package/libx/_runtime/remote/utils/data.js +5 -0
  122. package/libx/_runtime/sqlite/Service.js +7 -6
  123. package/libx/_runtime/sqlite/execute.js +41 -28
  124. package/libx/odata/afterburner.js +79 -2
  125. package/libx/odata/cqn2odata.js +15 -9
  126. package/libx/odata/grammar.pegjs +157 -76
  127. package/libx/odata/index.js +9 -3
  128. package/libx/odata/parser.js +1 -1
  129. package/libx/odata/utils.js +39 -5
  130. package/libx/rest/RestAdapter.js +3 -7
  131. package/libx/rest/middleware/delete.js +4 -5
  132. package/libx/rest/middleware/parse.js +3 -2
  133. package/package.json +3 -3
  134. package/server.js +1 -1
  135. package/srv/extensibility-service.cds +6 -3
  136. package/srv/model-provider.cds +3 -1
  137. package/srv/model-provider.js +86 -106
  138. package/srv/mtx.js +7 -1
  139. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +0 -240
@@ -5,16 +5,21 @@ const { BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY } = require('../consta
5
5
  class BuildTaskHandlerEdmx extends BuildTaskHandlerFeatureToggles {
6
6
  async compileToEdmx(model, edmxDest, compileOptions = {}) { // NOSONAR
7
7
  const promises = []
8
- const services = this.cds.reflect(model).services
8
+
9
+ // generate edmx for services only that have the odata protocol
10
+ const serviceNames = this.cds.reflect(model).services
11
+ .filter(service => this._isOdataProtocol(service))
12
+ .map(service => service.name)
9
13
 
10
14
  // TODO mtx build_helper tests currently expects this, strange...
11
15
  this._result.languages.add('')
12
16
 
13
17
  // new compile impl is throwing error in case no services exist!
14
- if (services.length > 0) {
18
+ if (serviceNames.length > 0) {
15
19
  const options = {
16
20
  ...this._options4edmx(),
17
- ...compileOptions
21
+ ...compileOptions,
22
+ serviceNames
18
23
  }
19
24
  this.logger._debug && this.logger.debug(`compiling edmx files using OData version ${options.version}`)
20
25
 
@@ -45,42 +50,9 @@ class BuildTaskHandlerEdmx extends BuildTaskHandlerFeatureToggles {
45
50
  return Promise.all(promises)
46
51
  }
47
52
 
48
- /**
49
- * Collect and write language bundles into a single i18n.json file.
50
- * @param {Object} model
51
- * @param {string} bundleDest
52
- */
53
- async collectLanguageBundles(model, bundleDest, saveResult = true) {
54
- // collect effective i18n properties...
55
- let bundles = {}
56
- const bundleGenerator = this.cds.localize.bundles4(model)
57
- if (bundleGenerator && bundleGenerator[Symbol.iterator]) {
58
- for (let [locale, bundle] of bundleGenerator) {
59
- // fallback bundle has the name ""
60
- if (typeof locale === 'string') {
61
- bundles[locale] = bundle
62
- }
63
- }
64
- }
65
-
66
- // omit bundles in case the fallback bundle is the only existing entry
67
- const keys = Object.keys(bundles)
68
- if (keys.length === 1 && keys[0] === "" && Object.keys(bundles[keys[0]]).length === 0) {
69
- bundles = {}
70
- }
71
- // copied from ../compile/i18n.js
72
- const { folders = ['i18n'], file = 'i18n' } = this.env.i18n
73
-
74
- // bundleDest might be null
75
- if (bundleDest && Object.keys(bundles).length > 0) {
76
- if (!this.hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY)) {
77
- await this.write(bundles).to(path.join(bundleDest, folders[0], file + '.json'))
78
- }
79
- }
80
- if (saveResult) {
81
- this._result.languageBundles = bundles
82
- }
83
- return bundles
53
+ _isOdataProtocol(service) {
54
+ const { _protocol4Service } = this.cds.service.adapters
55
+ return /^odata/.test(_protocol4Service(service))
84
56
  }
85
57
 
86
58
  _options4odata() {
@@ -1,6 +1,6 @@
1
1
  const fs = require('fs')
2
2
  const path = require('path')
3
- const { BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY } = require('../constants')
3
+ const { DEFAULT_CSN_FILE_NAME } = require('../constants')
4
4
  const BuildTaskHandlerInternal = require('./buildTaskHandlerInternal')
5
5
 
6
6
  class FeatureToggleBuilder extends BuildTaskHandlerInternal {
@@ -31,7 +31,10 @@ class FeatureToggleBuilder extends BuildTaskHandlerInternal {
31
31
  dictionary.base = csn
32
32
  }
33
33
  // preserve @location as @source properties
34
- await this.compileToJson(dictionary.base, destBase)
34
+ const csnStr = await this.compileToJson(dictionary.base, path.join(destBase, DEFAULT_CSN_FILE_NAME))
35
+ const csnModel = JSON.parse(csnStr)
36
+ csnModel.meta = csn.meta
37
+ this._result.csn = csnModel
35
38
 
36
39
  if (sources.features) {
37
40
  dictionary.features = await this._compileFeatures(sources.features, destBase, destFts)
@@ -41,13 +44,17 @@ class FeatureToggleBuilder extends BuildTaskHandlerInternal {
41
44
 
42
45
  async collectAllLanguageBundles(dictionary, paths, destBase, destFts) {
43
46
  // create language bundle for base model
44
- await this.collectLanguageBundles(dictionary.base, destBase)
47
+ const i18n = await this.collectLanguageBundles(dictionary.base, destBase)
48
+ if (i18n) {
49
+ this._result.languageBundles = i18n.bundles
50
+ }
51
+
45
52
  if (dictionary.features) {
46
53
  // create language bundles for all features
47
54
  for (const ftName in dictionary.features) {
48
55
  // attach the sources information for i18n location reference
49
56
  dictionary.features[ftName]['$sources'] = paths.features[ftName]
50
- await this.collectLanguageBundles(dictionary.features[ftName], path.join(destFts, this.ftsName, ftName), false)
57
+ await this.collectLanguageBundles(dictionary.features[ftName], path.join(destFts, this.ftsName, ftName))
51
58
  }
52
59
  }
53
60
  }
@@ -87,50 +94,23 @@ class FeatureToggleBuilder extends BuildTaskHandlerInternal {
87
94
  return paths
88
95
  }
89
96
 
90
- async compileToJson(model, csnDest, saveResult = true) {
91
- // This will als add a @source prop containing the relative path to the origin .cds source file
92
- // and a parsed _where clause for @restrict.{grant,where} annotations.
93
- // The @source annotation is required for correct custom handler resolution if no @impl annotation has been defined as
94
- // custom service handler implementations are relative to the origin .cds source files.
95
- // For staging builds (task.src !== task.dest) the csn.json file that is served at runtime is copied into a corresponding srv subfolder.
96
- // As a consequence the src folder name has to be included in the @source file name while for inplace builds (task.src === task.dest) this is not the case.
97
- // This ensures that the paths are relative to the cwd when executing cds run.
98
- const jsonOptions = {
99
- cwd: this.buildOptions.root,
100
- src: this.task.src === this.task.dest ? this.task.src : this.buildOptions.root
101
- }
102
- const csnStr = this.cds.compile.to.json(model, jsonOptions)
103
- const csn = JSON.parse(csnStr)
104
- if (saveResult) {
105
- this._result.csn = csn
106
- this._result.csn.meta = model.meta
107
- }
108
-
109
- // csnDest might be null
110
- if (csnDest && !this.hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY)) {
111
- await this.write(csnStr).to(path.join(csnDest, 'csn.json'))
112
- }
113
- return csn
114
- }
115
-
116
97
  async _compileFeatures(ftsPaths, destBase, destFts) {
117
98
  if (!ftsPaths) {
118
99
  return
119
100
  }
120
101
  const features = {}
121
102
  const options = { ...this.options(), flavor: 'parsed' }
122
- const srvFolder = path.relative(destFts, destBase)
123
103
 
124
104
  // create feature models
125
105
  for (const ftName in ftsPaths) {
126
106
  const ftCsn = await this.cds.load(ftsPaths[ftName], options)
107
+ const ftPath = path.join(destFts, this.ftsName, ftName)
127
108
 
128
109
  // replace require paths by base model path to ensure precedence of feature annos
129
110
  // see https://pages.github.tools.sap/cap/docs/cds/compiler-messages#anno-duplicate-unrelated-layer
130
- ftCsn.requires = [path.join('../..', srvFolder, 'csn.json')]
111
+ ftCsn.requires = [path.join(path.relative(ftPath, destBase), DEFAULT_CSN_FILE_NAME).replace(/\\/g,'/')]
131
112
 
132
- const ftPath = path.join(destFts, this.ftsName, ftName)
133
- await this.compileToJson(ftCsn, ftPath, false)
113
+ await this.compileToJson(ftCsn, path.join(ftPath, DEFAULT_CSN_FILE_NAME))
134
114
  await this._validateFeature(ftPath)
135
115
  features[ftName] = ftCsn
136
116
  }
@@ -2,7 +2,7 @@ const fs = require('fs')
2
2
  const path = require('path')
3
3
  const BuildTaskHandler = require('../buildTaskHandler')
4
4
  const { hasOptionValue } = require('../util')
5
- const { FOLDER_GEN } = require('../constants')
5
+ const { FOLDER_GEN, BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY } = require('../constants')
6
6
 
7
7
  class BuildTaskHandlerInternal extends BuildTaskHandler {
8
8
  /**
@@ -47,7 +47,7 @@ class BuildTaskHandlerInternal extends BuildTaskHandler {
47
47
  // the build results have already been deleted by the BuildTaskEngine if the build.target !== '.'
48
48
  // make sure that src is not a subfolder of dest
49
49
  if (this._buildOptions.root === this._buildOptions.target && this.task.src !== this.task.dest && !this._isSubDirectory(this.task.dest, this.task.src)) {
50
- await fs.promises.rm(this.task.dest, {force: true, recursive: true})
50
+ await fs.promises.rm(this.task.dest, { force: true, recursive: true })
51
51
  }
52
52
  }
53
53
 
@@ -164,8 +164,60 @@ class BuildTaskHandlerInternal extends BuildTaskHandler {
164
164
  )
165
165
  }
166
166
 
167
- options() {
168
- return { messages: this._messages }
167
+ async compileToJson(model, csnFile) {
168
+ // This will als add a @source prop containing the relative path to the origin .cds source file
169
+ // and a parsed _where clause for @restrict.{grant,where} annotations.
170
+ // The @source annotation is required for correct custom handler resolution if no @impl annotation has been defined as
171
+ // custom service handler implementations are relative to the origin .cds source files.
172
+ // For staging builds (task.src !== task.dest) the csn.json file that is served at runtime is copied into a corresponding srv subfolder.
173
+ // As a consequence the src folder name has to be included in the @source file name while for inplace builds (task.src === task.dest) this is not the case.
174
+ // This ensures that the paths are relative to the cwd when executing cds run.
175
+ const jsonOptions = {
176
+ cwd: this.buildOptions.root,
177
+ src: this.task.src === this.task.dest ? this.task.src : this.buildOptions.root
178
+ }
179
+ const csnStr = this.cds.compile.to.json(model, jsonOptions)
180
+ if (!this.hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY)) {
181
+ await this.write(csnStr).to(csnFile)
182
+ }
183
+ return csnStr
184
+ }
185
+
186
+ /**
187
+ * Collect and write language bundles into a single i18n.json file.
188
+ * @param {Object} model
189
+ * @param {string} bundleDest
190
+ */
191
+ async collectLanguageBundles(model, bundleDest) {
192
+ // collect effective i18n properties...
193
+ let bundles = {}
194
+ const bundleGenerator = this.cds.localize.bundles4(model)
195
+ if (bundleGenerator && bundleGenerator[Symbol.iterator]) {
196
+ for (let [locale, bundle] of bundleGenerator) {
197
+ // fallback bundle has the name ""
198
+ if (typeof locale === 'string') {
199
+ bundles[locale] = bundle
200
+ }
201
+ }
202
+ }
203
+
204
+ // omit bundles in case the fallback bundle is the only existing entry
205
+ const keys = Object.keys(bundles)
206
+ if (keys.length === 1 && keys[0] === "" && Object.keys(bundles[keys[0]]).length === 0) {
207
+ bundles = {}
208
+ }
209
+ // copied from ../compile/i18n.js
210
+ const { folders = ['i18n'], file: base = 'i18n' } = this.env.i18n
211
+ const file = path.join(bundleDest, folders[0], base + '.json')
212
+
213
+ // bundleDest might be null
214
+ if (bundleDest && Object.keys(bundles).length > 0) {
215
+ if (!this.hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY)) {
216
+ await this.write(bundles).to(file)
217
+ }
218
+ return { file, bundles }
219
+ }
220
+ return null
169
221
  }
170
222
 
171
223
  _isSubDirectory(parent, child) {
@@ -4,7 +4,7 @@ const { hasJavaNature, getProperty, isStreamlinedMtx } = 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,
7
- BUILD_TASK_PREFIX, BUILD_TASKS, BUILD_TASK_MTX_SIDECAR, MTX_SIDECAR_FOLDER } = require("../constants")
7
+ BUILD_TASK_PREFIX, BUILD_TASKS, BUILD_TASK_MTX_SIDECAR, MTX_SIDECAR_FOLDER, BUILD_TASK_MTX_EXTENSION } = require("../constants")
8
8
 
9
9
  class BuildTaskProviderInternal extends BuildTaskProvider {
10
10
  constructor(cds, logger) {
@@ -57,6 +57,7 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
57
57
  case BUILD_TASK_FIORI:
58
58
  task.src = BuildTaskProviderInternal._normalizePath(this.env.folders.app)
59
59
  break
60
+ case BUILD_TASK_MTX_EXTENSION:
60
61
  case BUILD_TASK_MTX:
61
62
  task.src = "."
62
63
  break
@@ -112,12 +113,9 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
112
113
  }
113
114
  }
114
115
  } else {
115
- if (!db) {
116
- this.logger.log(`project doesn't have a database module [${this.env.folders.db}]`)
117
- }
118
- if (!srv) {
119
- this.logger.log(`project doesn't have a service module '${this.env.folders.srv}'`)
120
- }
116
+ !db && this.logger._debug && this.logger.debug(`project doesn't have a database module '${this.env.folders.db}'`)
117
+ !srv && this.logger._debug && this.logger.debug(`project doesn't have a service module '${this.env.folders.srv}'`)
118
+
121
119
  // create hana build task
122
120
  const dbTask = this._createDbTask(projectPath, db, dbOptions, buildOptions)
123
121
  if (dbTask) {
@@ -138,7 +136,7 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
138
136
 
139
137
  _createDbTask(projectPath, src, taskOptions, buildOptions) {
140
138
  this.logger.debug("determining database kind.")
141
- if (!src) {
139
+ if (!src || BuildTaskProviderInternal._isExtension(this.env)) {
142
140
  return null
143
141
  }
144
142
  let task = null
@@ -190,7 +188,7 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
190
188
  if (this.env.requires["cds.xt.ModelProviderService"]) {
191
189
  // sidecar scenario for Nodejs or Java
192
190
  const sidecarEnv = this.env.for("cds", path.join(projectPath, MTX_SIDECAR_FOLDER))
193
- if (this._isSidecar(sidecarEnv)) {
191
+ if (BuildTaskProviderInternal._isSidecar(sidecarEnv)) {
194
192
  task = {
195
193
  for: BUILD_TASK_MTX_SIDECAR
196
194
  }
@@ -205,6 +203,12 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
205
203
  }
206
204
  this.logger.debug("Nodejs MTX scenario without sidecar")
207
205
  }
206
+ } else if (BuildTaskProviderInternal._isExtension(this.env)) {
207
+ // streamlined mtx extension project
208
+ task = {
209
+ for: BUILD_TASK_MTX_EXTENSION
210
+ }
211
+ this.logger.debug("Streamlined MTX extension")
208
212
  } else if (this.env.get("requires.multitenancy") && isNode) {
209
213
  // classic mtx
210
214
  task = {
@@ -219,11 +223,11 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
219
223
 
220
224
  _createSrvTask(projectPath, src, taskOptions) {
221
225
  this.logger.debug("determining implementation technology")
222
- if (!src) {
226
+ if (!src || BuildTaskProviderInternal._isExtension(this.env)) {
223
227
  return null
224
228
  }
225
229
  let task = null
226
- if (this._hasJavaNature(projectPath, src)) {
230
+ if (BuildTaskProviderInternal._hasJavaNature(projectPath, src)) {
227
231
  task = this._createJavaTask(projectPath, src, taskOptions)
228
232
  } else {
229
233
  task = this._createNodeTask(src, taskOptions)
@@ -255,8 +259,12 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
255
259
  }
256
260
  }
257
261
 
258
- _isSidecar(env) {
259
- return env.requires['cds.xt.ModelProviderService']?.root
262
+ static _isSidecar(env) {
263
+ return env.requires['cds.xt.ModelProviderService']?.kind === "in-sidecar"
264
+ }
265
+
266
+ static _isExtension(env) {
267
+ return !!env.extends
260
268
  }
261
269
 
262
270
  /**
@@ -264,7 +272,7 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
264
272
  * @param {string} projectPath - the absolute project path
265
273
  * @param {string} src - the folder name of the service module
266
274
  */
267
- _hasJavaNature(projectPath, src) {
275
+ static _hasJavaNature(projectPath, src) {
268
276
  return hasJavaNature([path.join(projectPath, src), projectPath])
269
277
  }
270
278
 
@@ -22,6 +22,8 @@ const FILE_NAME_PACKAGE_JSON = "package.json"
22
22
  const PATH_LAST_DEV_CSN = "last-dev/csn.json"
23
23
  const FILE_NAME_UNDEPLOY_JSON = "undeploy.json"
24
24
 
25
+ const DEPLOY_FORMAT = "deploy-format"
26
+
25
27
  // add well-known types supported by HANA Cloud Edition - see also https://github.wdf.sap.corp/cap/issues/issues/8056
26
28
  const REQUIRED_PLUGIN_TYPES = [FILE_EXT_CSV, FILE_EXT_HDBTABLEDATA, FILE_EXT_HDBTABLE, ".hdbview", ".hdbindex", ".hdbconstraint"]
27
29
  class HanaModuleBuilder extends BuildTaskHandlerInternal {
@@ -91,22 +93,24 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
91
93
  /**
92
94
  * Copies all files located at <src> (except HANA artefacts not contained in <db>/src/**) to the folder <dest>.
93
95
  * '*.csv' files are read based on the corresponding CDS model file location and copied as flat list into folder '<dest>/src/gen>'.
94
- *
95
- * @param {string} src
96
- * @param {string} dest
97
- * @param {Object} model
96
+ *
97
+ * @param {string} src
98
+ * @param {string} dest
99
+ * @param {Object} model
98
100
  */
99
101
  async _copyResourcesExt(src, dest, model) {
100
- const deploy = require('../../../../lib/deploy')
101
- const resources = Object.keys(await deploy.resources(model))
102
+ const resources = Object.keys(await this.cds.deploy.resources(model))
102
103
  const csvPath = path.join(src, "data")
103
104
  // determine subfolder name used by the application for backward compatibility
104
105
  const csvFolder = resources.some(res => res && res.startsWith(csvPath)) ? "data" : "csv"
105
106
 
106
107
  // 1. copy csv files into 'src/gen/data' or 'src/gen/csv' subfolder
107
108
  const promises = []
109
+ const dbSrc = path.join(src, 'src')
110
+
108
111
  resources.forEach(res => {
109
- if (res && /\.csv$/.test(res)) {
112
+ // do not duplicate resources that are already contained in db/src/**
113
+ if (res && /\.csv$/.test(res) && !res.startsWith(dbSrc)) {
110
114
  promises.push(this.copy(res).to(path.join(this.task.options.compileDest, csvFolder, path.basename(res))))
111
115
  }
112
116
  })
@@ -117,7 +121,6 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
117
121
  blockList += this.hasBuildOption(CONTENT_ENV, false) ? "|\\.env($|\\..*$)" : ""
118
122
  blockList += this.hasBuildOption(CONTENT_DEFAULT_ENV_JSON, false) ? "|default-env\\.json$" : ""
119
123
  blockList = new RegExp(blockList)
120
- const dbSrc = path.join(src, 'src')
121
124
 
122
125
  await this.copyNativeContent(src, dest, (entry) => {
123
126
  if (entry.startsWith(dbSrc)) {
@@ -241,7 +244,7 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
241
244
  const pluginTypes = new Set([...REQUIRED_PLUGIN_TYPES, ...undeployTypes])
242
245
 
243
246
  // compile to old format (.hdbcds) or new format (.hdbtable / .hdbview)
244
- const format = this.env.get("requires.db.deploy-format") || this.env.get("hana.deploy-format")
247
+ const format = this.getBuildOption(DEPLOY_FORMAT) || this.env.get("requires.db." + DEPLOY_FORMAT) || this.env.get("hana." + DEPLOY_FORMAT)
245
248
  if (!this.cds.compile.to[format]) {
246
249
  return Promise.reject(new Error(`Invalid deploy-format defined: ${format}`))
247
250
  }
@@ -4,12 +4,12 @@ const path = require('path')
4
4
  const BuildTaskHandlerEdmx = require('../buildTaskHandlerEdmx')
5
5
  const { isOldJavaStack, BuildError } = require('../../util')
6
6
 
7
- const { BUILD_OPTION_OUTPUT_MODE, ODATA_VERSION, ODATA_VERSION_V2, OUTPUT_MODE_RESULT_ONLY, FILE_EXT_CDS, SKIP_ASSERT_COMPILER_V2, CONTENT_LANGUAGE_BUNDLES, CONTENT_DEFAULT_CSN } = require('../../constants')
7
+ const { BUILD_OPTION_OUTPUT_MODE, ODATA_VERSION, ODATA_VERSION_V2, OUTPUT_MODE_RESULT_ONLY, FILE_EXT_CDS, SKIP_ASSERT_COMPILER_V2, CONTENT_LANGUAGE_BUNDLES, CONTENT_DEFAULT_CSN, DEFAULT_CSN_FILE_NAME } = require('../../constants')
8
8
  const { INFO } = require('../../buildTaskHandler')
9
9
 
10
10
  const DEFAULT_COMPILE_DEST_FOLDER = path.normalize('src/main/resources/edmx')
11
11
 
12
- class JavaCfModuleBuilder extends BuildTaskHandlerEdmx {
12
+ class JavaModuleBuilder extends BuildTaskHandlerEdmx {
13
13
  init() {
14
14
  super.init()
15
15
  this.task.options.compileDest = path.resolve(this.task.dest, this.task.options.compileDest || DEFAULT_COMPILE_DEST_FOLDER)
@@ -48,7 +48,10 @@ class JavaCfModuleBuilder extends BuildTaskHandlerEdmx {
48
48
 
49
49
  if (this.hasBuildOption(CONTENT_LANGUAGE_BUNDLES, true)) {
50
50
  // collect and write language bundles into single i18n.json file
51
- await this.collectLanguageBundles(model, this.task.dest)
51
+ const i18n = await this.collectLanguageBundles(model, this.task.dest)
52
+ if (i18n) {
53
+ this._result.languageBundles = i18n.bundles
54
+ }
52
55
  }
53
56
  if (!this.hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY)) {
54
57
  await this._copyNativeContent(src, dest)
@@ -62,7 +65,7 @@ class JavaCfModuleBuilder extends BuildTaskHandlerEdmx {
62
65
  return
63
66
  }
64
67
  this.logger._debug && this.logger.debug(`Deleting build target folder ${this.task.options.compileDest}`)
65
- await fs.promises.rm(this.task.options.compileDest, {force: true, recursive: true})
68
+ await fs.promises.rm(this.task.options.compileDest, { force: true, recursive: true })
66
69
  }
67
70
 
68
71
  async _copyNativeContent(src, dest) {
@@ -82,13 +85,20 @@ class JavaCfModuleBuilder extends BuildTaskHandlerEdmx {
82
85
  ...this._options4odata(),
83
86
  ...compileOptions
84
87
  })
85
-
88
+ const csnFile = path.join(csnDest, DEFAULT_CSN_FILE_NAME)
89
+ let csnModel
86
90
  // adding csn to build result containing @source and _where persisted properties
87
91
  if (this.hasBuildOption(CONTENT_DEFAULT_CSN, true)) { //default true or undefined
88
- await this.compileToJson(model, csnDest)
92
+ const csnStr = await this.compileToJson(model, csnFile)
93
+ csnModel = JSON.parse(csnStr)
94
+ csnModel.meta = model.meta
89
95
  } else {
90
- await this.compileToJson(m, csnDest)
96
+ const csnStr = await this.compileToJson(m, csnFile)
97
+ csnModel = JSON.parse(csnStr)
98
+ csnModel.meta = m.meta
91
99
  }
100
+ this._result.csn = csnModel
101
+
92
102
  return m
93
103
  }
94
104
 
@@ -98,4 +108,4 @@ class JavaCfModuleBuilder extends BuildTaskHandlerEdmx {
98
108
  return match && match[1] === 1
99
109
  }
100
110
  }
101
- module.exports = JavaCfModuleBuilder
111
+ module.exports = JavaModuleBuilder
@@ -47,7 +47,7 @@ class StreamlinedMtxBuilder {
47
47
  if (!model) {
48
48
  return
49
49
  }
50
- return new ResourcesTarProvider(this._handler).writeTar(dest, model)
50
+ return new ResourcesTarProvider(this._handler).createTar(dest, model)
51
51
  }
52
52
  async clean() {
53
53
  // staging build content is deleted by BuildTaskEngine
@@ -96,8 +96,11 @@ class ClassicMtxBuilder {
96
96
  }))
97
97
 
98
98
  // collect and write language bundles into single i18n.json file
99
- await this._handler.collectLanguageBundles(model, destSdc)
100
-
99
+ const i18n = await this._handler.collectLanguageBundles(model, destSdc)
100
+ if (i18n) {
101
+ this._handler._result.languageBundles = i18n.bundles
102
+ }
103
+
101
104
  // copy native hana content and templates
102
105
  await this._copyNativeContent(this._handler.task.src, destSdc, tenantDbPath)
103
106
  }
@@ -172,7 +175,7 @@ class ClassicMtxBuilder {
172
175
  function isValid(e, pattern, nsPattern, kind) {
173
176
  return e && e.name && (e.name === pattern || (nsPattern && e.name.startsWith(nsPattern))) && (!kind || e.kind === kind)
174
177
  }
175
-
178
+
176
179
  if (extensionAllowlist || entityWhitelist || serviceWhitelist) {
177
180
  const invalidEntries = new Set()
178
181
  const reflected = this._handler.cds.reflect(model)
@@ -1,52 +1,77 @@
1
- const fs = require('fs')
2
1
  const path = require('path')
3
2
  const BuildTaskHandlerInternal = require('../buildTaskHandlerInternal')
4
3
 
5
4
  const { BUILD_TASK_HANA } = require('../../constants')
6
- const { WARNING, INFO } = require('../../buildTaskHandler')
7
- const TAR_NAME = 'resources.tgz'
5
+ const { WARNING } = BuildTaskHandlerInternal
6
+ const DEFAULT_TAR_NAME = "resources.tgz"
8
7
 
9
8
  class ResourcesTarBuilder {
10
9
  constructor(handler) {
11
10
  this._handler = handler
12
11
  }
13
12
 
14
- async packTar(model) {
15
- let resourceRoot = await this._getHanaTenantDbDest()
16
- let files
17
- if (resourceRoot) {
18
- const hanaSrcFolder = path.join(resourceRoot, 'src')
19
- const undeployJson = path.join(resourceRoot, 'undeploy.json')
13
+ get handler() { return this._handler }
20
14
 
21
- // hana build task has already been executed
22
- files = BuildTaskHandlerInternal._find(resourceRoot, (res) => {
23
- if (res === undeployJson) {
24
- return true
25
- }
26
- if (res.startsWith(hanaSrcFolder)) {
27
- return true
28
- }
29
- return false
30
- })
31
- } else {
32
- resourceRoot = path.join(this._handler.buildOptions.root, this._handler.env.folders.db)
33
- const deploy = require('../../../../lib/deploy')
34
- files = Object.keys(await deploy.resources(model))
35
- }
36
- if (files.length > 0) {
37
- const { packTarArchive } = require('../../../../lib/utils/resources')
38
- return packTarArchive(files, resourceRoot, false)
39
- } else {
40
- this._handler.pushMessage("No deployment resources found - skip resource.tgz", WARNING)
15
+ async createTar(dest, model) {
16
+ const { root, files } = await this._getResources(model)
17
+ if (files.length === 0) {
18
+ // packTarArchive failes otherwise
19
+ this.handler.pushMessage("No deployment resources found - skip resources.tgz", WARNING)
20
+ return
41
21
  }
22
+ await this.writeTarFile(files, root, path.join(dest, DEFAULT_TAR_NAME))
42
23
  }
43
24
 
44
- async writeTar(dest, model) {
45
- const buffer = await this.packTar(model)
25
+ async writeTarFile(files, root, tarFile) {
26
+ const { packTarArchive } = require('../../../../lib/utils/resources')
27
+ const buffer = await packTarArchive(files, root)
46
28
  if (buffer) {
47
- await this._handler.write(buffer).to(path.join(dest, TAR_NAME))
29
+ await this.handler.write(buffer).to(tarFile)
30
+ }
31
+ }
32
+
33
+ async _getResources(model) {
34
+ // distinguish HANA and SQLITE deployment
35
+ let root = await this._getHanaTenantDbDest()
36
+ let files
37
+ if (root) {
38
+ files = this._getHanaResources(root)
39
+ } else {
40
+ root = path.join(this.handler.buildOptions.root, this.handler.env.folders.db)
41
+ files = await this._getSqliteResources(model)
48
42
  }
49
- await fs.promises.mkdir(dest, { recursive: true })
43
+ return { root, files }
44
+ }
45
+
46
+ /**
47
+ * Reads all resources for HANA deployment - 'db/src/**' and 'db/undeploy.json'.
48
+ * - CSV files, native HANA artefacts, generated HANA artefacts, undeploy.json
49
+ * See '../../../../lib.deploy'
50
+ * Note: the hana build task has already been executed
51
+ * @param {string} root
52
+ * @returns an array containing all resources
53
+ */
54
+ _getHanaResources(root) {
55
+ const hanaNativeFolders = [path.join(root, 'src'), path.join(root, 'cfg')]
56
+ const hanaNativeFiles = [path.join(root, 'undeploy.json'), path.join(root, '.hdiignore')]
57
+
58
+ return BuildTaskHandlerInternal._find(root, (res) => {
59
+ if (hanaNativeFolders.some(folder => res.startsWith(folder)) || hanaNativeFiles.some(file => res === file)) {
60
+ return true
61
+ }
62
+ return false
63
+ })
64
+ }
65
+
66
+ /**
67
+ * Reads all resources for Sqlite deployment - see '../../../../lib.deploy'
68
+ *
69
+ * @param {object} model
70
+ * @returns
71
+ */
72
+ async _getSqliteResources(model) {
73
+ const { resources } = this.handler.cds.deploy
74
+ return Object.keys(await resources(model))
50
75
  }
51
76
 
52
77
  /**
@@ -56,10 +81,10 @@ class ResourcesTarBuilder {
56
81
  * @returns {string} the src folder of the tenant db module
57
82
  */
58
83
  async _getHanaTenantDbDest() {
59
- const buildOptions = this._handler.buildOptions
84
+ const buildOptions = this.handler.buildOptions
60
85
  let hanaTask = buildOptions.tasks ? buildOptions.tasks.find(task => task.for === BUILD_TASK_HANA) : undefined
61
86
  if (!hanaTask) {
62
- this._handler.pushMessage(`Found SQLite database configuration. Ensure that productive cloud deployments requiring SAP HANA database are built with 'production' profile.`, INFO)
87
+ this.handler.pushMessage(`Found SQLite database configuration. Ensure that productive cloud deployments requiring SAP HANA database are built with 'production' profile.`, WARNING)
63
88
  return null
64
89
  }
65
90
  return hanaTask.dest