@sap/cds 5.8.4 → 5.9.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.
- package/CHANGELOG.md +198 -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 +37 -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/bindings.js +1 -1
- package/lib/connect/index.js +3 -4
- 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 +121 -50
- package/lib/index.js +2 -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 +11 -9
- package/lib/serve/factory.js +14 -9
- package/lib/serve/index.js +28 -15
- 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 +8 -3
- 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 +56 -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/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 +298 -0
- package/libx/_runtime/common/generic/auth/restrictions.js +85 -0
- package/libx/_runtime/common/generic/auth/utils.js +213 -0
- package/libx/_runtime/common/generic/crud.js +8 -6
- 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 +111 -35
- 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 +4 -1
- 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 +74 -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 +45 -75
- 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
|
@@ -4,7 +4,7 @@ const { ensureNoDraftsSuffix } = require('../../common/utils/draft.js')
|
|
|
4
4
|
|
|
5
5
|
const _convertDateTimeElement = (value, element) => {
|
|
6
6
|
value = new Date(value).toISOString()
|
|
7
|
-
if (element.
|
|
7
|
+
if (element._type === 'cds.DateTime') value = value.replace(/\.\d\d\d/, '')
|
|
8
8
|
return value
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -38,7 +38,7 @@ const _convertColumns = (data, elements, model, queryColumns) => {
|
|
|
38
38
|
// check all columns
|
|
39
39
|
for (let i = 0, length = queryColumns.length; i < length; i++) {
|
|
40
40
|
const col = queryColumns[i]
|
|
41
|
-
if (elements[col] && _isToConvert(elements[col].
|
|
41
|
+
if (elements[col] && _isToConvert(elements[col]._type)) {
|
|
42
42
|
const dataArray = Array.isArray(data[0]) ? data : [data]
|
|
43
43
|
for (const d of dataArray) {
|
|
44
44
|
const elementValue = d[i]
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const { prefixForStruct } = require('../../common/utils/csn')
|
|
4
|
+
|
|
5
|
+
const _autoGenerate = e => e && e.isUUID && e.key
|
|
4
6
|
|
|
5
7
|
const _generateParentField = ({ parentElement }, row) => {
|
|
6
8
|
if (_autoGenerate(parentElement) && !row[parentElement.name]) {
|
|
@@ -19,6 +21,7 @@ const _generateChildField = ({ deep, childElement }, childRow) => {
|
|
|
19
21
|
const _getNestedVal = (row, prefix) => {
|
|
20
22
|
let val = row
|
|
21
23
|
const splitted = prefix.split('_')
|
|
24
|
+
splitted.pop() // remove last `_`
|
|
22
25
|
let k = ''
|
|
23
26
|
|
|
24
27
|
while (splitted.length > 0) {
|
|
@@ -34,9 +37,10 @@ const _getNestedVal = (row, prefix) => {
|
|
|
34
37
|
return val
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
const _propagateToChid = ({ parentElement, childElement,
|
|
40
|
+
const _propagateToChid = ({ parentElement, childElement, parentFieldValue }, row, childRow) => {
|
|
38
41
|
if (!childElement) return
|
|
39
42
|
if (parentElement) {
|
|
43
|
+
const prefix = prefixForStruct(parentElement)
|
|
40
44
|
if (prefix) {
|
|
41
45
|
const nested = _getNestedVal(row, prefix)
|
|
42
46
|
childRow[childElement.name] = nested[parentElement.name]
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
const cds = require('../../cds')
|
|
2
|
+
if (cds.requires.db && !(cds.model && 'cds_r.ModelProviderService' in cds.model.definitions)) {
|
|
3
|
+
const model = require('path').join(__dirname, '../../../..', 'srv/mps.cds')
|
|
4
|
+
module.exports = cds.serve(model, { silent: true }).in(cds.app)
|
|
5
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
|
|
4
|
+
const cds = require('../../cds')
|
|
5
|
+
const { exists } = require('./utils')
|
|
6
|
+
const { supplementaryFiles } = require('./tar')
|
|
7
|
+
|
|
8
|
+
const { BASE_TENANT } = require('../../common/utils/extensibilityUtils')
|
|
9
|
+
|
|
10
|
+
const _getAllFeatures = async function () {
|
|
11
|
+
const features = path.join(global.cds.root, 'fts')
|
|
12
|
+
if (await exists(features)) {
|
|
13
|
+
return (await fs.promises.readdir(features, { withFileTypes: true })).map(dir => path.join(features, dir.name))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return []
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const _addExtensions = async function (csn, tenant) {
|
|
20
|
+
const rs = await cds.tx({ tenant }, tx => tx.run(SELECT.from('cds_r.Extensions')))
|
|
21
|
+
|
|
22
|
+
if (rs.length !== 0) {
|
|
23
|
+
const extensions = []
|
|
24
|
+
const definitions = {}
|
|
25
|
+
rs.forEach(row => {
|
|
26
|
+
const csn = JSON.parse(row.csn)
|
|
27
|
+
if (csn.extensions) extensions.push(...csn.extensions)
|
|
28
|
+
if (csn.definitions) {
|
|
29
|
+
Object.keys(csn.definitions).forEach(key => {
|
|
30
|
+
definitions[key] = csn.definitions[key]
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
const extCsn = cds.compile({
|
|
35
|
+
'base.csn': cds.compile.to.json(csn),
|
|
36
|
+
'ext.csn': cds.compile.to.json({ extensions, definitions })
|
|
37
|
+
})
|
|
38
|
+
// Sources are used to load resources like i18n
|
|
39
|
+
extCsn.$sources = csn.$sources
|
|
40
|
+
extCsn.$location = csn.$location
|
|
41
|
+
|
|
42
|
+
return extCsn
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return csn
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const _isExtended = async function (req) {
|
|
49
|
+
const rs = await cds.tx({ tenant: req.data.tenant }, tx => tx.run('SELECT * from cds_r_Extensions'))
|
|
50
|
+
return rs.length !== 0
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const _csn4 = async function (req, _flavor) {
|
|
54
|
+
let { tenant, toggles, flavor = _flavor } = req.data
|
|
55
|
+
if (!tenant) tenant = BASE_TENANT
|
|
56
|
+
|
|
57
|
+
let features = []
|
|
58
|
+
// Star is only for deployment - not a valid DwC protocol configuration.
|
|
59
|
+
if (toggles && toggles.includes('*')) {
|
|
60
|
+
features = await _getAllFeatures()
|
|
61
|
+
} else if (toggles) {
|
|
62
|
+
features = toggles.map(t => path.join(global.cds.root, 'fts', t))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let csn = await cds.load(['*', ...features]).then(cds.minify)
|
|
66
|
+
|
|
67
|
+
if (cds.requires.extensibility && !(cds.requires.multitenancy && tenant === BASE_TENANT))
|
|
68
|
+
csn = await _addExtensions(csn, tenant)
|
|
69
|
+
|
|
70
|
+
if (flavor) csn = cds.compile.for[flavor](csn)
|
|
71
|
+
|
|
72
|
+
return csn
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const _edmx4 = async function (req) {
|
|
76
|
+
const csn = await _csn4(req)
|
|
77
|
+
const { serviceName, locale, odataFlavor } = req.data
|
|
78
|
+
|
|
79
|
+
return cds.localize(csn, locale, cds.compile.to.edmx(csn, { service: serviceName, version: odataFlavor }))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = function (srv) {
|
|
83
|
+
srv.on('getCsn', async function (req) {
|
|
84
|
+
return await _csn4(req)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
// REVISIT: Replace by getCsn(..., flavor=node/java)
|
|
88
|
+
srv.on('getOdataCsn', async function (req) {
|
|
89
|
+
return await _csn4(req, 'java')
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
srv.on('getEdmx', async function (req) {
|
|
93
|
+
return await _edmx4(req)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
srv.after('getEdmx', function (result, req, res) {
|
|
97
|
+
req._.res && req._.res.set('Content-Type', 'application/xml')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
srv.on('isExtended', async function (req) {
|
|
101
|
+
return await _isExtended(req)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
srv.on('getResources', async function (req) {
|
|
105
|
+
return await supplementaryFiles(req)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
srv.after('getResources', function (_, req) {
|
|
109
|
+
req._.res && req._.res.set('content-type', 'application/octet-stream; charset=binary')
|
|
110
|
+
})
|
|
111
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const cds = require('../../cds')
|
|
4
|
+
const { TAR_CLI, findCsvFiles, collectCsvFiles, packTarArchive } = require('../../../../lib/utils/resources')
|
|
5
|
+
const { exists } = require('./utils')
|
|
6
|
+
|
|
7
|
+
// REVISIT: resource folder and file to be defined
|
|
8
|
+
const RESOURCE_FILE = 'to_be_defined/to_be_defined.tgz'
|
|
9
|
+
|
|
10
|
+
const supplementaryFiles = async req => {
|
|
11
|
+
// first look for files in the resource folder
|
|
12
|
+
const resource = path.join(global.cds.root, RESOURCE_FILE)
|
|
13
|
+
if (await exists(resource)) {
|
|
14
|
+
return fs.promises.readFile(resource, 'binary')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// check if collected in sdc folder
|
|
18
|
+
// REVISIT: to be deleted
|
|
19
|
+
let files
|
|
20
|
+
const sdc = path.join(global.cds.root, 'sdc')
|
|
21
|
+
if (await exists(sdc)) {
|
|
22
|
+
files = await findCsvFiles(sdc, true)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// if not collected, then collect
|
|
26
|
+
if (!files || !files.length) {
|
|
27
|
+
const features = (cds.env.features && cds.env.features.folders) || 'fts/*'
|
|
28
|
+
const csn = await cds.load(['*', features])
|
|
29
|
+
files = await collectCsvFiles(csn.$sources)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!files.length) {
|
|
33
|
+
req.reject(404)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return await packTarArchive(files, global.cds.root, req.data.flat, TAR_CLI)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
TAR_CLI,
|
|
41
|
+
supplementaryFiles
|
|
42
|
+
}
|
|
File without changes
|
|
@@ -7,15 +7,27 @@ const _pick = element => {
|
|
|
7
7
|
return element['@cds.extension']
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
// The problem here are the extended columss, which are in SELECT.column, but not in the backpack.
|
|
11
|
+
// Otherwise all backpack values could be inserted for row at once.
|
|
10
12
|
const _processorFn = ({ row, key }) => {
|
|
11
13
|
if (row[EXT_BACK_PACK]) {
|
|
12
14
|
const extensions = JSON.parse(row[EXT_BACK_PACK])
|
|
13
|
-
|
|
14
|
-
row[
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
if (extensions[key]) {
|
|
16
|
+
row[key] = extensions[key]
|
|
17
|
+
delete extensions[key]
|
|
18
|
+
if (Object.keys(extensions).length === 0) {
|
|
19
|
+
delete row[EXT_BACK_PACK]
|
|
20
|
+
} else {
|
|
21
|
+
row[EXT_BACK_PACK] = JSON.stringify(extensions)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return
|
|
25
|
+
}
|
|
18
26
|
}
|
|
27
|
+
|
|
28
|
+
// Extended fields are not in SELECT.columns.
|
|
29
|
+
// Workaround for fields not from backpack: provide them all
|
|
30
|
+
row[key] = null
|
|
19
31
|
}
|
|
20
32
|
|
|
21
33
|
function transformExtendedFieldsRESULT(result, req) {
|
|
@@ -22,6 +22,7 @@ const _processorFn = ({ row, key }) => {
|
|
|
22
22
|
|
|
23
23
|
function transformExtendedFieldsCREATE(req) {
|
|
24
24
|
if (!req.target) return
|
|
25
|
+
if (!req.query.INSERT.entries) return // REVISIT: breaks at cds.deploy -> should anyways not kick in during cds.deploy
|
|
25
26
|
|
|
26
27
|
const target = getTargetWrite(req.target, this.model)
|
|
27
28
|
const template = getTemplate('transform-write', this, target, { pick: _pick })
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module.exports = (async () => {
|
|
2
|
+
const cds = require('../../cds')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
|
|
5
|
+
if (!cds.requires.db) return
|
|
6
|
+
if (cds.db) return // avoid duplicates
|
|
7
|
+
|
|
8
|
+
// do not add extensions in case of multitenancy
|
|
9
|
+
if (!cds.requires.multitenancy) {
|
|
10
|
+
// add extensions
|
|
11
|
+
if (cds.requires.db.credentials && cds.requires.db.credentials.database !== ':memory:') {
|
|
12
|
+
const db = await cds.connect.to({ ...cds.requires.db, model: null, silent: true })
|
|
13
|
+
const rs = await db.read('cds_r.Extensions')
|
|
14
|
+
if (rs.length !== 0) {
|
|
15
|
+
const extensions = []
|
|
16
|
+
rs.forEach(row => extensions.push(...JSON.parse(row.csn).extensions))
|
|
17
|
+
cds.once('loaded', csn => {
|
|
18
|
+
if (cds.model) return // extend cds.model only
|
|
19
|
+
const extended = cds.compile({
|
|
20
|
+
'base.csn': cds.compile.to.json(csn),
|
|
21
|
+
'ext.csn': cds.compile.to.json({ extensions })
|
|
22
|
+
})
|
|
23
|
+
csn.definitions = extended.definitions
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
await db.disconnect()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
cds.on('connect', async srv => {
|
|
30
|
+
if (srv.name !== 'db') return
|
|
31
|
+
|
|
32
|
+
// deploy in case of in-memory
|
|
33
|
+
if (cds.requires.db.credentials && cds.requires.db.credentials.database === ':memory:') {
|
|
34
|
+
const mf = await cds.load(path.join(__dirname, '../../../..', 'srv/flex.cds'))
|
|
35
|
+
await cds.deploy(mf).to(srv)
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
cds.once('served', async () => {
|
|
41
|
+
const { transformExtendedFieldsCREATE, transformExtendedFieldsUPDATE } = require('./handler/transformWRITE')
|
|
42
|
+
const { transformExtendedFieldsREAD } = require('./handler/transformREAD')
|
|
43
|
+
const { transformExtendedFieldsRESULT } = require('./handler/transformRESULT')
|
|
44
|
+
cds.db
|
|
45
|
+
.before('CREATE', transformExtendedFieldsCREATE)
|
|
46
|
+
.before('UPDATE', transformExtendedFieldsUPDATE)
|
|
47
|
+
.before('READ', transformExtendedFieldsREAD)
|
|
48
|
+
.after('READ', transformExtendedFieldsRESULT)
|
|
49
|
+
if ('cds_r.ExtensibilityService' in cds.services) return
|
|
50
|
+
await require('../mps')
|
|
51
|
+
const model = require('path').join(__dirname, '../../../..', 'srv/flex.cds')
|
|
52
|
+
return cds.serve(model, { silent: true }).in(cds.app)
|
|
53
|
+
})
|
|
54
|
+
})()
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const cds = require('../../cds')
|
|
4
|
+
const { ensureDraftsSuffix } = require('../../common/utils/draft')
|
|
5
|
+
const { BASE_TENANT, channelName } = require('../../common/utils/extensibilityUtils')
|
|
6
|
+
const { EXT_BACK_PACK } = require('./utils')
|
|
7
|
+
const TMP_DIR = fs.realpathSync(require('os').tmpdir())
|
|
8
|
+
|
|
9
|
+
const _getDraftTable = view => {
|
|
10
|
+
return cds.model.definitions[view]._isDraftEnabled ? ensureDraftsSuffix(view) : undefined
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const _addAnnotation = extension => {
|
|
14
|
+
Object.values(extension.elements).forEach(el => {
|
|
15
|
+
el['@cds.extension'] = true
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const _isProjection = target => target && target.query && target.query._target
|
|
20
|
+
|
|
21
|
+
const _resolveViews = (target, views_ = []) => {
|
|
22
|
+
if (_isProjection(target)) {
|
|
23
|
+
views_.push(target)
|
|
24
|
+
return _resolveViews(target.query._target, views_)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return target
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const _getCsn = req => {
|
|
31
|
+
const csn = {
|
|
32
|
+
extensions: req.data.extensions.map(ext => JSON.parse(ext))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return csn
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const _addViews = csn => {
|
|
39
|
+
csn.extensions.forEach(extension => {
|
|
40
|
+
const target = cds.model.definitions[extension.extend]
|
|
41
|
+
const views_ = []
|
|
42
|
+
const view = _resolveViews(target, views_)
|
|
43
|
+
extension.extend = view && view.name
|
|
44
|
+
_addAnnotation(extension)
|
|
45
|
+
|
|
46
|
+
// All projection views leading to the db entity are extended with back pack in case view columns are explicitly listed.
|
|
47
|
+
// The views using projections with '*' obtain the back pack automatically.
|
|
48
|
+
views_.forEach(view => {
|
|
49
|
+
if (!view.projection || (view.projection.columns && !view.projection.columns.some(col => col === '*'))) {
|
|
50
|
+
csn.extensions.push({
|
|
51
|
+
extend: view.name,
|
|
52
|
+
columns: Object.keys(extension.elements).map(key => {
|
|
53
|
+
return { ref: [key] }
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const _needsQuotations = t => t instanceof cds.builtin.classes.string || t instanceof cds.builtin.classes.date
|
|
62
|
+
|
|
63
|
+
const _handleDefaults = async (extension, dbEntity, draftEntity) => {
|
|
64
|
+
const ext = Object.keys(extension.elements)
|
|
65
|
+
.filter(key => extension.elements[key].default)
|
|
66
|
+
.map(key => {
|
|
67
|
+
const element = extension.elements[key]
|
|
68
|
+
// .type as ui flex extensions are not linked
|
|
69
|
+
const t = cds.model.definitions[element.type] || cds.builtin.types[element.type]
|
|
70
|
+
const value = t && _needsQuotations(t) ? `"${element.default.val}"` : element.default.val
|
|
71
|
+
return `"${key}":${value}`
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
if (ext.length !== 0) {
|
|
75
|
+
const extStr = ext.join(',')
|
|
76
|
+
const changed = `'{${extStr},' || substr(${EXT_BACK_PACK}, 2, length(${EXT_BACK_PACK})-1)`
|
|
77
|
+
const assign = `${EXT_BACK_PACK} = CASE WHEN ${EXT_BACK_PACK} IS NULL THEN '{${extStr}}' ELSE ${changed} END`
|
|
78
|
+
await UPDATE(dbEntity).with(assign)
|
|
79
|
+
if (draftEntity) await UPDATE(draftEntity).with(assign)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const _validateCsn = (csn, req) => {
|
|
84
|
+
if (!csn) req.reject(400, 'Missing extension')
|
|
85
|
+
if (!csn.extensions) return
|
|
86
|
+
|
|
87
|
+
csn.extensions.forEach(extension => {
|
|
88
|
+
if (!extension.extend || !cds.model.definitions[extension.extend]) {
|
|
89
|
+
req.reject(400, 'Invalid extension. Parameter "extend" missing or malformed')
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!extension.elements) {
|
|
93
|
+
req.reject(400, 'Invalid extension. Missing parameter "elements"')
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const _validateExtensionFields = (csn, req) => {
|
|
99
|
+
if (!csn.extensions) return
|
|
100
|
+
|
|
101
|
+
csn.extensions.forEach(extension => {
|
|
102
|
+
if (extension.elements) {
|
|
103
|
+
Object.keys(extension.elements).forEach(name => {
|
|
104
|
+
if (!/^[A-Za-z]\w*$/.test(name)) {
|
|
105
|
+
req.reject(400, `Invalid extension. Bad element name "${name}"`)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (Object.keys(cds.model.definitions[extension.extend].elements).includes(name)) {
|
|
109
|
+
req.reject(400, `Invalid extension. Element "${name}" already exists`)
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const _getCompilerError = messages => {
|
|
117
|
+
const defaultMsg = 'Error while compiling extension'
|
|
118
|
+
if (!messages) return defaultMsg
|
|
119
|
+
|
|
120
|
+
for (const msg of messages) {
|
|
121
|
+
if (msg.severity === 'Error') return msg.message
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return defaultMsg
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const _validateExtension = async (ext, req) => {
|
|
128
|
+
try {
|
|
129
|
+
const { 'cds_r.ModelProviderService': mps } = cds.services
|
|
130
|
+
const csn = await mps.getCsn(req.tenant, ['*'])
|
|
131
|
+
const extCsn = cds.compile.to.json(ext)
|
|
132
|
+
await cds.compile.to.csn({ 'base.csn': JSON.stringify(csn), 'ext.csn': extCsn })
|
|
133
|
+
} catch (err) {
|
|
134
|
+
console.trace(err) // eslint-disable-line no-console
|
|
135
|
+
req.reject(400, _getCompilerError(err.messages))
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const _addExtension = async function (req) {
|
|
140
|
+
// validate extension
|
|
141
|
+
const csn = _getCsn(req)
|
|
142
|
+
_validateCsn(csn, req)
|
|
143
|
+
await _validateExtensionFields(csn, req)
|
|
144
|
+
_addViews(csn, cds)
|
|
145
|
+
await _validateExtension(csn, req)
|
|
146
|
+
|
|
147
|
+
// save extension
|
|
148
|
+
const ID = cds.utils.uuid()
|
|
149
|
+
await INSERT.into('cds_r.Extensions').entries([{ ID, csn: JSON.stringify(csn), activated: false }])
|
|
150
|
+
|
|
151
|
+
// defaults
|
|
152
|
+
for (const ext of req.data.extensions) {
|
|
153
|
+
const extension = JSON.parse(ext)
|
|
154
|
+
const draft = _getDraftTable(extension.extend)
|
|
155
|
+
const target = cds.model.definitions[extension.extend]
|
|
156
|
+
const dbEntity = _resolveViews(target).name
|
|
157
|
+
await _handleDefaults(extension, dbEntity, draft)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// update event
|
|
161
|
+
await this.redis.emit(channelName(), { tenant: req.tenant || BASE_TENANT })
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const _activate = async function (req) {
|
|
165
|
+
// get extensions
|
|
166
|
+
const rs = await cds.tx({ tenant: req.tenant }, tx => tx.run(SELECT.from('cds_r.Extensions')))
|
|
167
|
+
rs.forEach(row => {
|
|
168
|
+
row.csn = row.csn.replace(/,"@cds.extension":true/g, '')
|
|
169
|
+
row.activated = true
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
// activate
|
|
173
|
+
const { MultitenancyService: mts } = cds.services
|
|
174
|
+
await mts.activateExtensions(req.data.tenant)
|
|
175
|
+
|
|
176
|
+
// restore extensions
|
|
177
|
+
await INSERT.into('cds_r.Extensions').entries(rs)
|
|
178
|
+
|
|
179
|
+
// update event
|
|
180
|
+
await this.redis.emit(channelName(), { tenant: req.tenant || BASE_TENANT })
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const _exists = async fileOrDir => {
|
|
184
|
+
try {
|
|
185
|
+
return await fs.promises.stat(fileOrDir)
|
|
186
|
+
} catch (_) {
|
|
187
|
+
return false
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const _compileProject = async function (extension, req) {
|
|
192
|
+
let csn, root
|
|
193
|
+
try {
|
|
194
|
+
root = await fs.promises.mkdtemp(`${TMP_DIR}${path.sep}extension-`)
|
|
195
|
+
const files = []
|
|
196
|
+
for (const ext of extension) {
|
|
197
|
+
if (ext.filename.indexOf('/') > -1) {
|
|
198
|
+
await fs.promises.mkdir(path.join(root, path.dirname(ext.filename)), { recursive: true })
|
|
199
|
+
}
|
|
200
|
+
const file = path.join(root, ext.filename)
|
|
201
|
+
await fs.promises.writeFile(file, ext.model)
|
|
202
|
+
files.push(file)
|
|
203
|
+
}
|
|
204
|
+
csn = await cds.compile(files, { flavor: 'parsed' })
|
|
205
|
+
if (csn.requires) delete csn.requires
|
|
206
|
+
} catch (err) {
|
|
207
|
+
// req.reject(400, _getCompilerError(err.messages))
|
|
208
|
+
if (err.messages) req.reject(400, _getCompilerError(err.messages))
|
|
209
|
+
else {
|
|
210
|
+
// eslint-disable-next-line no-console
|
|
211
|
+
console.error(err)
|
|
212
|
+
throw err
|
|
213
|
+
}
|
|
214
|
+
} finally {
|
|
215
|
+
if (await _exists(root)) {
|
|
216
|
+
await (fs.promises.rm || fs.promises.rmdir)(root, { recursive: true, force: true })
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return csn
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const _activateProject = async function (req) {
|
|
224
|
+
if (req.data.extension.length === 0) return
|
|
225
|
+
|
|
226
|
+
const csn = await _compileProject(req.data.extension, req)
|
|
227
|
+
await _validateExtension(csn, req)
|
|
228
|
+
|
|
229
|
+
// keep only extension to be activated
|
|
230
|
+
const ID = cds.utils.uuid()
|
|
231
|
+
let activated, notActivated
|
|
232
|
+
await cds.tx({ tenant: req.tenant }, async tx => {
|
|
233
|
+
activated = await tx.run(SELECT.from('cds_r.Extensions').where({ activated: true }))
|
|
234
|
+
notActivated = await tx.run(SELECT.from('cds_r.Extensions').where({ activated: false }))
|
|
235
|
+
await tx.run(DELETE.from('cds_r.Extensions').where({ activated: false }))
|
|
236
|
+
await tx.run(INSERT.into('cds_r.Extensions').entries([{ ID, csn: JSON.stringify(csn), activated: false }]))
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
// activate
|
|
240
|
+
const { MultitenancyService: mts } = cds.services
|
|
241
|
+
await mts.activateExtensions(req.data.tenant)
|
|
242
|
+
|
|
243
|
+
// restore extensions
|
|
244
|
+
await cds.tx({ tenant: req.tenant }, async tx => {
|
|
245
|
+
const inserted = await tx.run(SELECT.from('cds_r.Extensions').where({ ID }))
|
|
246
|
+
if (!inserted.length) {
|
|
247
|
+
if (activated.length !== 0) await tx.run(INSERT.into('cds_r.Extensions').entries(activated))
|
|
248
|
+
await tx.run(INSERT.into('cds_r.Extensions').entries([{ ID, csn: JSON.stringify(csn), activated: true }]))
|
|
249
|
+
}
|
|
250
|
+
if (notActivated.length !== 0) await tx.run(INSERT.into('cds_r.Extensions').entries(notActivated))
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
// default values for not activated extensions
|
|
254
|
+
for (const na of notActivated) {
|
|
255
|
+
for (const extension of JSON.parse(na.csn).extensions) {
|
|
256
|
+
const target = cds.model.definitions[extension.extend]
|
|
257
|
+
const dbEntity = _resolveViews(target).name
|
|
258
|
+
|
|
259
|
+
// only db entities
|
|
260
|
+
if (target.name !== dbEntity) continue
|
|
261
|
+
|
|
262
|
+
const draft = _getDraftTable(extension.extend)
|
|
263
|
+
await _handleDefaults(extension, dbEntity, draft)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// update event
|
|
268
|
+
await this.redis.emit(channelName(), { tenant: req.tenant || BASE_TENANT })
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
module.exports = async function () {
|
|
272
|
+
this.redis = await cds.connect.to('mtx-messaging')
|
|
273
|
+
this.on('addExtension', _addExtension)
|
|
274
|
+
this.on('activate', _activate)
|
|
275
|
+
this.on('activateProject', _activateProject)
|
|
276
|
+
}
|
|
@@ -24,8 +24,9 @@ const getTargetWrite = (target, model) => {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
const isExtendedEntity = (entityName, model) => {
|
|
27
|
-
// REVISIT: Dass alle unsere und auch custom handlers immer die ensureUnlocalized + ensureNoDraftsSuffix schleife drehen müssen, kann nicht sein
|
|
28
27
|
const entity = model.definitions[ensureUnlocalized(ensureNoDraftsSuffix(entityName))]
|
|
28
|
+
if (!entity) return false
|
|
29
|
+
|
|
29
30
|
return entity.elements[EXT_BACK_PACK] || Object.values(entity.elements).some(el => el['@cds.extension'])
|
|
30
31
|
}
|
|
31
32
|
|
|
@@ -41,19 +42,33 @@ const _hasExtendedEntityArgs = (args, model) => {
|
|
|
41
42
|
})
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
const _hasExtendedExpand = (columns, targetName, model) => {
|
|
46
|
+
for (const col of columns) {
|
|
47
|
+
if (col.ref && col.expand) {
|
|
48
|
+
const targetNameModel = ensureUnlocalized(ensureNoDraftsSuffix(targetName))
|
|
49
|
+
const expTargetName = model.definitions[targetNameModel].elements[col.ref[0]].target
|
|
50
|
+
if (isExtendedEntity(expTargetName, model)) return true
|
|
51
|
+
_hasExtendedExpand(col.expand, expTargetName, model)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
44
56
|
const hasExtendedEntity = (req, model) => {
|
|
45
57
|
if (!req.query.SELECT) return false
|
|
46
58
|
|
|
59
|
+
if (req.query.SELECT.columns && req.target && _hasExtendedExpand(req.query.SELECT.columns, req.target.name, model)) {
|
|
60
|
+
return true
|
|
61
|
+
}
|
|
62
|
+
|
|
47
63
|
if (req.query.SELECT.from.join) {
|
|
48
|
-
// join
|
|
49
64
|
return _hasExtendedEntityArgs(req.query.SELECT.from.args, model)
|
|
50
|
-
}
|
|
51
|
-
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (req.target.name.SET) {
|
|
52
68
|
return isExtendedEntity(req.target.name.SET.args[0]._target.name, model)
|
|
53
|
-
} else {
|
|
54
|
-
// simple select
|
|
55
|
-
return isExtendedEntity(req.target.name, model)
|
|
56
69
|
}
|
|
70
|
+
|
|
71
|
+
return isExtendedEntity(req.target.name, model)
|
|
57
72
|
}
|
|
58
73
|
|
|
59
74
|
const getExtendedFields = (entityName, model) => {
|
|
@@ -12,7 +12,7 @@ const { readAndDeleteKeywords, isActiveEntityRequested, getKeyData } = require('
|
|
|
12
12
|
const { isDraftRootEntity } = require('../../fiori/utils/csn')
|
|
13
13
|
const { getColumns } = require('../../cds-services/services/utils/columns')
|
|
14
14
|
|
|
15
|
-
const {
|
|
15
|
+
const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
|
|
16
16
|
|
|
17
17
|
const _getRootCQN = (context, requestActiveData) => {
|
|
18
18
|
const keys = filterKeys(context.target.keys)
|
|
@@ -30,7 +30,7 @@ const _getExpandSubCqn = (model, parentEntityName, targets, isRoot = true) => {
|
|
|
30
30
|
|
|
31
31
|
for (const element of Object.values(parentEntity.elements)) {
|
|
32
32
|
const { name, target, cardinality } = element
|
|
33
|
-
if (
|
|
33
|
+
if (name in DRAFT_COLUMNS_MAP) {
|
|
34
34
|
continue
|
|
35
35
|
}
|
|
36
36
|
|