@sap/cds 5.5.4 → 5.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +138 -1
- package/apis/services.d.ts +27 -1
- package/app/index.js +22 -11
- package/bin/build/buildTaskFactory.js +1 -1
- package/bin/build/provider/buildTaskProviderInternal.js +1 -1
- package/bin/build/provider/fiori/index.js +1 -1
- package/bin/build/provider/hana/2migration.js +8 -7
- package/bin/build/provider/java-cf/index.js +1 -1
- package/bin/deploy/to-hana/hana.js +1 -17
- package/common.cds +8 -0
- package/lib/compile/to/sql.js +22 -2
- package/lib/connect/bindings.js +2 -1
- package/lib/core/reflect.js +4 -1
- package/lib/env/index.js +175 -41
- package/lib/env/requires.js +16 -1
- package/lib/i18n/localize.js +33 -5
- package/lib/index.js +3 -3
- package/lib/log/format/kibana.js +6 -2
- package/lib/ql/Query.js +1 -0
- package/lib/ql/SELECT.js +15 -8
- package/lib/ql/Whereable.js +5 -0
- package/lib/req/context.js +13 -5
- package/lib/serve/Service-dispatch.js +8 -1
- package/lib/serve/Service-methods.js +1 -1
- package/lib/utils/axios.js +7 -0
- package/lib/utils/data.js +1 -1
- package/lib/utils/tests.js +1 -1
- package/libx/_runtime/audit/Service.js +18 -18
- package/libx/_runtime/audit/generic/personal/access.js +1 -1
- package/libx/_runtime/audit/generic/personal/modification.js +3 -2
- package/libx/_runtime/audit/generic/personal/utils.js +23 -63
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +4 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +37 -35
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +3 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +5 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +13 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +84 -34
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +10 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +9 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +8 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +13 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +11 -95
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/ResourcePathParser.js +17 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/SetResponseHeadersCommand.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +6 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +3 -34
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +3 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +64 -31
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +10 -5
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/handlers/update.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +1 -3
- package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +20 -21
- package/libx/_runtime/cds-services/services/utils/columns.js +6 -1
- package/libx/_runtime/cds-services/services/utils/compareJson.js +1 -8
- package/libx/_runtime/cds-services/services/utils/differ.js +7 -26
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +2 -4
- package/libx/_runtime/cds-services/util/assert.js +29 -13
- package/libx/_runtime/cds.js +2 -1
- package/libx/_runtime/common/aspects/Association.js +72 -0
- package/libx/_runtime/common/aspects/any.js +8 -45
- package/libx/_runtime/common/aspects/entity.js +0 -1
- package/libx/_runtime/common/aspects/relation.js +40 -0
- package/libx/_runtime/common/aspects/utils.js +73 -1
- package/libx/_runtime/common/auth/strategies/utils/uaa.js +10 -14
- package/libx/_runtime/common/composition/data.js +3 -2
- package/libx/_runtime/common/composition/delete.js +3 -1
- package/libx/_runtime/common/composition/tree.js +23 -18
- package/libx/_runtime/common/composition/update.js +6 -1
- package/libx/_runtime/common/composition/utils.js +34 -8
- package/libx/_runtime/common/error/frontend.js +6 -1
- package/libx/_runtime/common/generic/auth.js +15 -13
- package/libx/_runtime/common/generic/crud.js +2 -2
- package/libx/_runtime/common/generic/etag.js +11 -8
- package/libx/_runtime/common/generic/input.js +3 -3
- package/libx/_runtime/common/generic/paging.js +9 -5
- package/libx/_runtime/common/generic/put.js +3 -2
- package/libx/_runtime/common/generic/sorting.js +3 -3
- package/libx/_runtime/common/generic/temporal.js +3 -3
- package/libx/_runtime/common/utils/cqn.js +20 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +125 -139
- package/libx/_runtime/common/utils/csn.js +50 -52
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +41 -176
- package/libx/_runtime/common/utils/generateOnCond.js +40 -70
- package/libx/_runtime/common/utils/{enrichWithKeysFromWhere.js → keys.js} +29 -28
- package/libx/_runtime/common/utils/postProcessing.js +3 -0
- package/libx/_runtime/common/utils/propagateForeignKeys.js +84 -0
- package/libx/_runtime/common/utils/resolveStructured.js +1 -1
- package/libx/_runtime/common/utils/resolveView.js +7 -5
- package/libx/_runtime/common/utils/rewriteAsterisks.js +94 -0
- package/libx/_runtime/common/utils/search2cqn4sql.js +9 -8
- package/libx/_runtime/common/utils/template.js +54 -46
- package/libx/_runtime/db/Service.js +9 -2
- package/libx/_runtime/db/expand/expandCQNToJoin.js +54 -33
- package/libx/_runtime/db/expand/rawToExpanded.js +2 -1
- package/libx/_runtime/db/generic/arrayed.js +13 -28
- package/libx/_runtime/db/generic/create.js +1 -0
- package/libx/_runtime/db/generic/input.js +7 -11
- package/libx/_runtime/db/generic/integrity.js +2 -2
- package/libx/_runtime/db/generic/rewrite.js +2 -5
- package/libx/_runtime/db/generic/update.js +1 -0
- package/libx/_runtime/db/query/read.js +9 -4
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +6 -0
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +7 -2
- package/libx/_runtime/db/sql-builder/annotations.js +1 -0
- package/libx/_runtime/db/utils/columns.js +14 -43
- package/libx/_runtime/db/utils/deep.js +5 -7
- package/libx/_runtime/fiori/generic/activate.js +3 -2
- package/libx/_runtime/fiori/generic/before.js +2 -2
- package/libx/_runtime/fiori/generic/cancel.js +3 -2
- package/libx/_runtime/fiori/generic/delete.js +3 -2
- package/libx/_runtime/fiori/generic/edit.js +3 -3
- package/libx/_runtime/fiori/generic/new.js +2 -2
- package/libx/_runtime/fiori/generic/patch.js +2 -2
- package/libx/_runtime/fiori/generic/prepare.js +2 -2
- package/libx/_runtime/fiori/generic/read.js +17 -63
- package/libx/_runtime/fiori/generic/readOverDraft.js +4 -4
- package/libx/_runtime/fiori/uiflex/extensibility/index.cds +15 -0
- package/libx/_runtime/fiori/uiflex/extensibility/index.js +148 -0
- package/libx/_runtime/fiori/uiflex/handler/transformREAD.js +119 -0
- package/libx/_runtime/fiori/uiflex/handler/transformRESULT.js +43 -0
- package/libx/_runtime/fiori/uiflex/handler/transformWRITE.js +62 -0
- package/libx/_runtime/fiori/uiflex/index.js +35 -0
- package/libx/_runtime/fiori/uiflex/utils.js +78 -0
- package/libx/_runtime/fiori/utils/handler.js +3 -13
- package/libx/_runtime/fiori/utils/where.js +6 -1
- package/libx/_runtime/hana/pool.js +12 -11
- package/libx/_runtime/hana/search2cqn4sql.js +34 -43
- package/libx/_runtime/hana/searchToContains.js +3 -3
- package/libx/_runtime/index.js +5 -2
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +1 -1
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +16 -3
- package/libx/_runtime/messaging/common-utils/connections.js +11 -14
- package/libx/_runtime/messaging/common-utils/naming-conventions.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -1
- package/libx/_runtime/messaging/message-queuing.js +18 -0
- package/libx/_runtime/remote/Service.js +20 -4
- package/libx/_runtime/remote/utils/client-types.d.ts +7 -0
- package/libx/_runtime/remote/utils/client.js +117 -23
- package/libx/_runtime/sqlite/Service.js +2 -2
- package/libx/_runtime/sqlite/convertAssocToOneManaged.js +1 -3
- package/libx/gql/GraphQLAdapter.js +33 -0
- package/libx/gql/constants/adapter.js +69 -0
- package/libx/gql/constants/cds.js +18 -0
- package/libx/gql/constants/graphql.js +33 -0
- package/libx/gql/resolvers/crud/create.js +15 -0
- package/libx/gql/resolvers/crud/delete.js +24 -0
- package/libx/gql/resolvers/crud/index.js +6 -0
- package/libx/gql/resolvers/crud/read.js +25 -0
- package/libx/gql/resolvers/crud/update.js +31 -0
- package/libx/gql/resolvers/crud/utils/index.js +36 -0
- package/libx/gql/resolvers/field.js +5 -0
- package/libx/gql/resolvers/index.js +7 -0
- package/libx/gql/resolvers/mutation.js +23 -0
- package/libx/gql/resolvers/parse/ast/enrich.js +51 -0
- package/libx/gql/resolvers/parse/ast/fragment.js +11 -0
- package/libx/gql/resolvers/parse/ast/fromObject.js +39 -0
- package/libx/gql/resolvers/parse/ast/index.js +3 -0
- package/libx/gql/resolvers/parse/ast/meta.js +4 -0
- package/libx/gql/resolvers/parse/ast/variable.js +7 -0
- package/libx/gql/resolvers/parse/ast2cqn/columns.js +42 -0
- package/libx/gql/resolvers/parse/ast2cqn/entries.js +31 -0
- package/libx/gql/resolvers/parse/ast2cqn/index.js +8 -0
- package/libx/gql/resolvers/parse/ast2cqn/limit.js +6 -0
- package/libx/gql/resolvers/parse/ast2cqn/orderBy.js +24 -0
- package/libx/gql/resolvers/parse/ast2cqn/utils/index.js +3 -0
- package/libx/gql/resolvers/parse/ast2cqn/where.js +70 -0
- package/libx/gql/resolvers/parse/utils/index.js +8 -0
- package/libx/gql/resolvers/query.js +13 -0
- package/libx/gql/resolvers/root.js +34 -0
- package/libx/gql/schema/generate.js +18 -0
- package/libx/gql/schema/index.js +5 -0
- package/libx/gql/schema/mutation.js +76 -0
- package/libx/gql/schema/query.js +108 -0
- package/libx/gql/schema/typeDefMap.js +45 -0
- package/libx/gql/schema/utils/index.js +54 -0
- package/libx/gql/utils/index.js +12 -0
- package/libx/{_runtime/odata/cqn2odata.js → odata/cqn2odata/index.js} +39 -100
- package/libx/odata/index.js +80 -0
- package/libx/odata/odata2cqn/afterburner.js +170 -0
- package/libx/{_runtime/odata/odata2cqn.pegjs → odata/odata2cqn/grammar.pegjs} +102 -123
- package/libx/odata/odata2cqn/index.js +3 -0
- package/libx/odata/odata2cqn/parser.js +1 -0
- package/libx/odata/utils/index.js +64 -0
- package/libx/rest/RestAdapter.js +101 -0
- package/libx/rest/RestRequest.js +30 -0
- package/libx/rest/index.js +3 -0
- package/libx/rest/middleware/auth.js +22 -0
- package/libx/rest/middleware/content.js +15 -0
- package/libx/rest/middleware/create.js +40 -0
- package/libx/rest/middleware/delete.js +20 -0
- package/libx/rest/middleware/error.js +56 -0
- package/libx/rest/middleware/operation.js +39 -0
- package/libx/rest/middleware/parse.js +90 -0
- package/libx/rest/middleware/read.js +29 -0
- package/libx/rest/middleware/update.js +42 -0
- package/libx/rest/utils/data.js +65 -0
- package/package.json +4 -1
- package/server.js +29 -7
- package/libx/_runtime/cds-services/services/utils/diff.js +0 -53
- package/libx/_runtime/cds-services/util/auditlog.js +0 -247
- package/libx/_runtime/cds-services/util/xsenv.js +0 -51
- package/libx/_runtime/common/utils/backlinks.js +0 -83
- package/libx/_runtime/common/utils/rewriteAsterisk.js +0 -72
- package/libx/_runtime/odata/index.js +0 -55
- package/libx/_runtime/odata/odata2cqn.js +0 -1
- package/libx/_runtime/odata/readToCqn.js +0 -129
- package/libx/_runtime/remote/cqn2odata/index.js +0 -2
|
@@ -9,12 +9,18 @@ const {
|
|
|
9
9
|
const { getSapMessages } = require('../../../../common/error/frontend')
|
|
10
10
|
const { validateResourcePath } = require('../utils/request')
|
|
11
11
|
const { isReturnMinimal } = require('../utils/handlerUtils')
|
|
12
|
-
const { foreignKeyPropagations } = require('../../../../common/utils/foreignKeyPropagations')
|
|
13
12
|
const readAfterWrite = require('../utils/readAfterWrite')
|
|
14
|
-
const { toODataResult, postProcess } = require('../utils/result')
|
|
13
|
+
const { toODataResult, postProcess, postProcessMinimal } = require('../utils/result')
|
|
15
14
|
const { hasOmitValuesPreference } = require('../utils/omitValues')
|
|
16
15
|
const { mergeJson } = require('../../../services/utils/compareJson')
|
|
17
16
|
|
|
17
|
+
/*
|
|
18
|
+
const { isStreaming } = require('../utils/stream')
|
|
19
|
+
const { findCsnTargetFor } = require('../../../../common/utils/csn')
|
|
20
|
+
const { isActiveEntityRequested, removeIsActiveEntityRecursively } = require('../../../../fiori/utils/where')
|
|
21
|
+
const { ensureDraftsSuffix } = require('../../../../fiori/utils/handler')
|
|
22
|
+
*/
|
|
23
|
+
|
|
18
24
|
const _isUpsertAllowed = target => {
|
|
19
25
|
return !(cds.env.runtime && cds.env.runtime.allow_upsert === false) && !(target && target._isDraftEnabled)
|
|
20
26
|
}
|
|
@@ -36,53 +42,41 @@ const _infoForeignKeyInParent = (req, odataReq, odataRes, tx) => {
|
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
const navID = typeof nav === 'string' ? nav : nav.id
|
|
39
|
-
const
|
|
45
|
+
const navElement = tx.model.definitions[parent].elements[navID]
|
|
40
46
|
|
|
41
47
|
// not a containment
|
|
42
|
-
if (!
|
|
48
|
+
if (!navElement['@odata.contained']) {
|
|
43
49
|
return info
|
|
44
50
|
}
|
|
45
51
|
|
|
46
52
|
const where = req.query.INSERT.into.ref[0].where
|
|
47
|
-
return { parent,
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const _getParentKey = (parentKeyObj, parentKey, childKey, req) => {
|
|
51
|
-
let parentKeyVal, parentUpdateRequired
|
|
52
|
-
|
|
53
|
-
if (parentKeyObj.length !== 0 && parentKeyObj[0][parentKey] !== null) {
|
|
54
|
-
parentKeyVal = parentKeyObj[0][parentKey]
|
|
55
|
-
} else if (req.target.keys[childKey].type === 'cds.UUID') {
|
|
56
|
-
parentUpdateRequired = true
|
|
57
|
-
parentKeyVal = cds.utils.uuid()
|
|
58
|
-
} else {
|
|
59
|
-
throw new Error('Only keys of type UUID can be generated: ' + childKey)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return { parentKeyVal, parentUpdateRequired }
|
|
53
|
+
return { parent, navElement, where }
|
|
63
54
|
}
|
|
64
55
|
|
|
65
56
|
const _create = async (req, odataReq, odataRes, tx) => {
|
|
66
57
|
let result
|
|
67
58
|
|
|
68
|
-
const { parent,
|
|
69
|
-
if (parent &&
|
|
70
|
-
const onKeys =
|
|
71
|
-
const parentKeys = onKeys.map(key => key.
|
|
59
|
+
const { parent, navElement, where } = _infoForeignKeyInParent(req, odataReq, odataRes, tx)
|
|
60
|
+
if (parent && navElement && where) {
|
|
61
|
+
const onKeys = navElement._foreignKeys
|
|
62
|
+
const parentKeys = onKeys.filter(key => key.parentElement).map(key => key.parentElement.name)
|
|
72
63
|
const parentKeyObj = await tx.run(SELECT.from(parent).columns(parentKeys).where(where))
|
|
73
64
|
|
|
74
65
|
const parentUpdateObj = {}
|
|
75
66
|
onKeys.forEach(key => {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
key.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
67
|
+
let parentKeyVal, parentUpdateRequired
|
|
68
|
+
if (parentKeyObj.length !== 0 && parentKeyObj[0][key.parentElement.name] !== null) {
|
|
69
|
+
parentKeyVal = parentKeyObj[0][key.parentElement.name]
|
|
70
|
+
} else if (key.childElement.type === 'cds.UUID' && key.childElement.key) {
|
|
71
|
+
parentUpdateRequired = true
|
|
72
|
+
parentKeyVal = cds.utils.uuid()
|
|
73
|
+
} else {
|
|
74
|
+
throw new Error('Only keys of type UUID can be generated: ' + key.childFieldName)
|
|
75
|
+
}
|
|
76
|
+
odataReq.getBody()[key.childElement.name] = parentKeyVal
|
|
83
77
|
|
|
84
78
|
if (parentUpdateRequired) {
|
|
85
|
-
parentUpdateObj[key.
|
|
79
|
+
parentUpdateObj[key.parentElement.name] = parentKeyVal
|
|
86
80
|
}
|
|
87
81
|
})
|
|
88
82
|
|
|
@@ -107,7 +101,7 @@ const _updateThenCreate = async (req, odataReq, odataRes, tx) => {
|
|
|
107
101
|
try {
|
|
108
102
|
result = await tx.dispatch(req)
|
|
109
103
|
} catch (e) {
|
|
110
|
-
if (e.code === 404 && _isUpsertAllowed(req.target)) {
|
|
104
|
+
if ((e.code === 404 || e.status === 404 || e.statusCode === 404) && _isUpsertAllowed(req.target)) {
|
|
111
105
|
// REVISIT: remove error (and child?) from tx.context? -> would require a unique req.id
|
|
112
106
|
;[result, req] = await _create(req, odataReq, odataRes, tx)
|
|
113
107
|
} else {
|
|
@@ -127,6 +121,58 @@ const _readAfterWriteAndVirtuals = async (req, service, result) => {
|
|
|
127
121
|
const _shouldReadPreviousResult = req =>
|
|
128
122
|
req.event === 'UPDATE' && !isReturnMinimal(req) && hasOmitValuesPreference(req.headers.prefer, 'defaults')
|
|
129
123
|
|
|
124
|
+
/*
|
|
125
|
+
const _getEntity = (segments, model) => {
|
|
126
|
+
let entityName, namespace
|
|
127
|
+
const previous = segments[segments.length - 2]
|
|
128
|
+
if (previous.getKind() === 'ENTITY') {
|
|
129
|
+
entityName = previous.getEntitySet().getName()
|
|
130
|
+
namespace = previous.getEdmType().getFullQualifiedName().namespace
|
|
131
|
+
} else if (previous.getKind() === 'NAVIGATION.TO.ONE') {
|
|
132
|
+
entityName = previous.getTarget().getName()
|
|
133
|
+
namespace = previous.getTarget().getEntityType().getFullQualifiedName().namespace
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (entityName) {
|
|
137
|
+
return findCsnTargetFor(entityName, model, namespace)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const _getMediaType = entity => {
|
|
142
|
+
if (entity._hasPersistenceSkip) return
|
|
143
|
+
|
|
144
|
+
return Object.values(entity.elements).find(ele => ele['@Core.IsMediaType'])
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const _getMediaTypeCQN = (mediaType, contentType, entity, req) => {
|
|
148
|
+
const where = req.query.UPDATE.entity.ref[0].where
|
|
149
|
+
const isActive = isActiveEntityRequested(where)
|
|
150
|
+
const data = {}
|
|
151
|
+
data[mediaType.name] = contentType
|
|
152
|
+
const cqn = UPDATE(entity).set(data)
|
|
153
|
+
cqn.UPDATE.where = removeIsActiveEntityRecursively(where)
|
|
154
|
+
if (!isActive) {
|
|
155
|
+
cqn.UPDATE.entity = ensureDraftsSuffix(entity.name)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return cqn
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const _handleMediaType = async (odataReq, model, tx, req) => {
|
|
162
|
+
const segments = odataReq.getUriInfo().getPathSegments()
|
|
163
|
+
const contentType = odataReq._inRequest.headers['content-type']
|
|
164
|
+
if (isStreaming(segments) && contentType) {
|
|
165
|
+
const entity = _getEntity(segments, model)
|
|
166
|
+
if (entity && !entity['@cds.persistence.skip']) {
|
|
167
|
+
const mediaType = _getMediaType(entity)
|
|
168
|
+
if (mediaType) {
|
|
169
|
+
await tx.run(_getMediaTypeCQN(mediaType, contentType, entity, req))
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
*/
|
|
175
|
+
|
|
130
176
|
/**
|
|
131
177
|
* The handler that will be registered with odata-v4.
|
|
132
178
|
*
|
|
@@ -155,8 +201,10 @@ const update = service => {
|
|
|
155
201
|
|
|
156
202
|
let result, err, commit
|
|
157
203
|
try {
|
|
158
|
-
|
|
204
|
+
// // REVISIT: should be handled somewhere else
|
|
205
|
+
// await _handleMediaType(odataReq, service.model, tx, req)
|
|
159
206
|
|
|
207
|
+
let previousResult
|
|
160
208
|
if (_shouldReadPreviousResult(req)) {
|
|
161
209
|
previousResult = await _readAfterWriteAndVirtuals(req, service, result)
|
|
162
210
|
}
|
|
@@ -171,6 +219,8 @@ const update = service => {
|
|
|
171
219
|
}
|
|
172
220
|
|
|
173
221
|
postProcess(req, odataRes, service, result, previousResult)
|
|
222
|
+
} else {
|
|
223
|
+
postProcessMinimal(req, result)
|
|
174
224
|
}
|
|
175
225
|
|
|
176
226
|
if (changeset) {
|
|
@@ -42,10 +42,16 @@ class ExpressionToCQN {
|
|
|
42
42
|
case EdmPrimitiveTypeKind.Double:
|
|
43
43
|
return { val: parseFloat(value) }
|
|
44
44
|
case EdmPrimitiveTypeKind.DateTimeOffset: {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
try {
|
|
46
|
+
let val = new Date(value).toISOString()
|
|
47
|
+
// cut off ms if cds.DateTime
|
|
48
|
+
if (expression._cdsType === 'cds.DateTime') val = val.replace(/\.\d\d\dZ$/, 'Z')
|
|
49
|
+
return { val }
|
|
50
|
+
} catch (e) {
|
|
51
|
+
throw Object.assign(new Error(`The type 'Edm.DateTimeOffset' is not compatible with '${value}'`), {
|
|
52
|
+
status: 400
|
|
53
|
+
})
|
|
54
|
+
}
|
|
49
55
|
}
|
|
50
56
|
default:
|
|
51
57
|
return { val: value }
|
|
@@ -195,17 +195,23 @@ const applyToCQN = (transformations, entity, model) => {
|
|
|
195
195
|
_handleTransformation(transformation, entity, res)
|
|
196
196
|
}
|
|
197
197
|
for (const item of transformation.getGroupByItems()) {
|
|
198
|
-
|
|
198
|
+
const pathSegment = item.getPathSegments().length > 0 && item.getPathSegments()[0]
|
|
199
|
+
if (!pathSegment) {
|
|
200
|
+
throw getFeatureNotSupportedError(
|
|
201
|
+
'Transformation "groupby" with query option $apply does not support this request'
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
if (pathSegment.getKind() === 'COMPLEX.PROPERTY') {
|
|
199
205
|
throw getFeatureNotSupportedError(
|
|
200
206
|
'Transformation "groupby" with query option $apply does not support complex properties'
|
|
201
207
|
)
|
|
202
208
|
// TODO support annotations Groupable
|
|
203
209
|
// Odata spec: http://docs.oasis-open.org/odata/odata-data-aggregation-ext/v4.0/cs01/odata-data-aggregation-ext-v4.0-cs01.html#_Toc378326318
|
|
204
210
|
// res.groupBy.push(_complexProperty(item.getPathSegments()))
|
|
205
|
-
} else if (
|
|
211
|
+
} else if (pathSegment.getProperty()) {
|
|
206
212
|
const name = item.getPathSegments()[0].getProperty().getName()
|
|
207
213
|
res.groupBy.push(name)
|
|
208
|
-
} else if (
|
|
214
|
+
} else if (pathSegment.getNavigationProperty()) {
|
|
209
215
|
res.groupBy.push(_createNavGroupBy(item.getPathSegments()))
|
|
210
216
|
}
|
|
211
217
|
}
|
|
@@ -98,7 +98,7 @@ const _getInnerSelect = expandItem => {
|
|
|
98
98
|
* @returns {Array}
|
|
99
99
|
* @private
|
|
100
100
|
*/
|
|
101
|
-
const _getSelectedElements = (expandItem, targetType, relatedEntity) => {
|
|
101
|
+
const _getSelectedElements = (expandItem, targetType, relatedEntity, options) => {
|
|
102
102
|
if (cds.env.effective.odata.proxies || cds.env.effective.odata.xrefs) {
|
|
103
103
|
// proxy target?
|
|
104
104
|
let proxy = true
|
|
@@ -113,7 +113,9 @@ const _getSelectedElements = (expandItem, targetType, relatedEntity) => {
|
|
|
113
113
|
let innerSelectItems = _getInnerSelect(expandItem)
|
|
114
114
|
|
|
115
115
|
if (innerSelectItems.length === 0 || innerSelectItems.some(item => item.isAll())) {
|
|
116
|
-
|
|
116
|
+
// REVISIT: Remove once we clean up our draft handling
|
|
117
|
+
if (options && options.rewriteAsterisks) return _getColumnsFromTargetType(targetType, relatedEntity, true)
|
|
118
|
+
return ['*']
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
// remove navigations from select clause
|
|
@@ -166,7 +168,7 @@ const _filter = (item, expression) => {
|
|
|
166
168
|
item.where = SELECT.from('a').where(expressionToCQN.parse(expression)).SELECT.where
|
|
167
169
|
}
|
|
168
170
|
|
|
169
|
-
const _getItemCQN = (model, name, navigationProperty, expandItem) => {
|
|
171
|
+
const _getItemCQN = (model, name, navigationProperty, expandItem, options) => {
|
|
170
172
|
_notSupported(expandItem)
|
|
171
173
|
|
|
172
174
|
const targetType = navigationProperty.getEntityType()
|
|
@@ -176,7 +178,7 @@ const _getItemCQN = (model, name, navigationProperty, expandItem) => {
|
|
|
176
178
|
const relatedEntity = findCsnTargetFor(entityName, model, namespace)
|
|
177
179
|
const item = {
|
|
178
180
|
ref: name, // ['structured', 'nested_', nestedAssocToOne] if expand on structured
|
|
179
|
-
expand: _getSelectedElements(expandItem, targetType, relatedEntity)
|
|
181
|
+
expand: _getSelectedElements(expandItem, targetType, relatedEntity, options)
|
|
180
182
|
}
|
|
181
183
|
|
|
182
184
|
item.expand.push(..._getInnerExpandItems(model, expandItem, targetType))
|
|
@@ -222,7 +224,7 @@ const _name = expandItem =>
|
|
|
222
224
|
* @param type
|
|
223
225
|
* @returns {Array}
|
|
224
226
|
*/
|
|
225
|
-
const expandToCQN = (model, expandItems, type) => {
|
|
227
|
+
const expandToCQN = (model, expandItems, type, options) => {
|
|
226
228
|
const allElements = []
|
|
227
229
|
const isAll = expandItems.some(item => item.isAll())
|
|
228
230
|
|
|
@@ -230,7 +232,7 @@ const expandToCQN = (model, expandItems, type) => {
|
|
|
230
232
|
const expandItem = _getExpandItem(isAll, expandItems, name)
|
|
231
233
|
|
|
232
234
|
if (isAll || expandItem) {
|
|
233
|
-
allElements.push(_getItemCQN(model, [name], navigationProperty, expandItem))
|
|
235
|
+
allElements.push(_getItemCQN(model, [name], navigationProperty, expandItem, options))
|
|
234
236
|
}
|
|
235
237
|
}
|
|
236
238
|
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
const cds = require('../../../../cds')
|
|
2
2
|
|
|
3
|
-
const { _newReadToCQN } = require('../../../../odata/readToCqn')
|
|
4
|
-
|
|
5
3
|
const {
|
|
6
4
|
Components: { DATA_CREATE_HANDLER, DATA_DELETE_HANDLER, DATA_READ_HANDLER, DATA_UPDATE_HANDLER }
|
|
7
5
|
} = require('../okra/odata-server')
|
|
@@ -32,7 +30,7 @@ module.exports = (component, service, target, data, odataReq, upsert) => {
|
|
|
32
30
|
case DATA_DELETE_HANDLER:
|
|
33
31
|
return deleteToCQN(service, odataReq)
|
|
34
32
|
case DATA_READ_HANDLER:
|
|
35
|
-
return odata2cqn ?
|
|
33
|
+
return odata2cqn ? cds.odata.parse(odataReq, { service }) : readToCQN(service, target, odataReq)
|
|
36
34
|
case DATA_UPDATE_HANDLER:
|
|
37
35
|
return updateToCQN(service, data, odataReq)
|
|
38
36
|
case 'BOUND.ACTION':
|
|
@@ -2,9 +2,8 @@ const cds = require('../../../../cds')
|
|
|
2
2
|
const { SELECT } = cds.ql
|
|
3
3
|
|
|
4
4
|
const QueryOptions = require('../okra/odata-server').QueryOptions
|
|
5
|
-
const { getColumns } = require('../../../services/utils/columns')
|
|
6
5
|
const { isNavigation, isPathSupported } = require('./selectHelper')
|
|
7
|
-
const { isViewWithParams,
|
|
6
|
+
const { isViewWithParams, getValidationQuery } = require('./selectHelper')
|
|
8
7
|
const { ensureUnlocalized } = require('../../../../fiori/utils/handler')
|
|
9
8
|
const ExpressionToCQN = require('./ExpressionToCQN')
|
|
10
9
|
const orderByToCQN = require('./orderByToCQN')
|
|
@@ -16,6 +15,7 @@ const { resolveStructuredName } = require('../utils/handlerUtils')
|
|
|
16
15
|
const { isStreaming } = require('../utils/stream')
|
|
17
16
|
const { convertUrlPathToCqn, getAllKeys } = require('./utils')
|
|
18
17
|
const { getMaxPageSize } = require('../../../../common/utils/page')
|
|
18
|
+
const { isAsteriskColumn } = require('../../../../common/utils/rewriteAsterisks')
|
|
19
19
|
|
|
20
20
|
const {
|
|
21
21
|
COUNT,
|
|
@@ -200,8 +200,10 @@ const _cleanupForApply = (apply, cqn) => {
|
|
|
200
200
|
const selectColumns = cqn.SELECT.columns.map(c => c.as || (c.ref && c.ref[c.ref.length - 1]))
|
|
201
201
|
if (cqn.SELECT.orderBy) {
|
|
202
202
|
// include path expressions
|
|
203
|
-
|
|
204
|
-
|
|
203
|
+
if (!cqn.SELECT.columns.some(c => isAsteriskColumn(c))) {
|
|
204
|
+
const newOrderBy = cqn.SELECT.orderBy.filter(o => _containsSelectedColumn(o, selectColumns))
|
|
205
|
+
cqn.SELECT.orderBy = newOrderBy
|
|
206
|
+
}
|
|
205
207
|
}
|
|
206
208
|
|
|
207
209
|
if (!cqn.SELECT.orderBy || !cqn.SELECT.orderBy.length) {
|
|
@@ -229,16 +231,16 @@ const _checkViewWithParamCall = (isView, segments, kind, name) => {
|
|
|
229
231
|
}
|
|
230
232
|
}
|
|
231
233
|
|
|
232
|
-
const
|
|
234
|
+
const addValidationQueryIfRequired = (segments, isView, cqn, service, kind) => {
|
|
233
235
|
if (isNavigation(segments) && !isView && (kind === NAVIGATION_TO_MANY || kind === NAVIGATION_TO_ONE)) {
|
|
234
|
-
cqn._validationQuery =
|
|
236
|
+
cqn._validationQuery = getValidationQuery(cqn.SELECT.from.ref, service.model)
|
|
235
237
|
cqn._validationQuery.__navToManyWithKeys =
|
|
236
238
|
kind === NAVIGATION_TO_ONE && segments[segments.length - 1].getKeyPredicates().length !== 0
|
|
237
239
|
}
|
|
238
240
|
}
|
|
239
241
|
|
|
240
242
|
const _addKeysToSelectIfNoStreaming = (entity, select, streaming) => {
|
|
241
|
-
// might also be
|
|
243
|
+
// might also be singleton w/o keys
|
|
242
244
|
if (!streaming && entity.keys) {
|
|
243
245
|
for (const k of Object.values(entity.keys)) {
|
|
244
246
|
// REVISIT: !select.includes(k.name) needed?
|
|
@@ -370,9 +372,7 @@ const readToCQN = (service, target, odataReq) => {
|
|
|
370
372
|
}
|
|
371
373
|
|
|
372
374
|
if (select.length === 0) {
|
|
373
|
-
select.push(
|
|
374
|
-
...getColumns(entity, { onlyNames: true, removeIgnore: true, filterDraft: false }).map(col => ({ ref: [col] }))
|
|
375
|
-
)
|
|
375
|
+
select.push('*')
|
|
376
376
|
}
|
|
377
377
|
|
|
378
378
|
if (expand.length) {
|
|
@@ -388,7 +388,7 @@ const readToCQN = (service, target, odataReq) => {
|
|
|
388
388
|
|
|
389
389
|
// keep target as input because of localized view
|
|
390
390
|
const cqn = SELECT.from(isView ? _convertUrlPathToViewCqn(segments) : convertUrlPathToCqn(segments, service), select)
|
|
391
|
-
|
|
391
|
+
addValidationQueryIfRequired(segments, isView, cqn, service, kind)
|
|
392
392
|
|
|
393
393
|
if (Object.keys(apply).length) {
|
|
394
394
|
_extendCqnWithApply(cqn, apply, entity)
|
|
@@ -409,6 +409,8 @@ const readToCQN = (service, target, odataReq) => {
|
|
|
409
409
|
}
|
|
410
410
|
|
|
411
411
|
_cleanupForApply(apply, cqn)
|
|
412
|
+
// just like in new parser
|
|
413
|
+
if (cqn.SELECT.columns.length === 1 && cqn.SELECT.columns[0] === '*') delete cqn.SELECT.columns
|
|
412
414
|
return cqn
|
|
413
415
|
}
|
|
414
416
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const { getFeatureNotSupportedError } = require('../../../util/errors')
|
|
2
|
-
const {
|
|
2
|
+
const { deepCopyArray } = require('../../../../common/utils/copy')
|
|
3
|
+
const cds = require('../../../../cds')
|
|
4
|
+
const { cqn2cqn4sql } = require('../../../../common/utils/cqn2cqn4sql')
|
|
3
5
|
|
|
4
6
|
const isNavigation = pathSegments => {
|
|
5
7
|
return pathSegments.length > 1 && pathSegments[1].getKind().startsWith('NAVIGATION')
|
|
@@ -9,99 +11,14 @@ const isViewWithParams = target => {
|
|
|
9
11
|
return target.params && Object.keys(target.params).length > 0
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return segment.getEntitySet().getEntityType().getFullQualifiedName().toString()
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return segment.getNavigationProperty().getEntityType().getFullQualifiedName().toString()
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const _keysFromSegment = segment => {
|
|
25
|
-
if (segment.getKeyPredicates().length > 0) {
|
|
26
|
-
const keys = {}
|
|
27
|
-
|
|
28
|
-
for (const keyPredicate of segment.getKeyPredicates()) {
|
|
29
|
-
const key = keyPredicate.getEdmRef().getName().replace(/\//g, '.')
|
|
30
|
-
keys[key] = keyPredicate.getText()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return keys
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const _addKeysToWhereIfNeeded = (cqn, keys, tableAlias) => {
|
|
38
|
-
if (keys) {
|
|
39
|
-
for (const key in keys) {
|
|
40
|
-
cqn.where([{ ref: [`${tableAlias}`, `${key}`] }, '=', { val: keys[key] }])
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const _addOnCondToWhere = (cqn, entity, tableAlias, identifier, csn) => {
|
|
46
|
-
const onConditionOptions = {
|
|
47
|
-
associationNames: entity.current,
|
|
48
|
-
csn: csn,
|
|
49
|
-
aliases: {
|
|
50
|
-
select: tableAlias,
|
|
51
|
-
join: identifier
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const onCond = getOnCond(csn.definitions[entity.previous].elements[entity.current], onConditionOptions)
|
|
56
|
-
cqn.where(onCond)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const enhanceCqnWithSubSelects = (cqn, pathSegments, csn, SELECT) => {
|
|
60
|
-
let previousCqn, previousEntityName
|
|
61
|
-
|
|
62
|
-
// if .../property or .../$count requested, must be ignored when building query
|
|
63
|
-
const segments = pathSegments.filter(s => s.getProperty() === null && s.getKind() !== 'COUNT')
|
|
64
|
-
|
|
65
|
-
for (let i = 0; i < segments.length; i++) {
|
|
66
|
-
const isLastElement = i === segments.length - 1
|
|
67
|
-
const tableAlias = `T${i}`
|
|
68
|
-
const entityName = _entityNameFromSegment(segments[i])
|
|
69
|
-
const keys = _keysFromSegment(segments[i])
|
|
70
|
-
let currentCqn
|
|
71
|
-
|
|
72
|
-
if (isLastElement) {
|
|
73
|
-
cqn.SELECT.from = { ref: [entityName], as: tableAlias }
|
|
74
|
-
_addKeysToWhereIfNeeded(cqn, keys, tableAlias)
|
|
75
|
-
} else {
|
|
76
|
-
currentCqn = SELECT.from(`${entityName} as ${tableAlias}`, [1])
|
|
77
|
-
_addKeysToWhereIfNeeded(currentCqn, keys, tableAlias)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (previousCqn) {
|
|
81
|
-
_addOnCondToWhere(
|
|
82
|
-
previousCqn,
|
|
83
|
-
{ current: segments[i].getNavigationProperty().getName(), previous: previousEntityName },
|
|
84
|
-
tableAlias,
|
|
85
|
-
`T${i - 1}`,
|
|
86
|
-
csn
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
if (isLastElement) {
|
|
90
|
-
cqn.where(['exists', previousCqn])
|
|
91
|
-
} else {
|
|
92
|
-
currentCqn.where(['exists', previousCqn])
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
previousCqn = currentCqn
|
|
97
|
-
previousEntityName = entityName
|
|
98
|
-
}
|
|
99
|
-
}
|
|
14
|
+
const getValidationQuery = (ref, model) => {
|
|
15
|
+
const refQuery = deepCopyArray(ref.slice(0, ref.length - 1))
|
|
16
|
+
const cqn = cds.ql.SELECT.from({ ref: refQuery }).columns({
|
|
17
|
+
val: 1,
|
|
18
|
+
as: 'validationQuery'
|
|
19
|
+
})
|
|
100
20
|
|
|
101
|
-
|
|
102
|
-
const cqn = SELECT.from('placeholder')
|
|
103
|
-
enhanceCqnWithSubSelects(cqn, pathSegments.slice(0, pathSegments.length - 1), csn, SELECT)
|
|
104
|
-
return cqn
|
|
21
|
+
return cqn2cqn4sql(cqn, model)
|
|
105
22
|
}
|
|
106
23
|
|
|
107
24
|
const isPathSupported = (supported, pathSegments) => {
|
|
@@ -113,9 +30,8 @@ const isPathSupported = (supported, pathSegments) => {
|
|
|
113
30
|
}
|
|
114
31
|
|
|
115
32
|
module.exports = {
|
|
116
|
-
enhanceCqnWithSubSelects,
|
|
117
33
|
isNavigation,
|
|
118
34
|
isViewWithParams,
|
|
119
35
|
isPathSupported,
|
|
120
|
-
|
|
36
|
+
getValidationQuery
|
|
121
37
|
}
|
package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/ResourcePathParser.js
CHANGED
|
@@ -41,11 +41,11 @@ class ResourcePathParser {
|
|
|
41
41
|
let tokenizer = new UriTokenizer(uriPathSegments[0])
|
|
42
42
|
|
|
43
43
|
tokenizer.requireNext(TokenKind.ODataIdentifier)
|
|
44
|
-
const currentToken = tokenizer.getText()
|
|
45
44
|
|
|
45
|
+
const currentToken = tokenizer.getText()
|
|
46
46
|
let currentResource = new UriResource()
|
|
47
|
-
|
|
48
47
|
let edmResult = this._edmContainer.getEntitySet(currentToken)
|
|
48
|
+
|
|
49
49
|
if (edmResult) {
|
|
50
50
|
currentResource
|
|
51
51
|
.setKind(UriResource.ResourceKind.ENTITY_COLLECTION)
|
|
@@ -53,11 +53,12 @@ class ResourcePathParser {
|
|
|
53
53
|
.setEntitySet(edmResult)
|
|
54
54
|
|
|
55
55
|
this._target = edmResult
|
|
56
|
-
|
|
57
|
-
return result.concat(
|
|
56
|
+
const uriResources = this._parseCollectionNavigation(uriPathSegments, currentResource, tokenizer)
|
|
57
|
+
return result.concat(uriResources)
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
edmResult = this._edmContainer.getSingleton(currentToken)
|
|
61
|
+
|
|
61
62
|
if (edmResult) {
|
|
62
63
|
currentResource
|
|
63
64
|
.setKind(UriResource.ResourceKind.SINGLETON)
|
|
@@ -65,14 +66,13 @@ class ResourcePathParser {
|
|
|
65
66
|
.setIsCollection(false)
|
|
66
67
|
|
|
67
68
|
this._target = edmResult
|
|
68
|
-
|
|
69
69
|
tokenizer.requireNext(TokenKind.EOF)
|
|
70
70
|
uriPathSegments.shift()
|
|
71
|
-
|
|
72
71
|
return result.concat(this._parseSingleNavigation(uriPathSegments, currentResource))
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
edmResult = this._edmContainer.getActionImport(currentToken)
|
|
75
|
+
|
|
76
76
|
if (edmResult) {
|
|
77
77
|
const unboundAction = edmResult.getUnboundAction()
|
|
78
78
|
|
|
@@ -91,6 +91,7 @@ class ResourcePathParser {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
edmResult = this._edmContainer.getFunctionImport(currentToken)
|
|
94
|
+
|
|
94
95
|
if (edmResult) {
|
|
95
96
|
const functions = edmResult.getUnboundFunctions()
|
|
96
97
|
const returnType = functions[0].getReturnType()
|
|
@@ -123,9 +124,9 @@ class ResourcePathParser {
|
|
|
123
124
|
if (tokenizer.next(TokenKind.OPEN)) {
|
|
124
125
|
throw new UriSyntaxError(UriSyntaxError.Message.FUNCTION_IMPORT_EOF, edmResult.getName())
|
|
125
126
|
}
|
|
127
|
+
|
|
126
128
|
tokenizer.requireNext(TokenKind.EOF)
|
|
127
129
|
uriPathSegments.shift()
|
|
128
|
-
|
|
129
130
|
return result.concat(this._parseSingleNavigation(uriPathSegments, currentResource))
|
|
130
131
|
}
|
|
131
132
|
|
|
@@ -133,6 +134,7 @@ class ResourcePathParser {
|
|
|
133
134
|
if (tokenizer.next(TokenKind.OPEN)) {
|
|
134
135
|
throw new UriSyntaxError(UriSyntaxError.Message.FUNCTION_IMPORT_EOF, edmResult.getName())
|
|
135
136
|
}
|
|
137
|
+
|
|
136
138
|
tokenizer.requireNext(TokenKind.EOF)
|
|
137
139
|
uriPathSegments.shift()
|
|
138
140
|
|
|
@@ -148,6 +150,7 @@ class ResourcePathParser {
|
|
|
148
150
|
? this._parseComplexPath(uriPathSegments, currentResource)
|
|
149
151
|
: this._parsePrimitivePath(uriPathSegments, currentResource)
|
|
150
152
|
}
|
|
153
|
+
|
|
151
154
|
return functionRest ? result.concat(currentResource, functionRest) : result.concat(currentResource)
|
|
152
155
|
}
|
|
153
156
|
|
|
@@ -211,7 +214,8 @@ class ResourcePathParser {
|
|
|
211
214
|
}
|
|
212
215
|
}
|
|
213
216
|
|
|
214
|
-
|
|
217
|
+
const uriResources = this._parseCollectionNavPath(uriPathSegments, currentResource, tokenizer)
|
|
218
|
+
return result.concat(uriResources)
|
|
215
219
|
}
|
|
216
220
|
|
|
217
221
|
/**
|
|
@@ -259,11 +263,9 @@ class ResourcePathParser {
|
|
|
259
263
|
if (tokenizer.next(TokenKind.CLOSE)) throw new UriSyntaxError(UriSyntaxError.Message.KEY_EXPECTED)
|
|
260
264
|
|
|
261
265
|
const edmType = currentResource.getEdmType()
|
|
262
|
-
|
|
263
266
|
const keyPredicates = new KeyPredicateParser(this._edm, this._aliases).parse(currentResource, edmType, tokenizer)
|
|
264
267
|
|
|
265
268
|
tokenizer.requireNext(TokenKind.CLOSE)
|
|
266
|
-
|
|
267
269
|
currentResource.setKeyPredicates(keyPredicates).setIsCollection(false)
|
|
268
270
|
|
|
269
271
|
if (currentResource.getKind() === UriResource.ResourceKind.ENTITY_COLLECTION) {
|
|
@@ -383,15 +385,19 @@ class ResourcePathParser {
|
|
|
383
385
|
TokenKind.VALUE,
|
|
384
386
|
UriResource.ResourceKind.VALUE
|
|
385
387
|
)
|
|
388
|
+
|
|
386
389
|
if (valueResource) {
|
|
387
390
|
const currentType = currentResource.getEdmType()
|
|
391
|
+
|
|
388
392
|
if (currentType.hasStream()) {
|
|
389
393
|
return result.concat(valueResource)
|
|
390
394
|
}
|
|
395
|
+
|
|
391
396
|
throw new UriSyntaxError(UriSyntaxError.Message.PREVIOUS_TYPE_HAS_NO_MEDIA, currentType.getName())
|
|
392
397
|
}
|
|
393
398
|
|
|
394
|
-
|
|
399
|
+
const uriResources = this._parsePropertyPath(uriPathSegments, currentResource, tokenizer)
|
|
400
|
+
return result.concat(uriResources)
|
|
395
401
|
}
|
|
396
402
|
|
|
397
403
|
/**
|
|
@@ -138,7 +138,8 @@ class UriParser {
|
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
const uriResources = this._parseRelativeUri(uriPathSegments, uriInfo.getAliases())
|
|
142
|
+
uriInfo.setPathSegments(uriResources)
|
|
142
143
|
|
|
143
144
|
let currentUriSegment = uriPathSegments.shift()
|
|
144
145
|
if (currentUriSegment || currentUriSegment === '') {
|
|
@@ -75,6 +75,7 @@ class SetResponseHeadersCommand extends Command {
|
|
|
75
75
|
? lastSegment.getTarget() && lastSegment.getTarget().isConcurrent()
|
|
76
76
|
: this._request.getConcurrentResource()) &&
|
|
77
77
|
representationKind !== RepresentationKinds.ENTITY_COLLECTION &&
|
|
78
|
+
representationKind !== RepresentationKinds.COUNT &&
|
|
78
79
|
representationKind !== RepresentationKinds.REFERENCE &&
|
|
79
80
|
representationKind !== RepresentationKinds.REFERENCE_COLLECTION
|
|
80
81
|
) {
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
const cds = require('../../../cds')
|
|
2
|
+
|
|
1
3
|
const OData = require('./OData')
|
|
2
4
|
const Dispatcher = require('./Dispatcher')
|
|
3
|
-
|
|
5
|
+
|
|
6
|
+
const { alias2ref } = require('../../../common/utils/csn')
|
|
4
7
|
|
|
5
8
|
const to = service => {
|
|
6
9
|
const edm = cds.compile.to.edm(service.model, { service: service.definition.name })
|
|
7
|
-
|
|
10
|
+
alias2ref(service, edm)
|
|
8
11
|
|
|
12
|
+
const odata = new OData(edm, service.model, service.options)
|
|
9
13
|
odata.addCDSServiceToChannel(service)
|
|
10
14
|
|
|
11
15
|
return new Dispatcher(odata).getService()
|