@sap/cds 7.9.4 → 8.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +128 -3659
- package/_i18n/i18n_en_US_saptrc.properties +113 -0
- package/_i18n/i18n_zh_CN.properties +7 -4
- package/app/index.css +129 -0
- package/app/index.html +16 -64
- package/app/index.js +14 -9
- package/bin/args.js +34 -0
- package/bin/serve.js +18 -24
- package/bin/test.js +97 -0
- package/common.cds +5 -12
- package/eslint.config.mjs +133 -0
- package/lib/auth/basic-auth.js +16 -20
- package/lib/auth/dummy-auth.js +1 -1
- package/lib/auth/ias-auth.js +9 -41
- package/lib/auth/index.js +1 -14
- package/lib/auth/jwt-auth.js +10 -40
- package/lib/compile/cds-compile.js +1 -2
- package/lib/compile/cdsc.js +21 -26
- package/lib/compile/etc/_localized.js +1 -6
- package/lib/compile/etc/csv.js +1 -1
- package/lib/compile/etc/properties.js +1 -1
- package/lib/compile/for/java.js +1 -1
- package/lib/compile/for/lean_drafts.js +4 -6
- package/lib/compile/for/nodejs.js +1 -1
- package/lib/compile/parse.js +4 -0
- package/lib/compile/resolve.js +4 -4
- package/lib/compile/to/edm-files.js +16 -23
- package/lib/compile/to/hana.js +27 -0
- package/lib/compile/to/json.js +1 -1
- package/lib/compile/to/sql.js +5 -1
- package/lib/compile/to/yaml.js +3 -3
- package/lib/dbs/cds-deploy.js +4 -2
- package/lib/env/cds-env.js +10 -14
- package/lib/env/cds-requires.js +30 -13
- package/lib/env/defaults.js +46 -16
- package/lib/env/plugins.js +1 -1
- package/lib/env/schemas/cds-rc.js +8 -4
- package/lib/env/schemas/index.js +7 -7
- package/lib/env/serviceBindings.js +1 -1
- package/lib/index.js +12 -10
- package/lib/lazy.js +1 -1
- package/lib/linked/classes.js +36 -8
- package/lib/linked/entities.js +2 -10
- package/lib/linked/models.js +2 -1
- package/lib/linked/validate.js +292 -0
- package/lib/log/cds-error.js +0 -6
- package/lib/log/cds-log.js +3 -3
- package/lib/log/format/json.js +1 -1
- package/lib/log/service/index.js +0 -1
- package/lib/plugins.js +2 -2
- package/lib/ql/Query.js +2 -10
- package/lib/ql/SELECT.js +1 -1
- package/lib/ql/Whereable.js +3 -2
- package/lib/req/cds-context.js +14 -25
- package/lib/req/context.js +23 -25
- package/lib/req/request.js +1 -34
- package/lib/req/user.js +47 -35
- package/lib/srv/bindings.js +1 -1
- package/lib/srv/cds-connect.js +4 -4
- package/lib/srv/cds-serve.js +2 -2
- package/lib/srv/factory.js +1 -1
- package/lib/srv/middlewares/cds-context.js +11 -22
- package/lib/srv/middlewares/ctx-model.js +2 -3
- package/lib/srv/middlewares/errors.js +41 -8
- package/lib/srv/middlewares/index.js +3 -3
- package/lib/srv/middlewares/trace.js +0 -2
- package/lib/srv/protocols/hcql.js +15 -10
- package/lib/srv/protocols/http.js +44 -49
- package/lib/srv/protocols/index.js +1 -23
- package/lib/srv/protocols/odata-v4.js +12 -74
- package/lib/srv/protocols/rest.js +1 -13
- package/lib/srv/srv-api.js +0 -20
- package/lib/srv/srv-dispatch.js +3 -2
- package/lib/srv/srv-handlers.js +22 -11
- package/lib/srv/srv-methods.js +2 -2
- package/lib/srv/srv-models.js +3 -36
- package/lib/test/expect.js +343 -0
- package/lib/test/index.js +2 -0
- package/lib/test/reporter.js +176 -0
- package/lib/utils/axios.js +10 -9
- package/lib/utils/cds-test.js +85 -36
- package/lib/utils/cds-utils.js +54 -7
- package/lib/utils/check-version.js +0 -4
- package/lib/utils/colors.js +49 -0
- package/lib/utils/data.js +5 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +2 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +3 -30
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +6 -12
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +4 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +12 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +2 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/AbstractEdmStructuredType.js +1 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +5 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ContextURLFactory.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +9 -43
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +8 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +4 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +1 -3
- package/libx/_runtime/cds-services/util/assert.js +1 -1
- package/libx/_runtime/cds.js +10 -3
- package/libx/_runtime/common/Service.js +12 -32
- package/libx/_runtime/common/aspects/any.js +1 -0
- package/libx/_runtime/common/code-ext/execute.js +1 -1
- package/libx/_runtime/common/code-ext/worker.js +0 -1
- package/libx/_runtime/common/composition/data.js +0 -1
- package/libx/_runtime/common/composition/delete.js +0 -1
- package/libx/_runtime/common/composition/tree.js +0 -1
- package/libx/_runtime/common/composition/update.js +3 -3
- package/libx/_runtime/common/error/frontend.js +21 -12
- package/libx/_runtime/common/error/log.js +36 -0
- package/libx/_runtime/common/error/utils.js +2 -5
- package/libx/_runtime/common/generic/auth/autoexpose.js +18 -17
- package/libx/_runtime/common/generic/auth/expand.js +1 -1
- package/libx/_runtime/common/generic/auth/readOnly.js +1 -2
- package/libx/_runtime/common/generic/auth/restrict.js +23 -42
- package/libx/_runtime/common/generic/auth/restrictions.js +2 -7
- package/libx/_runtime/common/generic/auth/utils.js +91 -88
- package/libx/_runtime/common/generic/crud.js +6 -5
- package/libx/_runtime/common/generic/etag.js +7 -12
- package/libx/_runtime/common/generic/input.js +70 -68
- package/libx/_runtime/common/generic/paging.js +1 -0
- package/libx/_runtime/common/generic/sorting.js +1 -0
- package/libx/_runtime/common/generic/temporal.js +8 -2
- package/libx/_runtime/common/i18n/index.js +1 -1
- package/libx/_runtime/common/i18n/messages.properties +3 -1
- package/libx/_runtime/common/utils/binary.js +8 -2
- package/libx/_runtime/common/utils/compareJson.js +5 -1
- package/libx/_runtime/common/utils/copy.js +6 -11
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +16 -14
- package/libx/_runtime/common/utils/differ.js +3 -6
- package/libx/_runtime/common/utils/keys.js +77 -18
- package/libx/_runtime/common/utils/postProcess.js +12 -15
- package/libx/_runtime/common/utils/propagateForeignKeys.js +0 -1
- package/libx/_runtime/common/utils/resolveView.js +2 -3
- package/libx/_runtime/common/utils/restrictions.js +45 -17
- package/libx/_runtime/common/utils/rewriteAsterisks.js +1 -8
- package/libx/_runtime/common/utils/stream.js +3 -16
- package/libx/_runtime/common/utils/streamProp.js +8 -18
- package/libx/_runtime/common/utils/structured.js +1 -1
- package/libx/_runtime/common/utils/ucsn.js +0 -2
- package/libx/_runtime/db/Service.js +0 -72
- package/libx/_runtime/db/data-conversion/post-processing.js +0 -1
- package/libx/_runtime/db/expand/expandCQNToJoin.js +9 -9
- package/libx/_runtime/db/expand/rawToExpanded.js +0 -8
- package/libx/_runtime/db/generic/input.js +3 -8
- package/libx/_runtime/db/generic/rewrite.js +1 -0
- package/libx/_runtime/db/query/read.js +2 -2
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +0 -1
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -1
- package/libx/_runtime/db/utils/columns.js +2 -6
- package/libx/_runtime/fiori/lean-draft.js +138 -56
- package/libx/_runtime/hana/Service.js +0 -1
- package/libx/_runtime/hana/driver.js +1 -1
- package/libx/_runtime/hana/dynatrace.js +1 -2
- package/libx/_runtime/hana/pool.js +11 -21
- package/libx/_runtime/hana/streaming.js +0 -1
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +0 -1
- package/libx/_runtime/messaging/common-utils/authorizedRequest.js +1 -1
- package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -33
- package/libx/_runtime/messaging/event-broker.js +54 -27
- package/libx/_runtime/messaging/file-based.js +3 -3
- package/libx/_runtime/messaging/http-utils/token.js +1 -1
- package/libx/_runtime/messaging/kafka.js +2 -2
- package/libx/_runtime/messaging/redis-messaging.js +0 -1
- package/libx/_runtime/remote/Service.js +25 -25
- package/libx/_runtime/remote/utils/client.js +4 -5
- package/libx/_runtime/remote/utils/cloudSdkProvider.js +0 -3
- package/libx/_runtime/remote/utils/data.js +0 -1
- package/libx/_runtime/sqlite/Service.js +1 -2
- package/libx/_runtime/ucl/Service.js +37 -78
- package/libx/common/assert/index.js +22 -21
- package/libx/common/assert/type-relaxed.js +39 -0
- package/libx/common/assert/utils.js +3 -2
- package/libx/common/assert/validation.js +3 -8
- package/libx/common/utils/index.js +5 -0
- package/libx/common/utils/path.js +51 -0
- package/libx/odata/ODataAdapter.js +126 -0
- package/libx/odata/index.js +15 -2
- package/libx/odata/middleware/batch.js +320 -84
- package/libx/odata/middleware/body-parser.js +33 -0
- package/libx/odata/middleware/create.js +44 -59
- package/libx/odata/middleware/delete.js +23 -12
- package/libx/odata/middleware/error.js +30 -6
- package/libx/odata/middleware/metadata.js +38 -26
- package/libx/odata/middleware/operation.js +93 -69
- package/libx/odata/middleware/parse.js +6 -8
- package/libx/odata/middleware/read.js +117 -93
- package/libx/odata/middleware/service-document.js +22 -19
- package/libx/odata/middleware/stream.js +54 -56
- package/libx/odata/middleware/update.js +79 -87
- package/libx/odata/parse/afterburner.js +191 -175
- package/libx/odata/parse/cqn2odata.js +5 -5
- package/libx/odata/parse/grammar.peggy +27 -20
- package/libx/odata/parse/multipartToJson.js +17 -9
- package/libx/odata/parse/parser.js +1 -1
- package/libx/odata/utils/etag.js +14 -6
- package/libx/odata/utils/index.js +84 -12
- package/libx/odata/utils/metadata.js +161 -0
- package/libx/odata/utils/postProcess.js +89 -0
- package/libx/odata/utils/readAfterWrite.js +134 -17
- package/libx/odata/utils/result.js +36 -142
- package/libx/outbox/index.js +4 -3
- package/libx/rest/RestAdapter.js +115 -182
- package/libx/rest/middleware/create.js +28 -24
- package/libx/rest/middleware/delete.js +7 -10
- package/libx/rest/middleware/error.js +26 -16
- package/libx/rest/middleware/operation.js +48 -41
- package/libx/rest/middleware/parse.js +128 -126
- package/libx/rest/middleware/read.js +20 -27
- package/libx/rest/middleware/update.js +26 -31
- package/package.json +17 -8
- package/server.js +4 -2
- package/apis/cds.d.ts +0 -3
- package/apis/core.d.ts +0 -21
- package/apis/cqn.d.ts +0 -18
- package/apis/csn.d.ts +0 -21
- package/apis/events.d.ts +0 -18
- package/apis/internal/inference.d.ts +0 -18
- package/apis/linked.d.ts +0 -18
- package/apis/log.d.ts +0 -20
- package/apis/models.d.ts +0 -18
- package/apis/ql.d.ts +0 -18
- package/apis/reflect.d.ts +0 -32
- package/apis/server.d.ts +0 -18
- package/apis/services.d.ts +0 -22
- package/bin/cds-serve.js +0 -56
- package/lib/compile/to/gql.js +0 -15
- package/lib/srv/protocols/_legacy.js +0 -44
- package/lib/utils/jest.js +0 -43
- package/libx/_runtime/auth/index.js +0 -193
- package/libx/_runtime/auth/strategies/JWT.js +0 -37
- package/libx/_runtime/auth/strategies/basic.js +0 -20
- package/libx/_runtime/auth/strategies/dummy.js +0 -14
- package/libx/_runtime/auth/strategies/ias-auth.js +0 -1
- package/libx/_runtime/auth/strategies/mock.js +0 -77
- package/libx/_runtime/auth/strategies/xssecUtils.js +0 -93
- package/libx/_runtime/auth/strategies/xsuaa.js +0 -38
- package/libx/_runtime/common/perf/index.js +0 -19
- package/libx/_runtime/common/utils/ensureIEEE754.js +0 -29
- package/libx/_runtime/fiori/draft.js +0 -2
- package/libx/_runtime/fiori/generic/activate.js +0 -190
- package/libx/_runtime/fiori/generic/before.js +0 -201
- package/libx/_runtime/fiori/generic/cancel.js +0 -19
- package/libx/_runtime/fiori/generic/delete.js +0 -21
- package/libx/_runtime/fiori/generic/edit.js +0 -157
- package/libx/_runtime/fiori/generic/index.js +0 -25
- package/libx/_runtime/fiori/generic/new.js +0 -82
- package/libx/_runtime/fiori/generic/patch.js +0 -101
- package/libx/_runtime/fiori/generic/prepare.js +0 -57
- package/libx/_runtime/fiori/generic/read.js +0 -1340
- package/libx/_runtime/fiori/generic/readOverDraft.js +0 -146
- package/libx/_runtime/fiori/utils/csn.js +0 -13
- package/libx/_runtime/fiori/utils/delete.js +0 -114
- package/libx/_runtime/fiori/utils/handler.js +0 -264
- package/libx/_runtime/fiori/utils/lockInfo.js +0 -27
- package/libx/_runtime/fiori/utils/req.js +0 -23
- package/libx/_runtime/fiori/utils/stream.js +0 -36
- package/libx/_runtime/fiori/utils/where.js +0 -254
- package/libx/_runtime/index.js +0 -22
- package/libx/odata/utils/handler.js +0 -120
- package/libx/odata/utils/metaInfo.js +0 -410
- package/libx/odata/utils/path.js +0 -75
- package/libx/rest/RestRequest.js +0 -32
- package/libx/rest/index.js +0 -3
- package/libx/rest/readme.md +0 -1
- /package/libx/common/assert/{type.js → type-strict.js} +0 -0
|
@@ -1,29 +1,27 @@
|
|
|
1
1
|
const cds = require('../../../')
|
|
2
2
|
const { INSERT } = cds.ql
|
|
3
3
|
|
|
4
|
-
const {
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} = require('
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
module.exports = srv =>
|
|
19
|
-
function create(req, res, next) {
|
|
4
|
+
const { calculateLocationHeader, handleSapMessages, getPreferReturnHeader } = require('../utils')
|
|
5
|
+
const getODataMetadata = require('../utils/metadata')
|
|
6
|
+
const postProcess = require('../utils/postProcess')
|
|
7
|
+
const readAfterWrite4 = require('../utils/readAfterWrite')
|
|
8
|
+
const getODataResult = require('../utils/result')
|
|
9
|
+
|
|
10
|
+
const { getKeysAndParamsFromPath } = require('../../common/utils')
|
|
11
|
+
|
|
12
|
+
module.exports = (adapter, isUpsert) => {
|
|
13
|
+
// REVISIT: adapter should be this
|
|
14
|
+
const { service } = adapter
|
|
15
|
+
const _readAfterWrite = readAfterWrite4(adapter, 'create')
|
|
16
|
+
|
|
17
|
+
return function create(req, res, next) {
|
|
20
18
|
const {
|
|
21
19
|
SELECT: { one, from },
|
|
22
20
|
target
|
|
23
21
|
} = req._query
|
|
24
22
|
|
|
25
23
|
// req.__proto__.method is set in case of upsert
|
|
26
|
-
const isUpsert = req.__proto__.method in { PUT: 1, PATCH: 1 }
|
|
24
|
+
// const isUpsert = req.__proto__.method in { PUT: 1, PATCH: 1 }
|
|
27
25
|
|
|
28
26
|
if (one && !isUpsert) {
|
|
29
27
|
const msg = 'Method POST is not allowed for singletons and individual entities'
|
|
@@ -31,18 +29,12 @@ module.exports = srv =>
|
|
|
31
29
|
}
|
|
32
30
|
|
|
33
31
|
// payload & params
|
|
34
|
-
const data =
|
|
35
|
-
const { keys, params } = getKeysAndParamsFromPath(from,
|
|
32
|
+
const data = req.body
|
|
33
|
+
const { keys, params } = getKeysAndParamsFromPath(from, service)
|
|
36
34
|
// add keys from url into payload (overwriting if already present)
|
|
37
35
|
Object.assign(data, keys)
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
const assertOptions = { filter: true, http: { req }, mandatories: true }
|
|
41
|
-
const errs = cds.assert(data, target, assertOptions)
|
|
42
|
-
if (errs) {
|
|
43
|
-
if (errs.length === 1) throw errs[0]
|
|
44
|
-
throw Object.assign(new Error('MULTIPLE_ERRORS'), { statusCode: 400, details: errs })
|
|
45
|
-
}
|
|
37
|
+
const _isDraft = target.drafts && data.IsActiveEntity !== true
|
|
46
38
|
|
|
47
39
|
// query
|
|
48
40
|
const query = INSERT.into(from).entries(data)
|
|
@@ -51,56 +43,49 @@ module.exports = srv =>
|
|
|
51
43
|
const headers = { ...cds.context.http.req.headers, ...req.headers }
|
|
52
44
|
|
|
53
45
|
// we need a cds.Request for multiple reasons, incl. params, headers, sap-messages, read after write, ...
|
|
54
|
-
const cdsReq =
|
|
55
|
-
Object.defineProperty(cdsReq, 'protocol', { value: 'odata-v4' })
|
|
56
|
-
|
|
57
|
-
// API for subrequests of $batch (or incoming request)
|
|
58
|
-
cdsReq.req = req
|
|
59
|
-
cdsReq.res = res
|
|
46
|
+
const cdsReq = adapter.request4({ query, params, headers, req, res })
|
|
60
47
|
|
|
61
48
|
// rewrite event for draft-enabled entities
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
|
|
49
|
+
if (_isDraft) cdsReq.event = 'NEW'
|
|
50
|
+
|
|
51
|
+
// NOTES:
|
|
52
|
+
// - only via srv.run in combination with srv.dispatch inside,
|
|
53
|
+
// we automatically either use a single auto-managed tx for the req (i.e., insert and read after write in same tx)
|
|
54
|
+
// or the auto-managed tx opened for the respective atomicity group, if exists
|
|
55
|
+
// - in the then block of .run(), the transaction is committed (i.e., before sending the response) if a single auto-managed tx is used
|
|
56
|
+
return service
|
|
68
57
|
.run(() => {
|
|
69
|
-
return
|
|
70
|
-
|
|
71
|
-
|
|
58
|
+
return service.dispatch(cdsReq).then(result => {
|
|
59
|
+
// REVISIT: shouldn't read after write be the default behavior (that could be suppressed)?
|
|
72
60
|
// generic handlers indicate that read after write is required
|
|
73
|
-
if (cdsReq._.readAfterWrite)
|
|
74
|
-
// const keys = cdsReq.target.keys?.filter(k => !k.isAssociation)?.reduce((prev, k) => { prev[k] = result[k]; return prev}, {} )
|
|
75
|
-
// const query = SELECT.one(cdsReq.query.INSERT.into, keys)
|
|
76
|
-
const query = cdsReq.event === 'NEW' ? getSimpleSelectCQN(cdsReq.target, result) : getDeepSelect(cdsReq)
|
|
77
|
-
return readAfterWrite(cdsReq, srv, query)
|
|
78
|
-
}
|
|
79
|
-
|
|
61
|
+
if (cdsReq._.readAfterWrite) return _readAfterWrite(cdsReq)
|
|
80
62
|
return result
|
|
81
63
|
})
|
|
82
64
|
})
|
|
83
65
|
.then(result => {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
// determine calculation based on result with req.data as fallback
|
|
87
|
-
if (!target._isSingleton)
|
|
88
|
-
res.set('location', calculateLocationHeader(cdsReq.target, srv, result || cdsReq.data))
|
|
66
|
+
handleSapMessages(cdsReq, req, res)
|
|
89
67
|
|
|
68
|
+
// case: read after write returns no results, e.g., due to auth (academic but possible)
|
|
90
69
|
if (result == null) return res.sendStatus(204)
|
|
91
|
-
const isMinimal = getPreferReturnHeader(req) === 'minimal'
|
|
92
|
-
postProcess(cdsReq.target, srv, result, isMinimal)
|
|
93
70
|
|
|
94
|
-
if (
|
|
71
|
+
if (!target._isSingleton) {
|
|
72
|
+
// determine calculation based on result with req.data as fallback
|
|
73
|
+
res.set('location', calculateLocationHeader(cdsReq.target, service, result || cdsReq.data))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const isMinimal = getPreferReturnHeader(req) === 'minimal'
|
|
77
|
+
postProcess(cdsReq.target, service, result, isMinimal)
|
|
78
|
+
if (result?.$etag) res.set('ETag', result.$etag) //> must be done after post processing
|
|
95
79
|
if (isMinimal) return res.sendStatus(204)
|
|
96
80
|
|
|
97
|
-
const
|
|
98
|
-
result =
|
|
99
|
-
res.set('content-type', 'application/json;IEEE754Compatible=true')
|
|
81
|
+
const metadata = getODataMetadata(query, { result })
|
|
82
|
+
result = getODataResult(result, metadata)
|
|
100
83
|
res.status(201).send(result)
|
|
101
84
|
})
|
|
102
85
|
.catch(err => {
|
|
103
86
|
handleSapMessages(cdsReq, req, res)
|
|
87
|
+
|
|
104
88
|
next(err)
|
|
105
89
|
})
|
|
106
90
|
}
|
|
91
|
+
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
const cds = require('../../../')
|
|
2
2
|
const { UPDATE, DELETE } = cds.ql
|
|
3
3
|
|
|
4
|
-
const {
|
|
4
|
+
const { handleSapMessages, getPreferReturnHeader } = require('../utils')
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const { getKeysAndParamsFromPath } = require('../../common/utils')
|
|
7
|
+
|
|
8
|
+
module.exports = adapter => {
|
|
9
|
+
const { service } = adapter
|
|
10
|
+
|
|
11
|
+
return function deleet(req, res, next) {
|
|
8
12
|
if (getPreferReturnHeader(req)) {
|
|
9
13
|
const msg = "The 'return' preference is not allowed in DELETE requests"
|
|
10
14
|
throw Object.assign(new Error(msg), { statusCode: 400 })
|
|
@@ -22,7 +26,7 @@ module.exports = srv =>
|
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
// payload & params
|
|
25
|
-
const { keys, params } = getKeysAndParamsFromPath(from,
|
|
29
|
+
const { keys, params } = getKeysAndParamsFromPath(from, service)
|
|
26
30
|
const data = keys //> for read and delete, we provide keys in req.data
|
|
27
31
|
if (_propertyAccess) data[_propertyAccess] = null //> delete of property -> set to null
|
|
28
32
|
|
|
@@ -33,18 +37,23 @@ module.exports = srv =>
|
|
|
33
37
|
const headers = { ...cds.context.http.req.headers, ...req.headers }
|
|
34
38
|
|
|
35
39
|
// we need a cds.Request for multiple reasons, incl. params, headers, sap-messages, read after write, ...
|
|
36
|
-
const cdsReq =
|
|
37
|
-
Object.defineProperty(cdsReq, 'protocol', { value: 'odata-v4' })
|
|
38
|
-
|
|
39
|
-
// API for subrequests of $batch (or incoming request)
|
|
40
|
-
cdsReq.req = req
|
|
41
|
-
cdsReq.res = res
|
|
40
|
+
const cdsReq = adapter.request4({ query, data, headers, params, req, res })
|
|
42
41
|
|
|
43
42
|
// rewrite event for draft-enabled entities
|
|
44
43
|
if (target._isDraftEnabled && cdsReq.data.IsActiveEntity === false) cdsReq.event = 'CANCEL'
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
// NOTES:
|
|
46
|
+
// - only via srv.run in combination with srv.dispatch inside,
|
|
47
|
+
// we automatically either use a single auto-managed tx for the req (i.e., insert and read after write in same tx)
|
|
48
|
+
// or the auto-managed tx opened for the respective atomicity group, if exists
|
|
49
|
+
// - in the then block of .run(), the transaction is committed (i.e., before sending the response) if a single auto-managed tx is used
|
|
50
|
+
return service
|
|
51
|
+
.run(() => {
|
|
52
|
+
return service.dispatch(cdsReq).then(result => {
|
|
53
|
+
// nothing to do
|
|
54
|
+
return result
|
|
55
|
+
})
|
|
56
|
+
})
|
|
48
57
|
.then(() => {
|
|
49
58
|
handleSapMessages(cdsReq, req, res)
|
|
50
59
|
|
|
@@ -52,6 +61,8 @@ module.exports = srv =>
|
|
|
52
61
|
})
|
|
53
62
|
.catch(err => {
|
|
54
63
|
handleSapMessages(cdsReq, req, res)
|
|
64
|
+
|
|
55
65
|
next(err)
|
|
56
66
|
})
|
|
57
67
|
}
|
|
68
|
+
}
|
|
@@ -1,9 +1,33 @@
|
|
|
1
|
-
const
|
|
1
|
+
const cds = require('../../../lib')
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
function error(err, req, res, _next) {
|
|
5
|
-
const { error, statusCode } = normalizeError(err, req)
|
|
3
|
+
const _log = require('../../_runtime/common/error/log')
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
const { normalizeError, unwrapMultipleErrors } = require('../../_runtime/common/error/frontend')
|
|
6
|
+
|
|
7
|
+
module.exports = () => {
|
|
8
|
+
return function odata_error(err, req, res, next) {
|
|
9
|
+
if (err == 401 || err.code == 401) return next(err) // speed up logins, at least temporary until we reviewed and eliminated overhead that may be involved below
|
|
10
|
+
// REVISIT: keep?
|
|
11
|
+
// log the error (4xx -> warn)
|
|
12
|
+
_log(err)
|
|
13
|
+
|
|
14
|
+
const { error, statusCode } = normalizeError(err, req) // REVISIT: that's killing error objects and stacks -> get rid of it
|
|
15
|
+
|
|
16
|
+
const content_id = req.headers['content-id']
|
|
17
|
+
if (content_id) {
|
|
18
|
+
error['@Core.ContentID'] = content_id
|
|
19
|
+
error.details?.forEach(e => (e['@Core.ContentID'] = content_id))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (error.details && cds.env.fiori.wrap_multiple_errors === false) {
|
|
23
|
+
unwrapMultipleErrors(error)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// NOTE: express also looks for numbers in err.status or err.statusCode
|
|
27
|
+
error.statusCode = statusCode
|
|
28
|
+
// if (err.stack) Object.defineProperty (error, 'stack', { value: err.stack })
|
|
29
|
+
// REVISIT: we can't do that ^^^^^ currently because the _http_common parsers we use for $batch requests can't handle that
|
|
30
|
+
|
|
31
|
+
next(error)
|
|
9
32
|
}
|
|
33
|
+
}
|
|
@@ -6,6 +6,7 @@ const crypto = require('crypto')
|
|
|
6
6
|
const _requestedFormat = (queryOption, header) => {
|
|
7
7
|
if (queryOption) return queryOption.match(/json/i) ? 'json' : 'xml'
|
|
8
8
|
if (header) {
|
|
9
|
+
if (header.indexOf('*/*') > -1) return 'xml' //> default to xml for backward compatibility
|
|
9
10
|
const jsonIndex = header.indexOf('application/json')
|
|
10
11
|
if (jsonIndex === -1) return 'xml'
|
|
11
12
|
const xmlIndex = header.indexOf('application/xml')
|
|
@@ -17,12 +18,12 @@ const _requestedFormat = (queryOption, header) => {
|
|
|
17
18
|
|
|
18
19
|
const _metadataFromFile = async srv => {
|
|
19
20
|
const fs = require('fs')
|
|
20
|
-
const filePath = cds.root + `/srv/odata/v4/${srv.name}.xml`
|
|
21
|
+
const filePath = cds.root + `/srv/odata/v4/${srv.definition.name}.xml`
|
|
21
22
|
let exists
|
|
22
23
|
try {
|
|
23
24
|
exists = !(await fs.promises.access(filePath, fs.constants.F_OK))
|
|
24
|
-
} catch
|
|
25
|
-
LOG._debug && LOG.debug(`No metadata file found for service ${srv.name} at ${filePath}`)
|
|
25
|
+
} catch {
|
|
26
|
+
LOG._debug && LOG.debug(`No metadata file found for service ${srv.definition.name} at ${filePath}`)
|
|
26
27
|
}
|
|
27
28
|
if (exists) {
|
|
28
29
|
const file = await fs.promises.readFile(filePath)
|
|
@@ -49,19 +50,20 @@ const mpSupportsEmptyLocale = () => {
|
|
|
49
50
|
return major > 1 || (major === 1 && minor >= 12)
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
module.exports =
|
|
53
|
-
|
|
53
|
+
module.exports = adapter => {
|
|
54
|
+
const { service } = adapter
|
|
55
|
+
|
|
56
|
+
return async function metadata(req, res, next) {
|
|
54
57
|
if (req.method !== 'GET') {
|
|
55
58
|
const msg = `Method ${req.method} is not allowed for calls to the metadata endpoint`
|
|
56
|
-
|
|
59
|
+
return next(Object.assign(new Error(msg), { statusCode: 405 }))
|
|
57
60
|
}
|
|
58
61
|
|
|
59
|
-
const tenant = cds.context.tenant
|
|
60
|
-
const locale = cds.context.locale
|
|
61
62
|
const format = _requestedFormat(req.query['$format'], req.headers['accept'])
|
|
63
|
+
const locale = cds.context.locale
|
|
62
64
|
|
|
63
65
|
// REVISIT: edm(x) and etag cache is only evicted with model
|
|
64
|
-
const csnService = (cds.context.model || cds.model).definitions[
|
|
66
|
+
const csnService = (cds.context.model || cds.model).definitions[service.definition.name]
|
|
65
67
|
const metadataCache = (csnService.metadataCache = csnService.metadataCache || { jsonEtag: {}, xmlEtag: {} }) // REVISIT: yet another uncontrolled cache?
|
|
66
68
|
|
|
67
69
|
const etag = format === 'json' ? metadataCache.jsonEtag?.[locale] : metadataCache.xmlEtag?.[locale]
|
|
@@ -77,8 +79,8 @@ module.exports = srv =>
|
|
|
77
79
|
if (etag) {
|
|
78
80
|
const unchanged = validate_etag(req.headers['if-none-match'], etag)
|
|
79
81
|
if (unchanged) {
|
|
80
|
-
res.set('
|
|
81
|
-
return res.
|
|
82
|
+
res.set('ETag', etag)
|
|
83
|
+
return res.sendStatus(304)
|
|
82
84
|
}
|
|
83
85
|
}
|
|
84
86
|
}
|
|
@@ -86,28 +88,35 @@ module.exports = srv =>
|
|
|
86
88
|
const { 'cds.xt.ModelProviderService': mps } = cds.services
|
|
87
89
|
if (mps) {
|
|
88
90
|
if (format === 'json') {
|
|
89
|
-
LOG._warn && LOG.warn('JSON metadata is not supported in case of cds.requires.
|
|
91
|
+
LOG._warn && LOG.warn('JSON metadata is not supported in case of cds.requires.extensibility: true')
|
|
90
92
|
const msg = 'JSON metadata is not supported for this service'
|
|
91
|
-
|
|
93
|
+
return next(Object.assign(new Error(msg), { statusCode: 501 }))
|
|
92
94
|
}
|
|
93
95
|
|
|
96
|
+
const { tenant, features } = cds.context
|
|
97
|
+
|
|
94
98
|
try {
|
|
95
99
|
let edmx
|
|
96
100
|
// REVISIT: remove check later
|
|
97
101
|
if (mpSupportsEmptyLocale()) {
|
|
98
102
|
// If no extensibility nor fts, do not provide model to mtxs
|
|
99
|
-
const modelNeeded = cds.env.requires.extensibility ||
|
|
103
|
+
const modelNeeded = cds.env.requires.extensibility || features?.given
|
|
100
104
|
edmx =
|
|
101
105
|
metadataCache.edm ||
|
|
102
|
-
(await mps.getEdmx({
|
|
106
|
+
(await mps.getEdmx({
|
|
107
|
+
tenant,
|
|
108
|
+
model: modelNeeded ? await mps.getCsn(tenant, features) : undefined,
|
|
109
|
+
service: service.definition.name
|
|
110
|
+
}))
|
|
103
111
|
metadataCache.edm = edmx
|
|
104
112
|
const extBundle = cds.env.requires.extensibility && (await mps.getI18n({ tenant, locale }))
|
|
105
|
-
edmx = cds.localize(
|
|
113
|
+
edmx = cds.localize(service.model, locale, edmx, extBundle)
|
|
106
114
|
} else {
|
|
107
|
-
edmx = await mps.getEdmx({ tenant, model:
|
|
115
|
+
edmx = await mps.getEdmx({ tenant, model: service.model, service: service.definition.name, locale })
|
|
108
116
|
}
|
|
109
117
|
metadataCache.xmlEtag[locale] = generateEtag(edmx)
|
|
110
|
-
res.set('
|
|
118
|
+
res.set('ETag', metadataCache.xmlEtag[locale])
|
|
119
|
+
res.set('Content-Type', 'application/xml')
|
|
111
120
|
res.send(edmx)
|
|
112
121
|
return
|
|
113
122
|
} catch (e) {
|
|
@@ -115,25 +124,28 @@ module.exports = srv =>
|
|
|
115
124
|
e.message = 'Unable to get EDMX for tenant ' + tenant + ' due to error: ' + e.message
|
|
116
125
|
LOG.error(e)
|
|
117
126
|
}
|
|
118
|
-
|
|
127
|
+
return next(Object.assign(new Error('503'), { statusCode: 503 }))
|
|
119
128
|
}
|
|
120
129
|
}
|
|
121
130
|
|
|
122
131
|
if (format === 'json') {
|
|
123
132
|
const edm =
|
|
124
|
-
metadataCache.edm ||
|
|
125
|
-
|
|
133
|
+
metadataCache.edm ||
|
|
134
|
+
(metadataCache.edm = cds.compile.to.edm(service.model, { service: service.definition.name }))
|
|
135
|
+
const localized = cds.localize(service.model, locale, edm)
|
|
126
136
|
metadataCache.jsonEtag[locale] = generateEtag(localized)
|
|
137
|
+
res.set('ETag', metadataCache.jsonEtag[locale])
|
|
127
138
|
return res.json(JSON.parse(localized))
|
|
128
139
|
}
|
|
129
140
|
|
|
130
141
|
const edmx =
|
|
131
142
|
metadataCache.edmx ||
|
|
132
|
-
(await _metadataFromFile(
|
|
133
|
-
(metadataCache.edmx = cds.compile.to.edmx(
|
|
134
|
-
const localized = cds.localize(
|
|
143
|
+
(await _metadataFromFile(service)) ||
|
|
144
|
+
(metadataCache.edmx = cds.compile.to.edmx(service.model, { service: service.definition.name }))
|
|
145
|
+
const localized = cds.localize(service.model, locale, edmx)
|
|
135
146
|
metadataCache.xmlEtag[locale] = generateEtag(localized)
|
|
136
|
-
res.set('
|
|
137
|
-
res.set('
|
|
147
|
+
res.set('ETag', metadataCache.xmlEtag[locale])
|
|
148
|
+
res.set('Content-Type', 'application/xml')
|
|
138
149
|
return res.send(localized)
|
|
139
150
|
}
|
|
151
|
+
}
|
|
@@ -1,48 +1,82 @@
|
|
|
1
1
|
const cds = require('../../../')
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
const
|
|
3
|
+
const { cds2edm, handleSapMessages, calculateLocationHeader } = require('../utils')
|
|
4
|
+
const getODataMetadata = require('../utils/metadata')
|
|
5
|
+
const postProcess = require('../utils/postProcess')
|
|
6
|
+
const getODataResult = require('../utils/result')
|
|
7
|
+
|
|
8
|
+
const { getKeysAndParamsFromPath } = require('../../common/utils')
|
|
9
|
+
|
|
10
|
+
const _findEdmNameFor = (definition, namespace, fullyQualified = false) => {
|
|
11
|
+
let name
|
|
12
|
+
if (!definition) return ''
|
|
13
|
+
if (definition._isStructured) {
|
|
14
|
+
const structured = [definition.name]
|
|
15
|
+
while (definition.parent) {
|
|
16
|
+
definition = definition.parent
|
|
17
|
+
structured.unshift(definition.name)
|
|
18
|
+
}
|
|
19
|
+
name = structured.join('_')
|
|
20
|
+
} else {
|
|
21
|
+
name = definition.name
|
|
22
|
+
}
|
|
23
|
+
if (!name.startsWith(`${namespace}.`)) return name
|
|
24
|
+
return fullyQualified ? name : name.replace(new RegExp(`^${namespace}\\.`), '')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const _opResultName = ({ service, returnType, operation }) => {
|
|
28
|
+
const { definition: { name: namespace } } = service // prettier-ignore
|
|
29
|
+
if (returnType.name) {
|
|
30
|
+
const resultName = _findEdmNameFor(returnType, namespace)
|
|
31
|
+
if (returnType.name.startsWith(`${namespace}.`)) {
|
|
32
|
+
return `${namespace}.${resultName.replace(/\./g, '_')}`
|
|
33
|
+
}
|
|
34
|
+
return resultName
|
|
35
|
+
}
|
|
36
|
+
// bound action / function returns inline structure
|
|
37
|
+
if (operation.parent) {
|
|
38
|
+
const boundEntityName = _findEdmNameFor(operation.parent, namespace, true).replace(/\./g, '_')
|
|
39
|
+
// REVISIT exactly this return type name is generated in edm by compiler
|
|
40
|
+
return `${namespace}.return_${boundEntityName}_${_findEdmNameFor(operation, namespace)}`
|
|
41
|
+
}
|
|
42
|
+
// unbound action / function returns inline structure
|
|
43
|
+
// REVISIT exactly this return type name is generated in edm by compiler
|
|
44
|
+
return `${namespace}.return_${_findEdmNameFor(operation, namespace, true).replace(/\./g, '_')}`
|
|
45
|
+
}
|
|
7
46
|
|
|
8
|
-
|
|
47
|
+
module.exports = adapter => {
|
|
48
|
+
const { service } = adapter
|
|
9
49
|
|
|
10
|
-
|
|
11
|
-
function operation(req, res, next) {
|
|
50
|
+
return function odata_operation(req, res, next) {
|
|
12
51
|
let { operation, args } = req._query.SELECT?.from.ref?.slice(-1)[0] || {}
|
|
13
52
|
if (!operation) return next() //> create or read
|
|
14
53
|
|
|
54
|
+
// REVISIT: should not be necessary
|
|
55
|
+
const _originalQuery = JSON.parse(JSON.stringify(req._query))
|
|
56
|
+
|
|
15
57
|
// unbound vs. bound
|
|
16
|
-
let entity,
|
|
17
|
-
if (
|
|
18
|
-
operation =
|
|
58
|
+
let entity, params
|
|
59
|
+
if (service.model.definitions[operation]) {
|
|
60
|
+
operation = service.model.definitions[operation]
|
|
19
61
|
} else {
|
|
20
62
|
req._query.SELECT.from.ref.pop()
|
|
21
|
-
let cur = { elements:
|
|
63
|
+
let cur = { elements: service.model.definitions }
|
|
22
64
|
for (const each of req._query.SELECT.from.ref) {
|
|
23
65
|
cur = cur.elements[each.id || each]
|
|
24
66
|
if (cur._target) cur = cur._target
|
|
25
67
|
}
|
|
26
68
|
operation = cur.actions[operation]
|
|
27
69
|
entity = cur
|
|
28
|
-
const keysAndParams = getKeysAndParamsFromPath(req._query.SELECT.from,
|
|
70
|
+
const keysAndParams = getKeysAndParamsFromPath(req._query.SELECT.from, service)
|
|
29
71
|
params = keysAndParams.params
|
|
30
72
|
}
|
|
31
73
|
|
|
32
74
|
// payload & params
|
|
33
|
-
const data = args ||
|
|
34
|
-
|
|
35
|
-
// assert payload
|
|
36
|
-
const assertOptions = { filter: true, http: { req }, mandatories: true }
|
|
37
|
-
const errs = cds.assert(data, operation, assertOptions)
|
|
38
|
-
if (errs) {
|
|
39
|
-
if (errs.length === 1) throw errs[0]
|
|
40
|
-
throw Object.assign(new Error('MULTIPLE_ERRORS'), { statusCode: 400, details: errs })
|
|
41
|
-
}
|
|
75
|
+
const data = args || req.body
|
|
42
76
|
|
|
43
77
|
// event
|
|
44
78
|
// REVISIT: when is operation.name actually prefixed with the service name?
|
|
45
|
-
const event = operation.name.replace(`${
|
|
79
|
+
const event = operation.name.replace(`${service.definition.name}.`, '')
|
|
46
80
|
|
|
47
81
|
const query = entity ? req._query : undefined
|
|
48
82
|
|
|
@@ -50,63 +84,52 @@ module.exports = srv =>
|
|
|
50
84
|
const headers = { ...cds.context.http.req.headers, ...req.headers }
|
|
51
85
|
|
|
52
86
|
// we need a cds.Request for multiple reasons, incl. params, headers, sap-messages, read after write, ...
|
|
53
|
-
const cdsReq =
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// or the auto-managed tx opened for the respective atomicity group, if exists
|
|
63
|
-
return srv
|
|
64
|
-
.run(() => {
|
|
65
|
-
return srv.dispatch(cdsReq).then(result => {
|
|
66
|
-
handleSapMessages(cdsReq, req, res)
|
|
67
|
-
return result
|
|
68
|
-
})
|
|
69
|
-
})
|
|
87
|
+
const cdsReq = adapter.request4({ query, event, data, params, headers, target: query?.target, req, res })
|
|
88
|
+
|
|
89
|
+
// NOTES:
|
|
90
|
+
// - only via srv.run in combination with srv.dispatch inside,
|
|
91
|
+
// we automatically either use a single auto-managed tx for the req (i.e., insert and read after write in same tx)
|
|
92
|
+
// or the auto-managed tx opened for the respective atomicity group, if exists
|
|
93
|
+
// - in the then block of .run(), the transaction is committed (i.e., before sending the response) if a single auto-managed tx is used
|
|
94
|
+
return service
|
|
95
|
+
.run(() => service.dispatch(cdsReq))
|
|
70
96
|
.then(result => {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (!operation.returns || result == null) return res.status(204).end()
|
|
97
|
+
handleSapMessages(cdsReq, req, res)
|
|
74
98
|
|
|
75
|
-
if (operation.returns
|
|
76
|
-
res.set('content-type', 'application/json;IEEE754Compatible=true')
|
|
77
|
-
// TODO: check result type
|
|
78
|
-
return res.send({
|
|
79
|
-
'@odata.context': `${'../'.repeat(query?.SELECT?.from?.ref?.length)}$metadata#${cds2edm[operation.returns._type]}`,
|
|
80
|
-
value: result
|
|
81
|
-
})
|
|
82
|
-
}
|
|
99
|
+
if (operation.returns?.items && result == null) result = []
|
|
83
100
|
|
|
84
|
-
|
|
101
|
+
if (!operation.returns || result == null) return res.sendStatus(204)
|
|
85
102
|
|
|
86
|
-
|
|
87
|
-
|
|
103
|
+
if (operation.returns._type?.match?.(/^cds\./)) {
|
|
104
|
+
const context = `${'../'.repeat(query?.SELECT?.from?.ref?.length)}$metadata#${cds2edm[operation.returns._type]}`
|
|
88
105
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
!info.metadata.isCollection &&
|
|
92
|
-
info.metadata.isServiceEntity &&
|
|
93
|
-
!info.metadata.contextUrl.endsWith('$entity')
|
|
94
|
-
) {
|
|
95
|
-
info.metadata.contextUrl += '/$entity'
|
|
106
|
+
result = { '@odata.context': context, value: result }
|
|
107
|
+
return res.send(result)
|
|
96
108
|
}
|
|
97
109
|
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
if (
|
|
110
|
+
if (res.statusCode === 201 && !res.hasHeader('location')) {
|
|
111
|
+
const locationHeader = calculateLocationHeader(operation.returns, service, result)
|
|
112
|
+
if (locationHeader) res.set('location', locationHeader)
|
|
101
113
|
}
|
|
102
114
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
result['@odata.context'] = '../' + result['@odata.context']
|
|
115
|
+
if (operation.returns) {
|
|
116
|
+
postProcess(operation.returns, service, result)
|
|
117
|
+
if (result?.$etag) res.set('ETag', result.$etag) //> must be done after post processing
|
|
118
|
+
}
|
|
108
119
|
|
|
109
|
-
|
|
120
|
+
// REVISIT: enterprise search result? -> simply return what was provided
|
|
121
|
+
if (operation.returns.type !== 'sap.esh.SearchResult') {
|
|
122
|
+
const isCollection = !!operation.returns.items
|
|
123
|
+
const _target = operation.returns.items ?? operation.returns
|
|
124
|
+
// REVISIT: when is edmName needed?
|
|
125
|
+
const edmName = _opResultName({ service, operation, returnType: _target })
|
|
126
|
+
const metadata = getODataMetadata(
|
|
127
|
+
{ SELECT: { from: _originalQuery?.SELECT?.from, one: !isCollection }, _target },
|
|
128
|
+
|
|
129
|
+
{ result, isCollection, edmName }
|
|
130
|
+
)
|
|
131
|
+
result = getODataResult(result, metadata, { isCollection })
|
|
132
|
+
}
|
|
110
133
|
res.send(result)
|
|
111
134
|
})
|
|
112
135
|
.catch(err => {
|
|
@@ -114,3 +137,4 @@ module.exports = srv =>
|
|
|
114
137
|
next(err)
|
|
115
138
|
})
|
|
116
139
|
}
|
|
140
|
+
}
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
const cds = require('../../../')
|
|
2
2
|
|
|
3
|
-
module.exports =
|
|
4
|
-
|
|
3
|
+
module.exports = adapter => {
|
|
4
|
+
const { service } = adapter
|
|
5
|
+
|
|
6
|
+
return function odata_parse_url(req, _, next) {
|
|
5
7
|
// REVISIT: can't we register the batch handler before the parse handler to avoid this?
|
|
6
8
|
if (req.path.startsWith('/$batch')) return next()
|
|
7
9
|
|
|
8
10
|
if (req._query) return next() //> already parsed (e.g., upsert)
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
req._query = cds.odata.parse(req.method === 'GET' ? req.url : req.path, {
|
|
12
|
-
service: srv,
|
|
13
|
-
baseUrl: req.baseUrl,
|
|
14
|
-
strict: true
|
|
15
|
-
})
|
|
12
|
+
req._query = cds.odata.parse(req.url, { service, baseUrl: req.baseUrl, strict: true })
|
|
16
13
|
|
|
17
14
|
next()
|
|
18
15
|
}
|
|
16
|
+
}
|