@sap/cds 5.8.3 → 5.9.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.
- package/CHANGELOG.md +193 -77
- package/app/fiori/preview.js +16 -11
- package/app/fiori/routes.js +15 -8
- package/app/index.js +1 -1
- package/bin/build/buildTaskFactory.js +3 -3
- package/bin/build/buildTaskProviderFactory.js +1 -1
- package/bin/build/constants.js +1 -1
- package/bin/build/provider/buildTaskHandlerEdmx.js +12 -7
- package/bin/build/provider/buildTaskHandlerInternal.js +1 -1
- package/bin/build/provider/buildTaskProviderInternal.js +8 -2
- package/bin/build/provider/hana/2migration.js +27 -24
- package/bin/build/provider/hana/index.js +17 -18
- package/bin/build/provider/hana/migrationtable.js +9 -10
- package/bin/build/provider/java-cf/index.js +4 -5
- package/bin/build/provider/node-cf/index.js +99 -6
- package/bin/cds.js +17 -18
- package/bin/deploy/to-hana/cfUtil.js +16 -19
- package/bin/deploy/to-hana/hana.js +7 -24
- package/bin/deploy/to-hana/hdiDeployUtil.js +8 -4
- package/bin/mtx/in-cds.js +2 -2
- package/bin/serve.js +10 -3
- package/bin/utils/modules.js +7 -0
- package/bin/version.js +56 -3
- package/lib/compile/cdsc.js +7 -2
- package/lib/compile/etc/_localized.js +36 -25
- package/lib/compile/etc/csv.js +8 -8
- package/lib/compile/for/drafts.js +9 -0
- package/lib/compile/for/java.js +16 -0
- package/lib/compile/for/nodejs.js +12 -0
- package/lib/compile/index.js +3 -0
- package/lib/compile/minify.js +16 -2
- package/lib/compile/parse.js +2 -2
- package/lib/compile/resolve.js +35 -18
- package/lib/compile/to/json.js +3 -1
- package/lib/compile/to/sql.js +2 -2
- package/lib/compile/to/srvinfo.js +4 -2
- package/lib/connect/index.js +1 -1
- package/lib/core/entities.js +15 -14
- package/lib/core/index.js +39 -36
- package/lib/core/reflect.js +4 -2
- package/lib/deploy.js +114 -127
- package/lib/env/defaults.js +1 -0
- package/lib/env/index.js +165 -165
- package/lib/env/presets.js +1 -0
- package/lib/env/requires.js +120 -49
- package/lib/index.js +1 -0
- package/lib/log/format/kibana.js +2 -2
- package/lib/ql/SELECT.js +10 -0
- package/lib/ql/parse.js +1 -0
- package/lib/req/cds-context.js +4 -1
- package/lib/req/context.js +50 -56
- package/lib/req/event.js +1 -6
- package/lib/req/locale.js +6 -5
- package/lib/req/request.js +2 -0
- package/lib/req/user.js +7 -5
- package/lib/serve/Service-api.js +10 -7
- package/lib/serve/Service-dispatch.js +9 -11
- package/lib/serve/Service-methods.js +30 -41
- package/lib/serve/Transaction.js +10 -7
- package/lib/serve/adapters.js +7 -5
- package/lib/serve/index.js +24 -12
- package/lib/utils/data.js +1 -1
- package/lib/utils/index.js +27 -30
- package/lib/utils/resources/index.js +101 -0
- package/lib/utils/resources/tar.js +71 -0
- package/lib/utils/resources/utils.js +11 -0
- package/libx/_runtime/audit/Service.js +36 -39
- package/libx/_runtime/audit/generic/personal/access.js +3 -4
- package/libx/_runtime/audit/generic/personal/modification.js +3 -4
- package/libx/_runtime/audit/utils/v2.js +1 -2
- package/libx/_runtime/auth/index.js +126 -84
- package/libx/_runtime/auth/strategies/JWT.js +12 -19
- package/libx/_runtime/auth/strategies/dummy.js +1 -5
- package/libx/_runtime/auth/strategies/dwc.js +11 -9
- package/libx/_runtime/auth/strategies/mock.js +0 -4
- package/libx/_runtime/auth/strategies/{utils/xssec.js → xssecUtils.js} +7 -4
- package/libx/_runtime/auth/strategies/xsuaa.js +12 -19
- package/libx/_runtime/auth/utils.js +22 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +104 -98
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/language.js +2 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +4 -29
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +3 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +4 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +24 -21
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +8 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +2 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/DispatcherCommand.js +2 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -12
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +33 -9
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/dispatcherUtils.js +51 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +10 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +9 -11
- package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +6 -3
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +4 -2
- package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/utils.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/utils/binary.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/utils/key-value-utils.js +2 -3
- package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +6 -4
- package/libx/_runtime/cds-services/adapter/rest/utils/result.js +1 -0
- package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +8 -5
- package/libx/_runtime/cds-services/services/Service.js +40 -0
- package/libx/_runtime/cds-services/services/utils/columns.js +4 -3
- package/libx/_runtime/cds-services/services/utils/compareJson.js +4 -4
- package/libx/_runtime/cds-services/services/utils/differ.js +3 -3
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +4 -4
- package/libx/_runtime/cds-services/services/utils/restrictions.js +78 -0
- package/libx/_runtime/cds-services/util/assert.js +20 -14
- package/libx/_runtime/cds.js +9 -1
- package/libx/_runtime/common/aspects/any.js +5 -0
- package/libx/_runtime/common/aspects/entity.js +25 -7
- package/libx/_runtime/common/aspects/utils.js +2 -2
- package/libx/_runtime/common/composition/data.js +6 -0
- package/libx/_runtime/common/composition/insert.js +3 -2
- package/libx/_runtime/common/composition/tree.js +4 -10
- package/libx/_runtime/common/composition/update.js +4 -4
- package/libx/_runtime/common/constants/draft.js +29 -26
- package/libx/_runtime/common/error/constants.js +2 -2
- package/libx/_runtime/common/error/frontend.js +7 -15
- package/libx/_runtime/common/generic/auth/capabilities.js +59 -0
- package/libx/_runtime/common/generic/auth/constants.js +20 -0
- package/libx/_runtime/common/generic/auth/expand.js +54 -0
- package/libx/_runtime/common/generic/auth/index.js +32 -0
- package/libx/_runtime/common/generic/auth/insertOnly.js +15 -0
- package/libx/_runtime/common/generic/auth/readOnly.js +26 -0
- package/libx/_runtime/common/generic/auth/requires.js +34 -0
- package/libx/_runtime/common/generic/auth/restrict.js +296 -0
- package/libx/_runtime/common/generic/auth/utils.js +213 -0
- package/libx/_runtime/common/generic/crud.js +14 -10
- package/libx/_runtime/common/generic/etag.js +1 -1
- package/libx/_runtime/common/generic/input.js +35 -35
- package/libx/_runtime/common/generic/sorting.js +2 -3
- package/libx/_runtime/common/generic/temporal.js +2 -2
- package/libx/_runtime/common/i18n/messages.properties +1 -1
- package/libx/_runtime/common/toggles/handler.js +21 -0
- package/libx/_runtime/common/utils/copy.js +10 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +100 -29
- package/libx/_runtime/common/utils/csn.js +63 -1
- package/libx/_runtime/common/utils/dollar.js +10 -1
- package/libx/_runtime/common/utils/draft.js +46 -7
- package/libx/_runtime/common/utils/entityFromCqn.js +13 -9
- package/libx/_runtime/common/utils/extensibilityUtils.js +18 -0
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +88 -104
- package/libx/_runtime/common/utils/generateOnCond.js +5 -2
- package/libx/_runtime/common/utils/quotingStyles.js +2 -0
- package/libx/_runtime/common/utils/resolveStructured.js +25 -9
- package/libx/_runtime/common/utils/resolveView.js +4 -1
- package/libx/_runtime/common/utils/rewriteAsterisks.js +3 -16
- package/libx/_runtime/common/utils/structured.js +33 -37
- package/libx/_runtime/common/utils/template.js +17 -8
- package/libx/_runtime/common/utils/templateProcessor.js +28 -28
- package/libx/_runtime/db/data-conversion/post-processing.js +118 -412
- package/libx/_runtime/db/expand/expandCQNToJoin.js +45 -41
- package/libx/_runtime/db/expand/rawToExpanded.js +29 -8
- package/libx/_runtime/db/generic/index.js +1 -3
- package/libx/_runtime/db/generic/input.js +5 -10
- package/libx/_runtime/db/generic/rewrite.js +5 -2
- package/libx/_runtime/db/generic/structured.js +2 -2
- package/libx/_runtime/db/query/delete.js +2 -2
- package/libx/_runtime/db/query/insert.js +1 -1
- package/libx/_runtime/db/query/update.js +9 -14
- package/libx/_runtime/db/sql-builder/CreateBuilder.js +4 -3
- package/libx/_runtime/db/sql-builder/FunctionBuilder.js +8 -8
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +14 -1
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -2
- package/libx/_runtime/db/sql-builder/dataTypes.js +3 -3
- package/libx/_runtime/db/utils/columns.js +3 -3
- package/libx/_runtime/db/utils/normalizeTimeData.js +2 -2
- package/libx/_runtime/db/utils/propagateForeignKeys.js +6 -2
- package/libx/_runtime/extensibility/mps/index.js +5 -0
- package/libx/_runtime/extensibility/mps/service.js +111 -0
- package/libx/_runtime/extensibility/mps/tar.js +42 -0
- package/libx/_runtime/extensibility/mps/utils.js +11 -0
- package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformREAD.js +0 -0
- package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformRESULT.js +17 -5
- package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformWRITE.js +1 -0
- package/libx/_runtime/extensibility/uiflex/index.js +54 -0
- package/libx/_runtime/extensibility/uiflex/service.js +276 -0
- package/libx/_runtime/{fiori → extensibility}/uiflex/utils.js +22 -7
- package/libx/_runtime/fiori/generic/activate.js +2 -2
- package/libx/_runtime/fiori/generic/before.js +4 -4
- package/libx/_runtime/fiori/generic/new.js +3 -3
- package/libx/_runtime/fiori/generic/patch.js +1 -1
- package/libx/_runtime/fiori/generic/read.js +58 -66
- package/libx/_runtime/fiori/generic/readOverDraft.js +71 -16
- package/libx/_runtime/fiori/utils/handler.js +6 -13
- package/libx/_runtime/fiori/utils/where.js +6 -5
- package/libx/_runtime/hana/Service.js +4 -10
- package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +1 -1
- package/libx/_runtime/hana/driver.js +2 -2
- package/libx/_runtime/hana/execute.js +27 -74
- package/libx/_runtime/hana/pool.js +1 -1
- package/libx/_runtime/hana/streaming.js +2 -1
- package/libx/_runtime/index.js +6 -6
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +5 -21
- package/libx/_runtime/messaging/Outbox.js +2 -2
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +4 -14
- package/libx/_runtime/messaging/common-utils/connections.js +5 -7
- package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +30 -0
- package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +36 -30
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -12
- package/libx/_runtime/messaging/enterprise-messaging.js +8 -8
- package/libx/_runtime/messaging/file-based.js +5 -5
- package/libx/_runtime/messaging/message-queuing.js +14 -12
- package/libx/_runtime/messaging/outbox/utils.js +18 -19
- package/libx/_runtime/messaging/redis-messaging.js +91 -0
- package/libx/_runtime/messaging/service.js +8 -6
- package/libx/_runtime/remote/Service.js +44 -8
- package/libx/_runtime/remote/utils/client.js +24 -19
- package/libx/_runtime/remote/utils/data.js +11 -11
- package/libx/_runtime/sqlite/Service.js +6 -9
- package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +5 -2
- package/libx/_runtime/types/api.js +10 -2
- package/libx/common/utils/ucsn.js +109 -0
- package/libx/gql/resolvers/crud/update.js +5 -0
- package/libx/gql/resolvers/parse/ast2cqn/columns.js +3 -1
- package/libx/gql/schema/typeDefMap.js +2 -2
- package/libx/odata/afterburner.js +110 -16
- package/libx/odata/cqn2odata.js +24 -27
- package/libx/odata/grammar.pegjs +9 -1
- package/libx/odata/parseToCqn.js +39 -0
- package/libx/odata/parser.js +1 -1
- package/libx/rest/RestAdapter.js +9 -1
- package/libx/rest/middleware/input.js +54 -0
- package/libx/rest/middleware/operation.js +14 -1
- package/libx/rest/middleware/parse.js +11 -7
- package/package.json +2 -2
- package/server.js +34 -19
- package/srv/audit-log.cds +2 -2
- package/srv/flex.cds +8 -2
- package/srv/flex.js +1 -1
- package/srv/mps.cds +23 -0
- package/srv/mps.js +1 -0
- package/libx/_runtime/auth/strategies/utils/uaa.js +0 -21
- package/libx/_runtime/common/generic/auth.js +0 -874
- package/libx/_runtime/common/toggles/alpha.js +0 -43
- package/libx/_runtime/db/generic/arrayed.js +0 -33
- package/libx/_runtime/fiori/uiflex/index.js +0 -35
- package/libx/_runtime/fiori/uiflex/service.js +0 -150
- package/libx/rest/utils/data.js +0 -60
package/app/fiori/preview.js
CHANGED
|
@@ -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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
|
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
|
}
|
package/app/fiori/routes.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
const cds = require('../../lib')
|
|
2
|
+
const DEBUG = cds.debug('fiori/routes')
|
|
3
|
+
const {dirname, relative, join} = require('path')
|
|
2
4
|
|
|
3
5
|
// Only for local cds runs w/o approuter:
|
|
4
6
|
// If there is a relative URL in UI5's manifest.json for the datasource,
|
|
@@ -11,16 +13,20 @@ cds.on ('bootstrap', app => {
|
|
|
11
13
|
const v2Prefix = (env.odata.v2proxy && env.odata.v2proxy.urlpath) || '/v2'
|
|
12
14
|
const serviceForUri = {}
|
|
13
15
|
|
|
14
|
-
dataSourceURIs (env.folders.app).forEach(
|
|
15
|
-
|
|
16
|
+
dataSourceURIs (env.folders.app).forEach(({appPath, dataSourceUri}) => {
|
|
17
|
+
const uiRoutes = [
|
|
18
|
+
join('/', appPath, dataSourceUri, '*'), // /uiApp/webapp/browse/*
|
|
19
|
+
join('/', appPath, '*', dataSourceUri, '*') // /uiApp/webapp/*/browse/*
|
|
20
|
+
].map(r => r.replace(/\\/g, '/')) // handle Windows \
|
|
21
|
+
DEBUG && DEBUG ('Register routes', uiRoutes)
|
|
22
|
+
|
|
23
|
+
app.use(uiRoutes, ({originalUrl}, res, next)=> {
|
|
16
24
|
// any of our special URLs ($fiori-, $api-docs) ? -> next
|
|
17
25
|
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
26
|
|
|
21
27
|
// is there a service for '[prefix]/browse' ?
|
|
22
|
-
const srv = serviceForUri[
|
|
23
|
-
cds.service.providers.find (srv => ('/'+
|
|
28
|
+
const srv = serviceForUri[dataSourceUri] || (serviceForUri[dataSourceUri] =
|
|
29
|
+
cds.service.providers.find (srv => ('/'+dataSourceUri).lastIndexOf(srv.path) >=0))
|
|
24
30
|
if (srv) {
|
|
25
31
|
let redirectUrl
|
|
26
32
|
// odata-proxy may be in the line with its /v2 prefix. Make sure we retain it.
|
|
@@ -30,7 +36,7 @@ cds.on ('bootstrap', app => {
|
|
|
30
36
|
else // --> /browse/webapp[/prefix]/browse/ -> /browse
|
|
31
37
|
redirectUrl = originalUrl.substring(originalUrl.lastIndexOf(srv.path+'/'))
|
|
32
38
|
if (originalUrl !== redirectUrl) {// safeguard to prevent running in loops
|
|
33
|
-
|
|
39
|
+
DEBUG && DEBUG ('Redirecting', {src: originalUrl}, '~>', {target: redirectUrl})
|
|
34
40
|
return res.redirect (308, redirectUrl)
|
|
35
41
|
}
|
|
36
42
|
}
|
|
@@ -41,10 +47,11 @@ cds.on ('bootstrap', app => {
|
|
|
41
47
|
function dataSourceURIs (dir) {
|
|
42
48
|
const uris = new Set()
|
|
43
49
|
find (dir, ['*/manifest.json', '*/*/manifest.json']).forEach(file => {
|
|
50
|
+
const appPath = relative(join(cds.root, dir), dirname(file))
|
|
44
51
|
const {dataSources: ds} = JSON.parse(fs.readFileSync(file))['sap.app'] || {}
|
|
45
52
|
Object.keys (ds||[])
|
|
46
53
|
.filter (k => ds[k].uri && !ds[k].uri.startsWith('/')) // only consider relative URLs)
|
|
47
|
-
.forEach(k => uris.add(ds[k].uri))
|
|
54
|
+
.forEach(k => uris.add({ appPath, dataSourceUri: ds[k].uri }))
|
|
48
55
|
})
|
|
49
56
|
return uris
|
|
50
57
|
}
|
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}"> → ${l.name}
|
|
71
|
+
.map (l => ` <a class="preview" href="${l.href}" title="${l.title||l.name}"> → ${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(
|
|
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.
|
|
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
|
|
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)
|
package/bin/build/constants.js
CHANGED
|
@@ -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/
|
|
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
|
-
|
|
72
|
-
|
|
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
|
|
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
|
-
|
|
116
|
+
if (saveResult) {
|
|
117
|
+
this._result.languageBundles = bundles
|
|
118
|
+
}
|
|
114
119
|
return bundles
|
|
115
120
|
}
|
|
116
121
|
|
|
@@ -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
|
-
|
|
288
|
-
|
|
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
|
-
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
|
|
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
|
-
|
|
110
|
-
|
|
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
|
-
|
|
124
|
-
|
|
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
|
|
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
|
|
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
|
|
215
|
+
await this._compileToHdb(model, pluginTypes, format)
|
|
220
216
|
} else {
|
|
221
|
-
await this._compileToHdbmigration(model, pluginTypes
|
|
217
|
+
await this._compileToHdbmigration(model, pluginTypes)
|
|
222
218
|
}
|
|
223
219
|
return pluginTypes
|
|
224
220
|
}
|
|
225
221
|
|
|
226
|
-
async _compileToHdb(model, pluginTypes, format
|
|
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
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
74
|
+
throw new Error(`Invalid format, TABLE statement missing`)
|
|
75
75
|
}
|
|
76
76
|
if (!isTableEnd) {
|
|
77
|
-
throw new Error(`Invalid format,
|
|
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
|
-
//
|
|
132
|
+
// skip empty lines
|
|
133
133
|
for (let mIdx = nextMigration ? idx - 1 : idx; mIdx > format.startLine; mIdx--) {
|
|
134
|
-
if (
|
|
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
|
|
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(
|
|
90
|
+
await this.compileToJson(m, csnDest)
|
|
92
91
|
}
|
|
93
|
-
return
|
|
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
|
-
|
|
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.
|
|
45
|
+
await this._collectAllLanguageBundles(dictionary, sources, this.destGen)
|
|
47
46
|
|
|
48
47
|
if (this.hasBuildOption(CONTENT_EDMX, true)) {
|
|
49
|
-
await this.compileToEdmx(
|
|
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
|