@sap/cds 7.9.4 → 8.0.4
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 +128 -3659
- package/_i18n/i18n_en_US_saptrc.properties +113 -0
- package/_i18n/i18n_zh_CN.properties +7 -4
- package/app/index.css +129 -0
- package/app/index.html +16 -64
- package/app/index.js +14 -9
- package/bin/args.js +34 -0
- package/bin/serve.js +18 -24
- package/bin/test.js +97 -0
- package/common.cds +5 -12
- package/eslint.config.mjs +133 -0
- package/lib/auth/basic-auth.js +16 -20
- package/lib/auth/dummy-auth.js +1 -1
- package/lib/auth/ias-auth.js +9 -41
- package/lib/auth/index.js +1 -14
- package/lib/auth/jwt-auth.js +10 -40
- package/lib/compile/cds-compile.js +1 -2
- package/lib/compile/cdsc.js +21 -26
- package/lib/compile/etc/_localized.js +1 -6
- package/lib/compile/etc/csv.js +1 -1
- package/lib/compile/etc/properties.js +1 -1
- package/lib/compile/for/java.js +1 -1
- package/lib/compile/for/lean_drafts.js +4 -6
- package/lib/compile/for/nodejs.js +1 -1
- package/lib/compile/parse.js +4 -0
- package/lib/compile/resolve.js +4 -4
- package/lib/compile/to/edm-files.js +16 -23
- package/lib/compile/to/hana.js +27 -0
- package/lib/compile/to/json.js +1 -1
- package/lib/compile/to/sql.js +5 -1
- package/lib/compile/to/yaml.js +3 -3
- package/lib/dbs/cds-deploy.js +4 -2
- package/lib/env/cds-env.js +10 -14
- package/lib/env/cds-requires.js +30 -13
- package/lib/env/defaults.js +46 -16
- package/lib/env/plugins.js +1 -1
- package/lib/env/schemas/cds-rc.js +8 -4
- package/lib/env/schemas/index.js +7 -7
- package/lib/env/serviceBindings.js +1 -1
- package/lib/index.js +12 -10
- package/lib/lazy.js +1 -1
- package/lib/linked/classes.js +36 -8
- package/lib/linked/entities.js +2 -10
- package/lib/linked/models.js +2 -1
- package/lib/linked/validate.js +292 -0
- package/lib/log/cds-error.js +0 -6
- package/lib/log/cds-log.js +3 -3
- package/lib/log/format/json.js +1 -1
- package/lib/log/service/index.js +0 -1
- package/lib/plugins.js +2 -2
- package/lib/ql/Query.js +2 -10
- package/lib/ql/SELECT.js +1 -1
- package/lib/ql/Whereable.js +3 -2
- package/lib/req/cds-context.js +14 -25
- package/lib/req/context.js +23 -25
- package/lib/req/request.js +1 -34
- package/lib/req/user.js +47 -35
- package/lib/srv/bindings.js +1 -1
- package/lib/srv/cds-connect.js +4 -4
- package/lib/srv/cds-serve.js +2 -2
- package/lib/srv/factory.js +1 -1
- package/lib/srv/middlewares/cds-context.js +11 -22
- package/lib/srv/middlewares/ctx-model.js +2 -3
- package/lib/srv/middlewares/errors.js +41 -8
- package/lib/srv/middlewares/index.js +3 -3
- package/lib/srv/middlewares/trace.js +0 -2
- package/lib/srv/protocols/hcql.js +15 -10
- package/lib/srv/protocols/http.js +44 -49
- package/lib/srv/protocols/index.js +1 -23
- package/lib/srv/protocols/odata-v4.js +12 -74
- package/lib/srv/protocols/rest.js +1 -13
- package/lib/srv/srv-api.js +0 -20
- package/lib/srv/srv-dispatch.js +3 -2
- package/lib/srv/srv-handlers.js +22 -11
- package/lib/srv/srv-methods.js +2 -2
- package/lib/srv/srv-models.js +3 -36
- package/lib/test/expect.js +343 -0
- package/lib/test/index.js +2 -0
- package/lib/test/reporter.js +176 -0
- package/lib/utils/axios.js +10 -9
- package/lib/utils/cds-test.js +85 -36
- package/lib/utils/cds-utils.js +54 -7
- package/lib/utils/check-version.js +0 -4
- package/lib/utils/colors.js +49 -0
- package/lib/utils/data.js +5 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +2 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +3 -30
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +6 -12
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +4 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +12 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +2 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/AbstractEdmStructuredType.js +1 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +5 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ContextURLFactory.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +9 -43
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +8 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +4 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +1 -3
- package/libx/_runtime/cds-services/util/assert.js +1 -1
- package/libx/_runtime/cds.js +10 -3
- package/libx/_runtime/common/Service.js +12 -32
- package/libx/_runtime/common/aspects/any.js +1 -0
- package/libx/_runtime/common/code-ext/execute.js +1 -1
- package/libx/_runtime/common/code-ext/worker.js +0 -1
- package/libx/_runtime/common/composition/data.js +0 -1
- package/libx/_runtime/common/composition/delete.js +0 -1
- package/libx/_runtime/common/composition/tree.js +0 -1
- package/libx/_runtime/common/composition/update.js +3 -3
- package/libx/_runtime/common/error/frontend.js +21 -12
- package/libx/_runtime/common/error/log.js +36 -0
- package/libx/_runtime/common/error/utils.js +2 -5
- package/libx/_runtime/common/generic/auth/autoexpose.js +18 -17
- package/libx/_runtime/common/generic/auth/expand.js +1 -1
- package/libx/_runtime/common/generic/auth/readOnly.js +1 -2
- package/libx/_runtime/common/generic/auth/restrict.js +23 -42
- package/libx/_runtime/common/generic/auth/restrictions.js +2 -7
- package/libx/_runtime/common/generic/auth/utils.js +91 -88
- package/libx/_runtime/common/generic/crud.js +6 -5
- package/libx/_runtime/common/generic/etag.js +7 -12
- package/libx/_runtime/common/generic/input.js +70 -68
- package/libx/_runtime/common/generic/paging.js +1 -0
- package/libx/_runtime/common/generic/sorting.js +1 -0
- package/libx/_runtime/common/generic/temporal.js +8 -2
- package/libx/_runtime/common/i18n/index.js +1 -1
- package/libx/_runtime/common/i18n/messages.properties +3 -1
- package/libx/_runtime/common/utils/binary.js +8 -2
- package/libx/_runtime/common/utils/compareJson.js +5 -1
- package/libx/_runtime/common/utils/copy.js +6 -11
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +16 -14
- package/libx/_runtime/common/utils/differ.js +3 -6
- package/libx/_runtime/common/utils/keys.js +77 -18
- package/libx/_runtime/common/utils/postProcess.js +12 -15
- package/libx/_runtime/common/utils/propagateForeignKeys.js +0 -1
- package/libx/_runtime/common/utils/resolveView.js +2 -3
- package/libx/_runtime/common/utils/restrictions.js +45 -17
- package/libx/_runtime/common/utils/rewriteAsterisks.js +1 -8
- package/libx/_runtime/common/utils/stream.js +3 -16
- package/libx/_runtime/common/utils/streamProp.js +8 -18
- package/libx/_runtime/common/utils/structured.js +1 -1
- package/libx/_runtime/common/utils/ucsn.js +0 -2
- package/libx/_runtime/db/Service.js +0 -72
- package/libx/_runtime/db/data-conversion/post-processing.js +0 -1
- package/libx/_runtime/db/expand/expandCQNToJoin.js +9 -9
- package/libx/_runtime/db/expand/rawToExpanded.js +0 -8
- package/libx/_runtime/db/generic/input.js +3 -8
- package/libx/_runtime/db/generic/rewrite.js +1 -0
- package/libx/_runtime/db/query/read.js +2 -2
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +0 -1
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -1
- package/libx/_runtime/db/utils/columns.js +2 -6
- package/libx/_runtime/fiori/lean-draft.js +138 -56
- package/libx/_runtime/hana/Service.js +0 -1
- package/libx/_runtime/hana/driver.js +1 -1
- package/libx/_runtime/hana/dynatrace.js +1 -2
- package/libx/_runtime/hana/pool.js +11 -21
- package/libx/_runtime/hana/streaming.js +0 -1
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +0 -1
- package/libx/_runtime/messaging/common-utils/authorizedRequest.js +1 -1
- package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -33
- package/libx/_runtime/messaging/event-broker.js +54 -27
- package/libx/_runtime/messaging/file-based.js +3 -3
- package/libx/_runtime/messaging/http-utils/token.js +1 -1
- package/libx/_runtime/messaging/kafka.js +2 -2
- package/libx/_runtime/messaging/redis-messaging.js +0 -1
- package/libx/_runtime/remote/Service.js +25 -25
- package/libx/_runtime/remote/utils/client.js +4 -5
- package/libx/_runtime/remote/utils/cloudSdkProvider.js +0 -3
- package/libx/_runtime/remote/utils/data.js +0 -1
- package/libx/_runtime/sqlite/Service.js +1 -2
- package/libx/_runtime/ucl/Service.js +37 -78
- package/libx/common/assert/index.js +22 -21
- package/libx/common/assert/type-relaxed.js +39 -0
- package/libx/common/assert/utils.js +3 -2
- package/libx/common/assert/validation.js +3 -8
- package/libx/common/utils/index.js +5 -0
- package/libx/common/utils/path.js +51 -0
- package/libx/odata/ODataAdapter.js +126 -0
- package/libx/odata/index.js +15 -2
- package/libx/odata/middleware/batch.js +320 -84
- package/libx/odata/middleware/body-parser.js +33 -0
- package/libx/odata/middleware/create.js +44 -59
- package/libx/odata/middleware/delete.js +23 -12
- package/libx/odata/middleware/error.js +30 -6
- package/libx/odata/middleware/metadata.js +38 -26
- package/libx/odata/middleware/operation.js +93 -69
- package/libx/odata/middleware/parse.js +6 -8
- package/libx/odata/middleware/read.js +117 -93
- package/libx/odata/middleware/service-document.js +22 -19
- package/libx/odata/middleware/stream.js +54 -56
- package/libx/odata/middleware/update.js +79 -87
- package/libx/odata/parse/afterburner.js +191 -175
- package/libx/odata/parse/cqn2odata.js +5 -5
- package/libx/odata/parse/grammar.peggy +27 -20
- package/libx/odata/parse/multipartToJson.js +17 -9
- package/libx/odata/parse/parser.js +1 -1
- package/libx/odata/utils/etag.js +14 -6
- package/libx/odata/utils/index.js +84 -12
- package/libx/odata/utils/metadata.js +161 -0
- package/libx/odata/utils/postProcess.js +89 -0
- package/libx/odata/utils/readAfterWrite.js +134 -17
- package/libx/odata/utils/result.js +36 -142
- package/libx/outbox/index.js +4 -3
- package/libx/rest/RestAdapter.js +115 -182
- package/libx/rest/middleware/create.js +28 -24
- package/libx/rest/middleware/delete.js +7 -10
- package/libx/rest/middleware/error.js +26 -16
- package/libx/rest/middleware/operation.js +48 -41
- package/libx/rest/middleware/parse.js +128 -126
- package/libx/rest/middleware/read.js +20 -27
- package/libx/rest/middleware/update.js +26 -31
- package/package.json +17 -8
- package/server.js +4 -2
- package/apis/cds.d.ts +0 -3
- package/apis/core.d.ts +0 -21
- package/apis/cqn.d.ts +0 -18
- package/apis/csn.d.ts +0 -21
- package/apis/events.d.ts +0 -18
- package/apis/internal/inference.d.ts +0 -18
- package/apis/linked.d.ts +0 -18
- package/apis/log.d.ts +0 -20
- package/apis/models.d.ts +0 -18
- package/apis/ql.d.ts +0 -18
- package/apis/reflect.d.ts +0 -32
- package/apis/server.d.ts +0 -18
- package/apis/services.d.ts +0 -22
- package/bin/cds-serve.js +0 -56
- package/lib/compile/to/gql.js +0 -15
- package/lib/srv/protocols/_legacy.js +0 -44
- package/lib/utils/jest.js +0 -43
- package/libx/_runtime/auth/index.js +0 -193
- package/libx/_runtime/auth/strategies/JWT.js +0 -37
- package/libx/_runtime/auth/strategies/basic.js +0 -20
- package/libx/_runtime/auth/strategies/dummy.js +0 -14
- package/libx/_runtime/auth/strategies/ias-auth.js +0 -1
- package/libx/_runtime/auth/strategies/mock.js +0 -77
- package/libx/_runtime/auth/strategies/xssecUtils.js +0 -93
- package/libx/_runtime/auth/strategies/xsuaa.js +0 -38
- package/libx/_runtime/common/perf/index.js +0 -19
- package/libx/_runtime/common/utils/ensureIEEE754.js +0 -29
- package/libx/_runtime/fiori/draft.js +0 -2
- package/libx/_runtime/fiori/generic/activate.js +0 -190
- package/libx/_runtime/fiori/generic/before.js +0 -201
- package/libx/_runtime/fiori/generic/cancel.js +0 -19
- package/libx/_runtime/fiori/generic/delete.js +0 -21
- package/libx/_runtime/fiori/generic/edit.js +0 -157
- package/libx/_runtime/fiori/generic/index.js +0 -25
- package/libx/_runtime/fiori/generic/new.js +0 -82
- package/libx/_runtime/fiori/generic/patch.js +0 -101
- package/libx/_runtime/fiori/generic/prepare.js +0 -57
- package/libx/_runtime/fiori/generic/read.js +0 -1340
- package/libx/_runtime/fiori/generic/readOverDraft.js +0 -146
- package/libx/_runtime/fiori/utils/csn.js +0 -13
- package/libx/_runtime/fiori/utils/delete.js +0 -114
- package/libx/_runtime/fiori/utils/handler.js +0 -264
- package/libx/_runtime/fiori/utils/lockInfo.js +0 -27
- package/libx/_runtime/fiori/utils/req.js +0 -23
- package/libx/_runtime/fiori/utils/stream.js +0 -36
- package/libx/_runtime/fiori/utils/where.js +0 -254
- package/libx/_runtime/index.js +0 -22
- package/libx/odata/utils/handler.js +0 -120
- package/libx/odata/utils/metaInfo.js +0 -410
- package/libx/odata/utils/path.js +0 -75
- package/libx/rest/RestRequest.js +0 -32
- package/libx/rest/index.js +0 -3
- package/libx/rest/readme.md +0 -1
- /package/libx/common/assert/{type.js → type-strict.js} +0 -0
|
@@ -1,1340 +0,0 @@
|
|
|
1
|
-
const cds = require('../../cds')
|
|
2
|
-
const { SELECT } = cds.ql
|
|
3
|
-
|
|
4
|
-
const { cqn2cqn4sql, convertWhereExists } = require('../../common/utils/cqn2cqn4sql')
|
|
5
|
-
const { getElementDeep } = require('../../common/utils/csn')
|
|
6
|
-
const { DRAFT_COLUMNS_MAP, SCENARIO } = require('../../common/constants/draft')
|
|
7
|
-
const { filterNonDraftColumns } = require('../../common/utils/draft')
|
|
8
|
-
const {
|
|
9
|
-
addColumnAlias,
|
|
10
|
-
draftIsLocked,
|
|
11
|
-
ensureDraftsSuffix,
|
|
12
|
-
ensureNoDraftsSuffix,
|
|
13
|
-
ensureUnlocalized,
|
|
14
|
-
getEnrichedCQN,
|
|
15
|
-
removeDraftUUIDIfNecessary,
|
|
16
|
-
replaceRefWithDraft,
|
|
17
|
-
entity_keys
|
|
18
|
-
} = require('../utils/handler')
|
|
19
|
-
const { deleteCondition, readAndDeleteKeywords, removeIsActiveEntityRecursively } = require('../utils/where')
|
|
20
|
-
const { adaptStreamCQN } = require('../utils/stream')
|
|
21
|
-
const getError = require('../../common/error')
|
|
22
|
-
|
|
23
|
-
const _findSubselect = where => {
|
|
24
|
-
return where.find((e, i) => {
|
|
25
|
-
if (e.xpr) return _findSubselect(e.xpr)
|
|
26
|
-
return e.SELECT && where[i - 1] === 'exists'
|
|
27
|
-
})
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const _findRootSubSelectFor = query => {
|
|
31
|
-
if (query.SELECT.where) {
|
|
32
|
-
const subSelect = _findSubselect(query.SELECT.where)
|
|
33
|
-
return subSelect ? _findRootSubSelectFor(subSelect) : query
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return query
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// append where with clauses from @restrict
|
|
40
|
-
const _getWhereWithAppendedDraftRestrictions = (where = [], req) => {
|
|
41
|
-
const restrictions = []
|
|
42
|
-
if (req.query._draftRestrictions) {
|
|
43
|
-
for (const each of req.query._draftRestrictions) {
|
|
44
|
-
const xpr = each._xpr
|
|
45
|
-
if (each.target.name === ensureUnlocalized(req.target.name)) {
|
|
46
|
-
// restriction might contain or clause -> use xpr for grouping
|
|
47
|
-
if (restrictions.length) restrictions.push('or')
|
|
48
|
-
xpr.includes('or') ? restrictions.push({ xpr }) : restrictions.push(...xpr)
|
|
49
|
-
} else {
|
|
50
|
-
// restriction inherited from parent via autoexposure
|
|
51
|
-
// find inner most sub select if available and append restriction to where clause
|
|
52
|
-
const rootSubSelect = _findRootSubSelectFor({ SELECT: { where } })
|
|
53
|
-
if (rootSubSelect && rootSubSelect.SELECT.from) {
|
|
54
|
-
if (rootSubSelect.SELECT.where && rootSubSelect.SELECT.where.length) rootSubSelect.SELECT.where.push('and')
|
|
55
|
-
const tableAlias = rootSubSelect.SELECT.from.as
|
|
56
|
-
rootSubSelect.SELECT.where.push(
|
|
57
|
-
...xpr.map(e => {
|
|
58
|
-
if (e.ref) return tableAlias ? { ref: [tableAlias, ...e.ref] } : { ref: [...e.ref] }
|
|
59
|
-
return e
|
|
60
|
-
})
|
|
61
|
-
)
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (restrictions.length) {
|
|
68
|
-
if (where.length) where.push('and', { xpr: restrictions })
|
|
69
|
-
else where.push(...restrictions)
|
|
70
|
-
}
|
|
71
|
-
return where
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const _isTrue = val => val === true || val === 'true'
|
|
75
|
-
const _isFalse = val => val === false || val === 'false'
|
|
76
|
-
const _inProcessByUserWhere = userId => [{ ref: ['filterAdmin', 'InProcessByUser'] }, '=', { val: userId }]
|
|
77
|
-
|
|
78
|
-
const _getTableName = (
|
|
79
|
-
{
|
|
80
|
-
target: { name },
|
|
81
|
-
query: {
|
|
82
|
-
SELECT: { from }
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
isDraft = false
|
|
86
|
-
) => {
|
|
87
|
-
const table = isDraft ? ensureDraftsSuffix(name) : ensureNoDraftsSuffix(name)
|
|
88
|
-
const as = from.args ? from.args[0].as : from.as
|
|
89
|
-
if (as) {
|
|
90
|
-
return {
|
|
91
|
-
table: {
|
|
92
|
-
ref: [table],
|
|
93
|
-
as: as
|
|
94
|
-
},
|
|
95
|
-
name: as
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return {
|
|
100
|
-
table: {
|
|
101
|
-
ref: [table]
|
|
102
|
-
},
|
|
103
|
-
name: table
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const _getTargetKeys = ({ target }) => entity_keys(target)
|
|
108
|
-
|
|
109
|
-
const DRAFT_COLUMNS_CASTED = [
|
|
110
|
-
{
|
|
111
|
-
ref: ['IsActiveEntity'],
|
|
112
|
-
cast: { type: 'cds.Boolean' }
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
ref: ['HasActiveEntity'],
|
|
116
|
-
cast: { type: 'cds.Boolean' }
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
ref: ['HasDraftEntity'],
|
|
120
|
-
cast: { type: 'cds.Boolean' }
|
|
121
|
-
}
|
|
122
|
-
]
|
|
123
|
-
|
|
124
|
-
const DRAFT_COLUMNS_CASTED_WITH_DRAFTADMIN_UUID = [
|
|
125
|
-
...DRAFT_COLUMNS_CASTED,
|
|
126
|
-
{ ref: ['DraftAdministrativeData_DraftUUID'] }
|
|
127
|
-
]
|
|
128
|
-
|
|
129
|
-
// default draft values for active entities
|
|
130
|
-
const _getDefaultDraftProperties = ({ hasDraft, isActive = true, withDraftUUID = true }) => {
|
|
131
|
-
const columns = [
|
|
132
|
-
{ val: isActive, as: 'IsActiveEntity', cast: { type: 'cds.Boolean' } },
|
|
133
|
-
{ val: false, as: 'HasActiveEntity', cast: { type: 'cds.Boolean' } }
|
|
134
|
-
]
|
|
135
|
-
|
|
136
|
-
if (hasDraft !== null) {
|
|
137
|
-
columns.push({
|
|
138
|
-
val: Boolean(hasDraft),
|
|
139
|
-
as: 'HasDraftEntity',
|
|
140
|
-
cast: { type: 'cds.Boolean' }
|
|
141
|
-
})
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (withDraftUUID) {
|
|
145
|
-
columns.push(
|
|
146
|
-
isActive
|
|
147
|
-
? { val: null, as: 'DraftAdministrativeData_DraftUUID' }
|
|
148
|
-
: { ref: ['DraftAdministrativeData_DraftUUID'], as: 'DraftAdministrativeData_DraftUUID' }
|
|
149
|
-
)
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return columns
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// draft values for active entities with calculated hasDraft property
|
|
156
|
-
const _getDraftPropertiesDetermineDraft = (req, where, tableName, calcDraftUUID = false) => {
|
|
157
|
-
const { table } = _getTableName(req, true)
|
|
158
|
-
tableName = tableName || table
|
|
159
|
-
|
|
160
|
-
const hasDraftQuery = SELECT.from(tableName, [{ val: 1 }])
|
|
161
|
-
if (where && where.length > 0) {
|
|
162
|
-
// clone where to protect from later modification
|
|
163
|
-
hasDraftQuery.where([...where])
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
let draftUUIDColumn
|
|
167
|
-
if (calcDraftUUID) {
|
|
168
|
-
draftUUIDColumn = SELECT.from(tableName, ['DraftAdministrativeData_DraftUUID'])
|
|
169
|
-
if (where && where.length > 0) {
|
|
170
|
-
draftUUIDColumn.where(where)
|
|
171
|
-
}
|
|
172
|
-
} else {
|
|
173
|
-
draftUUIDColumn = { val: null, as: 'DraftAdministrativeData_DraftUUID' }
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const xpr = {
|
|
177
|
-
xpr: ['case', 'when', hasDraftQuery, 'IS NOT NULL', 'then', 'true', 'else', 'false', 'end'],
|
|
178
|
-
as: 'HasDraftEntity',
|
|
179
|
-
cast: { type: 'cds.Boolean' }
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
hasDraftQuery.as = 'HasDraftEntity'
|
|
183
|
-
hasDraftQuery.cast = { type: 'cds.Boolean' }
|
|
184
|
-
|
|
185
|
-
return [
|
|
186
|
-
{ val: true, as: 'IsActiveEntity', cast: { type: 'cds.Boolean' } },
|
|
187
|
-
{ val: false, as: 'HasActiveEntity', cast: { type: 'cds.Boolean' } },
|
|
188
|
-
xpr,
|
|
189
|
-
draftUUIDColumn
|
|
190
|
-
]
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function _copyCQNPartial(partial) {
|
|
194
|
-
if (partial.SELECT) {
|
|
195
|
-
const newPartial = Object.assign({}, partial)
|
|
196
|
-
const newSELECT = Object.assign({}, partial.SELECT)
|
|
197
|
-
newSELECT.from = _copyCQNPartial(partial.SELECT.from)
|
|
198
|
-
newPartial.SELECT = newSELECT
|
|
199
|
-
if (partial.SELECT._4odata) newSELECT._4odata = true
|
|
200
|
-
if (partial.SELECT.columns) newPartial.SELECT.columns = _copyArray(partial.SELECT.columns)
|
|
201
|
-
if (partial.SELECT.where) newPartial.SELECT.where = _copyArray(partial.SELECT.where)
|
|
202
|
-
return newPartial
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (partial.id) {
|
|
206
|
-
const res = Object.assign({}, partial)
|
|
207
|
-
if (res.where) res.where = _copyArray(res.where)
|
|
208
|
-
return res
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (partial.ref) {
|
|
212
|
-
return Object.assign({}, partial, { ref: _copyArray(partial.ref) })
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return Object.assign({}, partial)
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function _copyArray(array) {
|
|
219
|
-
return array.map(entry => (typeof entry === 'object' && !(entry instanceof String) ? _copyCQNPartial(entry) : entry))
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const _isValidDraftOfWhichIAmOwner = isActiveEntity => isActiveEntity.op === '=' && _isFalse(isActiveEntity.value.val)
|
|
223
|
-
|
|
224
|
-
const _isValidActiveWithoutDraft = (isActiveEntity, hasDraftEntity) => {
|
|
225
|
-
return (
|
|
226
|
-
isActiveEntity.op === '=' &&
|
|
227
|
-
_isTrue(isActiveEntity.value.val) &&
|
|
228
|
-
hasDraftEntity.op === '=' &&
|
|
229
|
-
_isFalse(hasDraftEntity.value.val)
|
|
230
|
-
)
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const _isValidWithDraftLocked = (isActiveEntity, siblingIsActive, draftInProcessByUser) => {
|
|
234
|
-
return (
|
|
235
|
-
isActiveEntity.op === '=' &&
|
|
236
|
-
_isTrue(isActiveEntity.value.val) &&
|
|
237
|
-
siblingIsActive.op === '=' &&
|
|
238
|
-
siblingIsActive.value.val === null &&
|
|
239
|
-
draftInProcessByUser.op === '!=' &&
|
|
240
|
-
draftInProcessByUser.value.val === ''
|
|
241
|
-
)
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const _isValidWithDraftTimeout = (isActiveEntity, siblingIsActive, draftInProcessByUser) => {
|
|
245
|
-
return (
|
|
246
|
-
isActiveEntity.op === '=' &&
|
|
247
|
-
_isTrue(isActiveEntity.value.val) &&
|
|
248
|
-
siblingIsActive.op === '=' &&
|
|
249
|
-
siblingIsActive.value.val === null &&
|
|
250
|
-
draftInProcessByUser.op === '=' &&
|
|
251
|
-
draftInProcessByUser.value.val === ''
|
|
252
|
-
)
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const _isValidExcludeActiveDraftExists = (isActiveEntity, siblingIsActive) => {
|
|
256
|
-
return (
|
|
257
|
-
isActiveEntity.op === '=' &&
|
|
258
|
-
_isFalse(isActiveEntity.value.val) &&
|
|
259
|
-
siblingIsActive.op === '=' &&
|
|
260
|
-
siblingIsActive.value.val === null
|
|
261
|
-
)
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const _filterDraftColumnsBySelected = (draftColumns, columns) => {
|
|
265
|
-
const _findByAlias = (draftColumn, alias) => alias && draftColumn.as && alias === draftColumn.as
|
|
266
|
-
const _findByRef = (draftColumn, ref) => ref && draftColumn.ref && ref === draftColumn.ref[draftColumn.ref.length - 1]
|
|
267
|
-
// include draft-specific columns only if there is no SELECT.columns or if they are selected explicitly
|
|
268
|
-
return (
|
|
269
|
-
(!columns && draftColumns) ||
|
|
270
|
-
draftColumns.filter(
|
|
271
|
-
draftColumn =>
|
|
272
|
-
(!draftColumn.ref && !draftColumn.as) ||
|
|
273
|
-
columns.find(col => {
|
|
274
|
-
const ref = col.ref && col.ref[col.ref.length - 1]
|
|
275
|
-
return _findByRef(draftColumn, ref) || _findByAlias(draftColumn, ref) || _findByAlias(draftColumn, col.as)
|
|
276
|
-
})
|
|
277
|
-
)
|
|
278
|
-
)
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const _isOnlyCount = columns => columns.length === 1 && (columns[0].as === '_counted_' || columns[0].as === '$count')
|
|
282
|
-
|
|
283
|
-
const _getOuterMostColumns = (columnsFromRequest, additionalDraftColumns) => {
|
|
284
|
-
if (_isOnlyCount(columnsFromRequest)) return columnsFromRequest
|
|
285
|
-
|
|
286
|
-
// remove draft columns from columnsFromRequest (if present) to avoid duplicates
|
|
287
|
-
const columns = [...columnsFromRequest.filter(ele => !ele.as || !DRAFT_COLUMNS_MAP[ele.as])]
|
|
288
|
-
columns.push(...additionalDraftColumns)
|
|
289
|
-
return columns
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// adds base columns 'InProcessByUser' and 'CreatedByUser' to columns param if needed
|
|
293
|
-
// those are required for calculating 'DraftIsProcessedByMe' and 'DraftIsCreatedByMe'
|
|
294
|
-
const _ensureDraftAdminColumnsForCalculation = columns => {
|
|
295
|
-
columns.forEach(c => {
|
|
296
|
-
if (c.ref && c.ref[0] === 'DraftIsCreatedByMe' && !columns.find(e => e.ref && e.ref[0] === 'CreatedByUser')) {
|
|
297
|
-
columns.push({ ref: ['CreatedByUser'] })
|
|
298
|
-
} else if (
|
|
299
|
-
c.ref &&
|
|
300
|
-
c.ref[0] === 'DraftIsProcessedByMe' &&
|
|
301
|
-
!columns.find(e => e.ref && e.ref[0] === 'InProcessByUser')
|
|
302
|
-
) {
|
|
303
|
-
columns.push({ ref: ['InProcessByUser'] })
|
|
304
|
-
}
|
|
305
|
-
})
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const _draftAdminTable = req => {
|
|
309
|
-
const { table } = _getTableName(req)
|
|
310
|
-
|
|
311
|
-
let cqn = SELECT.from(table)
|
|
312
|
-
if (req.query.SELECT.columns) {
|
|
313
|
-
cqn = cqn.columns(...req.query.SELECT.columns)
|
|
314
|
-
_ensureDraftAdminColumnsForCalculation(cqn.SELECT.columns)
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
return {
|
|
318
|
-
cqn: getEnrichedCQN(cqn, req.query.SELECT, req.query.SELECT.where),
|
|
319
|
-
scenario: SCENARIO.DRAFT_ADMIN
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
const _allInactive = (req, columns) => {
|
|
324
|
-
const table = {
|
|
325
|
-
ref: [ensureDraftsSuffix(req.query.SELECT.from.ref[0])],
|
|
326
|
-
as: req.query.SELECT.from.as || 'drafts'
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
const outerMostColumns = _getOuterMostColumns(
|
|
330
|
-
addColumnAlias(columns, table.as),
|
|
331
|
-
_getDefaultDraftProperties({ hasDraft: false, isActive: false, withDraftUUID: false })
|
|
332
|
-
)
|
|
333
|
-
|
|
334
|
-
const isCount = columns.some(element => element.func === 'count')
|
|
335
|
-
|
|
336
|
-
// ensure only own drafts are read
|
|
337
|
-
const cqn = SELECT.from(table)
|
|
338
|
-
.join('DRAFT.DraftAdministrativeData', 'filterAdmin')
|
|
339
|
-
.on([{ ref: [table.as, 'DraftAdministrativeData_DraftUUID'] }, '=', { ref: ['filterAdmin', 'DraftUUID'] }])
|
|
340
|
-
.where(_inProcessByUserWhere(req.user.id))
|
|
341
|
-
|
|
342
|
-
if (isCount) {
|
|
343
|
-
cqn.columns(...outerMostColumns)
|
|
344
|
-
} else {
|
|
345
|
-
cqn.columns(...outerMostColumns.filter(o => o.as !== 'HasActiveEntity'), { ref: ['HasActiveEntity'] })
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
cqn.where(req.query.SELECT.where)
|
|
349
|
-
return { cqn: getEnrichedCQN(cqn, req.query.SELECT, []), scenario: SCENARIO.ALL_INACTIVE }
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const _setRefAlias = (ref, as) => {
|
|
353
|
-
if (ref && ref[0] !== as) {
|
|
354
|
-
ref.unshift(as)
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
const _buildWhere = (where, table) => {
|
|
359
|
-
for (const entry of where) {
|
|
360
|
-
if (entry.ref) {
|
|
361
|
-
_setRefAlias(entry.ref, table.as)
|
|
362
|
-
} else if (entry.func && entry.args) {
|
|
363
|
-
_buildWhere(entry.args, table)
|
|
364
|
-
} else if (entry.list) {
|
|
365
|
-
_buildWhere(entry.list, table)
|
|
366
|
-
} else if (entry.xpr) {
|
|
367
|
-
_buildWhere(entry.xpr, table)
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
const _allActive = (req, columns) => {
|
|
373
|
-
const { table } = _getTableName(req)
|
|
374
|
-
if (!table.as) {
|
|
375
|
-
table.as = 'active'
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
const outerMostColumns = _getOuterMostColumns(
|
|
379
|
-
addColumnAlias(columns, table.as),
|
|
380
|
-
_getDefaultDraftProperties({ hasDraft: null })
|
|
381
|
-
)
|
|
382
|
-
|
|
383
|
-
const ids = entity_keys(req.target)
|
|
384
|
-
const isCount = columns.some(element => element.func === 'count')
|
|
385
|
-
const xpr = {
|
|
386
|
-
xpr: [
|
|
387
|
-
'case',
|
|
388
|
-
'when',
|
|
389
|
-
'drafts.DraftAdministrativeData_DraftUUID',
|
|
390
|
-
'IS NOT NULL',
|
|
391
|
-
'then',
|
|
392
|
-
'true',
|
|
393
|
-
'else',
|
|
394
|
-
'false',
|
|
395
|
-
'end'
|
|
396
|
-
],
|
|
397
|
-
as: 'HasDraftEntity',
|
|
398
|
-
cast: { type: 'cds.Boolean' }
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
const cqn = SELECT.from(table)
|
|
402
|
-
|
|
403
|
-
if (isCount) {
|
|
404
|
-
cqn.columns(..._filterDraftColumnsBySelected(outerMostColumns, req.query.SELECT.columns))
|
|
405
|
-
} else {
|
|
406
|
-
cqn.columns(..._filterDraftColumnsBySelected([...outerMostColumns, xpr], req.query.SELECT.columns))
|
|
407
|
-
cqn.leftJoin(ensureDraftsSuffix(table.ref[0]) + ' as drafts').on(`${table.as}.${ids[0]} = drafts.${ids[0]}`)
|
|
408
|
-
|
|
409
|
-
for (let i = 1; i < ids.length; i++) {
|
|
410
|
-
// this 'and' belongs to the join condition and is not a where and
|
|
411
|
-
cqn.and({ ref: [table.as, ids[i]] }, '=', { ref: ['drafts', ids[i]] })
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
const scenarioAlias = 'active'
|
|
416
|
-
req.query.SELECT.where = _getWhereWithAppendedDraftRestrictions(req.query.SELECT.where, req)
|
|
417
|
-
|
|
418
|
-
if (req.query.SELECT.where) {
|
|
419
|
-
_buildWhere(req.query.SELECT.where, table)
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
return {
|
|
423
|
-
cqn: getEnrichedCQN(cqn, req.query.SELECT, req.query.SELECT.where, scenarioAlias),
|
|
424
|
-
scenario: SCENARIO.ALL_ACTIVE
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
const _activeWithoutDraft = (req, draftWhere, columns) => {
|
|
429
|
-
const { table } = _getTableName(req, true)
|
|
430
|
-
const draftName = table.ref[0]
|
|
431
|
-
const active = _getTableName(req)
|
|
432
|
-
const keys = _getTargetKeys(req)
|
|
433
|
-
|
|
434
|
-
let subSelect = SELECT.from(draftName).columns(...keys)
|
|
435
|
-
subSelect = keys.reduce(
|
|
436
|
-
(select, key) =>
|
|
437
|
-
subSelect.where([
|
|
438
|
-
{ ref: [active.name, key] },
|
|
439
|
-
'=',
|
|
440
|
-
{
|
|
441
|
-
ref: [draftName, key]
|
|
442
|
-
}
|
|
443
|
-
]),
|
|
444
|
-
subSelect
|
|
445
|
-
)
|
|
446
|
-
|
|
447
|
-
const outerMostColumns = _getOuterMostColumns(columns, _getDefaultDraftProperties({ hasDraft: false }))
|
|
448
|
-
const cqn = SELECT.from(active.table)
|
|
449
|
-
.columns(...outerMostColumns)
|
|
450
|
-
.where(['not exists', subSelect])
|
|
451
|
-
|
|
452
|
-
draftWhere = _getWhereWithAppendedDraftRestrictions(draftWhere, req)
|
|
453
|
-
return { cqn: getEnrichedCQN(cqn, req.query.SELECT, draftWhere), scenario: SCENARIO.ACTIVE_WITHOUT_DRAFT }
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
const _draftOfWhichIAmOwner = (req, draftWhere, columns) => {
|
|
457
|
-
const { table, name } = _getTableName(req, true)
|
|
458
|
-
const outerMostColumns = _getOuterMostColumns(
|
|
459
|
-
addColumnAlias(columns, name),
|
|
460
|
-
DRAFT_COLUMNS_CASTED_WITH_DRAFTADMIN_UUID
|
|
461
|
-
)
|
|
462
|
-
|
|
463
|
-
const cqn = SELECT.from(table)
|
|
464
|
-
.columns(...outerMostColumns)
|
|
465
|
-
.join('DRAFT.DraftAdministrativeData', 'filterAdmin')
|
|
466
|
-
.on([{ ref: [name, 'DraftAdministrativeData_DraftUUID'] }, '=', { ref: ['filterAdmin', 'DraftUUID'] }])
|
|
467
|
-
.where(_inProcessByUserWhere(req.user.id))
|
|
468
|
-
|
|
469
|
-
return { cqn: getEnrichedCQN(cqn, req.query.SELECT, draftWhere), scenario: SCENARIO.DRAFT_WHICH_OWNER }
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
const _activeWithDraftInProcess = (req, draftWhere, columns, isLocked) => {
|
|
473
|
-
const draft = _getTableName(req, true)
|
|
474
|
-
const draftName = draft.table.ref[0]
|
|
475
|
-
const active = _getTableName(req)
|
|
476
|
-
const keys = _getTargetKeys(req)
|
|
477
|
-
const draftColumns = _getDefaultDraftProperties({ hasDraft: true })
|
|
478
|
-
|
|
479
|
-
let subSelect = SELECT.from(draftName)
|
|
480
|
-
.columns(...keys)
|
|
481
|
-
.join('DRAFT.DraftAdministrativeData', 'filterAdmin')
|
|
482
|
-
.on([{ ref: [draftName, 'DraftAdministrativeData_DraftUUID'] }, '=', { ref: ['filterAdmin', 'DraftUUID'] }])
|
|
483
|
-
|
|
484
|
-
const DRAFT_CANCEL_TIMEOUT_IN_SEC = ((cds.env.drafts && cds.env.drafts.cancellationTimeout) || 15) * 60
|
|
485
|
-
|
|
486
|
-
subSelect = subSelect.where([
|
|
487
|
-
{ ref: ['filterAdmin', 'InProcessByUser'] },
|
|
488
|
-
'!=',
|
|
489
|
-
{ val: req.user.id },
|
|
490
|
-
'and',
|
|
491
|
-
{ ref: ['filterAdmin', 'InProcessByUser'] },
|
|
492
|
-
'is not null',
|
|
493
|
-
'and',
|
|
494
|
-
{ func: 'seconds_between', args: [{ ref: ['filterAdmin', 'LastChangeDateTime'] }, 'CURRENT_TIMESTAMP'] },
|
|
495
|
-
isLocked ? '<' : '>',
|
|
496
|
-
{ val: DRAFT_CANCEL_TIMEOUT_IN_SEC }
|
|
497
|
-
])
|
|
498
|
-
|
|
499
|
-
subSelect = keys.reduce(
|
|
500
|
-
(_select, key) => subSelect.where([{ ref: [active.name, key] }, '=', { ref: [draftName, key] }]),
|
|
501
|
-
subSelect
|
|
502
|
-
)
|
|
503
|
-
|
|
504
|
-
const outerMostColumns = _getOuterMostColumns(columns, draftColumns)
|
|
505
|
-
const cqn = SELECT.from(active.table).columns(outerMostColumns)
|
|
506
|
-
cqn.where(_getWhereWithAppendedDraftRestrictions([], req))
|
|
507
|
-
cqn.where(['exists', subSelect])
|
|
508
|
-
return { cqn: getEnrichedCQN(cqn, req.query.SELECT, draftWhere), scenario: SCENARIO.DRAFT_IN_PROCESS }
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
const _alignAliasForUnion = (table, as, select) => {
|
|
512
|
-
if (!as || !select.SELECT.where) {
|
|
513
|
-
return select
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
for (const entry of select.SELECT.where) {
|
|
517
|
-
if (entry.ref && entry.ref[0] === table) {
|
|
518
|
-
entry.ref[0] = as
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
return select
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
const isTargetRef = (el, targetAlias) => targetAlias && el.ref && el.ref.length > 1 && el.ref[0] === targetAlias
|
|
526
|
-
|
|
527
|
-
const _joinFromWhere = (where, parentAlias, targetAlias) => {
|
|
528
|
-
return where.reduce((links, el, idx, where) => {
|
|
529
|
-
if (el.xpr) {
|
|
530
|
-
const result = _joinFromWhere(el.xpr, parentAlias, targetAlias)
|
|
531
|
-
|
|
532
|
-
if (result.length) {
|
|
533
|
-
if (links.length) links.push('and')
|
|
534
|
-
links.push(...result)
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
return links
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
if (el.ref && el.ref[0] === parentAlias && el.ref[el.ref.length - 1] !== 'IsActiveEntity') {
|
|
541
|
-
if (where[idx - 1] && where[idx - 1] === '=' && isTargetRef(where[idx - 2], targetAlias)) {
|
|
542
|
-
if (links.length) links.push('and')
|
|
543
|
-
links.push(el, '=', where[idx - 2])
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
if (where[idx + 1] && where[idx + 1] === '=' && isTargetRef(where[idx + 2], targetAlias)) {
|
|
547
|
-
if (links.length) links.push('and')
|
|
548
|
-
links.push(el, '=', where[idx + 2])
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
return links
|
|
553
|
-
}, [])
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
const _findJoinInQuery = (query, parentAlias) => {
|
|
557
|
-
const targetAlias = query.SELECT.from.as
|
|
558
|
-
if (query.SELECT && query.SELECT.where) return _joinFromWhere(query.SELECT.where, parentAlias, targetAlias)
|
|
559
|
-
return []
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
const _isDraftField = element => element.ref && element.ref.length > 1 && element.ref[0] === 'DraftAdministrativeData'
|
|
563
|
-
|
|
564
|
-
const _functionContainsDraftField = obj =>
|
|
565
|
-
typeof obj === 'object' &&
|
|
566
|
-
obj.func &&
|
|
567
|
-
obj.args.some(arg => {
|
|
568
|
-
return _isDraftField(arg) || _functionContainsDraftField(arg)
|
|
569
|
-
})
|
|
570
|
-
|
|
571
|
-
const _isLogicalFunction = (where, index) => {
|
|
572
|
-
// REVISIT
|
|
573
|
-
const borders = ['(', ')', 'and', 'or', undefined]
|
|
574
|
-
|
|
575
|
-
return borders.includes(where[index - 1]) && borders.includes(where[index + 1])
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
const _getWhereForActive = where => {
|
|
579
|
-
const activeWhere = []
|
|
580
|
-
for (let i = 0; i < where.length; i++) {
|
|
581
|
-
if (where[i].xpr) {
|
|
582
|
-
activeWhere.push({ xpr: _getWhereForActive(where[i].xpr) })
|
|
583
|
-
continue
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
if (_isDraftField(where[i])) {
|
|
587
|
-
activeWhere.push({ val: null })
|
|
588
|
-
} else if (_functionContainsDraftField(where[i])) {
|
|
589
|
-
if (_isLogicalFunction(where, i)) {
|
|
590
|
-
activeWhere.push({ val: 1 }, '=', { val: 2 })
|
|
591
|
-
} else {
|
|
592
|
-
activeWhere.push({ val: null })
|
|
593
|
-
}
|
|
594
|
-
} else {
|
|
595
|
-
activeWhere.push(where[i])
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
for (let i = 0; i < activeWhere.length; i++) {
|
|
600
|
-
if (
|
|
601
|
-
activeWhere[i].val === null &&
|
|
602
|
-
activeWhere[i + 1] === '=' &&
|
|
603
|
-
activeWhere[i + 2] &&
|
|
604
|
-
activeWhere[i + 2].val === null
|
|
605
|
-
) {
|
|
606
|
-
activeWhere[i] = { val: 1 }
|
|
607
|
-
activeWhere[i + 2] = { val: 1 }
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
return activeWhere
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
const _siblingEntity = (
|
|
615
|
-
{ query, target, nav, params },
|
|
616
|
-
columns,
|
|
617
|
-
model,
|
|
618
|
-
draftAdminAlias,
|
|
619
|
-
parentQuery,
|
|
620
|
-
siblingIndex,
|
|
621
|
-
req
|
|
622
|
-
) => {
|
|
623
|
-
const parentLinks = parentQuery ? _findJoinInQuery(query, parentQuery.SELECT.from.as) : []
|
|
624
|
-
const keys = (nav[siblingIndex + 1].where && (params[siblingIndex] || params[0])) || {}
|
|
625
|
-
const siblingQuery = query.SELECT.where[query.SELECT.where.indexOf('exists') + 1]
|
|
626
|
-
const onCond = _findJoinInQuery(siblingQuery, target.as)
|
|
627
|
-
const siblingAlias = siblingQuery.SELECT.from.as
|
|
628
|
-
const subScenario = _siblingSubScenario(nav, siblingIndex, siblingQuery, target, params, model, onCond, req)
|
|
629
|
-
const isSiblingDraft = subScenario
|
|
630
|
-
? subScenario.isSiblingActive || subScenario.scenario === 'ACTIVE' || subScenario.scenario === 'ALL_ACTIVE'
|
|
631
|
-
: keys.IsActiveEntity && keys.IsActiveEntity !== false
|
|
632
|
-
const { table } = _getTableName({ query, target }, isSiblingDraft)
|
|
633
|
-
const cqn = SELECT.from(table)
|
|
634
|
-
|
|
635
|
-
if (siblingIndex === 0) {
|
|
636
|
-
const columnCqnPartial = columns.map(col => {
|
|
637
|
-
if (col.val) {
|
|
638
|
-
return Object.assign({}, col)
|
|
639
|
-
}
|
|
640
|
-
const colName = col.ref ? col.ref[col.ref.length - 1] : col
|
|
641
|
-
const ref = col.ref ? [table.as, ...col.ref] : [table.as, colName]
|
|
642
|
-
return Object.assign({}, col, { ref })
|
|
643
|
-
})
|
|
644
|
-
columnCqnPartial.push({ ref: ['draftAdmin', 'InProcessByUser'], as: 'draftAdmin_inProcessByUser' })
|
|
645
|
-
cqn.columns(...columnCqnPartial)
|
|
646
|
-
} else {
|
|
647
|
-
cqn.columns([{ val: 1 }])
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
if (isSiblingDraft) {
|
|
651
|
-
cqn
|
|
652
|
-
.join(ensureNoDraftsSuffix(target.name), siblingAlias)
|
|
653
|
-
.on(onCond)
|
|
654
|
-
.join('DRAFT.DraftAdministrativeData', 'draftAdmin')
|
|
655
|
-
.on(`${table.as}.DraftAdministrativeData_DraftUUID = draftAdmin.DraftUUID`)
|
|
656
|
-
} else {
|
|
657
|
-
cqn
|
|
658
|
-
.join(ensureDraftsSuffix(target.name), siblingAlias)
|
|
659
|
-
.on(onCond)
|
|
660
|
-
.join('DRAFT.DraftAdministrativeData', 'draftAdmin')
|
|
661
|
-
.on(`${siblingAlias}.DraftAdministrativeData_DraftUUID = draftAdmin.DraftUUID`)
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
for (const key in keys) {
|
|
665
|
-
if (key !== 'IsActiveEntity') cqn.where([{ ref: [table.as, key] }, '=', { val: keys[key] }])
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
if (subScenario) {
|
|
669
|
-
cqn.where(['exists', subScenario.cqn])
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
// in DraftAdminData scenario parent is linked via join
|
|
673
|
-
if (draftAdminAlias) {
|
|
674
|
-
cqn.where([{ ref: [draftAdminAlias, 'DraftUUID'] }, '=', { ref: ['draftAdmin', 'DraftUUID'] }])
|
|
675
|
-
} else if (parentLinks.length) {
|
|
676
|
-
cqn.where({ xpr: [...parentLinks] })
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
return { cqn, scenario: SCENARIO.SIBLING_ENTITY, isSiblingActive: !isSiblingDraft }
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
function _siblingSubScenario(nav, siblingIndex, siblingQuery, target, params, model, onCond, req) {
|
|
683
|
-
if (nav[siblingIndex + 1].where) return
|
|
684
|
-
let subScenario
|
|
685
|
-
const subNav = nav.slice(siblingIndex + 1)
|
|
686
|
-
const subSiblingIndex = subNav.indexOf('SiblingEntity')
|
|
687
|
-
const subReq = {
|
|
688
|
-
query: siblingQuery,
|
|
689
|
-
target: model.definitions[target.name],
|
|
690
|
-
params: [...params].reverse(),
|
|
691
|
-
user: req.user
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
if (subSiblingIndex > -1) {
|
|
695
|
-
subScenario = _getSiblingScenario(subReq, [{ val: 1 }], model, subSiblingIndex, subNav, params)
|
|
696
|
-
if (subSiblingIndex > 0) {
|
|
697
|
-
const subQuery = SELECT.from(siblingQuery.SELECT.from).columns([{ val: 1 }])
|
|
698
|
-
_mergeSiblingIntoCQN(subQuery, subScenario, subSiblingIndex - 1)
|
|
699
|
-
subQuery.where(onCond)
|
|
700
|
-
subScenario.cqn = subQuery
|
|
701
|
-
}
|
|
702
|
-
} else {
|
|
703
|
-
subReq.query = SELECT.from(siblingQuery.SELECT.from).columns([{ val: 1 }])
|
|
704
|
-
|
|
705
|
-
const existsIdx = siblingQuery.SELECT.where.indexOf('exists')
|
|
706
|
-
if (existsIdx > -1) subReq.query.where(siblingQuery.SELECT.where.slice(existsIdx, existsIdx + 2))
|
|
707
|
-
const subOrigFrom = { ref: [...subNav].reverse() }
|
|
708
|
-
subScenario = _generateCQN(subReq, [{ val: 1 }], subOrigFrom, model)
|
|
709
|
-
subScenario.cqn.where(onCond)
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
return subScenario
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
const _getSiblingScenario = (req, columns, model, siblingIndex, nav) => {
|
|
716
|
-
const draftAdminAlias = _isDraftAdminScenario(req) && req.query.SELECT.from.as
|
|
717
|
-
const params = [...req.params].reverse()
|
|
718
|
-
const _getSiblingQueryFromWhere = (query, queryIndex, parentQuery) => {
|
|
719
|
-
if (query.SELECT && query.SELECT.where && queryIndex > 0) {
|
|
720
|
-
for (let i = 0; i < query.SELECT.where.length; i++) {
|
|
721
|
-
if (query.SELECT.where[i] === 'exists' && queryIndex > 0) {
|
|
722
|
-
return _getSiblingQueryFromWhere(query.SELECT.where[i + 1], queryIndex - 1, query)
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
const target = { name: query.SELECT.from.ref[0].id || query.SELECT.from.ref[0], as: query.SELECT.from.as }
|
|
728
|
-
return _siblingEntity(
|
|
729
|
-
{ query, target, params, nav },
|
|
730
|
-
columns,
|
|
731
|
-
model,
|
|
732
|
-
draftAdminAlias,
|
|
733
|
-
parentQuery,
|
|
734
|
-
siblingIndex,
|
|
735
|
-
req
|
|
736
|
-
)
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
return _getSiblingQueryFromWhere(req.query, siblingIndex)
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
const _replaceWhereExists = (query, _siblingIndex, siblingCQN) => {
|
|
743
|
-
if (query.SELECT && query.SELECT.where) {
|
|
744
|
-
for (let i = 0; i < query.SELECT.where.length; i++) {
|
|
745
|
-
const whereElement = query.SELECT.where[i]
|
|
746
|
-
if (whereElement.xpr) {
|
|
747
|
-
const res = _replaceWhereExists({ SELECT: { where: whereElement.xpr } }, _siblingIndex, siblingCQN)
|
|
748
|
-
if (res) return res
|
|
749
|
-
continue
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
const indexExists = query.SELECT.where.indexOf('exists')
|
|
753
|
-
if (indexExists > -1) {
|
|
754
|
-
query.SELECT.where.splice(indexExists + 1, 1, siblingCQN)
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
const _mergeSiblingIntoCQN = (cqn, { cqn: siblingCQN }, siblingIndex) =>
|
|
761
|
-
_replaceWhereExists(cqn, siblingIndex, siblingCQN)
|
|
762
|
-
|
|
763
|
-
const _getDraftDoc = (req, draftName, draftWhere) => {
|
|
764
|
-
const refDraft = req.query.SELECT.from.as ? { ref: [draftName], as: req.query.SELECT.from.as } : draftName
|
|
765
|
-
|
|
766
|
-
const draftDocs = getEnrichedCQN(
|
|
767
|
-
SELECT.from(refDraft)
|
|
768
|
-
.join('DRAFT.DraftAdministrativeData', 'filterAdmin')
|
|
769
|
-
.on([
|
|
770
|
-
{ ref: [req.query.SELECT.from.as || draftName, 'DraftAdministrativeData_DraftUUID'] },
|
|
771
|
-
'=',
|
|
772
|
-
{ ref: ['filterAdmin', 'DraftUUID'] }
|
|
773
|
-
])
|
|
774
|
-
.where(_inProcessByUserWhere(req.user.id)),
|
|
775
|
-
req.query.SELECT,
|
|
776
|
-
draftWhere,
|
|
777
|
-
undefined,
|
|
778
|
-
false
|
|
779
|
-
)
|
|
780
|
-
|
|
781
|
-
return draftDocs
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
const _getOrderByEnrichedColumns = (orderBy, columns, entity) => {
|
|
785
|
-
const enrichedCol = []
|
|
786
|
-
|
|
787
|
-
if (orderBy && orderBy.length > 1) {
|
|
788
|
-
const colNames = columns.filter(el => el.ref).map(el => el.ref[el.ref.length - 1])
|
|
789
|
-
|
|
790
|
-
// REVISIT: GET Books?$select=title&$expand=NotBooks($select=pages)&$orderby=NotBooks/title - what's then?
|
|
791
|
-
for (const el of orderBy) {
|
|
792
|
-
// For associations we need to 'materialise' the resulting field, otherwise we cannot access it in an outer SELECT.
|
|
793
|
-
if (entity && entity.elements[el.ref[0]] && entity.elements[el.ref[0]].isAssociation) {
|
|
794
|
-
enrichedCol.push({ ref: [...el.ref], as: _poorMansAlias4(el) })
|
|
795
|
-
} else if (!(el.ref[el.ref.length - 1] in DRAFT_COLUMNS_MAP) && !colNames.includes(el.ref[el.ref.length - 1])) {
|
|
796
|
-
enrichedCol.push({ ref: [...el.ref] })
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
return enrichedCol
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
const _replaceDraftAlias = where => {
|
|
805
|
-
where.forEach(element => {
|
|
806
|
-
if (element.xpr) {
|
|
807
|
-
_replaceDraftAlias(element.xpr)
|
|
808
|
-
return
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
if (_isDraftField(element)) {
|
|
812
|
-
element.ref[0] = 'filterAdmin'
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
if (typeof element === 'object' && element.func) {
|
|
816
|
-
_replaceDraftAlias(element.args)
|
|
817
|
-
}
|
|
818
|
-
})
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
const _poorMansAlias4 = xpr => '_' + xpr.ref.join('_') + '_'
|
|
822
|
-
|
|
823
|
-
const _getUnionCQN = (req, draftName, columns, subSelect, draftWhere, model, entity) => {
|
|
824
|
-
const draftActiveWhere = _getWhereForActive(draftWhere)
|
|
825
|
-
const activeDocs = getEnrichedCQN(SELECT.from(req.target), req.query.SELECT, draftActiveWhere, undefined, false)
|
|
826
|
-
activeDocs.where(_getWhereWithAppendedDraftRestrictions([], req))
|
|
827
|
-
convertWhereExists(activeDocs.SELECT, model, {})
|
|
828
|
-
|
|
829
|
-
// @restrict.where not applicable for drafts (I can ALWAYS read mine)
|
|
830
|
-
_replaceDraftAlias(draftWhere)
|
|
831
|
-
const draftDocs = _getDraftDoc(req, draftName, draftWhere)
|
|
832
|
-
|
|
833
|
-
const union = SELECT.from({ SET: { op: 'union', all: true, args: [draftDocs, activeDocs] } })
|
|
834
|
-
if (req.query.SELECT.count) union.SELECT.count = true
|
|
835
|
-
if (req.query.SELECT.__countAggregated) union.SELECT.__countAggregated = true
|
|
836
|
-
|
|
837
|
-
if (req.query.SELECT.from.as) {
|
|
838
|
-
draftDocs.SELECT.from.as = req.query.SELECT.from.as
|
|
839
|
-
activeDocs.SELECT.from.as = req.query.SELECT.from.as
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
if (_isOnlyCount(columns)) {
|
|
843
|
-
draftDocs.columns(...columns)
|
|
844
|
-
activeDocs
|
|
845
|
-
.columns(...columns)
|
|
846
|
-
.where([
|
|
847
|
-
'not exists',
|
|
848
|
-
_alignAliasForUnion(ensureNoDraftsSuffix(req.target.name), req.query.SELECT.from.as, subSelect)
|
|
849
|
-
])
|
|
850
|
-
|
|
851
|
-
return union.columns({ func: 'sum', args: [{ ref: ['$count'] }], as: '$count' })
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
const enrichedColumns = _getOrderByEnrichedColumns(req.query.SELECT.orderBy, columns, entity)
|
|
855
|
-
|
|
856
|
-
for (const col of enrichedColumns) {
|
|
857
|
-
// if we have columns for outer order by that may also be needed for joins, we need to duplicate them
|
|
858
|
-
const element = getElementDeep(req.target, col.ref)
|
|
859
|
-
if (element && element._foreignKey4) {
|
|
860
|
-
columns.push({ ref: [...col.ref] })
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
col.as = _poorMansAlias4(col)
|
|
864
|
-
// add alias to outer order by
|
|
865
|
-
const ob = req.query.SELECT.orderBy.find(ele => _poorMansAlias4(ele) === col.as)
|
|
866
|
-
ob.ref = [col.as]
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
const draftColumns = [
|
|
870
|
-
...addColumnAlias([...columns, ...enrichedColumns], req.query.SELECT.from.as || draftName),
|
|
871
|
-
..._filterDraftColumnsBySelected(DRAFT_COLUMNS_CASTED, req.query.SELECT.columns),
|
|
872
|
-
'DraftAdministrativeData_DraftUUID'
|
|
873
|
-
]
|
|
874
|
-
draftDocs.columns(draftColumns)
|
|
875
|
-
|
|
876
|
-
const activeName = activeDocs.SELECT.from.as || (activeDocs.SELECT.from.ref && activeDocs.SELECT.from.ref[0])
|
|
877
|
-
const hasDraftWhere = []
|
|
878
|
-
const targetKeys = _getTargetKeys(req)
|
|
879
|
-
for (const key of targetKeys) {
|
|
880
|
-
// add 'and' token if not the first iteration
|
|
881
|
-
if (hasDraftWhere.length) hasDraftWhere.push('and')
|
|
882
|
-
hasDraftWhere.push({ ref: [activeName, key] }, '=', { ref: [draftName, key] })
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
const draftColumnsBySelected = _filterDraftColumnsBySelected(
|
|
886
|
-
_getDraftPropertiesDetermineDraft(req, hasDraftWhere, ensureDraftsSuffix(req.target.name), true),
|
|
887
|
-
req.query.SELECT.columns
|
|
888
|
-
)
|
|
889
|
-
|
|
890
|
-
const activeColumns = [...columns, ...enrichedColumns, ...draftColumnsBySelected]
|
|
891
|
-
activeDocs.columns(activeColumns)
|
|
892
|
-
|
|
893
|
-
const aliasForUnion = _alignAliasForUnion(ensureNoDraftsSuffix(req.target.name), req.query.SELECT.from.as, subSelect)
|
|
894
|
-
activeDocs.where(['not exists', aliasForUnion])
|
|
895
|
-
|
|
896
|
-
// groupBy, orderBy and limit do not support partial CQNs
|
|
897
|
-
if (req.query.SELECT.groupBy) {
|
|
898
|
-
union.SELECT.groupBy = req.query.SELECT.groupBy
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
if (req.query.SELECT.orderBy) {
|
|
902
|
-
union.SELECT.orderBy = req.query.SELECT.orderBy
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
if (req.query.SELECT.limit) {
|
|
906
|
-
union.SELECT.limit = req.query.SELECT.limit
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
return union
|
|
910
|
-
.columns(...columns.map(ref => (ref.as ? { ref: [ref.as], as: ref.as } : ref))) // needed for aliased stream property ref@odata.mediaContentType
|
|
911
|
-
.columns(..._filterDraftColumnsBySelected(DRAFT_COLUMNS_CASTED, req.query.SELECT.columns))
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
const _excludeActiveDraftExists = (req, columns, draftWhere, model) => {
|
|
915
|
-
const { table, name } = _getTableName(req, true)
|
|
916
|
-
const draftName = table.ref[0]
|
|
917
|
-
|
|
918
|
-
const subSelect = SELECT.from(draftName, [1])
|
|
919
|
-
.join('DRAFT.DraftAdministrativeData', 'filterAdmin')
|
|
920
|
-
.on([{ ref: [draftName, 'DraftAdministrativeData_DraftUUID'] }, '=', { ref: ['filterAdmin', 'DraftUUID'] }])
|
|
921
|
-
.where(_inProcessByUserWhere(req.user.id))
|
|
922
|
-
|
|
923
|
-
const targetName = ensureNoDraftsSuffix(req.target.name)
|
|
924
|
-
const targetKeys = _getTargetKeys(req)
|
|
925
|
-
for (const key of targetKeys) {
|
|
926
|
-
subSelect.where([{ ref: [targetName, key] }, '=', { ref: [draftName, key] }])
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
const entity = model.definitions[targetName]
|
|
930
|
-
draftWhere = removeIsActiveEntityRecursively(draftWhere)
|
|
931
|
-
const cqn = _getUnionCQN(req, draftName, columns, subSelect, draftWhere, model, entity)
|
|
932
|
-
cqn.SELECT.from.as = name
|
|
933
|
-
|
|
934
|
-
if (cqn.SELECT.orderBy) {
|
|
935
|
-
for (const entry of cqn.SELECT.orderBy || []) {
|
|
936
|
-
if (entry.ref.length > 1 && entry.ref[0] !== name) {
|
|
937
|
-
entry.ref[0] = name
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
return { cqn: cqn, scenario: SCENARIO.UNION }
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
const _readDraftParameters = where => {
|
|
946
|
-
const obj = {
|
|
947
|
-
isActiveEntity: readAndDeleteKeywords(['IsActiveEntity'], where),
|
|
948
|
-
hasDraftEntity: readAndDeleteKeywords(['HasDraftEntity'], where),
|
|
949
|
-
siblingIsActive: readAndDeleteKeywords(['SiblingEntity', 'IsActiveEntity'], where),
|
|
950
|
-
draftInProcessByUser: readAndDeleteKeywords(['DraftAdministrativeData', 'InProcessByUser'], where)
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
// remove "DraftAdministrativeData/InProcessByUser ne null" from request if necessary
|
|
954
|
-
readAndDeleteKeywords(['DraftAdministrativeData', 'InProcessByUser'], where)
|
|
955
|
-
|
|
956
|
-
return obj
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
const _validatedActiveWithoutDraft = (req, draftWhere, draftParameters, columns) =>
|
|
960
|
-
_isValidActiveWithoutDraft(draftParameters.isActiveEntity, draftParameters.hasDraftEntity) &&
|
|
961
|
-
_activeWithoutDraft(req, draftWhere, columns)
|
|
962
|
-
|
|
963
|
-
const _validatedWithSiblingInProcess = (req, draftWhere, draftParameters, columns, model) => {
|
|
964
|
-
const { isActiveEntity, siblingIsActive, draftInProcessByUser } = draftParameters
|
|
965
|
-
if (
|
|
966
|
-
!draftInProcessByUser &&
|
|
967
|
-
_isValidExcludeActiveDraftExists(draftParameters.isActiveEntity, draftParameters.siblingIsActive)
|
|
968
|
-
) {
|
|
969
|
-
return _excludeActiveDraftExists(req, columns, draftWhere, model)
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
if (
|
|
973
|
-
draftInProcessByUser &&
|
|
974
|
-
draftInProcessByUser.op === '!=' &&
|
|
975
|
-
_isValidWithDraftLocked(isActiveEntity, siblingIsActive, draftInProcessByUser)
|
|
976
|
-
) {
|
|
977
|
-
return _activeWithDraftInProcess(req, draftWhere, columns, req.user.id)
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
if (draftInProcessByUser && _isValidWithDraftTimeout(isActiveEntity, siblingIsActive, draftInProcessByUser)) {
|
|
981
|
-
return _activeWithDraftInProcess(req, draftWhere, columns, null)
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
const _validatedDraftOfWhichIAmOwner = (req, draftWhere, draftParameters, columns) =>
|
|
986
|
-
_isValidDraftOfWhichIAmOwner(draftParameters.isActiveEntity) && _draftOfWhichIAmOwner(req, draftWhere, columns)
|
|
987
|
-
|
|
988
|
-
const _draftInSubSelect = (where, req) => {
|
|
989
|
-
return where.some(({ SELECT, xpr }) => {
|
|
990
|
-
if (xpr) {
|
|
991
|
-
return _draftInSubSelect(xpr, req)
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
if (SELECT && SELECT.where) {
|
|
995
|
-
const isActiveEntity = readAndDeleteKeywords(['IsActiveEntity'], SELECT.where, false)
|
|
996
|
-
if (isActiveEntity) {
|
|
997
|
-
return _isFalse(isActiveEntity.value.val)
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
return _draftInSubSelect(SELECT.where, req)
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
return false
|
|
1004
|
-
})
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
const _isDraftAdminScenario = req =>
|
|
1008
|
-
req.target.query && req.target.query._target && req.target.query._target.name === 'DRAFT.DraftAdministrativeData'
|
|
1009
|
-
|
|
1010
|
-
const _generateCQN = (req, columns, from, model) => {
|
|
1011
|
-
const nav = [...from.ref].reverse() || []
|
|
1012
|
-
let siblingIndex = nav.indexOf('SiblingEntity')
|
|
1013
|
-
|
|
1014
|
-
// it can also be a property access (new parser), then we must shift it
|
|
1015
|
-
if (siblingIndex > 0 && req.target.elements[nav[0]] && !req.target.elements[nav[0]].isAssociation) {
|
|
1016
|
-
nav.shift()
|
|
1017
|
-
siblingIndex = siblingIndex - 1
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
let siblingScenario
|
|
1021
|
-
if (siblingIndex > -1) {
|
|
1022
|
-
siblingScenario = _getSiblingScenario(req, columns, model, siblingIndex, nav)
|
|
1023
|
-
if (siblingIndex === 0) return siblingScenario
|
|
1024
|
-
_mergeSiblingIntoCQN(req.query, siblingScenario, siblingIndex - 1)
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
if (_isDraftAdminScenario(req)) return _draftAdminTable(req)
|
|
1028
|
-
if (!req.query.SELECT.where) return _allActive(req, columns)
|
|
1029
|
-
|
|
1030
|
-
// REVISIT this function does not only read, but modifies where!
|
|
1031
|
-
const draftParameters = _readDraftParameters(req.query.SELECT.where)
|
|
1032
|
-
|
|
1033
|
-
if (
|
|
1034
|
-
draftParameters.isActiveEntity &&
|
|
1035
|
-
_isTrue(draftParameters.isActiveEntity.value.val) &&
|
|
1036
|
-
!(draftParameters.siblingIsActive && draftParameters.siblingIsActive.value.val === null) &&
|
|
1037
|
-
!draftParameters.hasDraftEntity
|
|
1038
|
-
) {
|
|
1039
|
-
return _allActive(req, columns)
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
if (!draftParameters.isActiveEntity) {
|
|
1043
|
-
if (_draftInSubSelect(req.query.SELECT.where, req) || (siblingScenario && !siblingScenario.isSiblingActive)) {
|
|
1044
|
-
// this is only the case when navigating into tree
|
|
1045
|
-
return _allInactive(req, columns)
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
return _allActive(req, columns)
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
if (draftParameters.hasDraftEntity) {
|
|
1052
|
-
return _validatedActiveWithoutDraft(req, req.query.SELECT.where, draftParameters, columns)
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
if (draftParameters.siblingIsActive) {
|
|
1056
|
-
return _validatedWithSiblingInProcess(req, req.query.SELECT.where, draftParameters, columns, model)
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
return _validatedDraftOfWhichIAmOwner(req, req.query.SELECT.where, draftParameters, columns)
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
const _isIsActiveEntity = element => element.ref && element.ref[element.ref.length - 1] === 'IsActiveEntity'
|
|
1063
|
-
|
|
1064
|
-
const _adaptSubSelects = ({ SELECT: { from, where } }, scenario) => {
|
|
1065
|
-
if (!where) return
|
|
1066
|
-
|
|
1067
|
-
if (scenario === 'ALL_INACTIVE') {
|
|
1068
|
-
replaceRefWithDraft(from.ref)
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
for (let i = 0; i < where.length; i++) {
|
|
1072
|
-
const element = where[i]
|
|
1073
|
-
|
|
1074
|
-
if (_isIsActiveEntity(element) && where.length > i + 2) {
|
|
1075
|
-
if (
|
|
1076
|
-
(scenario !== 'ALL_INACTIVE' && _isFalse(where[i + 2].val)) ||
|
|
1077
|
-
(scenario === SCENARIO.DRAFT_ADMIN && !_isFalse(where[i + 2].val))
|
|
1078
|
-
) {
|
|
1079
|
-
replaceRefWithDraft(from.ref)
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
if (!_isIsActiveEntity(where[i + 2])) {
|
|
1083
|
-
i = deleteCondition(i, where) - 1
|
|
1084
|
-
} else {
|
|
1085
|
-
i = i + 3 < where.length ? i + 2 : i + 3
|
|
1086
|
-
}
|
|
1087
|
-
} else if (element.SELECT) {
|
|
1088
|
-
_adaptSubSelects(element, scenario)
|
|
1089
|
-
} else if (element.xpr) {
|
|
1090
|
-
for (const ele of element.xpr.filter(e => e.SELECT)) {
|
|
1091
|
-
_adaptSubSelects(ele, scenario)
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
const _calculateDraftAdminColumns = (result, user, deleteLastChangeDateTime) => {
|
|
1098
|
-
if (!result) return
|
|
1099
|
-
if ('InProcessByUser' in result && !draftIsLocked(result.LastChangeDateTime)) result.InProcessByUser = ''
|
|
1100
|
-
if (deleteLastChangeDateTime) delete result.LastChangeDateTime
|
|
1101
|
-
if ('DraftIsCreatedByMe' in result && 'CreatedByUser' in result)
|
|
1102
|
-
result.DraftIsCreatedByMe = result.CreatedByUser === user
|
|
1103
|
-
if ('DraftIsProcessedByMe' in result && 'InProcessByUser' in result)
|
|
1104
|
-
result.DraftIsProcessedByMe = result.InProcessByUser === user
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
const _adaptDraftColumnsForSiblingEntity = (result, isSiblingActive) => {
|
|
1108
|
-
result.IsActiveEntity = isSiblingActive
|
|
1109
|
-
result.HasDraftEntity = isSiblingActive
|
|
1110
|
-
result.HasActiveEntity = !isSiblingActive
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
const _collectAliases = (from, aliases) => {
|
|
1114
|
-
if (!from) return
|
|
1115
|
-
if (from.ref && from.as) {
|
|
1116
|
-
// Actually table names in where annotations should be provided with '.' separator.
|
|
1117
|
-
// Normalization to '_' is done for the exceptional case if '_' is still used (based on db table names).
|
|
1118
|
-
aliases.set(from.ref[0].replace(/\./g, '_'), from.as)
|
|
1119
|
-
} else if (from.args) {
|
|
1120
|
-
from.args.forEach(arg => {
|
|
1121
|
-
_collectAliases(arg, aliases)
|
|
1122
|
-
})
|
|
1123
|
-
} else if (from.SET && from.SET.args) {
|
|
1124
|
-
from.SET.args.forEach(arg => {
|
|
1125
|
-
_collectAliases(arg, aliases)
|
|
1126
|
-
})
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
const _adaptAnnotationAliases = cqn => {
|
|
1131
|
-
const aliases = new Map()
|
|
1132
|
-
_collectAliases(cqn.SELECT.from, aliases)
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
const enhanceQueryForTimeoutIfNeeded = (scenario, columns = []) => {
|
|
1136
|
-
if (scenario !== SCENARIO.DRAFT_ADMIN) {
|
|
1137
|
-
const draftAdmin = columns.find(col => col.ref && col.ref[col.ref.length - 1] === 'DraftAdministrativeData')
|
|
1138
|
-
columns = (draftAdmin && draftAdmin.expand) || []
|
|
1139
|
-
}
|
|
1140
|
-
const inProcessByUser = columns.find(col => col.ref && col.ref[col.ref.length - 1] === 'InProcessByUser')
|
|
1141
|
-
const lastChangeDateTime = columns.find(col => col.ref && col.ref[col.ref.length - 1] === 'LastChangeDateTime')
|
|
1142
|
-
if (inProcessByUser && !lastChangeDateTime) {
|
|
1143
|
-
columns.push({ ref: [...inProcessByUser.ref.slice(0, inProcessByUser.ref.length - 1), 'LastChangeDateTime'] })
|
|
1144
|
-
return true
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
// REVISIT: HACK for sqlite support, union not yet properly supported in before handler on db
|
|
1149
|
-
// remove once union is removed, should be part of before handler
|
|
1150
|
-
const _getLocalizedEntity = (model, target, user) => {
|
|
1151
|
-
const prefix = 'localized'
|
|
1152
|
-
let localizedEntity
|
|
1153
|
-
/*
|
|
1154
|
-
* REVISIT: in case of not sqlite, model.definitions[`${prefix}.${user.locale}.${target.name}`] is undefined
|
|
1155
|
-
* and the fallback lookup model.definitions[`${prefix}.${target.name}`] gets the entity -> bad coding
|
|
1156
|
-
*/
|
|
1157
|
-
if (cds.env.i18n.for_sqlite.includes(user.locale)) {
|
|
1158
|
-
localizedEntity = model.definitions[`${prefix}.${user.locale}.${target.name}`]
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
return localizedEntity || model.definitions[`${prefix}.${target.name}`]
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
const _getLastSubQuery = query => (query.SELECT.from.SELECT ? _getLastSubQuery(query.SELECT.from) : query)
|
|
1165
|
-
const _setLastSubQuery = (query, last, prev = query) => {
|
|
1166
|
-
if (query.SELECT.from.SELECT) return _setLastSubQuery(query.SELECT.from, last, query)
|
|
1167
|
-
else prev.SELECT.from = _copyCQNPartial(last)
|
|
1168
|
-
return prev
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
const _adaptDraftAdminExpand = cqn => {
|
|
1172
|
-
const draftAdminExpand =
|
|
1173
|
-
cqn.SELECT.columns && cqn.SELECT.columns.find(c => c.expand && c.ref[0] === 'DraftAdministrativeData')
|
|
1174
|
-
|
|
1175
|
-
if (draftAdminExpand) {
|
|
1176
|
-
_ensureDraftAdminColumnsForCalculation(draftAdminExpand.expand)
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
const _getOriginalColumns = req => {
|
|
1181
|
-
const originalColumns = {}
|
|
1182
|
-
// expanded columns are handled generically in db
|
|
1183
|
-
for (const c of req.query.SELECT.columns) {
|
|
1184
|
-
originalColumns[c.as || (c.ref && c.ref[c.ref.length - 1]) || c] = true
|
|
1185
|
-
}
|
|
1186
|
-
|
|
1187
|
-
return originalColumns
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
// REVISIT: remove after stream_compat is removed
|
|
1191
|
-
const _handlerStreaming = async (req, query) => {
|
|
1192
|
-
adaptStreamCQN(query)
|
|
1193
|
-
query._streaming = true
|
|
1194
|
-
const result = await cds.tx(req).run(query)
|
|
1195
|
-
return result
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
const _postProcess = (result, req, cqnScenario, deleteLastChangeDateTime) => {
|
|
1199
|
-
const resultAsArray = Array.isArray(result) ? result : result ? [result] : []
|
|
1200
|
-
if (!result || !resultAsArray.length) return result
|
|
1201
|
-
|
|
1202
|
-
if (cqnScenario.scenario === SCENARIO.SIBLING_ENTITY) {
|
|
1203
|
-
if (resultAsArray[0].draftAdmin_inProcessByUser !== req.user.id) return []
|
|
1204
|
-
delete resultAsArray[0].draftAdmin_inProcessByUser
|
|
1205
|
-
_adaptDraftColumnsForSiblingEntity(resultAsArray[0], cqnScenario.isSiblingActive)
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
const removeDraftUUIDIfNecessaryFn = removeDraftUUIDIfNecessary(req)
|
|
1209
|
-
let notRequestedColumns
|
|
1210
|
-
if (!req.query.SELECT._4odata) {
|
|
1211
|
-
const originalColumns = _getOriginalColumns(req)
|
|
1212
|
-
notRequestedColumns = originalColumns && Object.keys(resultAsArray[0]).filter(key => !originalColumns[key])
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
if (cqnScenario.scenario === SCENARIO.DRAFT_ADMIN) {
|
|
1216
|
-
_calculateDraftAdminColumns(resultAsArray[0], req.user.id, deleteLastChangeDateTime)
|
|
1217
|
-
if (notRequestedColumns) {
|
|
1218
|
-
for (const key of notRequestedColumns) delete resultAsArray[0][key]
|
|
1219
|
-
}
|
|
1220
|
-
} else {
|
|
1221
|
-
for (const row of resultAsArray) {
|
|
1222
|
-
removeDraftUUIDIfNecessaryFn(row)
|
|
1223
|
-
_calculateDraftAdminColumns(row.DraftAdministrativeData, req.user.id, deleteLastChangeDateTime)
|
|
1224
|
-
if (notRequestedColumns) {
|
|
1225
|
-
for (const key of notRequestedColumns) delete row[key]
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
if (result.HasActiveEntity === null) result.HasActiveEntity = false
|
|
1231
|
-
if (result.HasDraftEntity === null) result.HasDraftEntity = false
|
|
1232
|
-
if (result.IsActiveEntity === null) result.IsActiveEntity = false
|
|
1233
|
-
return result
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
|
-
const _fnCompare = elName => c => (c.as && c.as === elName) || (c.ref && c.ref[c.ref.length - 1] === elName)
|
|
1237
|
-
const _adaptColumns4readAfterWrite = (req, cqnScenario, query4sql) => {
|
|
1238
|
-
if (
|
|
1239
|
-
!(req.context.event === 'EDIT' && cqnScenario.scenario === SCENARIO.DRAFT_WHICH_OWNER) &&
|
|
1240
|
-
!(req.context.event === 'draftActivate' && cqnScenario.scenario === SCENARIO.ALL_ACTIVE)
|
|
1241
|
-
)
|
|
1242
|
-
return
|
|
1243
|
-
|
|
1244
|
-
// cleanup columns if not requested with $select or $expand
|
|
1245
|
-
cqnScenario.cqn.SELECT.columns = cqnScenario.cqn.SELECT.columns.reduce((columns, column) => {
|
|
1246
|
-
const elName = column.as || (column.ref && column.ref[column.ref.length - 1])
|
|
1247
|
-
if (query4sql.SELECT.columns.find(_fnCompare(elName)) || elName in req.target.keys) columns.push(column)
|
|
1248
|
-
return columns
|
|
1249
|
-
}, [])
|
|
1250
|
-
|
|
1251
|
-
// add missing keys
|
|
1252
|
-
const isActive = req.context.event === 'draftActivate'
|
|
1253
|
-
// aliasing is fixed by scenarios
|
|
1254
|
-
const alias = isActive ? 'active' : req.target.drafts.name
|
|
1255
|
-
for (const key in req.target.keys) {
|
|
1256
|
-
if (req.target.keys[key].isAssociation) continue
|
|
1257
|
-
if (!cqnScenario.cqn.SELECT.columns.find(_fnCompare(key))) {
|
|
1258
|
-
const column =
|
|
1259
|
-
key === 'IsActiveEntity'
|
|
1260
|
-
? { val: isActive, as: 'IsActiveEntity', cast: { type: 'cds.Boolean' } }
|
|
1261
|
-
: { ref: [alias, key] }
|
|
1262
|
-
cqnScenario.cqn.SELECT.columns.push(column)
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
/**
|
|
1268
|
-
* Generic Handler for READ requests in the context of draft.
|
|
1269
|
-
*
|
|
1270
|
-
* @param req
|
|
1271
|
-
*/
|
|
1272
|
-
const fioriGenericRead = async function (req, next) {
|
|
1273
|
-
if (!req.target._isDraftEnabled) return next()
|
|
1274
|
-
|
|
1275
|
-
if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
|
|
1276
|
-
|
|
1277
|
-
const query = req.query
|
|
1278
|
-
const originalFrom = _copyCQNPartial(query.SELECT.from)
|
|
1279
|
-
|
|
1280
|
-
// handle localized here as it was previously handled for req.target
|
|
1281
|
-
req.target = _getLocalizedEntity(this.model, req.target, req.user) || req.target
|
|
1282
|
-
|
|
1283
|
-
// REVISIT DRAFT HANDLING: cqn2cqn4sql must not be called here
|
|
1284
|
-
const query4sql = cqn2cqn4sql(_copyCQNPartial(req.query), this.model, { _4fiori: true })
|
|
1285
|
-
|
|
1286
|
-
// Clone the request. Do not clone with Object.assign as that would skip all non-enumerable properties.
|
|
1287
|
-
// REVISIT: query4sql.clone() doesn't really clone the original query, hence _generateCQN will heavily modify
|
|
1288
|
-
// it, e.g. IsActiveEntity is stripped. This is a problem for subsequent handlers which rely on this information.
|
|
1289
|
-
const reqClone = { __proto__: req, query: query4sql }
|
|
1290
|
-
// Clone draft restrictions to the cloned query.
|
|
1291
|
-
reqClone.query._draftRestrictions = query._draftRestrictions
|
|
1292
|
-
|
|
1293
|
-
if (cds.env.features.stream_compat && query._streaming) return _handlerStreaming(req, reqClone.query)
|
|
1294
|
-
|
|
1295
|
-
let cqnScenario
|
|
1296
|
-
|
|
1297
|
-
// to replace scenario CQNs for queries with $apply SELECT chain (new odata2cqn parser)
|
|
1298
|
-
// just to make existing tests working with new parser. not really tested, not needed to be supported
|
|
1299
|
-
if (reqClone.query.SELECT.from.SELECT) {
|
|
1300
|
-
const subQueryReq = { __proto__: req, query: _copyCQNPartial(_getLastSubQuery(reqClone.query)) }
|
|
1301
|
-
const nonDraftColumns = filterNonDraftColumns(subQueryReq.query.SELECT.columns)
|
|
1302
|
-
cqnScenario = _generateCQN(subQueryReq, nonDraftColumns, originalFrom.SELECT.from, this.model)
|
|
1303
|
-
cqnScenario.cqn = _setLastSubQuery(reqClone.query, cqnScenario.cqn)
|
|
1304
|
-
} else {
|
|
1305
|
-
const nonDraftColumns = filterNonDraftColumns(reqClone.query.SELECT.columns)
|
|
1306
|
-
cqnScenario = _generateCQN(reqClone, nonDraftColumns, originalFrom, this.model)
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
if (!cqnScenario) throw getError(501)
|
|
1310
|
-
|
|
1311
|
-
// ensure base columns for calculation are selected in draft admin expand
|
|
1312
|
-
_adaptDraftAdminExpand(cqnScenario.cqn)
|
|
1313
|
-
|
|
1314
|
-
if (cqnScenario.scenario === SCENARIO.ALL_ACTIVE && cqnScenario.cqn.SELECT.where) {
|
|
1315
|
-
cqnScenario.cqn.SELECT.where = removeIsActiveEntityRecursively(cqnScenario.cqn.SELECT.where)
|
|
1316
|
-
}
|
|
1317
|
-
|
|
1318
|
-
const enhancedWithLastChangeDateTime = enhanceQueryForTimeoutIfNeeded(
|
|
1319
|
-
cqnScenario.scenario,
|
|
1320
|
-
cqnScenario.cqn.SELECT.columns
|
|
1321
|
-
)
|
|
1322
|
-
|
|
1323
|
-
_adaptSubSelects(cqnScenario.cqn, cqnScenario.scenario)
|
|
1324
|
-
_adaptAnnotationAliases(cqnScenario.cqn)
|
|
1325
|
-
|
|
1326
|
-
// unlocalize for db and after handlers as it was before
|
|
1327
|
-
req.target = this.model.definitions[ensureUnlocalized(req.target.name)]
|
|
1328
|
-
|
|
1329
|
-
_adaptColumns4readAfterWrite(req, cqnScenario, query4sql)
|
|
1330
|
-
|
|
1331
|
-
const result = await cds.tx(req).send({ query: cqnScenario.cqn, target: req.target })
|
|
1332
|
-
|
|
1333
|
-
if (result == null && req._etagValidationType === 'if-match') req.reject(412)
|
|
1334
|
-
|
|
1335
|
-
return _postProcess(result, reqClone, cqnScenario, enhancedWithLastChangeDateTime)
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
module.exports = cds.service.impl(function (srv) {
|
|
1339
|
-
srv.on('READ', '*', fioriGenericRead)
|
|
1340
|
-
})
|