@sap/cds 6.1.3 → 6.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +77 -8
- package/apis/cds.d.ts +18 -6
- package/apis/connect.d.ts +1 -1
- package/apis/cqn.d.ts +1 -1
- package/apis/log.d.ts +23 -5
- package/apis/ql.d.ts +128 -61
- package/apis/services.d.ts +11 -0
- package/apis/test.d.ts +61 -0
- package/apis/utils.d.ts +15 -0
- package/app/fiori/preview.js +1 -0
- package/bin/build/buildTaskEngine.js +70 -22
- package/bin/build/buildTaskFactory.js +18 -11
- package/bin/build/buildTaskHandler.js +1 -1
- package/bin/build/buildTaskProviderFactory.js +3 -13
- package/bin/build/constants.js +0 -1
- package/bin/build/index.js +14 -6
- package/bin/build/provider/buildTaskHandlerEdmx.js +2 -3
- package/bin/build/provider/buildTaskHandlerFeatureToggles.js +2 -2
- package/bin/build/provider/buildTaskHandlerInternal.js +3 -6
- package/bin/build/provider/buildTaskProviderInternal.js +51 -39
- package/bin/build/provider/fiori/index.js +3 -3
- package/bin/build/provider/hana/2migration.js +1 -1
- package/bin/build/provider/hana/index.js +34 -27
- package/bin/build/provider/java/index.js +6 -7
- package/bin/build/provider/mtx/index.js +20 -18
- package/bin/build/provider/mtx/resourcesTarBuilder.js +8 -11
- package/bin/build/provider/mtx-sidecar/index.js +13 -17
- package/bin/build/provider/nodejs/index.js +8 -7
- package/bin/build/util.js +22 -4
- package/bin/cds.js +8 -4
- package/bin/deploy/to-hana/cfUtil.js +53 -18
- package/bin/mtx/in-cds.js +1 -0
- package/bin/serve.js +37 -30
- package/lib/auth/basic-auth.js +33 -0
- package/lib/auth/dummy-auth.js +7 -0
- package/lib/auth/ias-auth.js +2 -0
- package/lib/auth/index.js +31 -0
- package/lib/auth/jwt-auth.js +3 -0
- package/lib/auth/mocked-users.js +72 -0
- package/lib/auth/passport-basic.js +12 -0
- package/lib/auth/passport-digest.js +14 -0
- package/lib/auth/xsuaa-auth.js +3 -0
- package/lib/compile/cds-compile.js +3 -3
- package/lib/compile/to/cdl.js +5 -1
- package/lib/compile/to/edm.js +8 -0
- package/lib/compile/to/gql.js +1 -0
- package/lib/compile/to/json.js +30 -5
- package/lib/compile/to/sql.js +3 -1
- package/lib/core/index.js +5 -1
- package/lib/dbs/cds-deploy.js +36 -6
- package/lib/env/cds-env.js +15 -5
- package/lib/env/cds-requires.js +51 -58
- package/lib/env/defaults.js +1 -0
- package/lib/env/schemas/cds-package.json +4 -0
- package/lib/env/schemas/cds-rc.json +63 -77
- package/lib/i18n/localize.js +16 -5
- package/lib/index.js +9 -4
- package/lib/log/cds-error.js +4 -6
- package/lib/log/cds-log.js +89 -53
- package/lib/log/service/index.js +1 -0
- package/lib/ql/CREATE.js +2 -5
- package/lib/ql/DELETE.js +1 -1
- package/lib/ql/DROP.js +1 -3
- package/lib/ql/INSERT.js +3 -3
- package/lib/ql/Query.js +10 -23
- package/lib/ql/SELECT.js +1 -2
- package/lib/ql/UPDATE.js +2 -2
- package/lib/ql/Whereable.js +7 -15
- package/lib/ql/cds-ql.js +9 -3
- package/lib/req/cds-context.js +11 -3
- package/lib/req/context.js +29 -23
- package/lib/req/locale.js +9 -5
- package/lib/req/request.js +1 -0
- package/lib/req/user.js +2 -1
- package/lib/srv/cds-connect.js +1 -1
- package/lib/srv/cds-serve.js +21 -14
- package/lib/srv/middlewares/cds-context.js +29 -0
- package/lib/srv/middlewares/ctx-model.js +24 -0
- package/lib/srv/middlewares/errors.js +9 -0
- package/lib/srv/middlewares/index.js +22 -0
- package/lib/srv/middlewares/sap-statistics.js +13 -0
- package/lib/srv/middlewares/trace.js +102 -0
- package/lib/srv/protocols/_legacy.js +42 -0
- package/lib/srv/protocols/graphql.js +39 -0
- package/lib/srv/protocols/hcql.js +37 -0
- package/lib/srv/protocols/index.js +86 -0
- package/lib/srv/protocols/odata-v2-proxy.js +3767 -0
- package/lib/srv/protocols/odata-v2.js +26 -0
- package/lib/srv/protocols/odata-v4.js +16 -0
- package/lib/srv/protocols/rest.js +13 -0
- package/lib/srv/srv-api.js +5 -0
- package/lib/srv/srv-models.js +4 -6
- package/lib/utils/axios.js +3 -2
- package/lib/utils/cds-test.js +27 -21
- package/lib/utils/cds-utils.js +19 -20
- package/lib/utils/tar.js +175 -0
- package/libx/_runtime/audit/generic/personal/utils.js +18 -7
- package/libx/_runtime/audit/utils/v2.js +1 -0
- package/libx/_runtime/auth/index.js +4 -0
- package/libx/_runtime/auth/strategies/ias-auth.js +76 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +8 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +15 -4
- 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/orderByToCQN.js +1 -1
- 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/uri/ResourcePathParser.js +9 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriInfo.js +5 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +12 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/utils/BufferedWriter.js +6 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/RequestValidator.js +47 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
- package/libx/_runtime/cds-services/util/assert.js +4 -0
- package/libx/_runtime/common/aspects/relation.js +1 -1
- package/libx/_runtime/common/composition/data.js +61 -15
- package/libx/_runtime/common/composition/delete.js +0 -1
- package/libx/_runtime/common/composition/insert.js +0 -1
- package/libx/_runtime/common/composition/tree.js +4 -10
- package/libx/_runtime/common/composition/update.js +44 -21
- package/libx/_runtime/common/generic/auth/capabilities.js +8 -10
- package/libx/_runtime/common/generic/crud.js +1 -2
- package/libx/_runtime/common/generic/etag.js +4 -4
- package/libx/_runtime/common/generic/input.js +4 -4
- package/libx/_runtime/common/generic/paging.js +3 -3
- package/libx/_runtime/common/generic/put.js +3 -3
- package/libx/_runtime/common/generic/sorting.js +4 -4
- package/libx/_runtime/common/generic/temporal.js +3 -3
- package/libx/_runtime/common/i18n/messages.properties +0 -7
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +11 -6
- package/libx/_runtime/common/utils/csn.js +0 -28
- package/libx/_runtime/common/utils/draft.js +8 -1
- package/libx/_runtime/common/utils/path.js +7 -1
- package/libx/_runtime/common/utils/resolveView.js +2 -3
- package/libx/_runtime/db/data-conversion/post-processing.js +3 -44
- package/libx/_runtime/db/generic/input.js +3 -3
- package/libx/_runtime/db/sql-builder/dataTypes.js +4 -0
- package/libx/_runtime/fiori/generic/activate.js +2 -2
- package/libx/_runtime/fiori/generic/before.js +40 -72
- package/libx/_runtime/fiori/generic/cancel.js +2 -2
- package/libx/_runtime/fiori/generic/delete.js +2 -2
- package/libx/_runtime/fiori/generic/edit.js +2 -2
- package/libx/_runtime/fiori/generic/new.js +2 -2
- package/libx/_runtime/fiori/generic/patch.js +49 -37
- package/libx/_runtime/fiori/generic/prepare.js +2 -2
- package/libx/_runtime/fiori/generic/read.js +27 -37
- package/libx/_runtime/fiori/utils/where.js +4 -2
- package/libx/_runtime/hana/Service.js +1 -3
- package/libx/_runtime/hana/conversion.js +3 -0
- package/libx/_runtime/hana/driver.js +33 -3
- package/libx/_runtime/hana/dynatrace.js +1 -0
- package/libx/_runtime/hana/search2Contains.js +12 -1
- package/libx/_runtime/hana/search2cqn4sql.js +10 -27
- package/libx/_runtime/hana/streaming.js +1 -0
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +4 -2
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +1 -0
- package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +5 -2
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -0
- package/libx/_runtime/messaging/enterprise-messaging.js +62 -3
- package/libx/_runtime/messaging/outbox/utils.js +1 -1
- package/libx/_runtime/messaging/redis-messaging.js +1 -0
- package/libx/_runtime/remote/Service.js +2 -2
- package/libx/_runtime/remote/utils/client.js +8 -3
- package/libx/_runtime/remote/utils/data.js +7 -2
- package/libx/_runtime/sqlite/Service.js +18 -7
- package/libx/_runtime/sqlite/conversion.js +3 -0
- package/libx/_runtime/sqlite/convertAssocToOneManaged.js +3 -3
- package/libx/_runtime/sqlite/localized.js +8 -8
- package/libx/odata/afterburner.js +39 -7
- package/libx/odata/cqn2odata.js +6 -3
- package/libx/odata/grammar.pegjs +66 -18
- package/libx/odata/index.js +3 -2
- package/libx/odata/parser.js +1 -1
- package/libx/odata/utils.js +2 -0
- package/libx/rest/RestAdapter.js +62 -43
- package/libx/rest/middleware/parse.js +2 -1
- package/libx/rest/middleware/update.js +1 -1
- package/package.json +2 -2
- package/server.js +5 -4
- package/srv/mtx.cds +1 -1
- package/srv/mtx.js +4 -33
- package/lib/srv/adapters.js +0 -85
- package/lib/utils/resources/index.js +0 -48
- package/lib/utils/resources/tar.js +0 -49
- package/lib/utils/resources/utils.js +0 -11
- package/libx/_runtime/extensibility/activate.js +0 -69
- package/libx/_runtime/extensibility/add.js +0 -50
- package/libx/_runtime/extensibility/addExtension.js +0 -72
- package/libx/_runtime/extensibility/defaults.js +0 -34
- package/libx/_runtime/extensibility/handler/transformREAD.js +0 -121
- package/libx/_runtime/extensibility/handler/transformRESULT.js +0 -51
- package/libx/_runtime/extensibility/handler/transformWRITE.js +0 -64
- package/libx/_runtime/extensibility/linter/allowlist_checker.js +0 -373
- package/libx/_runtime/extensibility/linter/annotations_checker.js +0 -113
- package/libx/_runtime/extensibility/linter/checker_base.js +0 -20
- package/libx/_runtime/extensibility/linter/namespace_checker.js +0 -180
- package/libx/_runtime/extensibility/linter.js +0 -32
- package/libx/_runtime/extensibility/push.js +0 -118
- package/libx/_runtime/extensibility/service.js +0 -38
- package/libx/_runtime/extensibility/token.js +0 -57
- package/libx/_runtime/extensibility/utils.js +0 -131
- package/libx/_runtime/extensibility/validation.js +0 -50
- package/libx/_runtime/extensibility/views.js +0 -12
- package/srv/extensibility-service.cds +0 -60
- package/srv/extensibility-service.js +0 -1
- package/srv/extensions.cds +0 -8
- package/srv/model-provider.cds +0 -61
- package/srv/model-provider.js +0 -143
|
@@ -43,7 +43,7 @@ const _getStaticOrders = req => {
|
|
|
43
43
|
*
|
|
44
44
|
* @param req
|
|
45
45
|
*/
|
|
46
|
-
const
|
|
46
|
+
const commonGenericSorting = function (req) {
|
|
47
47
|
if (!req.query || !req.query.SELECT || req.query.SELECT.one) return
|
|
48
48
|
|
|
49
49
|
let select = req.query.SELECT
|
|
@@ -77,9 +77,9 @@ const _handler = function (req) {
|
|
|
77
77
|
* handler registration
|
|
78
78
|
*/
|
|
79
79
|
module.exports = cds.service.impl(function () {
|
|
80
|
-
|
|
81
|
-
this.before('READ', '*',
|
|
80
|
+
commonGenericSorting._initial = true
|
|
81
|
+
this.before('READ', '*', commonGenericSorting)
|
|
82
82
|
})
|
|
83
83
|
|
|
84
84
|
// REVISIT: remove (currently needed for test)
|
|
85
|
-
module.exports.handler =
|
|
85
|
+
module.exports.handler = commonGenericSorting
|
|
@@ -38,7 +38,7 @@ const _getTimeDelta = (target, queryOption) => {
|
|
|
38
38
|
*
|
|
39
39
|
* @param req
|
|
40
40
|
*/
|
|
41
|
-
const
|
|
41
|
+
const commonGenericTemporal = function (req) {
|
|
42
42
|
// REVISIT: public API for query options
|
|
43
43
|
const { _queryOptions } = req
|
|
44
44
|
|
|
@@ -67,7 +67,7 @@ const _handler = function (req) {
|
|
|
67
67
|
* handler registration
|
|
68
68
|
*/
|
|
69
69
|
module.exports = cds.service.impl(function () {
|
|
70
|
-
|
|
70
|
+
commonGenericTemporal._initial = true
|
|
71
71
|
// always run to allow interaction with temporal data in custom handlers
|
|
72
|
-
this.before('*',
|
|
72
|
+
this.before('*', commonGenericTemporal)
|
|
73
73
|
})
|
|
@@ -49,10 +49,6 @@ ASSERT_REFERENCE_INTEGRITY=Reference integrity is violated for association "{0}"
|
|
|
49
49
|
ASSERT_TARGET="Value doesn't exist"
|
|
50
50
|
ASSERT_DEEP_ASSOCIATION=It is not allowed to modify sub documents in {0} Association "{1}"
|
|
51
51
|
|
|
52
|
-
# persistence
|
|
53
|
-
PERSISTENCE_SKIP_NO_GENERIC_CRUD=Entity "{0}" is annotated with "@sap.persistence.skip" and cannot be served generically.
|
|
54
|
-
NON_WRITABLE_VIEW={0} on views with join and/or union is not supported
|
|
55
|
-
|
|
56
52
|
# db
|
|
57
53
|
NO_DATABASE_CONNECTION=No database connection
|
|
58
54
|
ENTITY_ALREADY_EXISTS=Entity already exists
|
|
@@ -74,9 +70,6 @@ ENTITY_IS_NOT_CRUD=Entity "{0}" is not {1}
|
|
|
74
70
|
ENTITY_IS_NOT_CRUD_VIA_NAVIGATION=Entity "{0}" is not {1} via navigation "{2}"
|
|
75
71
|
ENTITY_IS_AUTOEXPOSED=Entity "{0}" is not explicitly exposed as part of the service
|
|
76
72
|
EXPAND_IS_RESTRICTED=Navigation property "{0}" is not allowed for expand operation
|
|
77
|
-
EXPAND_COUNT_UNSUPPORTED="/$count" is not supported for expand operation
|
|
78
|
-
ORDERBY_LAMBDA_UNSUPPORTED="$orderby" does not support lambda
|
|
79
|
-
EXPAND_APPLY_UNSUPPORTED="$apply" is not supported for expand operation
|
|
80
73
|
|
|
81
74
|
# rest protocol adapter
|
|
82
75
|
INVALID_RESOURCE="{0}" is not a valid resource
|
|
@@ -5,7 +5,7 @@ const { SELECT, INSERT, DELETE, UPDATE } = cds.ql
|
|
|
5
5
|
const Query = require('../../../../lib/ql/Query')
|
|
6
6
|
|
|
7
7
|
const { resolveView } = require('./resolveView')
|
|
8
|
-
const { ensureNoDraftsSuffix, getDraftColumnsCQNForDraft } = require('./draft')
|
|
8
|
+
const { ensureNoDraftsSuffix, getDraftColumnsCQNForDraft, ensureDraftsSuffix } = require('./draft')
|
|
9
9
|
const { flattenStructuredSelect, OPERATIONS_MAP } = require('./structured')
|
|
10
10
|
const search2cqn4sql = require('./search2cqn4sql')
|
|
11
11
|
const { getEntityNameFromCQN } = require('./entityFromCqn')
|
|
@@ -43,11 +43,11 @@ const convertPathExpressionToWhere = (fromClause, model, options) => {
|
|
|
43
43
|
return { target, alias, where, cardinality, args }
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
let previousSelect, previousEntityName, previousTableAlias, structParent, previousArgs
|
|
46
|
+
let previousSelect, previousEntityName, previousTableAlias, structParent, previousArgs, draft
|
|
47
47
|
let prefix = []
|
|
48
48
|
let columns
|
|
49
49
|
for (let i = 0; i < fromClause.ref.length; i++) {
|
|
50
|
-
const entity = structParent || model.definitions[previousEntityName]
|
|
50
|
+
const entity = structParent || (previousEntityName && model.definitions[ensureNoDraftsSuffix(previousEntityName)])
|
|
51
51
|
const element = _elementFromRef(fromClause.ref[i], entity)
|
|
52
52
|
|
|
53
53
|
if (element && element._isStructured) {
|
|
@@ -55,6 +55,7 @@ const convertPathExpressionToWhere = (fromClause, model, options) => {
|
|
|
55
55
|
structParent = element
|
|
56
56
|
continue
|
|
57
57
|
} else if (element && element.isAssociation) {
|
|
58
|
+
if (element._isAssociationStrict && !element['@odata.draft.enclosed']) draft = false
|
|
58
59
|
_modifyNavigationInWhere(fromClause.ref[i].where, element._target)
|
|
59
60
|
} else if (element && previousSelect && i === fromClause.ref.length - 1) {
|
|
60
61
|
columns = [{ ref: [...prefix, element.name] }]
|
|
@@ -62,8 +63,10 @@ const convertPathExpressionToWhere = (fromClause, model, options) => {
|
|
|
62
63
|
continue
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
const
|
|
66
|
+
const entityName = _getEntityName(fromClause, entity, i)
|
|
67
|
+
const currentEntityName = draft ? entityName && ensureDraftsSuffix(entityName) : entityName
|
|
66
68
|
if (!currentEntityName) continue
|
|
69
|
+
if (!draft && currentEntityName.endsWith('_drafts')) draft = true
|
|
67
70
|
const tableAlias = `T${i}`
|
|
68
71
|
const currentSelect = SELECT.from(`${currentEntityName} as ${tableAlias}`)
|
|
69
72
|
|
|
@@ -81,7 +84,10 @@ const convertPathExpressionToWhere = (fromClause, model, options) => {
|
|
|
81
84
|
if (previousSelect) {
|
|
82
85
|
const navigation = _getTargetFromRef(fromClause.ref[i])
|
|
83
86
|
previousSelect.where(
|
|
84
|
-
model.definitions[previousEntityName]._relations[[...prefix, navigation]].join(
|
|
87
|
+
model.definitions[ensureNoDraftsSuffix(previousEntityName)]._relations[[...prefix, navigation]].join(
|
|
88
|
+
tableAlias,
|
|
89
|
+
previousTableAlias
|
|
90
|
+
)
|
|
85
91
|
)
|
|
86
92
|
_convertSelect(previousSelect, model, options)
|
|
87
93
|
currentSelect.where('exists', previousSelect)
|
|
@@ -613,7 +619,6 @@ const _convertPathExpression = (query, model, options = {}) => {
|
|
|
613
619
|
for (const whereEl of query.SELECT.where || []) {
|
|
614
620
|
if (typeof whereEl === 'object' && whereEl.SELECT) _convertPathExpression(whereEl, model)
|
|
615
621
|
}
|
|
616
|
-
|
|
617
622
|
const conversion = convertPathExpressionToWhere(query.SELECT.from, model, options)
|
|
618
623
|
if (!conversion) return
|
|
619
624
|
const { target, alias, where, cardinality, columns, args } = conversion
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
2
|
const resolveStructured = require('../../common/utils/resolveStructured')
|
|
3
3
|
|
|
4
|
-
const { ensureNoDraftsSuffix } = require('./draft')
|
|
5
|
-
|
|
6
4
|
const getEtagElement = entity => {
|
|
7
5
|
return Object.values(entity.elements).find(element => element['@odata.etag'])
|
|
8
6
|
}
|
|
@@ -165,31 +163,6 @@ const getElementDeep = (entity, ref) => {
|
|
|
165
163
|
return current
|
|
166
164
|
}
|
|
167
165
|
|
|
168
|
-
const isRootEntity = (definitions, entityName) => {
|
|
169
|
-
const entity = definitions[entityName]
|
|
170
|
-
if (!entity) return false
|
|
171
|
-
|
|
172
|
-
// TODO: There can be unmanaged relations to some parent -> not detected by the following code
|
|
173
|
-
const associationElements = Object.keys(entity.elements)
|
|
174
|
-
.map(key => entity.elements[key])
|
|
175
|
-
.filter(element => element._isAssociationStrict)
|
|
176
|
-
|
|
177
|
-
for (const { target } of associationElements) {
|
|
178
|
-
const parentEntity = definitions[target]
|
|
179
|
-
for (const parentElementName in parentEntity.elements) {
|
|
180
|
-
const parentElement = parentEntity.elements[parentElementName]
|
|
181
|
-
if (
|
|
182
|
-
parentElement.isComposition &&
|
|
183
|
-
parentElement.target === entityName &&
|
|
184
|
-
!(parentElement.parent && ensureNoDraftsSuffix(parentElement.parent.name) === entityName)
|
|
185
|
-
) {
|
|
186
|
-
return false
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
return true
|
|
191
|
-
}
|
|
192
|
-
|
|
193
166
|
const _setAlias2ref = entity => {
|
|
194
167
|
const _ref2alias = {}
|
|
195
168
|
const _alias2ref = {}
|
|
@@ -270,7 +243,6 @@ module.exports = {
|
|
|
270
243
|
getEtagElement,
|
|
271
244
|
findCsnTargetFor,
|
|
272
245
|
getElementDeep,
|
|
273
|
-
isRootEntity,
|
|
274
246
|
getDataSubject,
|
|
275
247
|
alias2ref,
|
|
276
248
|
getComp2oneParents,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
|
+
const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
|
|
2
3
|
|
|
3
4
|
const _4sqlite = cds.env.i18n && Array.isArray(cds.env.i18n.for_sqlite) ? cds.env.i18n.for_sqlite : []
|
|
4
5
|
// compiler reserves 'localized' and raises a corresponding exception if used in models
|
|
@@ -65,10 +66,16 @@ const getDraftColumnsCQNForDraft = () => {
|
|
|
65
66
|
]
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
const filterNonDraftColumns = columns =>
|
|
70
|
+
columns.filter(
|
|
71
|
+
col => (col.ref && !(col.ref[col.ref.length - 1] in DRAFT_COLUMNS_MAP)) || (!col.ref && !(col in DRAFT_COLUMNS_MAP))
|
|
72
|
+
)
|
|
73
|
+
|
|
68
74
|
module.exports = {
|
|
69
75
|
ensureUnlocalized,
|
|
70
76
|
ensureDraftsSuffix,
|
|
71
77
|
ensureNoDraftsSuffix,
|
|
72
78
|
getDraftColumnsCQNForActive,
|
|
73
|
-
getDraftColumnsCQNForDraft
|
|
79
|
+
getDraftColumnsCQNForDraft,
|
|
80
|
+
filterNonDraftColumns
|
|
74
81
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
|
+
const { ensureNoDraftsSuffix } = require('./draft')
|
|
2
3
|
|
|
3
4
|
/*
|
|
4
5
|
* returns path like <service>.<entity>:<prop1>.<prop2> for ref = [{ id: '<service>.<entity>' }, '<prop1>', '<prop2>']
|
|
@@ -22,7 +23,12 @@ const getEntityFromPath = (path, def) => {
|
|
|
22
23
|
path = typeof path === 'string' ? cds.parse.path(path) : path
|
|
23
24
|
const segments = [...path.ref]
|
|
24
25
|
while (segments.length) {
|
|
25
|
-
|
|
26
|
+
let segment = segments.shift()
|
|
27
|
+
if (segment.id && typeof segment.id === 'string') {
|
|
28
|
+
segment.id = ensureNoDraftsSuffix(segment.id)
|
|
29
|
+
} else if (typeof segment === 'string') {
|
|
30
|
+
segment = ensureNoDraftsSuffix(segment)
|
|
31
|
+
}
|
|
26
32
|
current = current.elements[segment.id || segment]
|
|
27
33
|
if (current && current.target) current = current._target
|
|
28
34
|
}
|
|
@@ -501,9 +501,8 @@ const _checkForForbiddenViews = queryTarget => {
|
|
|
501
501
|
if (!select.from || select.from.join || select.from.length > 1) {
|
|
502
502
|
throw getError({
|
|
503
503
|
code: 501,
|
|
504
|
-
message: '
|
|
505
|
-
target: queryTarget.name
|
|
506
|
-
args: [_event || 'INSERT|UPDATE|DELETE']
|
|
504
|
+
message: `${_event || 'INSERT|UPDATE|DELETE'} on views with join and/or union is not supported`,
|
|
505
|
+
target: queryTarget.name
|
|
507
506
|
})
|
|
508
507
|
}
|
|
509
508
|
if (select.where) {
|
|
@@ -3,55 +3,14 @@ const { ensureNoDraftsSuffix } = require('../../common/utils/draft')
|
|
|
3
3
|
const { proxifyIfFlattened } = require('../../../common/utils/ucsn')
|
|
4
4
|
const cds = require('../../../../lib')
|
|
5
5
|
|
|
6
|
-
const _refs = (refs, as) => {
|
|
7
|
-
const arr = []
|
|
8
|
-
for (const element of refs) {
|
|
9
|
-
// multiple join are nested, so we need to find all the table names in there as well
|
|
10
|
-
if (Object.prototype.hasOwnProperty.call(element, 'join')) {
|
|
11
|
-
arr.push(..._extractRefs(element))
|
|
12
|
-
// Likely a union
|
|
13
|
-
} else if (Object.prototype.hasOwnProperty.call(element, 'SELECT')) {
|
|
14
|
-
arr.push(..._extractRefs(element.SELECT.from, element.as))
|
|
15
|
-
} else {
|
|
16
|
-
arr.push(..._extractRefs(element, as))
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return arr
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const _getActiveFromUnion = refs => {
|
|
24
|
-
if (refs.length !== 2) return
|
|
25
|
-
const [maybeDraft, maybeActive] = refs
|
|
26
|
-
if (ensureNoDraftsSuffix(maybeDraft.ref[0]) === maybeActive.ref[0]) return maybeActive
|
|
27
|
-
if (ensureNoDraftsSuffix(maybeActive.ref[0]) === maybeDraft.ref[0]) return maybeDraft
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const _extractRefs = (from, as) => {
|
|
31
|
-
if (from.SELECT) {
|
|
32
|
-
return _extractRefs(from.SELECT.from, as || from.SELECT.as)
|
|
33
|
-
}
|
|
34
|
-
if (Object.prototype.hasOwnProperty.call(from, 'join')) {
|
|
35
|
-
// cqn with join in from
|
|
36
|
-
return _refs(from.args)
|
|
37
|
-
}
|
|
38
|
-
if (Object.prototype.hasOwnProperty.call(from, 'SET')) {
|
|
39
|
-
let refs = _refs(from.SET.args).filter(a => !a.as || a.as !== 'filterAdmin')
|
|
40
|
-
refs = _getActiveFromUnion(refs) ? [_getActiveFromUnion(refs)] : refs
|
|
41
|
-
if (as) return refs.map(({ ref }) => ({ as, ref }))
|
|
42
|
-
return refs
|
|
43
|
-
}
|
|
44
|
-
if (!from.ref) return []
|
|
45
|
-
const ref = { ref: [...from.ref] }
|
|
46
|
-
if (as || from.as) ref.as = as || from.as
|
|
47
|
-
return [ref]
|
|
48
|
-
}
|
|
49
|
-
|
|
50
6
|
const _getCastFunction = ({ type }) => {
|
|
51
7
|
switch (type) {
|
|
52
8
|
case 'cds.Boolean':
|
|
53
9
|
return Boolean
|
|
54
10
|
case 'cds.Integer':
|
|
11
|
+
case 'cds.UInt8':
|
|
12
|
+
case 'cds.Int16':
|
|
13
|
+
case 'cds.Int32':
|
|
55
14
|
return Number
|
|
56
15
|
default:
|
|
57
16
|
return String
|
|
@@ -197,7 +197,7 @@ const _pickDraft = element => {
|
|
|
197
197
|
if (categories.length) return { categories }
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
function
|
|
200
|
+
function dbGenericInput(req) {
|
|
201
201
|
if (!this.model || typeof req.query === 'string' || !req.target) return
|
|
202
202
|
|
|
203
203
|
// call with this for this.model
|
|
@@ -231,6 +231,6 @@ function _handler(req) {
|
|
|
231
231
|
}
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
-
|
|
234
|
+
dbGenericInput._initial = true
|
|
235
235
|
|
|
236
|
-
module.exports =
|
|
236
|
+
module.exports = dbGenericInput
|
|
@@ -3,7 +3,11 @@ const typeConversionMap = new Map()
|
|
|
3
3
|
typeConversionMap.set('cds.UUID', { type: 'NVARCHAR', length: 36 })
|
|
4
4
|
typeConversionMap.set('cds.Boolean', 'BOOLEAN')
|
|
5
5
|
typeConversionMap.set('cds.Integer', 'INTEGER')
|
|
6
|
+
typeConversionMap.set('cds.UInt8', 'INTEGER')
|
|
7
|
+
typeConversionMap.set('cds.Int16', 'INTEGER')
|
|
8
|
+
typeConversionMap.set('cds.Int32', 'INTEGER')
|
|
6
9
|
typeConversionMap.set('cds.Integer64', 'BIGINT')
|
|
10
|
+
typeConversionMap.set('cds.Int64', 'BIGINT')
|
|
7
11
|
typeConversionMap.set('cds.Decimal', { type: 'DECIMAL' })
|
|
8
12
|
typeConversionMap.set('cds.DecimalFloat', { type: 'DECIMAL' })
|
|
9
13
|
typeConversionMap.set('cds.Double', 'DOUBLE')
|
|
@@ -106,7 +106,7 @@ const _draftCompositionTree = async (service, req) => {
|
|
|
106
106
|
*
|
|
107
107
|
* @param req
|
|
108
108
|
*/
|
|
109
|
-
const
|
|
109
|
+
const fioriGenericActivate = async function (req) {
|
|
110
110
|
if (
|
|
111
111
|
isActiveEntityRequested(req.query.SELECT.from.ref[0].where || []) ||
|
|
112
112
|
req.query.SELECT.from.ref.length > 2 ||
|
|
@@ -178,5 +178,5 @@ const _handler = async function (req) {
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
module.exports = cds.service.impl(function (srv, entity) {
|
|
181
|
-
srv.on('draftActivate', entity,
|
|
181
|
+
srv.on('draftActivate', entity, fioriGenericActivate)
|
|
182
182
|
})
|
|
@@ -3,30 +3,14 @@ const cds = require('../../cds')
|
|
|
3
3
|
const { SELECT } = cds.ql
|
|
4
4
|
|
|
5
5
|
const { isNavigationToMany } = require('../utils/req')
|
|
6
|
-
const { getKeysCondition } = require('../utils/where')
|
|
6
|
+
const { getKeysCondition, removeIsActiveEntityRecursively } = require('../utils/where')
|
|
7
7
|
const { isDraftActivateAction, ensureNoDraftsSuffix, ensureDraftsSuffix, draftIsLocked } = require('../utils/handler')
|
|
8
8
|
|
|
9
|
-
const { isCustomOperation } = require('../../cds-services/adapter/odata-v4/utils/request')
|
|
10
|
-
|
|
11
9
|
const { DRAFT_COLUMNS_ADMIN_MAP } = require('../../common/constants/draft')
|
|
10
|
+
const { deepCopyArray } = require('../../common/utils/copy')
|
|
12
11
|
const DRAFT_COLUMNS_ADMIN = Object.keys(DRAFT_COLUMNS_ADMIN_MAP)
|
|
13
12
|
const PREFIX_DRAFT_COLUMNS = DRAFT_COLUMNS_ADMIN.map(col => ({ ref: ['DRAFT_DraftAdministrativeData', col] }))
|
|
14
13
|
|
|
15
|
-
// copied from adapter/odata-v4/utils/context-object
|
|
16
|
-
const _getTargetEntityName = (service, pathSegments) => {
|
|
17
|
-
if (isCustomOperation(pathSegments, false)) return
|
|
18
|
-
|
|
19
|
-
let navSegmentName
|
|
20
|
-
let entityName = `${service.name}.${pathSegments[0].getEntitySet().getName()}`
|
|
21
|
-
|
|
22
|
-
for (const navSegment of pathSegments.filter(segment => segment.getNavigationProperty() !== null)) {
|
|
23
|
-
navSegmentName = navSegment.getNavigationProperty().getName()
|
|
24
|
-
entityName = service.model.definitions[entityName].elements[navSegmentName].target
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return entityName
|
|
28
|
-
}
|
|
29
|
-
|
|
30
14
|
/**
|
|
31
15
|
* Provide information about the parent entity, i.e. the entity that has the to-many composition element.
|
|
32
16
|
* Limitation: only works for one key (besides IsActiveEntity)
|
|
@@ -36,34 +20,6 @@ const _getTargetEntityName = (service, pathSegments) => {
|
|
|
36
20
|
* @returns {object}
|
|
37
21
|
* @private
|
|
38
22
|
*/
|
|
39
|
-
const _getParent = (req, service) => {
|
|
40
|
-
// REVISIT: get rid of getUriInfo
|
|
41
|
-
if (!req.getUriInfo) return
|
|
42
|
-
|
|
43
|
-
const segments = req.getUriInfo().getPathSegments()
|
|
44
|
-
if (segments.length === 1) return
|
|
45
|
-
|
|
46
|
-
const parent = {
|
|
47
|
-
entityName: _getTargetEntityName(service, segments.slice(0, segments.length - 1))
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const parentKeyPredicates = segments[segments.length - 2].getKeyPredicates()
|
|
51
|
-
let keyPredicateName, keyPredicateText
|
|
52
|
-
|
|
53
|
-
for (const keyPredicate of parentKeyPredicates) {
|
|
54
|
-
keyPredicateName = keyPredicate.getEdmRef().getName()
|
|
55
|
-
keyPredicateText = keyPredicate.getText()
|
|
56
|
-
|
|
57
|
-
if (keyPredicateName === 'IsActiveEntity') {
|
|
58
|
-
parent.IsActiveEntity = keyPredicateText === 'true'
|
|
59
|
-
} else {
|
|
60
|
-
parent.keyName = keyPredicateName
|
|
61
|
-
parent.keyValue = keyPredicateText
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return parent
|
|
66
|
-
}
|
|
67
23
|
|
|
68
24
|
const _validateDraft = (req, draftResult, isBoundAction) => {
|
|
69
25
|
if (!draftResult || draftResult.length === 0) req.reject(404)
|
|
@@ -102,36 +58,48 @@ const _getSelectDraftDataCqn = (entityName, where) => {
|
|
|
102
58
|
.where(where)
|
|
103
59
|
}
|
|
104
60
|
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
61
|
+
const _getRoot = req => {
|
|
62
|
+
if (!req.query) return
|
|
63
|
+
const refObj = req.query.SELECT?.from || req.query.UPDATE?.entity || req.query.INSERT?.into || req.query.DELETE?.from
|
|
64
|
+
const root = {
|
|
65
|
+
entityName: ensureDraftsSuffix(refObj.ref[0].id),
|
|
66
|
+
where: removeIsActiveEntityRecursively(deepCopyArray(refObj.ref[0].where))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
for (const item of refObj.ref[0].where) {
|
|
70
|
+
if (item.ref && item.ref[item.ref.length - 1] === 'IsActiveEntity') {
|
|
71
|
+
const index = refObj.ref[0].where.indexOf(item)
|
|
72
|
+
root.IsActiveEntity = refObj.ref[0].where[index + 2].val
|
|
73
|
+
break
|
|
112
74
|
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return root
|
|
78
|
+
}
|
|
113
79
|
|
|
114
|
-
|
|
80
|
+
const _getDraftDataFromExistingDraft = async (req, root) => {
|
|
81
|
+
if (!root) return []
|
|
82
|
+
if (root?.IsActiveEntity === false) {
|
|
83
|
+
const query = _getSelectDraftDataCqn(root.entityName, root.where)
|
|
84
|
+
const result = await cds.tx(req).run(query)
|
|
85
|
+
return result
|
|
115
86
|
}
|
|
116
87
|
|
|
117
|
-
const rootWhere = getKeysCondition(req
|
|
88
|
+
const rootWhere = getKeysCondition(req)
|
|
118
89
|
const query = _getSelectDraftDataCqn(ensureNoDraftsSuffix(req.target.name), rootWhere)
|
|
119
90
|
const result = await cds.tx(req).run(query)
|
|
120
91
|
return result
|
|
121
92
|
}
|
|
122
93
|
|
|
123
|
-
const _addDraftDataFromExistingDraft = async
|
|
124
|
-
const
|
|
125
|
-
const result = await _getDraftDataFromExistingDraft(req,
|
|
126
|
-
|
|
127
|
-
if (parent) {
|
|
128
|
-
if (parent.IsActiveEntity === false) {
|
|
129
|
-
_validateDraft(req, result)
|
|
130
|
-
_addDraftDataToContext(req, result)
|
|
131
|
-
return result
|
|
132
|
-
}
|
|
94
|
+
const _addDraftDataFromExistingDraft = async req => {
|
|
95
|
+
const root = _getRoot(req)
|
|
96
|
+
const result = await _getDraftDataFromExistingDraft(req, root)
|
|
133
97
|
|
|
134
|
-
|
|
98
|
+
if (!root) return []
|
|
99
|
+
if (root.IsActiveEntity === false) {
|
|
100
|
+
_validateDraft(req, result)
|
|
101
|
+
_addDraftDataToContext(req, result)
|
|
102
|
+
return result
|
|
135
103
|
}
|
|
136
104
|
|
|
137
105
|
if (result && result.length > 0) _validateDraft(req, result)
|
|
@@ -147,7 +115,7 @@ const _new = async function (req) {
|
|
|
147
115
|
|
|
148
116
|
if (isNavigationToMany(req)) {
|
|
149
117
|
// REVISIT: How can NEW be a navigation to many?
|
|
150
|
-
const result = await _addDraftDataFromExistingDraft(req
|
|
118
|
+
const result = await _addDraftDataFromExistingDraft(req)
|
|
151
119
|
|
|
152
120
|
// in order to fix corner case where active subitems are created in draft case
|
|
153
121
|
if (result.length === 0) req.reject(404)
|
|
@@ -165,7 +133,7 @@ const _new = async function (req) {
|
|
|
165
133
|
const _patchUpdate = async function (req) {
|
|
166
134
|
if (isDraftActivateAction(req)) return
|
|
167
135
|
|
|
168
|
-
const result = await _addDraftDataFromExistingDraft(req
|
|
136
|
+
const result = await _addDraftDataFromExistingDraft(req)
|
|
169
137
|
|
|
170
138
|
// means that the draft does not exists
|
|
171
139
|
if (result.length === 0) req.reject(404)
|
|
@@ -175,11 +143,11 @@ const _patchUpdate = async function (req) {
|
|
|
175
143
|
* Generic Handler for before DELETE and CANCEL requests.
|
|
176
144
|
*/
|
|
177
145
|
const _deleteCancel = async function (req) {
|
|
178
|
-
await _addDraftDataFromExistingDraft(req
|
|
146
|
+
await _addDraftDataFromExistingDraft(req)
|
|
179
147
|
}
|
|
180
148
|
|
|
181
|
-
const _validateDraftBoundAction = async function (req
|
|
182
|
-
const result = await _getDraftDataFromExistingDraft(req,
|
|
149
|
+
const _validateDraftBoundAction = async function (req) {
|
|
150
|
+
const result = await _getDraftDataFromExistingDraft(req, _getRoot(req))
|
|
183
151
|
const isBoundAction = true
|
|
184
152
|
if (result && result.length > 0) _validateDraft(req, result, isBoundAction)
|
|
185
153
|
}
|
|
@@ -196,7 +164,7 @@ const _registerBoundActionHandlers = function (entityName, actions) {
|
|
|
196
164
|
)
|
|
197
165
|
|
|
198
166
|
for (const action of boundActions) {
|
|
199
|
-
this.before(action.name, entityName, req => _validateDraftBoundAction(req
|
|
167
|
+
this.before(action.name, entityName, req => _validateDraftBoundAction(req))
|
|
200
168
|
}
|
|
201
169
|
}
|
|
202
170
|
|
|
@@ -8,10 +8,10 @@ const { deleteDraft } = require('../utils/delete')
|
|
|
8
8
|
*
|
|
9
9
|
* @param req
|
|
10
10
|
*/
|
|
11
|
-
const
|
|
11
|
+
const fioriGenericCancel = function (req) {
|
|
12
12
|
return deleteDraft(req, this)
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
module.exports = cds.service.impl(function (srv, entity) {
|
|
16
|
-
srv.on('CANCEL', entity,
|
|
16
|
+
srv.on('CANCEL', entity, fioriGenericCancel)
|
|
17
17
|
})
|
|
@@ -8,12 +8,12 @@ const { deleteDraft } = require('../utils/delete')
|
|
|
8
8
|
*
|
|
9
9
|
* @param req
|
|
10
10
|
*/
|
|
11
|
-
const
|
|
11
|
+
const fioriGenericDelete = function (req) {
|
|
12
12
|
// we should call deleteDraft only for draft tables and call next
|
|
13
13
|
// to pass delete of active tables to our general implementation
|
|
14
14
|
return deleteDraft(req, this, true)
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
module.exports = cds.service.impl(function (srv, entity) {
|
|
18
|
-
srv.on('DELETE', entity,
|
|
18
|
+
srv.on('DELETE', entity, fioriGenericDelete)
|
|
19
19
|
})
|
|
@@ -68,7 +68,7 @@ const _select = async (lockRecordCQN, draftExistsCQN, selectCQNs, req, dbtx) =>
|
|
|
68
68
|
*
|
|
69
69
|
* @param req
|
|
70
70
|
*/
|
|
71
|
-
const
|
|
71
|
+
const fioriGenericEdit = async function (req) {
|
|
72
72
|
if (!isActiveEntityRequested(req.query.SELECT.where || [])) {
|
|
73
73
|
req.reject(400)
|
|
74
74
|
}
|
|
@@ -160,5 +160,5 @@ const _handler = async function (req) {
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
module.exports = cds.service.impl(function (srv, entity) {
|
|
163
|
-
srv.on('EDIT', entity,
|
|
163
|
+
srv.on('EDIT', entity, fioriGenericEdit)
|
|
164
164
|
})
|
|
@@ -48,7 +48,7 @@ const _getInsertDataCQN = (req, draftUUID) => {
|
|
|
48
48
|
* @param req
|
|
49
49
|
* @param next
|
|
50
50
|
*/
|
|
51
|
-
const
|
|
51
|
+
const fioriGenericNew = async function (req, next) {
|
|
52
52
|
if (!req._draftMetadata) {
|
|
53
53
|
// REVISIT: when is this the case?
|
|
54
54
|
return onDraftActivate(req, next)
|
|
@@ -70,5 +70,5 @@ const _handler = async function (req, next) {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
module.exports = cds.service.impl(function (srv, entity) {
|
|
73
|
-
srv.on('NEW', entity,
|
|
73
|
+
srv.on('NEW', entity, fioriGenericNew)
|
|
74
74
|
})
|