@sap/cds 6.6.1 → 6.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -3
- package/README.md +1 -1
- package/apis/connect.d.ts +11 -4
- package/apis/core.d.ts +1 -1
- package/apis/csn.d.ts +1 -0
- package/apis/internal/inference.d.ts +15 -2
- package/apis/log.d.ts +10 -0
- package/apis/serve.d.ts +4 -9
- package/apis/services.d.ts +86 -19
- package/bin/build/buildTaskEngine.js +16 -42
- package/bin/build/constants.js +4 -2
- package/bin/build/provider/buildTaskProviderInternal.js +117 -85
- package/bin/build/provider/hana/index.js +6 -1
- package/bin/build/provider/mtx-extension/index.js +74 -34
- package/bin/build/provider/mtx-sidecar/index.js +3 -3
- package/bin/build/provider/nodejs/index.js +2 -2
- package/bin/build/util.js +63 -14
- package/bin/cds-serve.js +6 -0
- package/bin/cds.js +20 -4
- package/bin/deploy/to-hana/cfUtil.js +15 -1
- package/bin/deploy/to-hana/hana.js +1 -1
- package/bin/deploy/to-hana/hdiDeployUtil.js +1 -1
- package/bin/mtx/in-cds.js +2 -9
- package/bin/plugins.js +31 -0
- package/bin/serve.js +12 -12
- package/lib/compile/etc/_localized.js +1 -1
- package/lib/compile/for/lean_drafts.js +22 -6
- package/lib/compile/for/nodejs.js +4 -1
- package/lib/compile/load.js +4 -2
- package/lib/core/index.js +35 -15
- package/lib/dbs/cds-deploy.js +129 -133
- package/lib/env/cds-env.js +25 -17
- package/lib/env/cds-requires.js +10 -40
- package/lib/env/compat.js +12 -0
- package/lib/env/defaults.js +17 -9
- package/lib/env/plugins.js +29 -0
- package/lib/env/schemas/cds-rc.json +14 -0
- package/lib/index.js +3 -0
- package/lib/log/cds-log.js +7 -4
- package/lib/ql/CREATE.js +1 -1
- package/lib/ql/DELETE.js +1 -1
- package/lib/ql/DROP.js +3 -3
- package/lib/ql/INSERT.js +1 -1
- package/lib/ql/Query.js +14 -6
- package/lib/ql/SELECT.js +8 -2
- package/lib/ql/UPDATE.js +1 -1
- package/lib/ql/Whereable.js +1 -1
- package/lib/ql/cds-ql.js +1 -9
- package/lib/req/cds-context.js +1 -4
- package/lib/req/request.js +63 -2
- package/lib/req/response.js +3 -2
- package/lib/srv/bindings.js +69 -71
- package/lib/srv/cds-connect.js +4 -1
- package/lib/srv/cds-serve.js +4 -0
- package/lib/srv/middlewares/index.js +37 -6
- package/lib/srv/protocols/_legacy.js +1 -1
- package/lib/srv/protocols/index.js +1 -1
- package/lib/srv/srv-api.js +4 -6
- package/lib/srv/srv-dispatch.js +4 -3
- package/lib/srv/srv-handlers.js +1 -1
- package/lib/srv/srv-methods.js +8 -2
- package/lib/utils/cds-test.js +4 -1
- package/libx/_runtime/audit/Service.js +8 -9
- package/libx/_runtime/audit/generic/personal/index.js +1 -1
- package/libx/_runtime/audit/generic/personal/utils.js +1 -1
- package/libx/_runtime/audit/utils/v2.js +17 -20
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +2 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +11 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +3 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +4 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/oDataConfiguration.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +1 -1
- package/libx/_runtime/cds-services/services/Service.js +1 -1
- package/libx/_runtime/cds-services/util/assert.js +41 -65
- package/libx/_runtime/common/code-ext/WorkerPool.js +90 -0
- package/libx/_runtime/common/code-ext/WorkerReq.js +0 -4
- package/libx/_runtime/common/code-ext/execute.js +28 -18
- package/libx/_runtime/common/code-ext/handlers.js +5 -4
- package/libx/_runtime/common/code-ext/worker.js +45 -3
- package/libx/_runtime/common/code-ext/workerQueryExecutor.js +8 -7
- package/libx/_runtime/common/composition/delete.js +1 -1
- package/libx/_runtime/common/composition/update.js +3 -5
- package/libx/_runtime/common/generic/auth/expand.js +1 -1
- package/libx/_runtime/common/generic/auth/readOnly.js +5 -4
- package/libx/_runtime/common/generic/auth/restrict.js +7 -2
- package/libx/_runtime/common/generic/crud.js +12 -1
- package/libx/_runtime/common/generic/etag.js +11 -3
- package/libx/_runtime/common/generic/input.js +8 -6
- package/libx/_runtime/common/generic/paging.js +25 -8
- package/libx/_runtime/common/generic/put.js +1 -1
- package/libx/_runtime/common/generic/sorting.js +0 -1
- package/libx/_runtime/common/i18n/messages.properties +1 -0
- package/libx/_runtime/common/utils/cqn.js +5 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +2 -2
- package/libx/_runtime/common/utils/resolveView.js +14 -10
- package/libx/_runtime/common/utils/rewriteAsterisks.js +2 -3
- package/libx/_runtime/common/utils/templateProcessor.js +15 -17
- package/libx/_runtime/common/utils/templateProcessorPathSerializer.js +18 -6
- package/libx/_runtime/db/Service.js +1 -0
- package/libx/_runtime/db/data-conversion/post-processing.js +0 -18
- package/libx/_runtime/db/expand/expand-v2.js +2 -2
- package/libx/_runtime/db/expand/rawToExpanded.js +6 -6
- package/libx/_runtime/db/generic/integrity.js +1 -1
- package/libx/_runtime/db/utils/columns.js +5 -5
- package/libx/_runtime/fiori/generic/activate.js +3 -3
- package/libx/_runtime/fiori/generic/edit.js +1 -1
- package/libx/_runtime/fiori/generic/new.js +4 -0
- package/libx/_runtime/fiori/lean-draft.js +138 -46
- package/libx/_runtime/hana/execute.js +3 -1
- package/libx/_runtime/hana/pool.js +10 -2
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +6 -1
- package/libx/_runtime/messaging/enterprise-messaging.js +1 -0
- package/libx/_runtime/remote/Service.js +16 -13
- package/libx/_runtime/remote/utils/client.js +6 -1
- package/libx/_runtime/sqlite/Service.js +5 -59
- package/libx/_runtime/sqlite/convertDraftAdminPathExpression.js +1 -0
- package/libx/_runtime/sqlite/customBuilder/CustomUpsertBuilder.js +2 -2
- package/libx/_runtime/sqlite/execute.js +3 -1
- package/libx/_runtime/types/api.js +12 -3
- package/libx/odata/afterburner.js +36 -0
- package/libx/odata/cqn2odata.js +1 -1
- package/libx/odata/grammar.pegjs +5 -3
- package/libx/odata/parser.js +1 -1
- package/libx/odata/utils.js +1 -1
- package/libx/rest/RestAdapter.js +1 -1
- package/libx/rest/RestRequest.js +1 -0
- package/package.json +5 -2
- package/libx/_runtime/common/code-ext/workerQuery.js +0 -45
- package/libx/_runtime/common/constants/limit.js +0 -12
- package/libx/_runtime/common/utils/page.js +0 -39
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
const DELIMITER = require('./templateDelimiter')
|
|
2
|
-
const pathSerializer = require('./templateProcessorPathSerializer')
|
|
3
2
|
|
|
4
|
-
const _processElement = (processFn, row, key, target, picked = {}, isRoot,
|
|
3
|
+
const _processElement = (processFn, row, key, target, picked = {}, isRoot, pathSegmentsInfo) => {
|
|
5
4
|
const element = (target.elements || target.params)[key]
|
|
6
5
|
const { plain } = picked
|
|
7
6
|
|
|
8
7
|
if (!plain) return
|
|
9
8
|
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
elementInfo.
|
|
16
|
-
elementInfo.pathSegments.push(...target._flat2struct[key])
|
|
9
|
+
/** @type import('../../types/api').templateElementInfo */
|
|
10
|
+
const elementInfo = { row, key, element, target, plain, isRoot, pathSegmentsInfo }
|
|
11
|
+
|
|
12
|
+
if (!element && target._flat2struct?.[key] && elementInfo.pathSegmentsInfo) {
|
|
13
|
+
elementInfo.pathSegmentsInfo = pathSegmentsInfo.slice(0)
|
|
14
|
+
elementInfo.pathSegmentsInfo.push(...target._flat2struct[key])
|
|
17
15
|
}
|
|
18
16
|
|
|
19
17
|
processFn(elementInfo)
|
|
@@ -23,7 +21,7 @@ const _processRow = (processFn, row, template, tKey, tValue, isRoot, pathOptions
|
|
|
23
21
|
const { template: subTemplate, picked } = tValue
|
|
24
22
|
const key = tKey.split(DELIMITER).pop()
|
|
25
23
|
|
|
26
|
-
_processElement(processFn, row, key, template.target, picked, isRoot, pathOptions.
|
|
24
|
+
_processElement(processFn, row, key, template.target, picked, isRoot, pathOptions.pathSegmentsInfo)
|
|
27
25
|
|
|
28
26
|
// process deep
|
|
29
27
|
if (subTemplate && typeof row === 'object' && row) {
|
|
@@ -48,20 +46,20 @@ const _processComplex = (processFn, row, template, key, pathOptions) => {
|
|
|
48
46
|
if (rows.length === 0) return
|
|
49
47
|
const keyNames = pathOptions.includeKeyValues && _getTargetKeyNames(template.target)
|
|
50
48
|
|
|
51
|
-
for (
|
|
52
|
-
const row = rows[idx]
|
|
49
|
+
for (const row of rows) {
|
|
53
50
|
if (row == null) continue
|
|
54
51
|
const args = { processFn, row, template, isRoot: false, pathOptions }
|
|
55
52
|
|
|
56
|
-
let
|
|
53
|
+
let pathSegmentInfo
|
|
57
54
|
if (pathOptions.includeKeyValues) {
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
pathOptions.rowUUIDGenerator?.(keyNames, row, template)
|
|
56
|
+
/** @type import('../../types/api').pathSegmentInfo */
|
|
57
|
+
pathSegmentInfo = { key, keyNames, row, elements: template.target.elements, draftKeys: pathOptions.draftKeys }
|
|
60
58
|
}
|
|
61
59
|
|
|
62
|
-
if (pathOptions.
|
|
60
|
+
if (pathOptions.pathSegmentsInfo) pathOptions.pathSegmentsInfo.push(pathSegmentInfo || key)
|
|
63
61
|
templateProcessor(args)
|
|
64
|
-
if (pathOptions.
|
|
62
|
+
if (pathOptions.pathSegmentsInfo) pathOptions.pathSegmentsInfo.pop()
|
|
65
63
|
}
|
|
66
64
|
}
|
|
67
65
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
const segmentSerializer = pathSegmentInfo => {
|
|
4
|
+
const { key: tKey, row, elements, draftKeys } = pathSegmentInfo
|
|
5
|
+
let keyNames = pathSegmentInfo.keyNames
|
|
6
|
+
|
|
3
7
|
const keyValuePairs = keyNames
|
|
4
|
-
.filter(key => {
|
|
5
|
-
if (cds.env.features.lean_draft && key === 'IsActiveEntity') return false
|
|
6
|
-
return true
|
|
7
|
-
})
|
|
8
8
|
.map(key => {
|
|
9
9
|
let quote
|
|
10
10
|
|
|
@@ -19,11 +19,23 @@ const templatePathSerializer = (tKey, keyNames, row, elements, draftKeys) => {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
const keyValue = row[key] ?? draftKeys?.[key]
|
|
22
|
+
if (keyValue == null) return
|
|
22
23
|
return `${key}=${quote}${keyValue}${quote}`
|
|
23
24
|
})
|
|
25
|
+
.filter(c => c)
|
|
24
26
|
|
|
25
27
|
const keyValuePairsSerialized = keyValuePairs.join(',')
|
|
26
|
-
|
|
28
|
+
const pathSegment = `${tKey}(${keyValuePairsSerialized})`
|
|
29
|
+
return pathSegment
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const templatePathSerializer = (elementName, pathSegmentsInfo) => {
|
|
33
|
+
const pathSegments = pathSegmentsInfo.map(pathSegmentInfo => {
|
|
34
|
+
if (typeof pathSegmentInfo === 'string') return pathSegmentInfo
|
|
35
|
+
return segmentSerializer(pathSegmentInfo)
|
|
36
|
+
})
|
|
37
|
+
const path = `${pathSegments.join('/')}${pathSegments.length ? '/' : ''}${elementName}`
|
|
38
|
+
return path
|
|
27
39
|
}
|
|
28
40
|
|
|
29
41
|
module.exports = templatePathSerializer
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
const { getEntityNameFromCQN, traverseFroms } = require('../../common/utils/entityFromCqn')
|
|
2
2
|
const { ensureNoDraftsSuffix } = require('../../common/utils/draft')
|
|
3
|
-
const { proxifyIfFlattened } = require('../../../common/utils/ucsn')
|
|
4
|
-
const cds = require('../../../../lib')
|
|
5
3
|
|
|
6
4
|
const _getCastFunction = ({ type }) => {
|
|
7
5
|
switch (type) {
|
|
@@ -34,13 +32,6 @@ const _getNestedElement = (entity, key) => {
|
|
|
34
32
|
return _structElement(entity, structPath)
|
|
35
33
|
}
|
|
36
34
|
|
|
37
|
-
const _structure = csnEntity => (value, key, row, unaliasedKey) => {
|
|
38
|
-
proxifyIfFlattened(csnEntity, row)
|
|
39
|
-
const effectiveKey = unaliasedKey || key
|
|
40
|
-
delete row[effectiveKey]
|
|
41
|
-
row[effectiveKey] = value
|
|
42
|
-
}
|
|
43
|
-
|
|
44
35
|
const _addConverter = (mapper, name, converter) => {
|
|
45
36
|
if (mapper.has(name)) {
|
|
46
37
|
const oldConverter = mapper.get(name)
|
|
@@ -126,15 +117,6 @@ const _getMapperForListedElements = (conversionMap, csn, cqn) => {
|
|
|
126
117
|
row[effectiveKey] = JSON.parse(val)
|
|
127
118
|
}) // > arrayed elements
|
|
128
119
|
}
|
|
129
|
-
|
|
130
|
-
if (
|
|
131
|
-
cds.env.features.ucsn_struct_conversion &&
|
|
132
|
-
element.parent &&
|
|
133
|
-
element.parent._isStructured &&
|
|
134
|
-
!element._isStructured
|
|
135
|
-
) {
|
|
136
|
-
_addConverter(mapper, col.as ? col.as : name, _structure(entity))
|
|
137
|
-
}
|
|
138
120
|
}
|
|
139
121
|
}
|
|
140
122
|
|
|
@@ -119,7 +119,7 @@ const _addForeignKeys = (columns, entity, options) => {
|
|
|
119
119
|
* @returns object
|
|
120
120
|
*/
|
|
121
121
|
const expandV2 = async (model, dbc, query, user, locale, txTimestamp, executeSelectCQN) => {
|
|
122
|
-
const expandColumn = query.SELECT.columns.find(c => c.expand && typeof c.expand === 'string')
|
|
122
|
+
const expandColumn = query.SELECT.columns.find(c => c.expand && typeof c.expand[0] === 'string')
|
|
123
123
|
const options = Object.assign({ onlyKeys: false, onlyCompositions: false }, expandColumn._options)
|
|
124
124
|
// remove expand columns from query without modifying
|
|
125
125
|
const topLevelSelect = query.clone().columns(query.SELECT.columns.filter(c => !c.expand))
|
|
@@ -137,7 +137,7 @@ const expandV2 = async (model, dbc, query, user, locale, txTimestamp, executeSel
|
|
|
137
137
|
|
|
138
138
|
// _associations contains compositions and associations
|
|
139
139
|
if (entity._associations) {
|
|
140
|
-
const depth = expandColumn.expand === '**' ? -1 : Number(expandColumn.expand.replace('*', ''))
|
|
140
|
+
const depth = expandColumn.expand[0] === '**' ? -1 : Number(expandColumn.expand[0].replace('*', ''))
|
|
141
141
|
await _autoExpandNavsAndAttachToResult(entity, Array.isArray(result) ? result : [result], depth, options)
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -111,7 +111,7 @@ class RawToExpanded {
|
|
|
111
111
|
let expandedItems = this._getResultCache(toManyTree.concat(key))[mapping[GET_KEY_VALUE](false, entry)] || []
|
|
112
112
|
|
|
113
113
|
// the expanded items may include the actives of the deleted drafts -> filter out
|
|
114
|
-
if (!cds.env.
|
|
114
|
+
if (!cds.env.fiori.lean_draft && rootIsActiveEntity !== null) {
|
|
115
115
|
if (mapping[TO_ACTIVE]) expandedItems = expandedItems.filter(ele => ele.IsActiveEntity !== false)
|
|
116
116
|
else expandedItems = expandedItems.filter(ele => !!ele.IsActiveEntity === rootIsActiveEntity)
|
|
117
117
|
}
|
|
@@ -144,7 +144,7 @@ class RawToExpanded {
|
|
|
144
144
|
else if (rootIsActiveEntity) row[key] = parsed && parsed.IsActiveEntity !== false ? parsed : null
|
|
145
145
|
else row[key] = parsed && parsed.IsActiveEntity === rootIsActiveEntity ? parsed : null
|
|
146
146
|
}
|
|
147
|
-
if (mapping[CLEANUP_KEYS]) {
|
|
147
|
+
if (parsed && mapping[CLEANUP_KEYS]) {
|
|
148
148
|
for (const key in mapping[CLEANUP_KEYS]) delete parsed[key]
|
|
149
149
|
}
|
|
150
150
|
} else {
|
|
@@ -153,7 +153,7 @@ class RawToExpanded {
|
|
|
153
153
|
// Assume a DB will not return undefined, but always null
|
|
154
154
|
this._convertValue(rawValue, conversionMapper.get(mapping), mapping, row, key)
|
|
155
155
|
|
|
156
|
-
isEntityNull = this._isNull(isEntityNull, rawValue)
|
|
156
|
+
isEntityNull = this._isNull(isEntityNull, rawValue, key)
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
|
|
@@ -173,12 +173,12 @@ class RawToExpanded {
|
|
|
173
173
|
* @returns {boolean}
|
|
174
174
|
* @private
|
|
175
175
|
*/
|
|
176
|
-
_isNull(isEntityNull, value) {
|
|
176
|
+
_isNull(isEntityNull, value, key) {
|
|
177
177
|
if (isEntityNull === undefined) {
|
|
178
|
-
return value === null || value === undefined
|
|
178
|
+
return value === null || value === undefined || key === 'IsActiveEntity'
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
return isEntityNull === true && (value === null || value === undefined)
|
|
181
|
+
return isEntityNull === true && (value === null || value === undefined || key === 'IsActiveEntity')
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
/**
|
|
@@ -333,7 +333,7 @@ const _checkReferenceIntegrity = (entity, data, req, csn, run) => {
|
|
|
333
333
|
}
|
|
334
334
|
|
|
335
335
|
const _checkIntegrityWrapper = (req, csn, run) => async (data, entity) => {
|
|
336
|
-
if (cds.env.
|
|
336
|
+
if (cds.env.fiori.lean_draft && entity.name?.endsWith('.drafts')) return
|
|
337
337
|
const errors = await _checkReferenceIntegrity(entity, data, req, csn, run)
|
|
338
338
|
if (errors && errors.length !== 0) for (const err of errors) req.error(err)
|
|
339
339
|
}
|
|
@@ -16,15 +16,15 @@ const getColumns = (entity, { _4db, onlyKeys } = { _4db: true, onlyKeys: false }
|
|
|
16
16
|
if (!(entity && entity.elements)) return []
|
|
17
17
|
const columnNames = []
|
|
18
18
|
// REVISIT!!!
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
const { structs = cds.env.features.ucsn_struct_conversion } = cds.env.effective.odata
|
|
20
|
+
const { lean_draft } = cds.env.fiori
|
|
21
|
+
const elements = lean_draft ? entity.elements : Object.getPrototypeOf(entity.elements) || entity.elements
|
|
22
22
|
for (const elementName in elements) {
|
|
23
23
|
const element = elements[elementName]
|
|
24
24
|
if (onlyKeys && !element.key) continue
|
|
25
25
|
if (element.isAssociation) continue
|
|
26
|
-
if (!
|
|
27
|
-
if (
|
|
26
|
+
if (!lean_draft && _4db && entity._isDraftEnabled && elementName in DRAFT_COLUMNS_MAP) continue
|
|
27
|
+
if (structs && element.elements) {
|
|
28
28
|
columnNames.push(...resolveStructured({ element, structProperties: [] }, false))
|
|
29
29
|
continue
|
|
30
30
|
}
|
|
@@ -146,8 +146,8 @@ const fioriGenericActivate = async function (req) {
|
|
|
146
146
|
|
|
147
147
|
// REVISIT: should not be necessary
|
|
148
148
|
r._ = Object.assign(r._, req._)
|
|
149
|
-
r.getUriInfo = () => req.getUriInfo()
|
|
150
|
-
r.getUrlObject = () => req.getUrlObject()
|
|
149
|
+
if (req.getUriInfo) r.getUriInfo = () => req.getUriInfo()
|
|
150
|
+
if (req.getUrlObject) r.getUrlObject = () => req.getUrlObject()
|
|
151
151
|
r._.params = req.params
|
|
152
152
|
r._.query = req.query
|
|
153
153
|
|
|
@@ -178,7 +178,7 @@ const fioriGenericActivate = async function (req) {
|
|
|
178
178
|
|
|
179
179
|
// REVISIT: we need to use okra API here because it must be set in the batched request
|
|
180
180
|
// status code must be set in handler to allow overriding for FE V2
|
|
181
|
-
req?._?.odataRes
|
|
181
|
+
req?._?.odataRes?.setStatusCode(201)
|
|
182
182
|
|
|
183
183
|
return result
|
|
184
184
|
}
|
|
@@ -164,7 +164,7 @@ const fioriGenericEdit = async function (req) {
|
|
|
164
164
|
|
|
165
165
|
// REVISIT: we need to use okra API here because it must be set in the batched request
|
|
166
166
|
// status code must be set in handler to allow overriding for FE V2
|
|
167
|
-
req?._?.odataRes
|
|
167
|
+
req?._?.odataRes?.setStatusCode(201)
|
|
168
168
|
|
|
169
169
|
return results[0][0]
|
|
170
170
|
}
|
|
@@ -56,6 +56,10 @@ const fioriGenericNew = async function (req, next) {
|
|
|
56
56
|
|
|
57
57
|
if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
|
|
58
58
|
|
|
59
|
+
const isRoot = typeof req.query.INSERT.into === 'string' || req.query.INSERT.into.ref?.length === 1
|
|
60
|
+
// Only allowed for pseudo draft roots (entities with this action)
|
|
61
|
+
if (isRoot && !req.target['@Common.DraftRoot.ActivationAction']) req.reject(403, 'DRAFT_MODIFICATION_ONLY_VIA_ROOT')
|
|
62
|
+
|
|
59
63
|
const navigationToMany = isNavigationToMany(req)
|
|
60
64
|
|
|
61
65
|
const adminDataCQN = navigationToMany
|