@sap/cds 5.8.2 → 5.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. package/CHANGELOG.md +214 -78
  2. package/app/fiori/preview.js +16 -11
  3. package/app/fiori/routes.js +3 -0
  4. package/app/index.js +1 -1
  5. package/bin/build/buildTaskFactory.js +3 -3
  6. package/bin/build/buildTaskProviderFactory.js +1 -1
  7. package/bin/build/constants.js +1 -1
  8. package/bin/build/provider/buildTaskHandlerEdmx.js +12 -7
  9. package/bin/build/provider/buildTaskHandlerInternal.js +1 -1
  10. package/bin/build/provider/buildTaskProviderInternal.js +8 -2
  11. package/bin/build/provider/hana/2migration.js +27 -24
  12. package/bin/build/provider/hana/index.js +17 -18
  13. package/bin/build/provider/hana/migrationtable.js +9 -10
  14. package/bin/build/provider/java-cf/index.js +4 -5
  15. package/bin/build/provider/node-cf/index.js +99 -6
  16. package/bin/cds.js +20 -17
  17. package/bin/deploy/to-hana/cfUtil.js +16 -19
  18. package/bin/deploy/to-hana/hana.js +7 -24
  19. package/bin/deploy/to-hana/hdiDeployUtil.js +8 -4
  20. package/bin/mtx/in-cds.js +2 -2
  21. package/bin/serve.js +12 -5
  22. package/bin/utils/modules.js +7 -0
  23. package/bin/version.js +56 -3
  24. package/lib/compile/cdsc.js +26 -3
  25. package/lib/compile/etc/_localized.js +36 -25
  26. package/lib/compile/etc/csv.js +8 -8
  27. package/lib/compile/for/drafts.js +9 -0
  28. package/lib/compile/for/java.js +16 -0
  29. package/lib/compile/for/nodejs.js +12 -0
  30. package/lib/compile/for/odata.js +1 -1
  31. package/lib/compile/index.js +3 -0
  32. package/lib/compile/minify.js +16 -2
  33. package/lib/compile/parse.js +2 -2
  34. package/lib/compile/resolve.js +35 -18
  35. package/lib/compile/to/json.js +3 -1
  36. package/lib/compile/to/sql.js +2 -2
  37. package/lib/compile/to/srvinfo.js +4 -2
  38. package/lib/connect/index.js +1 -1
  39. package/lib/core/entities.js +15 -14
  40. package/lib/core/index.js +39 -36
  41. package/lib/core/reflect.js +4 -2
  42. package/lib/deploy.js +114 -127
  43. package/lib/env/defaults.js +1 -0
  44. package/lib/env/index.js +165 -165
  45. package/lib/env/presets.js +1 -0
  46. package/lib/env/requires.js +120 -49
  47. package/lib/index.js +1 -0
  48. package/lib/log/format/kibana.js +2 -2
  49. package/lib/ql/SELECT.js +10 -0
  50. package/lib/ql/parse.js +1 -0
  51. package/lib/req/cds-context.js +4 -1
  52. package/lib/req/context.js +50 -56
  53. package/lib/req/event.js +1 -6
  54. package/lib/req/locale.js +6 -5
  55. package/lib/req/request.js +2 -0
  56. package/lib/req/user.js +7 -5
  57. package/lib/serve/Service-api.js +10 -7
  58. package/lib/serve/Service-dispatch.js +9 -11
  59. package/lib/serve/Service-methods.js +30 -41
  60. package/lib/serve/Transaction.js +10 -7
  61. package/lib/serve/adapters.js +7 -5
  62. package/lib/serve/index.js +24 -12
  63. package/lib/utils/data.js +1 -1
  64. package/lib/utils/index.js +27 -30
  65. package/lib/utils/resources/index.js +101 -0
  66. package/lib/utils/resources/tar.js +71 -0
  67. package/lib/utils/resources/utils.js +11 -0
  68. package/libx/_runtime/audit/Service.js +36 -39
  69. package/libx/_runtime/audit/generic/personal/access.js +3 -4
  70. package/libx/_runtime/audit/generic/personal/modification.js +3 -4
  71. package/libx/_runtime/audit/utils/v2.js +1 -2
  72. package/libx/_runtime/auth/index.js +126 -84
  73. package/libx/_runtime/auth/strategies/JWT.js +12 -19
  74. package/libx/_runtime/auth/strategies/dummy.js +1 -5
  75. package/libx/_runtime/auth/strategies/dwc.js +11 -9
  76. package/libx/_runtime/auth/strategies/mock.js +0 -4
  77. package/libx/_runtime/auth/strategies/{utils/xssec.js → xssecUtils.js} +7 -4
  78. package/libx/_runtime/auth/strategies/xsuaa.js +12 -19
  79. package/libx/_runtime/auth/utils.js +22 -1
  80. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +104 -98
  81. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +2 -2
  82. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
  83. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +13 -0
  84. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/language.js +2 -8
  85. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +4 -29
  86. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +2 -1
  87. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +3 -2
  88. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +2 -2
  89. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +4 -6
  90. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +24 -21
  91. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +8 -2
  92. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriHelper.js +1 -1
  93. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +2 -0
  94. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +5 -6
  95. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/DispatcherCommand.js +2 -6
  96. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/utils/UriHelper.js +4 -1
  97. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -12
  98. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +33 -9
  99. package/libx/_runtime/cds-services/adapter/odata-v4/utils/dispatcherUtils.js +50 -0
  100. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +2 -2
  101. package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +10 -3
  102. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +9 -11
  103. package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +6 -3
  104. package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +4 -2
  105. package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/utils.js +1 -1
  106. package/libx/_runtime/cds-services/adapter/rest/utils/binary.js +1 -1
  107. package/libx/_runtime/cds-services/adapter/rest/utils/key-value-utils.js +2 -3
  108. package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +6 -4
  109. package/libx/_runtime/cds-services/adapter/rest/utils/result.js +1 -0
  110. package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +8 -5
  111. package/libx/_runtime/cds-services/services/Service.js +40 -0
  112. package/libx/_runtime/cds-services/services/utils/columns.js +4 -3
  113. package/libx/_runtime/cds-services/services/utils/compareJson.js +4 -4
  114. package/libx/_runtime/cds-services/services/utils/differ.js +3 -3
  115. package/libx/_runtime/cds-services/services/utils/handlerUtils.js +4 -4
  116. package/libx/_runtime/cds-services/services/utils/restrictions.js +78 -0
  117. package/libx/_runtime/cds-services/util/assert.js +20 -14
  118. package/libx/_runtime/cds.js +9 -1
  119. package/libx/_runtime/common/aspects/any.js +5 -0
  120. package/libx/_runtime/common/aspects/entity.js +25 -7
  121. package/libx/_runtime/common/aspects/utils.js +2 -2
  122. package/libx/_runtime/common/composition/data.js +6 -0
  123. package/libx/_runtime/common/composition/insert.js +3 -2
  124. package/libx/_runtime/common/composition/tree.js +4 -10
  125. package/libx/_runtime/common/composition/update.js +4 -4
  126. package/libx/_runtime/common/constants/draft.js +29 -26
  127. package/libx/_runtime/common/error/constants.js +2 -2
  128. package/libx/_runtime/common/error/frontend.js +7 -15
  129. package/libx/_runtime/common/generic/auth/capabilities.js +59 -0
  130. package/libx/_runtime/common/generic/auth/constants.js +20 -0
  131. package/libx/_runtime/common/generic/auth/expand.js +54 -0
  132. package/libx/_runtime/common/generic/auth/index.js +32 -0
  133. package/libx/_runtime/common/generic/auth/insertOnly.js +15 -0
  134. package/libx/_runtime/common/generic/auth/readOnly.js +26 -0
  135. package/libx/_runtime/common/generic/auth/requires.js +34 -0
  136. package/libx/_runtime/common/generic/auth/restrict.js +296 -0
  137. package/libx/_runtime/common/generic/auth/utils.js +213 -0
  138. package/libx/_runtime/common/generic/crud.js +14 -10
  139. package/libx/_runtime/common/generic/etag.js +1 -1
  140. package/libx/_runtime/common/generic/input.js +35 -35
  141. package/libx/_runtime/common/generic/sorting.js +2 -3
  142. package/libx/_runtime/common/generic/temporal.js +2 -2
  143. package/libx/_runtime/common/i18n/index.js +2 -31
  144. package/libx/_runtime/common/i18n/messages.properties +1 -1
  145. package/libx/_runtime/common/toggles/handler.js +21 -0
  146. package/libx/_runtime/common/utils/copy.js +10 -1
  147. package/libx/_runtime/common/utils/cqn2cqn4sql.js +100 -29
  148. package/libx/_runtime/common/utils/csn.js +63 -1
  149. package/libx/_runtime/common/utils/dollar.js +10 -1
  150. package/libx/_runtime/common/utils/draft.js +46 -7
  151. package/libx/_runtime/common/utils/entityFromCqn.js +13 -9
  152. package/libx/_runtime/common/utils/extensibilityUtils.js +18 -0
  153. package/libx/_runtime/common/utils/foreignKeyPropagations.js +88 -104
  154. package/libx/_runtime/common/utils/generateOnCond.js +9 -6
  155. package/libx/_runtime/common/utils/quotingStyles.js +2 -0
  156. package/libx/_runtime/common/utils/resolveStructured.js +25 -9
  157. package/libx/_runtime/common/utils/resolveView.js +4 -1
  158. package/libx/_runtime/common/utils/rewriteAsterisks.js +3 -16
  159. package/libx/_runtime/common/utils/structured.js +33 -37
  160. package/libx/_runtime/common/utils/template.js +17 -8
  161. package/libx/_runtime/common/utils/templateProcessor.js +28 -28
  162. package/libx/_runtime/db/data-conversion/post-processing.js +118 -417
  163. package/libx/_runtime/db/expand/expandCQNToJoin.js +45 -41
  164. package/libx/_runtime/db/expand/rawToExpanded.js +29 -8
  165. package/libx/_runtime/db/generic/index.js +1 -3
  166. package/libx/_runtime/db/generic/input.js +5 -10
  167. package/libx/_runtime/db/generic/rewrite.js +5 -2
  168. package/libx/_runtime/db/generic/structured.js +2 -2
  169. package/libx/_runtime/db/query/delete.js +2 -2
  170. package/libx/_runtime/db/query/insert.js +1 -1
  171. package/libx/_runtime/db/query/update.js +9 -14
  172. package/libx/_runtime/db/sql-builder/CreateBuilder.js +4 -3
  173. package/libx/_runtime/db/sql-builder/InsertBuilder.js +14 -1
  174. package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -2
  175. package/libx/_runtime/db/sql-builder/dataTypes.js +3 -3
  176. package/libx/_runtime/db/utils/columns.js +3 -3
  177. package/libx/_runtime/db/utils/normalizeTimeData.js +2 -2
  178. package/libx/_runtime/db/utils/propagateForeignKeys.js +6 -2
  179. package/libx/_runtime/extensibility/mps/index.js +5 -0
  180. package/libx/_runtime/extensibility/mps/service.js +111 -0
  181. package/libx/_runtime/extensibility/mps/tar.js +42 -0
  182. package/libx/_runtime/extensibility/mps/utils.js +11 -0
  183. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformREAD.js +0 -0
  184. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformRESULT.js +17 -5
  185. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformWRITE.js +1 -0
  186. package/libx/_runtime/extensibility/uiflex/index.js +54 -0
  187. package/libx/_runtime/extensibility/uiflex/service.js +276 -0
  188. package/libx/_runtime/{fiori → extensibility}/uiflex/utils.js +22 -7
  189. package/libx/_runtime/fiori/generic/activate.js +2 -2
  190. package/libx/_runtime/fiori/generic/before.js +4 -4
  191. package/libx/_runtime/fiori/generic/new.js +3 -3
  192. package/libx/_runtime/fiori/generic/patch.js +1 -1
  193. package/libx/_runtime/fiori/generic/read.js +58 -66
  194. package/libx/_runtime/fiori/generic/readOverDraft.js +71 -16
  195. package/libx/_runtime/fiori/utils/handler.js +6 -13
  196. package/libx/_runtime/fiori/utils/where.js +6 -5
  197. package/libx/_runtime/hana/Service.js +4 -10
  198. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +2 -2
  199. package/libx/_runtime/hana/driver.js +2 -2
  200. package/libx/_runtime/hana/execute.js +29 -75
  201. package/libx/_runtime/hana/pool.js +1 -1
  202. package/libx/_runtime/hana/streaming.js +2 -1
  203. package/libx/_runtime/index.js +6 -6
  204. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +5 -21
  205. package/libx/_runtime/messaging/Outbox.js +2 -2
  206. package/libx/_runtime/messaging/common-utils/AMQPClient.js +4 -14
  207. package/libx/_runtime/messaging/common-utils/connections.js +5 -7
  208. package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +30 -0
  209. package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -1
  210. package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +36 -30
  211. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -12
  212. package/libx/_runtime/messaging/enterprise-messaging.js +8 -8
  213. package/libx/_runtime/messaging/file-based.js +5 -5
  214. package/libx/_runtime/messaging/message-queuing.js +14 -12
  215. package/libx/_runtime/messaging/outbox/utils.js +18 -19
  216. package/libx/_runtime/messaging/redis-messaging.js +91 -0
  217. package/libx/_runtime/messaging/service.js +8 -6
  218. package/libx/_runtime/remote/Service.js +44 -8
  219. package/libx/_runtime/remote/utils/client.js +25 -13
  220. package/libx/_runtime/remote/utils/data.js +11 -11
  221. package/libx/_runtime/sqlite/Service.js +6 -9
  222. package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +5 -2
  223. package/libx/_runtime/types/api.js +10 -2
  224. package/libx/common/utils/ucsn.js +109 -0
  225. package/libx/gql/resolvers/crud/create.js +6 -1
  226. package/libx/gql/resolvers/crud/delete.js +6 -1
  227. package/libx/gql/resolvers/crud/read.js +6 -1
  228. package/libx/gql/resolvers/crud/update.js +9 -1
  229. package/libx/gql/resolvers/parse/ast2cqn/columns.js +3 -1
  230. package/libx/gql/schema/typeDefMap.js +2 -2
  231. package/libx/odata/afterburner.js +110 -16
  232. package/libx/odata/grammar.pegjs +9 -1
  233. package/libx/odata/parseToCqn.js +39 -0
  234. package/libx/odata/parser.js +1 -1
  235. package/libx/rest/RestAdapter.js +9 -1
  236. package/libx/rest/middleware/input.js +54 -0
  237. package/libx/rest/middleware/operation.js +14 -1
  238. package/libx/rest/middleware/parse.js +11 -7
  239. package/package.json +1 -1
  240. package/server.js +34 -19
  241. package/srv/audit-log.cds +2 -2
  242. package/srv/flex.cds +8 -2
  243. package/srv/flex.js +1 -1
  244. package/srv/mps.cds +23 -0
  245. package/srv/mps.js +1 -0
  246. package/libx/_runtime/auth/strategies/utils/uaa.js +0 -21
  247. package/libx/_runtime/common/generic/auth.js +0 -874
  248. package/libx/_runtime/common/toggles/alpha.js +0 -43
  249. package/libx/_runtime/db/generic/arrayed.js +0 -33
  250. package/libx/_runtime/fiori/uiflex/index.js +0 -35
  251. package/libx/_runtime/fiori/uiflex/service.js +0 -150
  252. package/libx/rest/utils/data.js +0 -60
