@sap/cds 5.5.3 → 5.6.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 +134 -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/index.js +1 -1
- package/lib/compile/to/sql.js +22 -2
- package/lib/connect/bindings.js +2 -1
- package/lib/connect/index.js +1 -1
- package/lib/core/infer.js +1 -1
- package/lib/core/reflect.js +3 -1
- package/lib/env/index.js +175 -41
- package/lib/env/requires.js +24 -3
- package/lib/i18n/localize.js +33 -5
- package/lib/index.js +7 -6
- package/lib/log/format/kibana.js +6 -2
- package/lib/ql/DELETE.js +1 -1
- package/lib/ql/INSERT.js +1 -1
- package/lib/ql/Query.js +13 -10
- package/lib/ql/SELECT.js +15 -8
- package/lib/ql/UPDATE.js +1 -1
- package/lib/ql/Whereable.js +5 -0
- package/lib/req/context.js +87 -37
- package/lib/req/{impl.js → request.js} +1 -1
- package/lib/req/{res.js → response.js} +0 -0
- package/lib/serve/Service-api.js +1 -1
- package/lib/serve/Service-dispatch.js +12 -2
- package/lib/serve/Service-handlers.js +21 -7
- package/lib/serve/Service-methods.js +1 -1
- package/lib/serve/Transaction.js +7 -6
- package/lib/serve/index.js +1 -1
- package/lib/utils/axios.js +7 -0
- package/lib/utils/data.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 +6 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +37 -35
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
- 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/metadata.js +1 -1
- 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/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 +48 -18
- 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/parse-url.js +9 -2
- 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/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/toggles/alpha.js +1 -1
- 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 +19 -9
- 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 +10 -24
- package/libx/_runtime/db/expand/rawToExpanded.js +2 -1
- 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 +10 -5
- 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 +2 -2
- 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/Service.js +5 -2
- package/libx/_runtime/hana/execute.js +1 -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/enterprise-messaging.js +1 -1
- package/libx/_runtime/messaging/message-queuing.js +18 -0
- package/libx/_runtime/remote/Service.js +14 -2
- 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 +4 -3
- package/libx/_runtime/sqlite/convertAssocToOneManaged.js +1 -3
- package/libx/_runtime/sqlite/execute.js +1 -1
- 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 +42 -29
- package/lib/req/cls.js +0 -39
- 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
|
@@ -159,6 +159,7 @@ class OData {
|
|
|
159
159
|
|
|
160
160
|
this._odataService.on(ATOMICITY_GROUP_START, (odataContext, done) => {
|
|
161
161
|
const data = odataContext.applicationData
|
|
162
|
+
|
|
162
163
|
// start tx
|
|
163
164
|
const txs = (data.txs = data.txs || {})
|
|
164
165
|
const {
|
|
@@ -179,28 +180,14 @@ class OData {
|
|
|
179
180
|
this._odataService.on(ATOMICITY_GROUP_END, async (odataErr, odataContext, done) => {
|
|
180
181
|
const tx = odataContext.applicationData.txs[odataContext.id]
|
|
181
182
|
let errors = odataErr || odataContext.failedRequests.length > 0
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
await tx.commit(odataContext.applicationData.results[odataContext.id])
|
|
185
|
-
done()
|
|
186
|
-
} catch (e) {
|
|
187
|
-
// tx gets rolled back automatically
|
|
188
|
-
// set error on each request of changeset, if commit failed
|
|
189
|
-
const changesetResults = odataContext.applicationData.results[odataContext.id]
|
|
190
|
-
const failedRequests = changesetResults.reduce((obj, resultEntry) => {
|
|
191
|
-
const requestId = resultEntry.req._.odataReq.getOdataRequestId()
|
|
192
|
-
const { error, statusCode } = normalizeError(e, resultEntry.req)
|
|
193
|
-
obj[requestId] = Object.assign(error, { statusCode })
|
|
194
|
-
return obj
|
|
195
|
-
}, {})
|
|
196
|
-
done(e, { failedRequests })
|
|
197
|
-
}
|
|
198
|
-
} else {
|
|
183
|
+
|
|
184
|
+
if (errors) {
|
|
199
185
|
// rollback without errors to not trigger srv.on('error') with array
|
|
200
186
|
await tx.rollback()
|
|
201
187
|
// invoke srv.on('error') for each error and build failedRequests that reflects error modifications
|
|
202
188
|
errors = odataContext.applicationData.errors[odataContext.id]
|
|
203
189
|
const failedRequests = {}
|
|
190
|
+
|
|
204
191
|
for (const e of errors) {
|
|
205
192
|
const { error: err, req } = e
|
|
206
193
|
for (const each of cdsService._handlers._error) each.handler.call(cdsService, err, req)
|
|
@@ -208,7 +195,26 @@ class OData {
|
|
|
208
195
|
const { error, statusCode } = normalizeError(err, req)
|
|
209
196
|
failedRequests[requestId] = Object.assign(error, { statusCode })
|
|
210
197
|
}
|
|
198
|
+
|
|
211
199
|
done(new Error(`Atomicity group "${odataContext.id}" failed`), { failedRequests })
|
|
200
|
+
return
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
await tx.commit(odataContext.applicationData.results[odataContext.id])
|
|
205
|
+
done()
|
|
206
|
+
} catch (e) {
|
|
207
|
+
// tx gets rolled back automatically
|
|
208
|
+
// set error on each request of changeset, if commit failed
|
|
209
|
+
const changesetResults = odataContext.applicationData.results[odataContext.id]
|
|
210
|
+
const failedRequests = changesetResults.reduce((obj, resultEntry) => {
|
|
211
|
+
const requestId = resultEntry.req._.odataReq.getOdataRequestId()
|
|
212
|
+
const { error, statusCode } = normalizeError(e, resultEntry.req)
|
|
213
|
+
obj[requestId] = Object.assign(error, { statusCode })
|
|
214
|
+
return obj
|
|
215
|
+
}, {})
|
|
216
|
+
|
|
217
|
+
done(e, { failedRequests })
|
|
212
218
|
}
|
|
213
219
|
})
|
|
214
220
|
|
|
@@ -233,37 +239,33 @@ class OData {
|
|
|
233
239
|
/**
|
|
234
240
|
* Process request.
|
|
235
241
|
*
|
|
236
|
-
* @param req
|
|
237
|
-
* @param res
|
|
242
|
+
* @param {http.IncomingMessage} req
|
|
243
|
+
* @param {http.ServerResponse} res
|
|
238
244
|
* @private
|
|
239
245
|
*/
|
|
240
246
|
// REVISIT: Remove this when we replaced Okra
|
|
241
247
|
process(req, res) {
|
|
248
|
+
const headers = req.headers
|
|
249
|
+
const acceptHeader = headers && headers.accept
|
|
250
|
+
|
|
242
251
|
// default to combination [...];IEEE754Compatible=true;ExponentialDecimals=true if one is omitted
|
|
243
|
-
if (
|
|
244
|
-
if (
|
|
245
|
-
req.headers.accept.includes('IEEE754Compatible=true') &&
|
|
246
|
-
!req.headers.accept.includes('ExponentialDecimals')
|
|
247
|
-
) {
|
|
252
|
+
if (acceptHeader && acceptHeader.startsWith('application/json')) {
|
|
253
|
+
if (acceptHeader.includes('IEEE754Compatible=true') && !acceptHeader.includes('ExponentialDecimals')) {
|
|
248
254
|
req.headers.accept += ';ExponentialDecimals=true'
|
|
249
|
-
} else if (
|
|
250
|
-
req.headers.accept.includes('ExponentialDecimals=true') &&
|
|
251
|
-
!req.headers.accept.includes('IEEE754Compatible')
|
|
252
|
-
) {
|
|
255
|
+
} else if (acceptHeader.includes('ExponentialDecimals=true') && !acceptHeader.includes('IEEE754Compatible')) {
|
|
253
256
|
req.headers.accept += ';IEEE754Compatible=true'
|
|
254
257
|
}
|
|
255
258
|
|
|
259
|
+
const contentType = headers['content-type']
|
|
260
|
+
|
|
256
261
|
// add IEEE754Compatible=true if !strict_numbers
|
|
257
262
|
if (
|
|
258
263
|
!cds.env.features.strict_numbers &&
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
!
|
|
264
|
+
contentType &&
|
|
265
|
+
contentType.includes('application/json') &&
|
|
266
|
+
!contentType.includes('IEEE754Compatible')
|
|
262
267
|
) {
|
|
263
|
-
req.headers['content-type'] =
|
|
264
|
-
'application/json',
|
|
265
|
-
'application/json;IEEE754Compatible=true'
|
|
266
|
-
)
|
|
268
|
+
req.headers['content-type'] = contentType.replace('application/json', 'application/json;IEEE754Compatible=true')
|
|
267
269
|
}
|
|
268
270
|
}
|
|
269
271
|
|
|
@@ -206,7 +206,7 @@ class ODataRequest extends cds.Request {
|
|
|
206
206
|
})
|
|
207
207
|
|
|
208
208
|
// req.attr
|
|
209
|
-
const attr = { identityZone: this.
|
|
209
|
+
const attr = { identityZone: this.tenant }
|
|
210
210
|
Object.defineProperty(this, 'attr', {
|
|
211
211
|
get() {
|
|
212
212
|
if (!cds._deprecationWarningForAttr) {
|
|
@@ -9,7 +9,7 @@ const { getSapMessages } = require('../../../../common/error/frontend')
|
|
|
9
9
|
const { validateResourcePath } = require('../utils/request')
|
|
10
10
|
const { isReturnMinimal } = require('../utils/handlerUtils')
|
|
11
11
|
const readAfterWrite = require('../utils/readAfterWrite')
|
|
12
|
-
const { toODataResult, postProcess } = require('../utils/result')
|
|
12
|
+
const { toODataResult, postProcess, postProcessMinimal } = require('../utils/result')
|
|
13
13
|
const { mergeJson } = require('../../../services/utils/compareJson')
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -44,6 +44,8 @@ const create = service => {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
postProcess(req, odataRes, service, result)
|
|
47
|
+
} else {
|
|
48
|
+
postProcessMinimal(req, result)
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
if (changeset) {
|
|
@@ -93,6 +93,11 @@ const getErrorHandler = (crashOnError = true, srv) => {
|
|
|
93
93
|
req = odataReq.getIncomingRequest()
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
if (err.getRootCause && typeof err.getRootCause === 'function') {
|
|
97
|
+
// > an OKRA error
|
|
98
|
+
err = _betterOkraError(err)
|
|
99
|
+
}
|
|
100
|
+
|
|
96
101
|
// invoke srv.on('error', function (err, req) { ... }) here in special situations
|
|
97
102
|
// REVISIT: if for compat reasons, remove once cds^5.1
|
|
98
103
|
if (srv._handlers._error) {
|
|
@@ -107,11 +112,6 @@ const getErrorHandler = (crashOnError = true, srv) => {
|
|
|
107
112
|
}
|
|
108
113
|
}
|
|
109
114
|
|
|
110
|
-
if (err.getRootCause && typeof err.getRootCause === 'function') {
|
|
111
|
-
// > an OKRA error
|
|
112
|
-
err = _betterOkraError(err)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
115
|
// add content id if not generated by okra ("~...")
|
|
116
116
|
const contentId = odataReq.getOdataRequestId()
|
|
117
117
|
if (contentId && !contentId.match(/^~/)) err['@Core.ContentID'] = contentId
|
|
@@ -36,7 +36,7 @@ const metadata = service => {
|
|
|
36
36
|
try {
|
|
37
37
|
const req = odataReq.getIncomingRequest()
|
|
38
38
|
|
|
39
|
-
const tenant = req.
|
|
39
|
+
const tenant = req.tenant
|
|
40
40
|
// REVISIT: can we take locale from user, or is there some odata special wrt metadata?
|
|
41
41
|
const locale = odataRes.getContract().getLocale()
|
|
42
42
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const cds = require('../../../../cds')
|
|
2
2
|
const { SELECT } = cds.ql
|
|
3
3
|
const ODataRequest = require('../ODataRequest')
|
|
4
|
+
const { rewriteExpandAsterisk } = require('../../../../common/utils/rewriteAsterisks')
|
|
4
5
|
|
|
5
6
|
const {
|
|
6
7
|
QueryOptions,
|
|
@@ -78,7 +79,7 @@ const _getCount = async (tx, readReq) => {
|
|
|
78
79
|
|
|
79
80
|
// Copy CQN including from and where and changing columns
|
|
80
81
|
const select = SELECT.from(readReq.query.SELECT.from)
|
|
81
|
-
select.SELECT.columns = [{ func: 'count', args: [{ val: '1' }], as: '
|
|
82
|
+
select.SELECT.columns = [{ func: 'count', args: [{ val: '1' }], as: '$count' }]
|
|
82
83
|
|
|
83
84
|
if (readReq.query.SELECT.where) select.SELECT.where = readReq.query.SELECT.where
|
|
84
85
|
if (readReq.query.SELECT.search) select.SELECT.search = readReq.query.SELECT.search
|
|
@@ -92,13 +93,13 @@ const _getCount = async (tx, readReq) => {
|
|
|
92
93
|
|
|
93
94
|
// Define new CQN
|
|
94
95
|
req.query = select
|
|
96
|
+
// todo check limit
|
|
97
|
+
const result = await tx.dispatch(req)
|
|
95
98
|
|
|
96
|
-
|
|
99
|
+
const count = (result[0] && (result[0].$count || result[0]._counted_)) || 0
|
|
97
100
|
|
|
98
101
|
// Transform into scalar result
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return toODataResult(result)
|
|
102
|
+
return toODataResult(count)
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
/**
|
|
@@ -318,6 +319,7 @@ const _readStream = async (tx, req, segments) => {
|
|
|
318
319
|
if (
|
|
319
320
|
headers &&
|
|
320
321
|
headers.accept &&
|
|
322
|
+
contentType &&
|
|
321
323
|
!headers.accept.includes('*/*') &&
|
|
322
324
|
!headers.accept.includes(contentType) &&
|
|
323
325
|
!headers.accept.includes(contentType.split('/')[0] + '/*')
|
|
@@ -327,7 +329,7 @@ const _readStream = async (tx, req, segments) => {
|
|
|
327
329
|
|
|
328
330
|
if (contentType) streamObj['*@odata.mediaContentType'] = contentType
|
|
329
331
|
if (contentDisposition) {
|
|
330
|
-
req._.odataRes.setHeader('Content-Disposition', `attachment; filename="${contentDisposition}"`)
|
|
332
|
+
req._.odataRes.setHeader('Content-Disposition', `attachment; filename="${encodeURIComponent(contentDisposition)}"`)
|
|
331
333
|
}
|
|
332
334
|
return streamObj
|
|
333
335
|
}
|
|
@@ -381,6 +383,7 @@ const _readAndTransform = (tx, req, odataReq) => {
|
|
|
381
383
|
break
|
|
382
384
|
}
|
|
383
385
|
}
|
|
386
|
+
|
|
384
387
|
return _readStream(tx, req, segments)
|
|
385
388
|
}
|
|
386
389
|
|
|
@@ -431,11 +434,13 @@ const _getTarget = (ref, target, definitions) => {
|
|
|
431
434
|
}
|
|
432
435
|
|
|
433
436
|
const _getRestrictedExpand = (columns, target, definitions) => {
|
|
434
|
-
if (!columns || !target) return
|
|
437
|
+
if (!columns || !target || columns === '*') return
|
|
435
438
|
|
|
436
439
|
const annotation = target['@Capabilities.ExpandRestrictions.NonExpandableProperties']
|
|
437
440
|
const restrictions = annotation && annotation.map(element => element['='])
|
|
438
441
|
|
|
442
|
+
rewriteExpandAsterisk(columns, target)
|
|
443
|
+
|
|
439
444
|
for (const col of columns) {
|
|
440
445
|
if (col.expand) {
|
|
441
446
|
if (restrictions && restrictions.length !== 0) {
|
|
@@ -473,6 +478,7 @@ const read = service => {
|
|
|
473
478
|
return next(e)
|
|
474
479
|
}
|
|
475
480
|
|
|
481
|
+
// REVISIT: this should be in common/generic/auth.js with the rest of the access control stuff
|
|
476
482
|
const restricted = _getRestrictedExpand(
|
|
477
483
|
req.query.SELECT && req.query.SELECT.columns,
|
|
478
484
|
req.target,
|
|
@@ -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
|
|