@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
|
@@ -18,18 +18,57 @@ const ensureUnlocalized = table => {
|
|
|
18
18
|
return _table
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const ensureDraftsSuffix = name => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
21
|
+
const ensureDraftsSuffix = name => (name.endsWith('_drafts') ? name : `${ensureUnlocalized(name)}_drafts`)
|
|
22
|
+
|
|
23
|
+
const ensureNoDraftsSuffix = name => name.replace(/_drafts$/g, '')
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
const getDraftColumnsCQNForActive = target => {
|
|
26
|
+
const draftName = ensureDraftsSuffix(target.name)
|
|
27
|
+
const subSelect = SELECT.from(draftName).columns([1])
|
|
28
|
+
for (const key in target.keys) {
|
|
29
|
+
if (key !== 'IsActiveEntity') subSelect.where([{ ref: [target.name, key] }, '=', { ref: [draftName, key] }])
|
|
30
|
+
}
|
|
31
|
+
return [
|
|
32
|
+
{ val: true, as: 'IsActiveEntity', cast: { type: 'cds.Boolean' } },
|
|
33
|
+
{ val: false, as: 'HasActiveEntity', cast: { type: 'cds.Boolean' } },
|
|
34
|
+
{
|
|
35
|
+
xpr: ['case', 'when', 'exists', subSelect, 'then', 'true', 'else', 'false', 'end'],
|
|
36
|
+
as: 'HasDraftEntity',
|
|
37
|
+
cast: { type: 'cds.Boolean' }
|
|
38
|
+
}
|
|
39
|
+
]
|
|
27
40
|
}
|
|
28
41
|
|
|
29
|
-
const
|
|
42
|
+
const getDraftColumnsCQNForDraft = target => {
|
|
43
|
+
/*
|
|
44
|
+
* NOTE: the following with xpr could be used to detect if there really is an active or not, but that breaks tests
|
|
45
|
+
*/
|
|
46
|
+
// const activeName = ensureNoDraftsSuffix(target.name)
|
|
47
|
+
// const subSelect = SELECT.from(activeName).columns([1])
|
|
48
|
+
// for (const key in target.keys) {
|
|
49
|
+
// if (key !== 'IsActiveEntity') subSelect.where([{ ref: [target.name, key] }, '=', { ref: [activeName, key] }])
|
|
50
|
+
// }
|
|
51
|
+
// return [
|
|
52
|
+
// { val: false, as: 'IsActiveEntity', cast: { type: 'cds.Boolean' } },
|
|
53
|
+
// {
|
|
54
|
+
// xpr: ['case', 'when', 'exists', subSelect, 'then', 'true', 'else', 'false', 'end'],
|
|
55
|
+
// as: 'HasActiveEntity',
|
|
56
|
+
// cast: { type: 'cds.Boolean' }
|
|
57
|
+
// },
|
|
58
|
+
// { val: false, as: 'HasDraftEntity', cast: { type: 'cds.Boolean' } }
|
|
59
|
+
// ]
|
|
60
|
+
|
|
61
|
+
return [
|
|
62
|
+
{ val: false, as: 'IsActiveEntity', cast: { type: 'cds.Boolean' } },
|
|
63
|
+
{ ref: ['HasActiveEntity'], cast: { type: 'cds.Boolean' } },
|
|
64
|
+
{ val: false, as: 'HasDraftEntity', cast: { type: 'cds.Boolean' } }
|
|
65
|
+
]
|
|
66
|
+
}
|
|
30
67
|
|
|
31
68
|
module.exports = {
|
|
32
69
|
ensureUnlocalized,
|
|
33
70
|
ensureDraftsSuffix,
|
|
34
|
-
ensureNoDraftsSuffix
|
|
71
|
+
ensureNoDraftsSuffix,
|
|
72
|
+
getDraftColumnsCQNForActive,
|
|
73
|
+
getDraftColumnsCQNForDraft
|
|
35
74
|
}
|
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
const { ensureNoDraftsSuffix } = require('../../common/utils/draft')
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const traverseFroms = (cqn, cb) => {
|
|
4
4
|
while (cqn.SELECT) cqn = cqn.SELECT.from
|
|
5
5
|
|
|
6
6
|
// Do the most likely first -> {ref}
|
|
7
7
|
if (cqn.ref) {
|
|
8
|
-
return
|
|
8
|
+
return cb(cqn)
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
// TODO cleanup
|
|
12
|
-
// REVISIT infer should do this for req.target
|
|
13
|
-
// REVISIT2 No, req.target doesn't make sense for joins
|
|
14
11
|
if (cqn.SET) {
|
|
15
|
-
return cqn.SET.args.map(
|
|
12
|
+
return cqn.SET.args.map(a => traverseFroms(a, cb))
|
|
16
13
|
}
|
|
14
|
+
|
|
17
15
|
if (cqn.join) {
|
|
18
|
-
return cqn.args.map(
|
|
16
|
+
return cqn.args.map(a => traverseFroms(a, cb))
|
|
19
17
|
}
|
|
20
|
-
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const getEntityNameFromCQN = cqn => {
|
|
21
|
+
const res = []
|
|
22
|
+
traverseFroms(cqn, from => res.push({ entityName: from.ref[0].id || from.ref[0], alias: from.as }))
|
|
23
|
+
return res.length === 1 ? res[0] : res.find(n => n.entityName !== 'DRAFT.DraftAdministrativeData') || {}
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
// Note: This also works for the common draft scenarios
|
|
@@ -31,5 +34,6 @@ const getEntityFromCQN = (req, service) => {
|
|
|
31
34
|
|
|
32
35
|
module.exports = {
|
|
33
36
|
getEntityFromCQN,
|
|
34
|
-
getEntityNameFromCQN
|
|
37
|
+
getEntityNameFromCQN,
|
|
38
|
+
traverseFroms
|
|
35
39
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const optionsApp = require('./vcap.js')
|
|
2
|
+
|
|
3
|
+
const BASE_TENANT = 'anonymous'
|
|
4
|
+
|
|
5
|
+
const channelName = () => {
|
|
6
|
+
// REVISIT: Why so complicated?
|
|
7
|
+
|
|
8
|
+
const appName = optionsApp.appName || 'CAP'
|
|
9
|
+
const appID = optionsApp.appID || '00000000'
|
|
10
|
+
const shrunkAppID = appID.substring(0, 4)
|
|
11
|
+
|
|
12
|
+
return `${appName}/${shrunkAppID}`
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = {
|
|
16
|
+
BASE_TENANT,
|
|
17
|
+
channelName
|
|
18
|
+
}
|
|
@@ -60,8 +60,7 @@ const _foreignKeyPropagationsFromToManyOn = (element, childFieldName) => {
|
|
|
60
60
|
const foreignKeys = _foreignKeysForTarget(element, childFieldName)
|
|
61
61
|
// REVISIT foreignKeys is empty if we have deep operations where a sub element is annotated with persistence skip
|
|
62
62
|
if (foreignKeys && foreignKeys.length) {
|
|
63
|
-
|
|
64
|
-
return _resolvedKeys(parentKeys, foreignKeys, true)
|
|
63
|
+
return _resolvedKeys(foreignKeys, true)
|
|
65
64
|
}
|
|
66
65
|
return []
|
|
67
66
|
}
|
|
@@ -118,62 +117,11 @@ const _foreignKeyPropagationsFromOn = element => {
|
|
|
118
117
|
return foreignKeyPropagations
|
|
119
118
|
}
|
|
120
119
|
|
|
121
|
-
/*
|
|
122
|
-
* recursive resolvedKeys for a structured element
|
|
123
|
-
* returns how many indexes can be skipped in the outer loop
|
|
124
|
-
* example:
|
|
125
|
-
* foo: {
|
|
126
|
-
* bar: {
|
|
127
|
-
* moo: Integer;
|
|
128
|
-
* shu: Integer;
|
|
129
|
-
* };
|
|
130
|
-
* baz: Integer;
|
|
131
|
-
* };
|
|
132
|
-
* -> foo_bar_moo, foo_bar_shu, foo_baz
|
|
133
|
-
* -> processed three instead of one from outer loop perspective
|
|
134
|
-
*/
|
|
135
|
-
const _resolve4struct = (others, struct, fkps, fillChild, childIsStruct, i) => {
|
|
136
|
-
let j = 0
|
|
137
|
-
|
|
138
|
-
for (const k in struct.elements) {
|
|
139
|
-
const other = others[i + j]
|
|
140
|
-
const current = struct.elements[k]
|
|
141
|
-
|
|
142
|
-
if (current._isStructured) {
|
|
143
|
-
// call recursive and increment skip
|
|
144
|
-
j += _resolve4struct(others, current, fkps, fillChild, childIsStruct, i + j)
|
|
145
|
-
} else if (current.isAssociation) {
|
|
146
|
-
continue
|
|
147
|
-
} else {
|
|
148
|
-
// calc prefix
|
|
149
|
-
let prefix = struct.name
|
|
150
|
-
let cur = struct.parent
|
|
151
|
-
while (cur._isStructured) {
|
|
152
|
-
prefix = cur.name + '_' + prefix
|
|
153
|
-
cur = cur.parent
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// push propagation
|
|
157
|
-
fkps.push({
|
|
158
|
-
childElement: childIsStruct ? current : other,
|
|
159
|
-
parentElement: childIsStruct ? other : current,
|
|
160
|
-
fillChild,
|
|
161
|
-
prefix,
|
|
162
|
-
deep: !fillChild && _resolveTargetForeignKey(childIsStruct ? current : other)
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
// increment skip
|
|
166
|
-
j++
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return j
|
|
171
|
-
}
|
|
172
|
-
|
|
173
120
|
const _resolveTargetForeignKey = targetKey => {
|
|
174
|
-
const targetName = targetKey
|
|
121
|
+
const targetName = targetKey._foreignKey4
|
|
175
122
|
if (!targetName) return
|
|
176
|
-
const
|
|
123
|
+
const parentElements = targetKey.parent.elements
|
|
124
|
+
const _foreignKeyProps = foreignKeyPropagations(parentElements[targetName])
|
|
177
125
|
const propagation = _foreignKeyProps.find(_fkp => _fkp.parentElement && targetKey.name === _fkp.parentElement.name)
|
|
178
126
|
return { targetName, propagation }
|
|
179
127
|
}
|
|
@@ -184,39 +132,22 @@ const _resolveColumnsFromQuery = query => {
|
|
|
184
132
|
return []
|
|
185
133
|
}
|
|
186
134
|
|
|
187
|
-
const _resolvedKeys = (
|
|
188
|
-
const
|
|
135
|
+
const _resolvedKeys = (keys, fillChild) => {
|
|
136
|
+
const foreignKeys = fillChild ? keys.map(fk => Object.getPrototypeOf(fk)) : keys
|
|
137
|
+
const targetKeys = fillChild ? keys : keys.map(fk => Object.getPrototypeOf(fk))
|
|
189
138
|
|
|
139
|
+
const foreignKeyPropagations = []
|
|
190
140
|
for (let i = 0; i < foreignKeys.length; i++) {
|
|
191
141
|
const fk = foreignKeys[i]
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
*/
|
|
200
|
-
const tkCol = _resolveColumnsFromQuery(targetKeys[i].parent.query).find(
|
|
201
|
-
c => c.ref && `${fk['@odata.foreignKey4']}_${c.ref.join('_')}` === fk.name
|
|
202
|
-
)
|
|
203
|
-
tk = tkCol && targetKeys.find(tk => tk.name === (tkCol.as ? tkCol.as : tkCol.ref.join('_')))
|
|
204
|
-
// with composition of aspects, the lookup fails -> we need this final fallback
|
|
205
|
-
if (!tk) tk = targetKeys[i]
|
|
206
|
-
|
|
207
|
-
if (fk._isStructured) {
|
|
208
|
-
i += _resolve4struct(targetKeys, fk, foreignKeyPropagations, fillChild, false, i)
|
|
209
|
-
} else if (tk._isStructured) {
|
|
210
|
-
i += _resolve4struct(foreignKeys, tk, foreignKeyPropagations, fillChild, true, i)
|
|
211
|
-
} else {
|
|
212
|
-
foreignKeyPropagations.push({
|
|
213
|
-
fillChild,
|
|
214
|
-
parentElement: fk,
|
|
215
|
-
childElement: tk,
|
|
216
|
-
// needed only for child -> parent propagation since template loops in other direction
|
|
217
|
-
deep: !fillChild && _resolveTargetForeignKey(tk)
|
|
218
|
-
})
|
|
142
|
+
const tk = targetKeys[i]
|
|
143
|
+
const propagation = {
|
|
144
|
+
fillChild,
|
|
145
|
+
parentElement: fk,
|
|
146
|
+
childElement: tk,
|
|
147
|
+
// needed only for child -> parent propagation since template loops in other direction
|
|
148
|
+
deep: !fillChild && _resolveTargetForeignKey(tk)
|
|
219
149
|
}
|
|
150
|
+
foreignKeyPropagations.push(propagation)
|
|
220
151
|
}
|
|
221
152
|
|
|
222
153
|
return foreignKeyPropagations
|
|
@@ -229,10 +160,7 @@ const foreignKeyPropagations = element => {
|
|
|
229
160
|
if (element.is2one) {
|
|
230
161
|
if (!element.on) {
|
|
231
162
|
const foreignKeys = _foreignKeys(element)
|
|
232
|
-
if (foreignKeys)
|
|
233
|
-
const targetKeys = _targetKeys(element)
|
|
234
|
-
return _resolvedKeys(foreignKeys, targetKeys)
|
|
235
|
-
}
|
|
163
|
+
if (foreignKeys) return _resolvedKeys(foreignKeys, false)
|
|
236
164
|
} else {
|
|
237
165
|
// It's a link through a backlink
|
|
238
166
|
return _foreignKeyPropagationsFromOn(element)
|
|
@@ -241,29 +169,85 @@ const foreignKeyPropagations = element => {
|
|
|
241
169
|
return []
|
|
242
170
|
}
|
|
243
171
|
|
|
244
|
-
|
|
245
|
-
|
|
172
|
+
// REVISIT: Flattening shouldn't be necessary in the future.
|
|
173
|
+
// It's better to deal with structures instead, but
|
|
174
|
+
// that would require changing a lot of code.
|
|
175
|
+
const _foreignKeys = element => {
|
|
176
|
+
const foreignKeys = element.foreignKeys
|
|
177
|
+
const path = [element.name]
|
|
178
|
+
const parent = element.parent
|
|
179
|
+
const result = []
|
|
180
|
+
_addToForeignKeysRec(foreignKeys, path, parent, result)
|
|
181
|
+
return result
|
|
246
182
|
}
|
|
247
183
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
184
|
+
/*
|
|
185
|
+
* REVISIT: poor man's look-up of target key
|
|
186
|
+
* Look at elements, then try to find it in query and resolve recursively until you have the full path.
|
|
187
|
+
* Once you have the full path, you can find it in the target entity.
|
|
188
|
+
* NOTE: There can be projections upon projections and renamings in every projection. -> not yet covered!!!
|
|
189
|
+
*/
|
|
190
|
+
const _poorMansLookup = (el, name, foreignKeySource) => {
|
|
191
|
+
// REVISIT: Dirty hack
|
|
192
|
+
const tkCol = _resolveColumnsFromQuery(el.parent.query).find(
|
|
193
|
+
c => c.ref && `${foreignKeySource}_${c.ref.join('_')}` === name
|
|
252
194
|
)
|
|
195
|
+
return tkCol && Object.values(el.parent.elements).find(tk => tk.name === (tkCol.as ? tkCol.as : tkCol.ref.join('_')))
|
|
253
196
|
}
|
|
254
197
|
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
)
|
|
198
|
+
const _createForeignKey = (name, el, parent, foreignKeySource) => {
|
|
199
|
+
const tk = _poorMansLookup(el, name, foreignKeySource)
|
|
200
|
+
const navigationCsn = parent.elements[foreignKeySource]
|
|
201
|
+
const foreignKeyCsn = Object.assign(Object.create(tk || el), { parent, name, foreignKeySource })
|
|
202
|
+
// annotation propagation is not possible via prototyping
|
|
203
|
+
for (const key of Object.keys(navigationCsn).filter(key => key.startsWith('@')))
|
|
204
|
+
foreignKeyCsn[key] = navigationCsn[key]
|
|
205
|
+
return foreignKeyCsn
|
|
259
206
|
}
|
|
260
207
|
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
208
|
+
const _addToForeignKeysRec = (elements, path, parent, result) => {
|
|
209
|
+
for (const elName in elements) {
|
|
210
|
+
const el = elements[elName]
|
|
211
|
+
const foreignKeySource = path[0]
|
|
212
|
+
const newPath = [...path, elName]
|
|
213
|
+
if (el.isAssociation) {
|
|
214
|
+
const foreignKeysOftarget = _foreignKeys(el)
|
|
215
|
+
for (const fk of foreignKeysOftarget) {
|
|
216
|
+
const name = [...path, fk.name].join('_')
|
|
217
|
+
if (result.some(x => x.name === name)) return
|
|
218
|
+
const foreignKeyCsn = _createForeignKey(name, fk, parent, foreignKeySource)
|
|
219
|
+
result.push(foreignKeyCsn)
|
|
220
|
+
}
|
|
221
|
+
} else if (!el.elements) {
|
|
222
|
+
const name = newPath.join('_')
|
|
223
|
+
if (result.some(x => x.name === name)) return
|
|
224
|
+
const foreignKeyCsn = _createForeignKey(name, el, parent, foreignKeySource)
|
|
225
|
+
result.push(foreignKeyCsn)
|
|
226
|
+
} else _addToForeignKeysRec(el.elements, newPath, parent, result)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const _foreignKeysForTarget = (csnElement, name) => {
|
|
231
|
+
const target = csnElement._target.elements[name || csnElement.name]
|
|
232
|
+
return _foreignKeys(target)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const foreignKey4 = element => {
|
|
236
|
+
if (!element || !element.parent) return
|
|
237
|
+
const parentElements = element.parent.elements
|
|
238
|
+
for (const assoc of Object.keys(parentElements)
|
|
239
|
+
.map(n => parentElements[n])
|
|
240
|
+
.filter(e => e.isAssociation)) {
|
|
241
|
+
const foreignKeys = _foreignKeys(assoc)
|
|
242
|
+
if (!foreignKeys.length) continue
|
|
243
|
+
const target = foreignKeys.find(fk => fk.name === element.name)
|
|
244
|
+
if (target) {
|
|
245
|
+
return target && target.foreignKeySource
|
|
246
|
+
}
|
|
247
|
+
}
|
|
265
248
|
}
|
|
266
249
|
|
|
267
250
|
module.exports = {
|
|
268
|
-
foreignKeyPropagations
|
|
251
|
+
foreignKeyPropagations,
|
|
252
|
+
foreignKey4
|
|
269
253
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const { prefixForStruct } = require('./csn')
|
|
2
|
+
|
|
1
3
|
const _toRef = (alias, column) => {
|
|
2
4
|
if (Array.isArray(column)) column = column.join('_')
|
|
3
5
|
return { ref: alias ? [alias, column] : [column] }
|
|
@@ -18,7 +20,7 @@ const _adaptRefs = (onCond, path, { select, join }) => {
|
|
|
18
20
|
|
|
19
21
|
return _toRef(join, ref.slice(0))
|
|
20
22
|
} else if (el.xpr) {
|
|
21
|
-
|
|
23
|
+
return { xpr: el.xpr.map(_adaptEl) }
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
return el
|
|
@@ -73,7 +75,8 @@ const _foreignToOn = (csnElement, path, { select, join }) => {
|
|
|
73
75
|
if (on.length !== 0) {
|
|
74
76
|
on.push('and')
|
|
75
77
|
}
|
|
76
|
-
const
|
|
78
|
+
const prefixChild = prefixForStruct(key.childElement)
|
|
79
|
+
const ref1 = _toRef(select, prefixChild + key.childElement.name)
|
|
77
80
|
const structPrefix = path.length > 1 ? path.slice(0, -1) : []
|
|
78
81
|
const ref2 = _toRef(join, [...structPrefix, key.parentElement.name])
|
|
79
82
|
on.push(ref1, '=', ref2)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const _flattenProps = (subElement, structName, structProperties, structElement, asRef) => {
|
|
1
|
+
const _flattenProps = (subElement, structName, structProperties, structElement, asRef, withKey) => {
|
|
2
2
|
if (subElement.elements) {
|
|
3
3
|
return _resolveStructured(
|
|
4
4
|
{
|
|
@@ -6,34 +6,50 @@ const _flattenProps = (subElement, structName, structProperties, structElement,
|
|
|
6
6
|
structProperties: structProperties.slice(1)
|
|
7
7
|
},
|
|
8
8
|
subElement.elements,
|
|
9
|
-
asRef
|
|
9
|
+
asRef,
|
|
10
|
+
withKey
|
|
10
11
|
)
|
|
11
12
|
} else if (subElement.isAssociation) {
|
|
12
13
|
if (structProperties.length && subElement.is2one && !subElement.on) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
: [
|
|
14
|
+
const flattenedName = `${structName}_${structProperties.join('_')}`
|
|
15
|
+
if (withKey) {
|
|
16
|
+
return [{ key: structElement, resolved: [flattenedName] }]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return asRef ? [{ ref: [flattenedName] }] : [flattenedName]
|
|
16
20
|
}
|
|
17
21
|
|
|
18
22
|
return []
|
|
19
23
|
}
|
|
20
|
-
|
|
24
|
+
const flattenedName = `${structName}_${structElement}`
|
|
25
|
+
if (withKey) {
|
|
26
|
+
return [{ key: structElement, resolved: [flattenedName] }]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return asRef ? [{ ref: [flattenedName] }] : [flattenedName]
|
|
21
30
|
}
|
|
22
31
|
|
|
23
|
-
const _resolveStructured = ({ structName, structProperties }, subElements, asRef = true) => {
|
|
32
|
+
const _resolveStructured = ({ structName, structProperties }, subElements, asRef = true, withKey = false) => {
|
|
24
33
|
if (!subElements) {
|
|
25
34
|
return []
|
|
26
35
|
}
|
|
27
36
|
|
|
28
37
|
// only add from structProperties
|
|
29
38
|
if (structProperties.length) {
|
|
30
|
-
return _flattenProps(
|
|
39
|
+
return _flattenProps(
|
|
40
|
+
subElements[structProperties[0]],
|
|
41
|
+
structName,
|
|
42
|
+
structProperties,
|
|
43
|
+
structProperties[0],
|
|
44
|
+
asRef,
|
|
45
|
+
withKey
|
|
46
|
+
)
|
|
31
47
|
}
|
|
32
48
|
|
|
33
49
|
const flattenedElements = []
|
|
34
50
|
for (const structElement in subElements) {
|
|
35
51
|
flattenedElements.push(
|
|
36
|
-
..._flattenProps(subElements[structElement], structName, structProperties, structElement, asRef)
|
|
52
|
+
..._flattenProps(subElements[structElement], structName, structProperties, structElement, asRef, withKey)
|
|
37
53
|
)
|
|
38
54
|
}
|
|
39
55
|
return flattenedElements
|
|
@@ -346,7 +346,10 @@ const _newSelect = (query, transitions, service) => {
|
|
|
346
346
|
}
|
|
347
347
|
if (!newSelect.columns && targetTransition.mapping.size) newSelect.columns = _initialColumns(targetTransition)
|
|
348
348
|
if (newSelect.columns) {
|
|
349
|
-
rewriteAsterisks({ SELECT: query.SELECT }, service.model, {
|
|
349
|
+
rewriteAsterisks({ SELECT: query.SELECT }, service.model, {
|
|
350
|
+
_4db: service instanceof cds.DatabaseService,
|
|
351
|
+
target: targetTransition.queryTarget
|
|
352
|
+
})
|
|
350
353
|
newSelect.columns = _newColumns(newSelect.columns, targetTransition, service, service.kind !== 'app-service')
|
|
351
354
|
}
|
|
352
355
|
if (newSelect.having) newSelect.having = _newColumns(newSelect.having, targetTransition)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { getNavigationIfStruct } = require('./structured')
|
|
2
2
|
const getColumns = require('../../db/utils/columns')
|
|
3
|
-
const {
|
|
3
|
+
const { ensureNoDraftsSuffix, getDraftColumnsCQNForActive } = require('./draft')
|
|
4
4
|
const { getEntityNameFromCQN } = require('./entityFromCqn')
|
|
5
5
|
|
|
6
6
|
const isAsteriskColumn = col => col === '*' || (col.ref && col.ref[0] === '*' && !col.expand)
|
|
@@ -14,20 +14,7 @@ const _isDuplicate = newColumn => column => {
|
|
|
14
14
|
|
|
15
15
|
const _cqlDraftColumns = target => {
|
|
16
16
|
if (target.name.endsWith('DraftAdministrativeData')) return []
|
|
17
|
-
|
|
18
|
-
const subSelect = SELECT.from(draftName).columns([1])
|
|
19
|
-
for (const key in target.keys) {
|
|
20
|
-
if (key !== 'IsActiveEntity') subSelect.where([{ ref: [target.name, key] }, '=', { ref: [draftName, key] }])
|
|
21
|
-
}
|
|
22
|
-
return [
|
|
23
|
-
{ val: true, as: 'IsActiveEntity', cast: { type: 'cds.Boolean' } },
|
|
24
|
-
{ val: false, as: 'HasActiveEntity', cast: { type: 'cds.Boolean' } },
|
|
25
|
-
{
|
|
26
|
-
xpr: ['case', 'when', 'exists', subSelect, 'then', 'true', 'else', 'false', 'end'],
|
|
27
|
-
as: 'HasDraftEntity',
|
|
28
|
-
cast: { type: 'cds.Boolean' }
|
|
29
|
-
}
|
|
30
|
-
]
|
|
17
|
+
return getDraftColumnsCQNForActive(target)
|
|
31
18
|
}
|
|
32
19
|
|
|
33
20
|
const _expandColumn = (column, target, _4db) => {
|
|
@@ -135,7 +122,7 @@ const rewriteAsterisks = (query, model, options) => {
|
|
|
135
122
|
return
|
|
136
123
|
}
|
|
137
124
|
|
|
138
|
-
const target = _targetOfQueryIfNotDraft(query, model)
|
|
125
|
+
const target = options.target || _targetOfQueryIfNotDraft(query, model)
|
|
139
126
|
if (!target) return
|
|
140
127
|
|
|
141
128
|
// REVISIT: Also support JOINs/SETs here
|
|
@@ -1,30 +1,21 @@
|
|
|
1
1
|
const resolveStructured = require('./resolveStructured')
|
|
2
2
|
const { ensureNoDraftsSuffix } = require('../../common/utils/draft')
|
|
3
|
+
const { traverseFroms } = require('../../common/utils/entityFromCqn')
|
|
3
4
|
// TODO move to commons as also used in cqn2cqn4sql
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}, new Set())
|
|
19
|
-
)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (Array.isArray(from.args)) {
|
|
23
|
-
// TODO this only considers first level refs and not from sub selects
|
|
24
|
-
return from.args.filter(arg => arg.ref).map(arg => ensureNoDraftsSuffix(arg.ref[0]))
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return []
|
|
5
|
+
const OPERATIONS_MAP = ['=', '>', '<', '!=', '<>', '>=', '<=', 'like', 'between', 'in', 'not in'].reduce((acc, cur) => {
|
|
6
|
+
acc[cur] = 1
|
|
7
|
+
return acc
|
|
8
|
+
}, {})
|
|
9
|
+
|
|
10
|
+
const _getEntityNamesAndIds = from => {
|
|
11
|
+
const nameAndIds = []
|
|
12
|
+
traverseFroms(from, from => {
|
|
13
|
+
const nameAndId = { name: ensureNoDraftsSuffix(from.ref[0].id || from.ref[0]), id: from.as || from.ref[0] }
|
|
14
|
+
if (nameAndIds.some(x => x.name === nameAndId.name)) return // no duplicates
|
|
15
|
+
nameAndIds.push(nameAndId)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
return nameAndIds
|
|
28
19
|
}
|
|
29
20
|
|
|
30
21
|
const _flattenStructuredInExpand = (column, { _target: expandedEntity }) => {
|
|
@@ -117,8 +108,8 @@ const _filterForStructProperty = (structElement, structData, op, prefix = '', na
|
|
|
117
108
|
)
|
|
118
109
|
} else {
|
|
119
110
|
if (element.isAssociation) continue
|
|
120
|
-
|
|
121
|
-
|
|
111
|
+
const assocName = element._foreignKey4
|
|
112
|
+
if (assocName) {
|
|
122
113
|
const assoc = structElement.elements[assocName]
|
|
123
114
|
if (assoc.is2one && !assoc.on) {
|
|
124
115
|
for (const key in assoc._target.keys) {
|
|
@@ -214,7 +205,7 @@ const flattenStructuredWhereHaving = (filterArray, csnEntity, model) => {
|
|
|
214
205
|
|
|
215
206
|
const newFilterArray = []
|
|
216
207
|
for (let i = 0; i < filterArray.length; i++) {
|
|
217
|
-
if (
|
|
208
|
+
if (filterArray[i + 1] in OPERATIONS_MAP) {
|
|
218
209
|
const refElement = filterArray[i].ref ? filterArray[i] : filterArray[i + 2]
|
|
219
210
|
|
|
220
211
|
// copy for processing
|
|
@@ -254,42 +245,47 @@ const getNavigationIfStruct = (entity, ref) => {
|
|
|
254
245
|
return element
|
|
255
246
|
}
|
|
256
247
|
|
|
257
|
-
const _flattenColumns = (SELECT, flattenedElements, toBeDeleted, csnEntity) => {
|
|
248
|
+
const _flattenColumns = (SELECT, flattenedElements, toBeDeleted, csnEntity, tableId) => {
|
|
258
249
|
for (const column of SELECT.columns) {
|
|
259
250
|
if (!column.ref) continue
|
|
260
251
|
|
|
261
|
-
//
|
|
262
|
-
const
|
|
252
|
+
// might begin with table id
|
|
253
|
+
const cleanedUpRef = column.ref.length > 1 && column.ref[0] === tableId ? column.ref.slice(1) : column.ref
|
|
254
|
+
const structName = cleanedUpRef[0]
|
|
263
255
|
|
|
264
256
|
const element = csnEntity.elements[structName]
|
|
265
257
|
if (!element) continue
|
|
266
258
|
|
|
267
259
|
if (column.expand) {
|
|
268
|
-
_flattenStructuredInExpand(column, getNavigationIfStruct(csnEntity,
|
|
260
|
+
_flattenStructuredInExpand(column, getNavigationIfStruct(csnEntity, cleanedUpRef))
|
|
269
261
|
continue
|
|
270
262
|
}
|
|
271
263
|
|
|
272
264
|
if (element._isStructured) {
|
|
273
265
|
toBeDeleted.push(structName) // works with aliases?
|
|
274
266
|
flattenedElements.push(
|
|
275
|
-
...resolveStructured({ structName, structProperties:
|
|
267
|
+
...resolveStructured({ structName, structProperties: cleanedUpRef.slice(1) }, element.elements)
|
|
276
268
|
)
|
|
277
269
|
}
|
|
270
|
+
if (cleanedUpRef.length < column.ref.length) {
|
|
271
|
+
flattenedElements.forEach(e => e.ref.unshift(tableId))
|
|
272
|
+
}
|
|
278
273
|
}
|
|
279
274
|
}
|
|
280
275
|
|
|
281
276
|
const flattenStructuredSelect = ({ SELECT }, model) => {
|
|
282
|
-
const
|
|
277
|
+
const entityNamesAndIds = _getEntityNamesAndIds(SELECT.from)
|
|
283
278
|
|
|
284
|
-
for (const
|
|
285
|
-
const entity = model.definitions[
|
|
279
|
+
for (const entityNameAndId of entityNamesAndIds) {
|
|
280
|
+
const entity = model.definitions[entityNameAndId.name]
|
|
281
|
+
const tableId = entityNameAndId.id
|
|
286
282
|
|
|
287
283
|
if (Array.isArray(SELECT.columns) && SELECT.columns.length > 0) {
|
|
288
284
|
const flattenedElements = []
|
|
289
285
|
const toBeDeleted = []
|
|
290
|
-
_flattenColumns(SELECT, flattenedElements, toBeDeleted, entity)
|
|
286
|
+
_flattenColumns(SELECT, flattenedElements, toBeDeleted, entity, tableId)
|
|
291
287
|
SELECT.columns = SELECT.columns.filter(column => {
|
|
292
|
-
const columnName = column.ref ? column.ref[0] : column.as
|
|
288
|
+
const columnName = column.ref ? (column.ref[0] === tableId ? column.ref[1] : column.ref[0]) : column.as
|
|
293
289
|
return (columnName && !toBeDeleted.includes(columnName)) || column.func || column.expand || 'val' in column
|
|
294
290
|
})
|
|
295
291
|
if (flattenedElements.length) SELECT.columns.push(...flattenedElements)
|