@@ -31,8 +31,11 @@ cds.on('served', ()=>{
31
31
  },
32
32
  },
33
33
  'sap.ui5': {
34
+ flexEnabled: true,
34
35
  dependencies: {
36
+ minUI5Version: '1.96.0',
35
37
  libs: {
38
+ 'sap.ui.core': {},
36
39
  'sap.fe.templates': {}
37
40
  }
38
41
  },
@@ -104,7 +107,12 @@ cds.on('served', ()=>{
104
107
  },
105
108
  'sap.ui': {
106
109
  technology: 'UI5',
107
- fullWidth: true
110
+ fullWidth: true,
111
+ deviceTypes: {
112
+ desktop: true,
113
+ tablet: true,
114
+ phone: true
115
+ }
108
116
  },
109
117
  'sap.fiori': {
110
118
  registrationIds: [],
@@ -176,19 +184,16 @@ cds.on('served', ()=>{
176
184
  </script>
177
185
  <script id="sap-ushell-bootstrap" src="${ui5Host}test-resources/sap/ushell/bootstrap/sandbox.js"></script>
178
186
  <script id="sap-ui-bootstrap" src="${ui5Host}resources/sap-ui-core.js"
179
- data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout" data-sap-ui-compatVersion="edge"
180
- data-sap-ui-theme="sap_fiori_3" data-sap-ui-frameOptions="allow">
181
- </script>
182
- <script src="${ui5Host}test-resources/sap/ushell/bootstrap/standalone.js"></script>
187
+ data-sap-ui-theme="sap_horizon"
188
+ data-sap-ui-oninit="module:sap/ui/core/ComponentSupport"
189
+ data-sap-ui-compatVersion="edge"
190
+ data-sap-ui-async="true"
191
+ data-sap-ui-preload="async"></script>
183
192
  <script>
184
- // load and register Fiori2 icon font
185
- jQuery.sap.require("sap.ushell.iconfonts");
186
- jQuery.sap.require("sap.ushell.services.AppConfiguration");
187
- sap.ushell.iconfonts.registerFiori2IconFont();
188
193
  sap.ui.getCore().attachInit(function() { sap.ushell.Container.createRenderer().placeAt("content") })
189
194
  </script>
190
195
  </head>
191
- <body class="sapUiBody sapUShellFullHeight" id="content"></body>
196
+ <body class="sapUiBody sapUiSizeCompact" id="content"></body>
192
197
  </html>
193
198
  `
194
199
  }
@@ -260,7 +265,7 @@ cds.on('served', ()=>{
260
265
  return {
261
266
  href: _appURL(service.name, entity),
262
267
  title: 'Preview in Fiori elements',
263
- name: 'Fiori'
268
+ name: 'Fiori preview'
264
269
  }
265
270
  }
266
271
  }
@@ -15,6 +15,9 @@ cds.on ('bootstrap', app => {
15
15
  app.use('*/'+uri, ({originalUrl}, res, next)=> { // */browse/webapp[/prefix]/browse/
16
16
  // any of our special URLs ($fiori-, $api-docs) ? -> next
17
17
  if (originalUrl.startsWith('/$')) return next()
18
+ // is there a service starting with the URL? -> next
19
+ if (cds.service.providers.find (srv => originalUrl.startsWith(srv.path))) return next()
20
+
18
21
  // is there a service for '[prefix]/browse' ?
19
22
  const srv = serviceForUri[uri] || (serviceForUri[uri] =
20
23
  cds.service.providers.find (srv => ('/'+uri).lastIndexOf(srv.path) >=0))
package/app/index.js CHANGED
@@ -68,7 +68,7 @@ function _moreLinks (srv, entity) {
68
68
  .map (linkProv => linkProv(entity))
69
69
  .filter (l => l && l.href && l.name)
70
70
  .sort ((l1, l2) => l1.name.localeCompare(l2))
71
- .map (l => ` <a class="preview" href="${l.href}" title="${l.title||l.name}"> &rarr; ${l.name} preview </a>`)
71
+ .map (l => ` <a class="preview" href="${l.href}" title="${l.title||l.name}"> &rarr; ${l.name}</a>`)
72
72
  .join (' ')
73
73
  }
74
74
 
@@ -128,8 +128,8 @@ class BuildTaskFactory {
128
128
  // Required as cds.serve is invoking cds.resolve('*') which caused cds to cache the current model state
129
129
  // which in turn screwed-up all subsequent tests - see ./lib/compile/resolve.js#L67 and ./lib/compile/resolve.js#L58
130
130
  this.cds.resolve.cache = {}
131
-
132
- const modelPaths = this.cds.resolve("*", false)
131
+ const fts = this.env.features.folders
132
+ const modelPaths = this.cds.resolve(!fts ? '*' : ['*', fts], false)
133
133
  return this._pushModelPaths(projectPath, [], modelPaths)
134
134
  }
135
135
 
@@ -245,7 +245,7 @@ class BuildTaskFactory {
245
245
  model = new Set(model)
246
246
  // may contain nested arrays
247
247
  modelPaths = BuildTaskFactory._flatten(modelPaths)
248
- const { roots } = this.cds.env
248
+ const { roots } = this.env
249
249
  modelPaths.forEach(m => {
250
250
  if (m && !model.has(m) && !model.has(m + "/")) {
251
251
  // filter root model paths that do not exist
@@ -189,7 +189,7 @@ class DefaultBuildTaskProvider extends BuildTaskProvider {
189
189
  throw new Error(`Invalid build task definition [${task.for}] - property 'use' missing`)
190
190
  }
191
191
  try {
192
- return module.parent.require(task.use)
192
+ return require(require.resolve(task.use, { paths: [this._cds.root] }))
193
193
  }
194
194
  catch (e) {
195
195
  throw new Error(`Build task could not be resolved - module [${task.use}] cannot be loaded:\n` + e)
@@ -5,7 +5,7 @@ exports.OUTPUT_MODE_PREVIEW = "preview"
5
5
  exports.OUTPUT_MODE_RESULT_ONLY = "resultOnly"
6
6
 
7
7
  exports.BUILD_TASK_NPM_SCOPE = "@sap"
8
- exports.BUILD_TASK_PREFIX = exports.BUILD_TASK_NPM_SCOPE + "/cds/lib/build"
8
+ exports.BUILD_TASK_PREFIX = exports.BUILD_TASK_NPM_SCOPE + "/cds/build"
9
9
  exports.BUILD_TASK_JAVA = "java-cf"
10
10
  exports.BUILD_TASK_NODE = "node-cf"
11
11
  exports.BUILD_TASK_HANA = "hana"
@@ -55,7 +55,7 @@ class BuildTaskHandlerEdmx extends BuildTaskHandlerInternal {
55
55
  return Promise.all(promises)
56
56
  }
57
57
 
58
- async compileToJson(model, csnDest) {
58
+ async compileToJson(model, csnDest, saveResult = true) {
59
59
  // This will als add a @source prop containing the relative path to the origin .cds source file
60
60
  // and a parsed _where clause for @restrict.{grant,where} annotations.
61
61
  // The @source annotation is required for correct custom handler resolution if no @impl annotation has been defined as
@@ -68,14 +68,17 @@ class BuildTaskHandlerEdmx extends BuildTaskHandlerInternal {
68
68
  src: this.task.src === this.task.dest ? this.task.src : this.buildOptions.root
69
69
  }
70
70
  const csnStr = this.cds.compile.to.json(model, jsonOptions)
71
- this._result.csn = JSON.parse(csnStr)
72
- this._result.csn.meta = model.meta
71
+ const csn = JSON.parse(csnStr)
72
+ if (saveResult) {
73
+ this._result.csn = csn
74
+ this._result.csn.meta = model.meta
75
+ }
73
76
 
74
77
  // csnDest might be null
75
78
  if (csnDest && !this.hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY)) {
76
79
  await this.write(csnStr).to(path.join(csnDest, 'csn.json'))
77
80
  }
78
- return this._result.csn
81
+ return csn
79
82
  }
80
83
 
81
84
  /**
@@ -83,7 +86,7 @@ class BuildTaskHandlerEdmx extends BuildTaskHandlerInternal {
83
86
  * @param {Object} model
84
87
  * @param {string} bundleDest
85
88
  */
86
- async collectLanguageBundles(model, bundleDest) {
89
+ async collectLanguageBundles(model, bundleDest, saveResult = true) {
87
90
  // collect effective i18n properties...
88
91
  let bundles = {}
89
92
  const bundleGenerator = this.cds.localize.bundles4(model)
@@ -103,14 +106,16 @@ class BuildTaskHandlerEdmx extends BuildTaskHandlerInternal {
103
106
  }
104
107
  // copied from ../compile/i18n.js
105
108
  const { folders = ['i18n'], file = 'i18n' } = this.env.i18n
106
-
109
+
107
110
  // bundleDest might be null
108
111
  if (bundleDest && Object.keys(bundles).length > 0) {
109
112
  if (!this.hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY)) {
110
113
  await this.write(bundles).to(path.join(bundleDest, folders[0], file + '.json'))
111
114
  }
112
115
  }
113
- this._result.languageBundles = bundles
116
+ if (saveResult) {
117
+ this._result.languageBundles = bundles
118
+ }
114
119
  return bundles
115
120
  }
116
121
 
@@ -166,7 +166,7 @@ class BuildTaskHandlerInternal extends BuildTaskHandler {
166
166
  }
167
167
 
168
168
  options() {
169
- return { messages: this._messages, logger: this.logger }
169
+ return { messages: this._messages }
170
170
  }
171
171
 
172
172
  _isSubDirectory(parent, child) {
@@ -25,6 +25,8 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
25
25
  canHandleTask(task) {
26
26
  return BUILD_TASKS.includes(task.for)
27
27
  || task.use && task.use.startsWith(BUILD_TASK_PREFIX)
28
+ // support different build task implementations of the same build task ID defined by 'for'
29
+ //return (BUILD_TASKS.includes(task.for) && !task.use) || (task.use && task.use.startsWith(BUILD_TASK_PREFIX))
28
30
  }
29
31
 
30
32
  loadHandler(task) {
@@ -284,8 +286,12 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
284
286
  }
285
287
 
286
288
  _getDefaultModelOptions(projectPath) {
287
- const rm = Object.values(this.env.requires).map(r => r.model).filter(m => m)
288
- return BuildTaskProviderInternal._pushModelPaths(projectPath, [], this.env.roots, rm)
289
+ // clear model cache - see https://github.tools.sap/cap/cds/pull/181
290
+ // Required as cds.serve is invoking cds.resolve('*') which caused cds to cache the current model state
291
+ // which in turn screwed-up all subsequent tests - see ./lib/compile/resolve.js#L67 and ./lib/compile/resolve.js#L58
292
+ const fts = this.env.features.folders
293
+ const modelPaths = this.cds.resolve(!fts ? '*' : ['*', fts], false)
294
+ return BuildTaskProviderInternal._pushModelPaths(projectPath, [], modelPaths)
289
295
  }
290
296
 
291
297
  _getFioriAppModelPaths(tasks, projectPath) {
@@ -33,7 +33,6 @@ module.exports = async (model, lastDevVersion, srcPath, options = {}) => {
33
33
  }
34
34
 
35
35
  function _toHdiMigration(model, lastDevVersion, journalFileNames, options) {
36
- options.sqlChangeMode = getProperty(options, 'hana.journal.change-mode')
37
36
  const result = cdsc.to.hdi.migration(cds.minify(model), options, lastDevVersion);
38
37
  if (logger._debug) {
39
38
  logger.debug('cdsc.to.hdi.migration returned')
@@ -96,34 +95,38 @@ async function _2migrationtable(srcPath, migration, tableSql, options) {
96
95
 
97
96
  function _getNewMigrationEntry(changeset, currentVersion, options) {
98
97
  if (changeset && changeset.length > 0) {
99
- let dropColumns = false
100
- const enableDrop = getProperty(options, 'hana.journal.enable-drop')
101
- const changesetStr = changeset.map(entry => {
102
- dropColumns = dropColumns || entry.drop
103
- if (entry.drop && enableDrop !== true) {
104
- return entry.sql.replace(/^/gm, '-- ');
105
- }
106
- return entry.sql
107
- }).join('\n')
98
+ const dropColumns = changeset.some(e => e.drop)
99
+ const manualChange = changeset.some(e => !e.sql)
100
+ const enableDrop = cds.env.get('hana.journal.enable-drop')
101
+ const content = changeset.reduce((acc, e) => {
102
+ if (!acc) {
103
+ acc = `== migration=${currentVersion + 1}\n`
104
+ acc += `${cdscVersion}\n`
108
105
 
109
- if (!dropColumns || enableDrop === true) {
110
- return {
111
- dropColumns,
112
- content: `== migration=${currentVersion + 1}\n${cdscVersion}\n${changesetStr}`
113
- }
114
- } else {
115
- return {
116
- dropColumns,
117
- content: `== migration=${currentVersion + 1}
118
- >>>>> Manual resolution required - DROP statements causing data loss are disabled by default.
106
+ if (dropColumns && enableDrop !== true) {
107
+ acc += `>>>>> Manual resolution required - DROP statements causing data loss are disabled by default.
119
108
  >>>>> You may either:
120
109
  >>>>> uncomment statements to allow incompatible changes, or
121
110
  >>>>> refactor statements, e.g. replace DROP/ADD by single RENAME statement
122
- >>>>> After manual resolution delete all lines starting with >>>>>
123
- ${cdscVersion}
124
- ${changesetStr}`
111
+ >>>>> After manual resolution delete all lines starting with >>>>>\n`
112
+ } else if (manualChange) {
113
+ acc += `>>>>> Manual resolution required - insert ALTER statement(s) as described below.
114
+ >>>>> After manual resolution delete all lines starting with >>>>>\n`
115
+ }
125
116
  }
126
- }
117
+ if (e.sql) {
118
+ if (e.drop && enableDrop !== true) {
119
+ acc += `${e.sql.replace(/^/gm, '-- ')}\n`;
120
+ } else {
121
+ acc += `${e.sql}\n`
122
+ }
123
+ } else {
124
+ acc +=
125
+ `>>>>> Insert ALTER statement for: ${e.description}\n`
126
+ }
127
+ return acc
128
+ }, null)
129
+ return { dropColumns, content }
127
130
  }
128
131
  return null
129
132
  }
@@ -4,7 +4,7 @@ const BuildTaskHandlerInternal = require('../buildTaskHandlerInternal')
4
4
  const { getHanaDbModuleDescriptor } = require('../../mtaUtil')
5
5
  const { OUTPUT_MODE_RESULT_ONLY, BUILD_OPTION_OUTPUT_MODE, SKIP_HDBTABLEDATA_GENERATION, SKIP_PACKAGE_JSON_GENERATION,
6
6
  SKIP_MANIFEST_GENERATION, CONTENT_MANIFEST, CONTENT_PACKAGE_JSON, CONTENT_HDBTABLEDATA } = require('../../constants')
7
- const { BuildError, setProperty, relativePaths } = require('../../util')
7
+ const { BuildError, setProperty, relativePaths, getProperty } = require('../../util')
8
8
  const CSV = require('../../csv-reader')
9
9
  const to_hdbmigration = require('./2migration')
10
10
  const to_hdbtabledata = require('./2tabledata')
@@ -55,7 +55,7 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
55
55
  // Note:
56
56
  // Native hana artifacts are currently not supported by extensions - thus content copied from the base model cannot overwrite
57
57
  // content defined by the extension.
58
- if (this.task.options.build && this.task.options.build.mtxOriginalSrc) {
58
+ if (getProperty(this.task, "options.build.mtxOriginalSrc")) {
59
59
  const baseModelDbSrcPath = path.join(this.buildOptions.root, this.task.options.build.mtxOriginalSrc)
60
60
  if (await fs.pathExists(baseModelDbSrcPath)) {
61
61
  await this._copyNativeContent(baseModelDbSrcPath, dest)
@@ -65,7 +65,7 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
65
65
  await this._copyNativeContent(src, dest)
66
66
  await this._writeHdiConfig(plugins)
67
67
  await this._writeHdiNamespace()
68
- // TODO disabled as this contradicts the MTX domain concept which allows partial app deployments
68
+ // TODO disabled as this contradicts the MTX domain concept which allows partial app deployments
69
69
  //await this._writeUndeployJson()
70
70
 
71
71
  if (this.hasBuildOption(CONTENT_HDBTABLEDATA, true)) {
@@ -170,9 +170,9 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
170
170
 
171
171
  const promises = []
172
172
  const relDest = path.relative(this.task.dest, this.task.options.compileDest)
173
- const options = { ...this.options(), dirs: csvDirs, baseDir: this.task.options.compileDest }
174
-
173
+ const options = { ...this.options(), logger: this.logger, dirs: csvDirs, baseDir: this.task.options.compileDest }
175
174
  const tableDatas = await to_hdbtabledata(model, options)
175
+
176
176
  for (let [tableData, { file, csvFolder }] of tableDatas) {
177
177
  // create .hdbtabledata side-by-side if .csv is contained in 'src/gen/**' subfolder
178
178
  // otherwise create in 'src/gen'
@@ -205,26 +205,24 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
205
205
  const undeployTypes = await this._readTypesFromUndeployJson()
206
206
  const pluginTypes = new Set([...REQUIRED_PLUGIN_TYPES, ...undeployTypes])
207
207
 
208
- // enforces sqlNames option for compiler in tests
209
- const options = this.options()
210
- options.sql_mapping = this.env.sql.names
211
-
212
208
  // compile to old format (.hdbcds) or new format (.hdbtable / .hdbview)
213
- const format = this.env.hana['deploy-format']
209
+ const format = this.env.get("requires.db.deploy-format") || this.env.get("hana.deploy-format")
214
210
  if (!this.cds.compile.to[format]) {
215
211
  return Promise.reject(new Error(`Invalid deploy-format defined: ${format}`))
216
212
  }
217
213
 
218
214
  if (this.hasCdsEnvOption('features.journal', false) || format === 'hdbcds') {
219
- await this._compileToHdb(model, pluginTypes, format, options)
215
+ await this._compileToHdb(model, pluginTypes, format)
220
216
  } else {
221
- await this._compileToHdbmigration(model, pluginTypes, options)
217
+ await this._compileToHdbmigration(model, pluginTypes)
222
218
  }
223
219
  return pluginTypes
224
220
  }
225
221
 
226
- async _compileToHdb(model, pluginTypes, format, options) {
222
+ async _compileToHdb(model, pluginTypes, format) {
227
223
  const relDest = path.relative(this.task.dest, this.task.options.compileDest)
224
+ // enforces sqlNames option for compiler in tests
225
+ const options = { ...this.options(), sql_mapping: this.env.sql.names }
228
226
  const result = this.cds.compile.to[format](model, options)
229
227
  const promises = []
230
228
 
@@ -239,7 +237,7 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
239
237
  await Promise.all(promises)
240
238
  }
241
239
 
242
- async _compileToHdbmigration(model, pluginTypes, options) {
240
+ async _compileToHdbmigration(model, pluginTypes) {
243
241
  const relDestDir = path.relative(this.task.dest, this.task.options.compileDest)
244
242
  const relDbDestDir = path.relative(this.buildOptions.root, this.task.options.compileDest)
245
243
  const dbSrcDir = path.join(this.task.src, "src")
@@ -253,8 +251,9 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
253
251
  if (await fs.pathExists(lastDevCsnDir)) {
254
252
  lastDev = await fs.readJSON(lastDevCsnDir, 'utf-8')
255
253
  }
254
+ // enforces sqlNames option for compiler in tests
256
255
  // pass options from cds env
257
- setProperty(options, 'hana.journal', this.env.get('hana.journal'))
256
+ const options = { ...this.options(), logger: this.logger, sql_mapping: this.env.sql.names }
258
257
 
259
258
  const compilationResult = await to_hdbmigration(model, lastDev, dbSrcDir, options)
260
259
  const definitions = compilationResult.definitions
@@ -454,12 +453,12 @@ applications:
454
453
  await Promise.all(migrationTableFiles.map(async file => {
455
454
  try {
456
455
  const tableModel = await parser.read(file)
457
- if (tableModel && /^>>>>>/m.test(tableModel.migrations.toString())) {
456
+ if (/^>>>>>/m.test(tableModel.migrations.toString())) {
457
+ // as this is not a build error, we do not abort cds build, instead only log as error
458
458
  this.pushMessage(`Current model changes require manual resolution. See migration file ${path.relative(this.buildOptions.root, file)} for further details.`, ERROR)
459
459
  }
460
460
  } catch (e) {
461
- // do not abort build in post validation step
462
- this.pushMessage(`${path.relative(this.buildOptions.root, file)}: ${e.toString()}`, ERROR)
461
+ throw new Error(`${path.relative(this.buildOptions.root, file)}: ${e.toString()}`, ERROR)
463
462
  }
464
463
  }))
465
464
  }
@@ -33,15 +33,15 @@ class MigrationTableParser {
33
33
  if (isTableBegin || isTableEnd || isMigration) {
34
34
  throw new Error(`Invalid format, version defintion must be very first statement`)
35
35
  }
36
- } else if (/^\s*COLUMN TABLE\s/.test(lines[idx])) {
36
+ } else if (/^\s*(ROW(\s*COLUMN)?|COLUMN)\s*TABLE\s/.test(lines[idx])) {
37
37
  if (tVersion === -1) {
38
38
  throw new Error(`Invalid format, version entry not complying to format '^== version=d+'`)
39
39
  }
40
40
  if (isTableBegin) {
41
- throw new Error(`Invalid format, multiple COLUMN TABLE definitions found`)
41
+ throw new Error(`Invalid format, multiple TABLE definitions found`)
42
42
  }
43
43
  if (isMigration) {
44
- throw new Error(`Invalid format, migrations must not be mixed with COLUMN TABLE definitions`)
44
+ throw new Error(`Invalid format, migrations must not be mixed with TABLE definitions`)
45
45
  }
46
46
  isTableBegin = true
47
47
  } else if (MigrationTableParser._isMigrationMarker(lines[idx])) {
@@ -54,7 +54,7 @@ class MigrationTableParser {
54
54
  }
55
55
  if (!isMigration) {
56
56
  if (!isTableBegin) {
57
- throw new Error(`Invalid format, COLUMN TABLE statement missing`)
57
+ throw new Error(`Invalid format, TABLE statement missing`)
58
58
  }
59
59
  // back search for end table
60
60
  for (let tIdx = idx - 1; tIdx > 0; tIdx--) {
@@ -71,10 +71,10 @@ class MigrationTableParser {
71
71
  }
72
72
  }
73
73
  if (!isTableBegin) {
74
- throw new Error(`Invalid format, COLUMN TABLE statement missing`)
74
+ throw new Error(`Invalid format, TABLE statement missing`)
75
75
  }
76
76
  if (!isTableEnd) {
77
- throw new Error(`Invalid format, COLUMN TABLE statement not correctly terminated`)
77
+ throw new Error(`Invalid format, TABLE statement not correctly terminated`)
78
78
  }
79
79
  if (!isMigration && tVersion > 1) {
80
80
  throw new Error(`Invalid format, '== migration=${tVersion}' entry missing`)
@@ -129,10 +129,9 @@ class MigrationTableParser {
129
129
  }
130
130
  }
131
131
  if (format.startLine !== -1 && (nextMigration || (idx + 1) === lines.length)) {
132
- // back search for end of migration, comments belong to former migration
132
+ // skip empty lines
133
133
  for (let mIdx = nextMigration ? idx - 1 : idx; mIdx > format.startLine; mIdx--) {
134
- if (MigrationTableParser._isDDL(lines[mIdx])
135
- || MigrationTableParser._isComment(lines[mIdx])) {
134
+ if (!/^\s*$/.test(lines[mIdx])) {
136
135
  format.endLine = mIdx
137
136
  break
138
137
  }
@@ -469,4 +468,4 @@ class Migration {
469
468
  }
470
469
  }
471
470
 
472
- module.exports = new MigrationTableParser()
471
+ module.exports = new MigrationTableParser()
@@ -78,19 +78,18 @@ class JavaCfModuleBuilder extends BuildTaskHandlerEdmx {
78
78
 
79
79
  async _compileForOdata(model, csnDest, compileOptions) {
80
80
  // csn for service providers
81
- const odataOptions = {
81
+ const m = this.cds.compile.for.java(model, {
82
82
  ...this._options4odata(),
83
83
  ...compileOptions
84
- }
85
- const odataModel = this.cds.compile.for.odata(model, odataOptions)
84
+ })
86
85
 
87
86
  // adding csn to build result containing @source and _where persisted properties
88
87
  if (this.hasBuildOption(CONTENT_DEFAULT_CSN, true)) { //default true or undefined
89
88
  await this.compileToJson(model, csnDest)
90
89
  } else {
91
- await this.compileToJson(odataModel, csnDest)
90
+ await this.compileToJson(m, csnDest)
92
91
  }
93
- return odataModel
92
+ return m
94
93
  }
95
94
 
96
95
  _isCompilerV1() {
@@ -6,7 +6,7 @@ const { BuildError } = require('../../util')
6
6
  const { BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY, ODATA_VERSION, ODATA_VERSION_V2,
7
7
  BUILD_TASK_HANA, FOLDER_GEN, BUILD_NODEJS_EDMX_GENERAION, EDMX_GENERATION,
8
8
  SKIP_PACKAGE_JSON_GENERATION, SKIP_MANIFEST_GENERATION,
9
- CONTENT_EDMX, CONTENT_MANIFEST, CONTENT_PACKAGE_JSON, CONTENT_PACKAGELOCK_JSON }
9
+ CONTENT_EDMX, CONTENT_MANIFEST, CONTENT_PACKAGE_JSON, CONTENT_PACKAGELOCK_JSON }
10
10
  = require('../../constants')
11
11
  const { WARNING } = require('../../buildTaskHandler')
12
12
 
@@ -33,20 +33,19 @@ class NodeCfModuleBuilder extends BuildTaskHandlerEdmx {
33
33
  // log warning as nodejs is only supporting odata version V4
34
34
  this.pushMessage("OData v2 is not supported by node runtime. Make sure to define OData v2 in cds configuration.", WARNING)
35
35
  }
36
-
36
+ // by default model contains all features
37
37
  const model = await this.model()
38
38
  if (!model) {
39
39
  return this._result
40
40
  }
41
41
 
42
- // adding csn to build result containing @source and _where persisted properties
43
- await this.compileToJson(model, this.destGen)
42
+ const { dictionary, sources } = await this._compileAll(model)
44
43
 
45
44
  // collect and write language bundles into single i18n.json file
46
- await this.collectLanguageBundles(model, this.destGen)
45
+ await this._collectAllLanguageBundles(dictionary, sources, this.destGen)
47
46
 
48
47
  if (this.hasBuildOption(CONTENT_EDMX, true)) {
49
- await this.compileToEdmx(model, this.destGen)
48
+ await this.compileToEdmx(dictionary.base, this.destGen)
50
49
  }
51
50
 
52
51
  if (!this.hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY)) {
@@ -210,6 +209,100 @@ ${hanaServiceBinding}`
210
209
  this.logger.error(e)
211
210
  }
212
211
  }
212
+
213
+ async _compileAll(csn) {
214
+ const sources = await this._resolveSourcePaths(csn)
215
+ const dictionary = { base: null, features: null }
216
+
217
+ if (sources.features) {
218
+ // create base model
219
+ dictionary.base = await this.cds.load(sources.base, this.options())
220
+ dictionary.features = await this._compileFeatures(dictionary.base, sources.features)
221
+ } else {
222
+ // model already represents the base model as no feature toggles exist
223
+ dictionary.base = csn
224
+ }
225
+ // adding csn to build result containing @source and _where persisted properties
226
+ await this.compileToJson(dictionary.base, this.destGen)
227
+
228
+ return { dictionary, sources }
229
+ }
230
+
231
+ async _collectAllLanguageBundles(dictionary, paths, dest) {
232
+ // create language bundle for base model
233
+ await this.collectLanguageBundles(dictionary.base, dest)
234
+ if (dictionary.features) {
235
+ const fts = path.dirname(this.env.features.folders)
236
+ // create language bundles for all features
237
+ for (const ftName in dictionary.features) {
238
+ // attach the sources information for i18n location reference
239
+ dictionary.features[ftName]['$sources'] = paths.features[ftName]
240
+ await this.collectLanguageBundles(dictionary.features[ftName], path.join(dest, fts, ftName), false)
241
+ }
242
+ }
243
+ }
244
+
245
+ async _resolveSourcePaths(csn) {
246
+ const fts = this.env.features.folders ? path.dirname(this.env.features.folders) : undefined
247
+ const regex = new RegExp(path.resolve(this.buildOptions.root, fts || 'fts').replace(/\\/g, '\\\\') + '[/|\\\\](?<ftName>[^/|\\\\]*)')
248
+ let paths = { base: [] }
249
+
250
+ // add ROOT source file paths for the base model
251
+ paths.base = this._resolveModel().reduce((acc, file) => {
252
+ const match = file.match(regex)
253
+ if (!match) {
254
+ acc.push(file)
255
+ }
256
+ return acc
257
+ }, [])
258
+
259
+ // check whether feature toggles are used by this project
260
+ if (!fts || !await fs.pathExists(path.join(this.buildOptions.root, fts))) {
261
+ return paths
262
+ }
263
+
264
+ // add source file paths for the features
265
+ paths.features = csn['$sources'].reduce((acc, file) => {
266
+ const match = file.match(regex)
267
+ if (match) {
268
+ const { ftName } = match.groups
269
+ //feature
270
+ if (!acc[ftName]) {
271
+ acc[ftName] = []
272
+ }
273
+ acc[ftName].push(file)
274
+ }
275
+ return acc
276
+ }, {})
277
+
278
+ return paths
279
+ }
280
+
281
+ async _compileFeatures(baseCsn, ftsPaths) {
282
+ if (!ftsPaths) {
283
+ return
284
+ }
285
+ const features = {}
286
+ const fts = path.dirname(this.env.features.folders)
287
+ const options = { ...this.options(), ...{ flavor: 'parsed' } }
288
+ // create feature models
289
+ for (const ftName in ftsPaths) {
290
+ const parsedCsn = await this.cds.load(ftsPaths[ftName], options)
291
+ // delete any requires references
292
+ if (parsedCsn.requires) {
293
+ delete parsedCsn.requires
294
+ }
295
+ await this.compileToJson(parsedCsn, path.join(this.destGen, fts, ftName), false)
296
+ this._validateFeature(baseCsn, parsedCsn)
297
+ features[ftName] = parsedCsn
298
+ }
299
+ return features
300
+ }
301
+
302
+ _validateFeature(baseCsn, parsedCsn) {
303
+ // features must not contain any other dependencies than base model
304
+ require('@sap/cds-compiler').compileSources({ 'base.csn': baseCsn, 'parsed.csn': parsedCsn }, this.options())
305
+ }
213
306
  }
214
307
 
215
308
  module.exports = NodeCfModuleBuilder