@sap/cds 5.6.4 → 5.7.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 +102 -0
- package/_i18n/i18n_fr.properties +4 -4
- package/apis/cds.d.ts +7 -10
- package/apis/connect.d.ts +3 -3
- package/apis/core.d.ts +2 -4
- package/apis/models.d.ts +2 -3
- package/apis/ql.d.ts +0 -1
- package/apis/services.d.ts +3 -3
- package/bin/build/buildTaskFactory.js +16 -10
- package/bin/build/buildTaskProviderFactory.js +3 -3
- package/bin/build/constants.js +2 -1
- package/bin/build/provider/buildTaskProviderInternal.js +14 -14
- package/bin/build/provider/hana/2migration.js +2 -3
- package/bin/build/provider/hana/index.js +34 -0
- package/bin/build/provider/hana/migrationtable.js +90 -22
- package/bin/build/provider/hana/template/undeploy.json +5 -0
- package/bin/build/provider/node-cf/index.js +9 -2
- package/bin/serve.js +16 -18
- package/lib/compile/cdsc.js +15 -5
- package/lib/compile/etc/_localized.js +4 -4
- package/lib/compile/extend.js +8 -0
- package/lib/compile/index.js +3 -1
- package/lib/compile/minify.js +61 -0
- package/lib/compile/resolve.js +4 -1
- package/lib/compile/to/gql.js +9 -0
- package/lib/compile/to/sql.js +26 -30
- package/lib/connect/index.js +1 -1
- package/lib/core/entities.js +0 -3
- package/lib/core/infer.js +1 -0
- package/lib/core/reflect.js +0 -34
- package/lib/deploy.js +25 -17
- package/lib/env/defaults.js +3 -1
- package/lib/env/index.js +8 -3
- package/lib/env/presets.js +38 -0
- package/lib/env/requires.js +16 -11
- package/lib/index.js +13 -11
- package/lib/log/format/kibana.js +3 -1
- package/lib/log/index.js +2 -2
- package/lib/req/cds-context.js +79 -0
- package/lib/req/context.js +5 -77
- package/lib/req/request.js +1 -1
- package/lib/serve/Service-api.js +8 -4
- package/lib/serve/Service-dispatch.js +0 -7
- package/lib/serve/Service-methods.js +6 -8
- package/lib/serve/Transaction.js +35 -30
- package/lib/serve/adapters.js +1 -4
- package/lib/utils/axios.js +1 -1
- package/libx/_runtime/audit/Service.js +44 -20
- package/libx/_runtime/audit/generic/personal/access.js +16 -11
- package/libx/_runtime/audit/generic/personal/modification.js +5 -5
- package/libx/_runtime/audit/generic/personal/utils.js +46 -37
- package/libx/_runtime/{common/auth → auth}/index.js +21 -7
- package/libx/_runtime/{common/auth → auth}/strategies/JWT.js +2 -2
- package/libx/_runtime/{common/auth → auth}/strategies/basic.js +2 -2
- package/libx/_runtime/{common/auth → auth}/strategies/dummy.js +1 -1
- package/libx/_runtime/{common/auth → auth}/strategies/mock.js +2 -2
- package/libx/_runtime/{common/auth → auth}/strategies/utils/uaa.js +1 -1
- package/libx/_runtime/{common/auth → auth}/strategies/utils/xssec.js +0 -0
- package/libx/_runtime/{common/auth → auth}/strategies/xsuaa.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +7 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +0 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +0 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +3 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +6 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +3 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +2 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +16 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +26 -65
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +0 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +3 -66
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +24 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +13 -10
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/ConditionalRequestControlCommand.js +0 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/ConditionalRequestValidator.js +0 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +54 -76
- package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +0 -7
- package/libx/_runtime/cds-services/adapter/rest/handlers/create.js +3 -6
- package/libx/_runtime/cds-services/adapter/rest/handlers/delete.js +3 -6
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +3 -6
- package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +3 -6
- package/libx/_runtime/cds-services/adapter/rest/handlers/update.js +3 -6
- package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +8 -4
- package/libx/_runtime/cds-services/services/Service.js +0 -6
- package/libx/_runtime/cds-services/services/utils/columns.js +10 -3
- package/libx/_runtime/cds-services/services/utils/compareJson.js +3 -6
- package/libx/_runtime/cds-services/services/utils/differ.js +4 -1
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +1 -41
- package/libx/_runtime/cds-services/util/assert.js +1 -262
- package/libx/_runtime/cds.js +6 -9
- package/libx/_runtime/common/aspects/entity.js +1 -1
- package/libx/_runtime/common/composition/delete.js +4 -2
- package/libx/_runtime/common/composition/update.js +22 -38
- package/libx/_runtime/common/composition/utils.js +3 -7
- package/libx/_runtime/common/error/standardError.js +11 -0
- package/libx/_runtime/common/generic/auth.js +61 -30
- package/libx/_runtime/common/generic/crud.js +11 -23
- package/libx/_runtime/common/generic/input.js +20 -0
- package/libx/_runtime/common/generic/put.js +4 -10
- package/libx/_runtime/common/generic/sorting.js +12 -30
- package/libx/_runtime/common/perf/index.js +24 -0
- package/libx/_runtime/common/utils/cqn.js +58 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +289 -114
- package/libx/_runtime/common/utils/csn.js +38 -56
- package/libx/_runtime/common/utils/entityFromCqn.js +6 -6
- package/libx/_runtime/common/utils/resolveView.js +4 -5
- package/libx/_runtime/common/utils/rewriteAsterisks.js +46 -5
- package/libx/_runtime/common/utils/search2cqn4sql.js +21 -9
- package/libx/_runtime/common/utils/structured.js +35 -25
- package/libx/_runtime/db/Service.js +0 -6
- package/libx/_runtime/db/expand/expand-v2.js +130 -0
- package/libx/_runtime/db/expand/expandCQNToJoin.js +38 -52
- package/libx/_runtime/db/expand/index.js +3 -1
- package/libx/_runtime/db/generic/input.js +52 -10
- package/libx/_runtime/db/generic/integrity.js +367 -26
- package/libx/_runtime/db/generic/virtual.js +51 -13
- package/libx/_runtime/db/query/update.js +9 -3
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +8 -9
- package/libx/_runtime/{common → db}/utils/propagateForeignKeys.js +11 -14
- package/libx/_runtime/fiori/generic/activate.js +1 -0
- package/libx/_runtime/fiori/generic/before.js +2 -1
- package/libx/_runtime/fiori/generic/edit.js +1 -0
- package/libx/_runtime/fiori/generic/patch.js +1 -1
- package/libx/_runtime/fiori/generic/read.js +123 -57
- package/libx/_runtime/fiori/uiflex/index.js +1 -1
- package/libx/_runtime/fiori/uiflex/{extensibility/index.js → service.js} +3 -3
- package/libx/_runtime/fiori/utils/delete.js +7 -1
- package/libx/_runtime/hana/Service.js +1 -8
- package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +5 -14
- package/libx/_runtime/hana/execute.js +10 -4
- package/libx/_runtime/hana/pool.js +55 -45
- package/libx/_runtime/hana/search.js +7 -6
- package/libx/_runtime/hana/search2cqn4sql.js +8 -5
- package/libx/_runtime/hana/searchToContains.js +3 -1
- package/libx/_runtime/index.js +5 -5
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +3 -3
- package/libx/_runtime/messaging/Outbox.js +53 -0
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +17 -10
- package/libx/_runtime/messaging/common-utils/connections.js +14 -9
- package/libx/_runtime/messaging/common-utils/waitingTime.js +2 -0
- package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -3
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -2
- package/libx/_runtime/messaging/enterprise-messaging.js +21 -15
- package/libx/_runtime/messaging/file-based.js +5 -5
- package/libx/_runtime/messaging/message-queuing.js +2 -3
- package/libx/_runtime/messaging/outbox/OutboxRunner.js +75 -0
- package/libx/_runtime/messaging/outbox/utils.js +192 -0
- package/libx/_runtime/messaging/service.js +16 -30
- package/libx/_runtime/remote/Service.js +15 -0
- package/libx/_runtime/remote/utils/client.js +15 -3
- package/libx/_runtime/remote/utils/{dataConversion.js → data.js} +12 -2
- package/libx/_runtime/sqlite/Service.js +7 -10
- package/libx/_runtime/sqlite/customBuilder/CustomExpressionBuilder.js +19 -0
- package/libx/_runtime/sqlite/execute.js +18 -12
- package/libx/_runtime/types/api.js +2 -1
- package/libx/odata/{odata2cqn/afterburner.js → afterburner.js} +19 -15
- package/libx/odata/{cqn2odata/index.js → cqn2odata.js} +1 -1
- package/libx/odata/{odata2cqn/grammar.pegjs → grammar.pegjs} +171 -130
- package/libx/odata/index.js +16 -14
- package/libx/odata/parser.js +1 -0
- package/libx/odata/utils.js +57 -0
- package/libx/rest/RestAdapter.js +2 -6
- package/libx/rest/utils/data.js +1 -6
- package/package.json +4 -3
- package/server.js +4 -5
- package/srv/audit-log.cds +87 -0
- package/{libx/_runtime/fiori/uiflex/extensibility/index.cds → srv/flex.cds} +0 -0
- package/srv/flex.js +1 -0
- package/srv/outbox.cds +11 -0
- package/srv/outbox.js +0 -0
- package/libx/_runtime/cds-services/adapter/perf/performance.js +0 -104
- package/libx/_runtime/cds-services/adapter/perf/performanceMeasurement.js +0 -33
- package/libx/odata/odata2cqn/index.js +0 -3
- package/libx/odata/odata2cqn/parser.js +0 -1
- package/libx/odata/readme.md +0 -1
- package/libx/odata/utils/index.js +0 -64
|
@@ -218,14 +218,6 @@ class ODataRequest extends cds.Request {
|
|
|
218
218
|
}
|
|
219
219
|
})
|
|
220
220
|
|
|
221
|
-
if (this._.req.performanceMeasurement) {
|
|
222
|
-
this.performanceMeasurement = this._.req.performanceMeasurement
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (this._.req.dynatrace) {
|
|
226
|
-
this.dynatrace = this._.req.dynatrace
|
|
227
|
-
}
|
|
228
|
-
|
|
229
221
|
/*
|
|
230
222
|
* req.isConcurrentResource
|
|
231
223
|
*/
|
|
@@ -89,7 +89,7 @@ const action = service => {
|
|
|
89
89
|
const tx = changeset ? odataReq.getBatchApplicationData().txs[changeset] : service.tx(req)
|
|
90
90
|
cds.context = tx
|
|
91
91
|
|
|
92
|
-
let result, err
|
|
92
|
+
let result, err
|
|
93
93
|
try {
|
|
94
94
|
result = await tx.dispatch(req)
|
|
95
95
|
|
|
@@ -107,13 +107,12 @@ const action = service => {
|
|
|
107
107
|
// for passing into commit
|
|
108
108
|
odataReq.getBatchApplicationData().results[changeset].push({ result, req })
|
|
109
109
|
} else {
|
|
110
|
-
commit = true
|
|
111
110
|
await tx.commit(result)
|
|
112
111
|
}
|
|
113
112
|
} catch (e) {
|
|
114
113
|
err = e
|
|
115
|
-
if (!changeset
|
|
116
|
-
//
|
|
114
|
+
if (!changeset) {
|
|
115
|
+
// REVISIT: rollback needed if error occured before commit attempted -> how to distinguish?
|
|
117
116
|
await tx.rollback(e).catch(() => {})
|
|
118
117
|
} else if (changeset) {
|
|
119
118
|
// for passing into rollback
|
|
@@ -32,11 +32,13 @@ const create = service => {
|
|
|
32
32
|
const tx = changeset ? odataReq.getBatchApplicationData().txs[changeset] : service.tx(req)
|
|
33
33
|
cds.context = tx
|
|
34
34
|
|
|
35
|
-
let result, err
|
|
35
|
+
let result, err
|
|
36
36
|
try {
|
|
37
37
|
result = await tx.dispatch(req)
|
|
38
38
|
|
|
39
|
-
if (
|
|
39
|
+
if (isReturnMinimal(req)) {
|
|
40
|
+
postProcessMinimal(req, result)
|
|
41
|
+
} else {
|
|
40
42
|
// REVISIT: find better solution
|
|
41
43
|
if (req._.readAfterWrite) {
|
|
42
44
|
const dataInDb = await readAfterWrite(req, service)
|
|
@@ -44,15 +46,12 @@ const create = service => {
|
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
postProcess(req, odataRes, service, result)
|
|
47
|
-
} else {
|
|
48
|
-
postProcessMinimal(req, result)
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
if (changeset) {
|
|
52
52
|
// for passing into commit
|
|
53
53
|
odataReq.getBatchApplicationData().results[changeset].push({ result, req })
|
|
54
54
|
} else {
|
|
55
|
-
commit = true
|
|
56
55
|
await tx.commit(result)
|
|
57
56
|
}
|
|
58
57
|
|
|
@@ -61,8 +60,8 @@ const create = service => {
|
|
|
61
60
|
}
|
|
62
61
|
} catch (e) {
|
|
63
62
|
err = e
|
|
64
|
-
if (!changeset
|
|
65
|
-
//
|
|
63
|
+
if (!changeset) {
|
|
64
|
+
// REVISIT: rollback needed if error occured before commit attempted -> how to distinguish?
|
|
66
65
|
await tx.rollback(e).catch(() => {})
|
|
67
66
|
} else if (changeset) {
|
|
68
67
|
// for passing into rollback
|
|
@@ -28,7 +28,7 @@ const del = service => {
|
|
|
28
28
|
const tx = changeset ? odataReq.getBatchApplicationData().txs[changeset] : service.tx(req)
|
|
29
29
|
cds.context = tx
|
|
30
30
|
|
|
31
|
-
let err
|
|
31
|
+
let err
|
|
32
32
|
try {
|
|
33
33
|
await tx.dispatch(req)
|
|
34
34
|
const result = null
|
|
@@ -37,13 +37,12 @@ const del = service => {
|
|
|
37
37
|
// for passing into commit
|
|
38
38
|
odataReq.getBatchApplicationData().results[changeset].push({ result, req })
|
|
39
39
|
} else {
|
|
40
|
-
commit = true
|
|
41
40
|
await tx.commit(result)
|
|
42
41
|
}
|
|
43
42
|
} catch (e) {
|
|
44
43
|
err = e
|
|
45
|
-
if (!changeset
|
|
46
|
-
//
|
|
44
|
+
if (!changeset) {
|
|
45
|
+
// REVISIT: rollback needed if error occured before commit attempted -> how to distinguish?
|
|
47
46
|
await tx.rollback(e).catch(() => {})
|
|
48
47
|
} else if (changeset) {
|
|
49
48
|
// for passing into rollback
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const cds = require('../../../../cds')
|
|
2
|
+
const { isStandardError } = require('../../../../common/error/standardError')
|
|
2
3
|
|
|
3
4
|
const { StatusCodes: HttpStatusCodes } = require('../okra/odata-commons/http/HttpStatusCode')
|
|
4
5
|
|
|
@@ -24,16 +25,6 @@ const ERROR_TO_HTTP_CODE = {
|
|
|
24
25
|
|
|
25
26
|
const { normalizeError } = require('../../../../common/error/frontend')
|
|
26
27
|
|
|
27
|
-
const _isStandardError = err => {
|
|
28
|
-
return (
|
|
29
|
-
err instanceof TypeError ||
|
|
30
|
-
err instanceof ReferenceError ||
|
|
31
|
-
err instanceof SyntaxError ||
|
|
32
|
-
err instanceof RangeError ||
|
|
33
|
-
err instanceof URIError
|
|
34
|
-
)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
28
|
const _beautifyMessage = msg => (msg.endsWith('.') ? msg : msg + '.')
|
|
38
29
|
|
|
39
30
|
const _buildRootCauseMessage = (message, rootCause) => {
|
|
@@ -79,7 +70,7 @@ const _betterOkraError = err => {
|
|
|
79
70
|
const getErrorHandler = (crashOnError = true, srv) => {
|
|
80
71
|
return (odataReq, odataRes, next, err) => {
|
|
81
72
|
// REVISIT: crashOnError
|
|
82
|
-
if (
|
|
73
|
+
if (isStandardError(err) && crashOnError) {
|
|
83
74
|
err.__crashOnError = true
|
|
84
75
|
throw err
|
|
85
76
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
const cds = require('../../../../cds')
|
|
2
|
+
const LOG = cds.log('odata')
|
|
2
3
|
|
|
3
4
|
const { toODataResult } = require('../utils/result')
|
|
4
5
|
|
|
6
|
+
const { normalizeError } = require('../../../../common/error/frontend')
|
|
7
|
+
|
|
5
8
|
let _mps
|
|
6
9
|
|
|
7
10
|
const _get4Tenant = async (tenant, locale, service) => {
|
|
@@ -33,13 +36,14 @@ const _get4Toggles = async (tenant, locale, service, req) => {
|
|
|
33
36
|
*/
|
|
34
37
|
const metadata = service => {
|
|
35
38
|
return async (odataReq, odataRes, next) => {
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
const req = odataReq.getIncomingRequest()
|
|
40
|
+
|
|
41
|
+
const tenant = req.user && req.user.tenant
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const locale = odataRes.getContract().getLocale()
|
|
43
|
+
// REVISIT: can we take locale from user, or is there some odata special wrt metadata?
|
|
44
|
+
const locale = odataRes.getContract().getLocale()
|
|
42
45
|
|
|
46
|
+
try {
|
|
43
47
|
let edmx
|
|
44
48
|
|
|
45
49
|
if (tenant) {
|
|
@@ -60,7 +64,13 @@ const metadata = service => {
|
|
|
60
64
|
|
|
61
65
|
return next(null, toODataResult(edmx))
|
|
62
66
|
} catch (e) {
|
|
63
|
-
|
|
67
|
+
if (LOG._error) {
|
|
68
|
+
e.message = 'Unable to get EDMX for tenant ' + tenant + ' due to error: ' + e.message
|
|
69
|
+
LOG.error(e)
|
|
70
|
+
}
|
|
71
|
+
// return 503 to client
|
|
72
|
+
const { error, statusCode } = normalizeError(Object.assign(e, { statusCode: 503 }), req)
|
|
73
|
+
return next(Object.assign(error, { statusCode }))
|
|
64
74
|
}
|
|
65
75
|
}
|
|
66
76
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const cds = require('../../../../cds')
|
|
2
|
+
|
|
2
3
|
const { SELECT } = cds.ql
|
|
4
|
+
|
|
3
5
|
const ODataRequest = require('../ODataRequest')
|
|
4
|
-
const { rewriteExpandAsterisk } = require('../../../../common/utils/rewriteAsterisks')
|
|
5
6
|
|
|
6
7
|
const {
|
|
7
8
|
QueryOptions,
|
|
@@ -13,15 +14,14 @@ const {
|
|
|
13
14
|
}
|
|
14
15
|
} = require('../okra/odata-server')
|
|
15
16
|
|
|
16
|
-
const getError = require('../../../../common/error')
|
|
17
|
-
const { getSapMessages } = require('../../../../common/error/frontend')
|
|
18
17
|
const { isCustomOperation, skipToken } = require('../utils/request')
|
|
19
18
|
const { actionAndFunctionQueries, getActionOrFunctionReturnType } = require('../utils/handlerUtils')
|
|
20
19
|
const { validateResourcePath } = require('../utils/request')
|
|
21
20
|
const { toODataResult, postProcess } = require('../utils/result')
|
|
22
21
|
const { isStreaming, getStreamProperties } = require('../utils/stream')
|
|
23
22
|
const { resolveStructuredName } = require('../utils/handlerUtils')
|
|
24
|
-
const
|
|
23
|
+
const getError = require('../../../../common/error')
|
|
24
|
+
const { getSapMessages } = require('../../../../common/error/frontend')
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Checks whether a bound function or function import is invoked.
|
|
@@ -32,6 +32,10 @@ const { ensureNoDraftsSuffix } = require('../../../../common/utils/draft')
|
|
|
32
32
|
*/
|
|
33
33
|
const _isFunction = segments => [BOUND_FUNCTION, FUNCTION_IMPORT].includes(segments[segments.length - 1].getKind())
|
|
34
34
|
|
|
35
|
+
const _selectOrExpandInQueryOptions = queryOptions => {
|
|
36
|
+
return queryOptions && (queryOptions.$select || queryOptions.$expand)
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
/**
|
|
36
40
|
* Invoke a function.
|
|
37
41
|
*
|
|
@@ -49,7 +53,12 @@ const _invokeFunction = async (tx, req, odataReq) => {
|
|
|
49
53
|
tx.model.definitions
|
|
50
54
|
)
|
|
51
55
|
|
|
52
|
-
if
|
|
56
|
+
// if $select or $expand is present, do it
|
|
57
|
+
if (
|
|
58
|
+
functionReturnType &&
|
|
59
|
+
functionReturnType.kind === 'entity' &&
|
|
60
|
+
_selectOrExpandInQueryOptions(odataReq.getQueryOptions())
|
|
61
|
+
) {
|
|
53
62
|
await actionAndFunctionQueries(req, odataReq, result, tx, functionReturnType)
|
|
54
63
|
}
|
|
55
64
|
|
|
@@ -283,13 +292,14 @@ const _readCollection = async (tx, req, odataReq) => {
|
|
|
283
292
|
*
|
|
284
293
|
* @param {object} tx
|
|
285
294
|
* @param {object} req
|
|
286
|
-
* @param {Array} segments
|
|
287
295
|
* @returns {Promise}
|
|
288
296
|
* @private
|
|
289
297
|
*/
|
|
290
|
-
const _readStream = async (tx, req
|
|
298
|
+
const _readStream = async (tx, req) => {
|
|
291
299
|
req.query._streaming = true
|
|
292
300
|
|
|
301
|
+
const { contentType, contentDisposition } = await getStreamProperties(req, tx.model)
|
|
302
|
+
|
|
293
303
|
let result = await tx.dispatch(req)
|
|
294
304
|
|
|
295
305
|
// REVISIT: compat, should actually be treated as object
|
|
@@ -313,13 +323,12 @@ const _readStream = async (tx, req, segments) => {
|
|
|
313
323
|
})
|
|
314
324
|
}
|
|
315
325
|
|
|
316
|
-
const { contentType, contentDisposition } = await getStreamProperties(segments, tx, req)
|
|
317
|
-
|
|
318
326
|
const headers = req._.odataReq.getHeaders()
|
|
327
|
+
|
|
319
328
|
if (
|
|
329
|
+
contentType &&
|
|
320
330
|
headers &&
|
|
321
331
|
headers.accept &&
|
|
322
|
-
contentType &&
|
|
323
332
|
!headers.accept.includes('*/*') &&
|
|
324
333
|
!headers.accept.includes(contentType) &&
|
|
325
334
|
!headers.accept.includes(contentType.split('/')[0] + '/*')
|
|
@@ -328,9 +337,9 @@ const _readStream = async (tx, req, segments) => {
|
|
|
328
337
|
}
|
|
329
338
|
|
|
330
339
|
if (contentType) streamObj['*@odata.mediaContentType'] = contentType
|
|
331
|
-
if (contentDisposition)
|
|
340
|
+
if (contentDisposition)
|
|
332
341
|
req._.odataRes.setHeader('Content-Disposition', `attachment; filename="${encodeURIComponent(contentDisposition)}"`)
|
|
333
|
-
|
|
342
|
+
|
|
334
343
|
return streamObj
|
|
335
344
|
}
|
|
336
345
|
|
|
@@ -384,11 +393,11 @@ const _readAndTransform = (tx, req, odataReq) => {
|
|
|
384
393
|
}
|
|
385
394
|
}
|
|
386
395
|
|
|
387
|
-
return _readStream(tx, req
|
|
396
|
+
return _readStream(tx, req)
|
|
388
397
|
}
|
|
389
398
|
|
|
390
399
|
if (isStreaming(segments)) {
|
|
391
|
-
return _readStream(tx, req
|
|
400
|
+
return _readStream(tx, req)
|
|
392
401
|
}
|
|
393
402
|
|
|
394
403
|
if (req.target._isSingleton) {
|
|
@@ -418,43 +427,6 @@ const _removeKeysForParams = result => {
|
|
|
418
427
|
return options
|
|
419
428
|
}
|
|
420
429
|
|
|
421
|
-
const _getTarget = (ref, target, definitions) => {
|
|
422
|
-
if (cds.env.effective.odata.proxies) {
|
|
423
|
-
const target_ = target.elements[ref[0]]
|
|
424
|
-
|
|
425
|
-
if (ref.length === 1) {
|
|
426
|
-
return definitions[ensureNoDraftsSuffix(target_.target)]
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
return _getTarget(ref.slice(1), target_, definitions)
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
const target_ = target.elements[ref.join('_')]
|
|
433
|
-
return definitions[ensureNoDraftsSuffix(target_.target)]
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
const _getRestrictedExpand = (columns, target, definitions) => {
|
|
437
|
-
if (!columns || !target || columns === '*') return
|
|
438
|
-
|
|
439
|
-
const annotation = target['@Capabilities.ExpandRestrictions.NonExpandableProperties']
|
|
440
|
-
const restrictions = annotation && annotation.map(element => element['='])
|
|
441
|
-
|
|
442
|
-
rewriteExpandAsterisk(columns, target)
|
|
443
|
-
|
|
444
|
-
for (const col of columns) {
|
|
445
|
-
if (col.expand) {
|
|
446
|
-
if (restrictions && restrictions.length !== 0) {
|
|
447
|
-
const ref = col.ref.join('_')
|
|
448
|
-
const ref_ = restrictions.find(element => element.replace(/\./g, '_') === ref)
|
|
449
|
-
if (ref_) return ref_
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
const restricted = _getRestrictedExpand(col.expand, _getTarget(col.ref, target, definitions), definitions)
|
|
453
|
-
if (restricted) return restricted
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
430
|
/**
|
|
459
431
|
* The handler that will be registered with odata-v4.
|
|
460
432
|
*
|
|
@@ -478,21 +450,11 @@ const read = service => {
|
|
|
478
450
|
return next(e)
|
|
479
451
|
}
|
|
480
452
|
|
|
481
|
-
// REVISIT: this should be in common/generic/auth.js with the rest of the access control stuff
|
|
482
|
-
const restricted = _getRestrictedExpand(
|
|
483
|
-
req.query.SELECT && req.query.SELECT.columns,
|
|
484
|
-
req.target,
|
|
485
|
-
service.model.definitions
|
|
486
|
-
)
|
|
487
|
-
if (restricted) {
|
|
488
|
-
return next(getError(400, 'EXPAND_IS_RESTRICTED', [restricted]))
|
|
489
|
-
}
|
|
490
|
-
|
|
491
453
|
const changeset = odataReq.getAtomicityGroupId()
|
|
492
454
|
const tx = changeset ? odataReq.getBatchApplicationData().txs[changeset] : service.tx(req)
|
|
493
455
|
cds.context = tx
|
|
494
456
|
|
|
495
|
-
let result, err
|
|
457
|
+
let result, err
|
|
496
458
|
let additional = {}
|
|
497
459
|
try {
|
|
498
460
|
// REVISIT: refactor _readAndTransform
|
|
@@ -509,13 +471,12 @@ const read = service => {
|
|
|
509
471
|
// for passing into commit
|
|
510
472
|
odataReq.getBatchApplicationData().results[changeset].push({ result, req })
|
|
511
473
|
} else {
|
|
512
|
-
commit = true
|
|
513
474
|
await tx.commit(result)
|
|
514
475
|
}
|
|
515
476
|
} catch (e) {
|
|
516
477
|
err = e
|
|
517
|
-
if (!changeset
|
|
518
|
-
//
|
|
478
|
+
if (!changeset) {
|
|
479
|
+
// REVISIT: rollback needed if error occured before commit attempted -> how to distinguish?
|
|
519
480
|
await tx.rollback(e).catch(() => {})
|
|
520
481
|
} else if (changeset) {
|
|
521
482
|
// for passing into rollback
|
|
@@ -2,8 +2,6 @@ const cds = require('../../../../cds')
|
|
|
2
2
|
|
|
3
3
|
const { UNAUTHORIZED, FORBIDDEN, getRequiresAsArray } = require('../../../../common/utils/auth')
|
|
4
4
|
|
|
5
|
-
const measurePerformance = require('../../perf/performance')
|
|
6
|
-
|
|
7
5
|
module.exports = srv => {
|
|
8
6
|
const requires = getRequiresAsArray(srv.definition)
|
|
9
7
|
|
|
@@ -55,11 +53,6 @@ module.exports = srv => {
|
|
|
55
53
|
odataReq.setApplicationData({ req })
|
|
56
54
|
}
|
|
57
55
|
|
|
58
|
-
// in case of batch request with sap-statistics=true also measure performance of batched requests
|
|
59
|
-
if (odataReq.getBatchApplicationData()) {
|
|
60
|
-
measurePerformance(req, res)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
56
|
next()
|
|
64
57
|
}
|
|
65
58
|
}
|
|
@@ -14,13 +14,6 @@ const { toODataResult, postProcess, postProcessMinimal } = require('../utils/res
|
|
|
14
14
|
const { hasOmitValuesPreference } = require('../utils/omitValues')
|
|
15
15
|
const { mergeJson } = require('../../../services/utils/compareJson')
|
|
16
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
|
-
|
|
24
17
|
const _isUpsertAllowed = target => {
|
|
25
18
|
return !(cds.env.runtime && cds.env.runtime.allow_upsert === false) && !(target && target._isDraftEnabled)
|
|
26
19
|
}
|
|
@@ -121,58 +114,6 @@ const _readAfterWriteAndVirtuals = async (req, service, result) => {
|
|
|
121
114
|
const _shouldReadPreviousResult = req =>
|
|
122
115
|
req.event === 'UPDATE' && !isReturnMinimal(req) && hasOmitValuesPreference(req.headers.prefer, 'defaults')
|
|
123
116
|
|
|
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
|
-
|
|
176
117
|
/**
|
|
177
118
|
* The handler that will be registered with odata-v4.
|
|
178
119
|
*
|
|
@@ -199,11 +140,8 @@ const update = service => {
|
|
|
199
140
|
// putting a property?
|
|
200
141
|
const primitive = odataReq.getUriInfo().getLastSegment().getKind() === 'PRIMITIVE.PROPERTY'
|
|
201
142
|
|
|
202
|
-
let result, err
|
|
143
|
+
let result, err
|
|
203
144
|
try {
|
|
204
|
-
// // REVISIT: should be handled somewhere else
|
|
205
|
-
// await _handleMediaType(odataReq, service.model, tx, req)
|
|
206
|
-
|
|
207
145
|
let previousResult
|
|
208
146
|
if (_shouldReadPreviousResult(req)) {
|
|
209
147
|
previousResult = await _readAfterWriteAndVirtuals(req, service, result)
|
|
@@ -227,7 +165,6 @@ const update = service => {
|
|
|
227
165
|
// for passing into commit
|
|
228
166
|
odataReq.getBatchApplicationData().results[changeset].push({ result, req })
|
|
229
167
|
} else {
|
|
230
|
-
commit = true
|
|
231
168
|
await tx.commit(result)
|
|
232
169
|
}
|
|
233
170
|
|
|
@@ -236,8 +173,8 @@ const update = service => {
|
|
|
236
173
|
}
|
|
237
174
|
} catch (e) {
|
|
238
175
|
err = e
|
|
239
|
-
if (!changeset
|
|
240
|
-
//
|
|
176
|
+
if (!changeset) {
|
|
177
|
+
// REVISIT: rollback needed if error occured before commit attempted -> how to distinguish?
|
|
241
178
|
await tx.rollback(e).catch(() => {})
|
|
242
179
|
} else if (changeset) {
|
|
243
180
|
// for passing into rollback
|
|
@@ -84,6 +84,8 @@ class ExpressionToCQN {
|
|
|
84
84
|
case ResourceKind.ALL_EXPRESSION:
|
|
85
85
|
case ResourceKind.ANY_EXPRESSION:
|
|
86
86
|
return segment.getExpression() ? this.parse(segment.getExpression()) : undefined
|
|
87
|
+
case ResourceKind.COUNT:
|
|
88
|
+
return '$count'
|
|
87
89
|
default:
|
|
88
90
|
throw getFeatureNotSupportedError(`Segment kind "${segment.getKind()}" in $filter query option`)
|
|
89
91
|
}
|
|
@@ -95,6 +97,20 @@ class ExpressionToCQN {
|
|
|
95
97
|
if (!segment) return []
|
|
96
98
|
|
|
97
99
|
if (segment.getKind() === ResourceKind.NAVIGATION_TO_ONE) {
|
|
100
|
+
const name = this._segmentFromMember(segment)
|
|
101
|
+
const where =
|
|
102
|
+
nextSegments &&
|
|
103
|
+
nextSegments[nextSegments.length - 1].getKind() === ResourceKind.COUNT &&
|
|
104
|
+
segment.getKeyPredicates().reduce((prev, curr) => {
|
|
105
|
+
if (prev.length > 0) prev.push('and')
|
|
106
|
+
prev.push({ ref: [curr.getEdmRef().getName()] }, '=', { val: curr.getText() })
|
|
107
|
+
return prev
|
|
108
|
+
}, [])
|
|
109
|
+
|
|
110
|
+
return [where ? { id: name, where } : name, ...this._getMemberRecursively(nextSegments)]
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (segment.getKind() === ResourceKind.NAVIGATION_TO_MANY) {
|
|
98
114
|
return [this._segmentFromMember(segment), ...this._getMemberRecursively(nextSegments)]
|
|
99
115
|
}
|
|
100
116
|
|
|
@@ -102,6 +118,10 @@ class ExpressionToCQN {
|
|
|
102
118
|
return [...this._getMemberRecursively(nextSegments)]
|
|
103
119
|
}
|
|
104
120
|
|
|
121
|
+
if (segment.getKind() === ResourceKind.COUNT) {
|
|
122
|
+
return [this._segmentFromMember(segment), ...this._getMemberRecursively(nextSegments)]
|
|
123
|
+
}
|
|
124
|
+
|
|
105
125
|
if (segment.getKind() === ResourceKind.COMPLEX_PROPERTY) {
|
|
106
126
|
if (nextSegments.length) {
|
|
107
127
|
return [this._segmentFromMember(segment), ...this._getMemberRecursively(nextSegments)]
|
|
@@ -130,6 +150,10 @@ class ExpressionToCQN {
|
|
|
130
150
|
return entry
|
|
131
151
|
}
|
|
132
152
|
}
|
|
153
|
+
if (members.length > 1 && members[members.length - 1] === '$count') {
|
|
154
|
+
return { func: 'count', args: [{ ref: members.slice(0, members.length - 1) }], as: '$count' }
|
|
155
|
+
}
|
|
156
|
+
|
|
133
157
|
return { ref: members }
|
|
134
158
|
}
|
|
135
159
|
|
|
@@ -55,13 +55,13 @@ const convertUrlPathToCqn = (segments, service) => {
|
|
|
55
55
|
const entity = segment.getEntitySet().getEntityType().getFullQualifiedName()
|
|
56
56
|
const keys = convertKeyPredicatesToStringExpr(segment.getKeyPredicates())
|
|
57
57
|
|
|
58
|
-
return `${findCsnTargetFor(entity.name, service.model, service.
|
|
58
|
+
return `${findCsnTargetFor(entity.name, service.model, service.namespace).name}${keys}`
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
if (segment.getKind() === 'SINGLETON') {
|
|
62
62
|
const singleton = segment.getSingleton().getEntityType().getFullQualifiedName()
|
|
63
63
|
|
|
64
|
-
return `${findCsnTargetFor(singleton.name, service.model, service.
|
|
64
|
+
return `${findCsnTargetFor(singleton.name, service.model, service.namespace).name}`
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
if (segment.getKind() === 'COMPLEX.PROPERTY') {
|
|
@@ -25,7 +25,7 @@ const FORMAT_REGEXP = new RegExp(
|
|
|
25
25
|
)
|
|
26
26
|
|
|
27
27
|
const parseNonNegativeInteger = value => {
|
|
28
|
-
|
|
28
|
+
const tokenizer = new UriTokenizer(value)
|
|
29
29
|
if (tokenizer.next(TokenKind.UnsignedIntegerValue) && tokenizer.next(TokenKind.EOF)) {
|
|
30
30
|
const result = Number.parseInt(value, 10)
|
|
31
31
|
if (Number.isSafeInteger(result)) return result
|
|
@@ -66,19 +66,22 @@ queryOptionParserMap.set(QueryOptions.FORMAT, value => {
|
|
|
66
66
|
throw new UriSyntaxError(UriSyntaxError.Message.WRONG_OPTION_VALUE, QueryOptions.FORMAT)
|
|
67
67
|
})
|
|
68
68
|
queryOptionParserMap.set(QueryOptions.SEARCH, value => {
|
|
69
|
-
|
|
69
|
+
const trimmedValue = value.trim()
|
|
70
|
+
if (!trimmedValue) return
|
|
71
|
+
|
|
72
|
+
const tokenizer = new UriTokenizer(trimmedValue)
|
|
70
73
|
const searchOption = new SearchParser().parse(tokenizer)
|
|
71
74
|
tokenizer.requireNext(TokenKind.EOF)
|
|
72
75
|
return searchOption
|
|
73
76
|
})
|
|
74
77
|
queryOptionParserMap.set(QueryOptions.FILTER, (value, edm, referringType, crossjoinEntitySets, aliases) => {
|
|
75
|
-
|
|
78
|
+
const tokenizer = new UriTokenizer(value)
|
|
76
79
|
const filterOption = new FilterParser(edm).parse(tokenizer, referringType, crossjoinEntitySets, aliases)
|
|
77
80
|
tokenizer.requireNext(TokenKind.EOF)
|
|
78
81
|
return filterOption
|
|
79
82
|
})
|
|
80
83
|
queryOptionParserMap.set(QueryOptions.ORDERBY, (value, edm, referringType, crossjoinEntitySets, aliases) => {
|
|
81
|
-
|
|
84
|
+
const tokenizer = new UriTokenizer(value)
|
|
82
85
|
const orderByOption = new OrderByParser(edm).parse(tokenizer, referringType, crossjoinEntitySets, aliases)
|
|
83
86
|
tokenizer.requireNext(TokenKind.EOF)
|
|
84
87
|
return orderByOption
|
|
@@ -86,14 +89,14 @@ queryOptionParserMap.set(QueryOptions.ORDERBY, (value, edm, referringType, cross
|
|
|
86
89
|
queryOptionParserMap.set(
|
|
87
90
|
QueryOptions.SELECT,
|
|
88
91
|
(value, edm, referringType, crossjoinEntitySets, aliases, isCollection) => {
|
|
89
|
-
|
|
92
|
+
const tokenizer = new UriTokenizer(value)
|
|
90
93
|
const selectOption = new SelectParser(edm).parse(tokenizer, referringType, isCollection)
|
|
91
94
|
tokenizer.requireNext(TokenKind.EOF)
|
|
92
95
|
return selectOption
|
|
93
96
|
}
|
|
94
97
|
)
|
|
95
98
|
queryOptionParserMap.set(QueryOptions.EXPAND, (value, edm, referringType, crossjoinEntitySets, aliases) => {
|
|
96
|
-
|
|
99
|
+
const tokenizer = new UriTokenizer(value)
|
|
97
100
|
const expandOption = new ExpandParser(edm).parse(tokenizer, referringType, crossjoinEntitySets, aliases)
|
|
98
101
|
tokenizer.requireNext(TokenKind.EOF)
|
|
99
102
|
return expandOption
|
|
@@ -170,7 +173,7 @@ class UriParser {
|
|
|
170
173
|
|
|
171
174
|
// $apply must be parsed first.
|
|
172
175
|
if (queryOptions[QueryOptions.APPLY] !== undefined) {
|
|
173
|
-
|
|
176
|
+
const tokenizer = new UriTokenizer(queryOptions[QueryOptions.APPLY])
|
|
174
177
|
referringType = new TransientStructuredType(referringType)
|
|
175
178
|
const parseApplyQueryOptionPm = parseQueryOptionsPm
|
|
176
179
|
? parseQueryOptionsPm.createChild('Parse query option $apply').start()
|
|
@@ -219,7 +222,7 @@ class UriParser {
|
|
|
219
222
|
*/
|
|
220
223
|
_parseRelativeUri (uriPathSegments, aliases) {
|
|
221
224
|
let currentUriSegment = uriPathSegments[0]
|
|
222
|
-
|
|
225
|
+
const tokenizer = new UriTokenizer(currentUriSegment)
|
|
223
226
|
|
|
224
227
|
if (tokenizer.next(TokenKind.EOF)) {
|
|
225
228
|
uriPathSegments.shift()
|
|
@@ -257,7 +260,7 @@ class UriParser {
|
|
|
257
260
|
// Type casts are explicitly not supported (although the parser can parse them)
|
|
258
261
|
FeatureSupport.failUnsupported(FeatureSupport.features.TypeCast, uriPathSegment, 0)
|
|
259
262
|
|
|
260
|
-
|
|
263
|
+
const tokenizer = new UriTokenizer(uriPathSegment)
|
|
261
264
|
tokenizer.requireNext(TokenKind.QualifiedName)
|
|
262
265
|
const qualifiedName = tokenizer.getText()
|
|
263
266
|
const type = this._edm.getEntityType(FullQualifiedName.createFromNameSpaceAndName(qualifiedName))
|
|
@@ -275,7 +278,7 @@ class UriParser {
|
|
|
275
278
|
*/
|
|
276
279
|
_parseResourcePath (uriPathSegments, aliases) {
|
|
277
280
|
let currentUriSegment = uriPathSegments[0]
|
|
278
|
-
|
|
281
|
+
const tokenizer = new UriTokenizer(currentUriSegment)
|
|
279
282
|
|
|
280
283
|
if (tokenizer.next(TokenKind.ALL)) {
|
|
281
284
|
FeatureSupport.failUnsupported(FeatureSupport.features.All)
|
|
@@ -34,13 +34,6 @@ class ConditionalRequestControlCommand extends Command {
|
|
|
34
34
|
execute (next) {
|
|
35
35
|
// Validate that the ETag validation has been called by the data handler.
|
|
36
36
|
if (this._request.isConditional()) {
|
|
37
|
-
if (!this._request.validateEtagHasBeenCalled()) {
|
|
38
|
-
throw new InternalServerError(
|
|
39
|
-
'Error in conditional request processing. ' +
|
|
40
|
-
' The function validateEtag(etag) has to be called by the application.'
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
37
|
// Do not set statusCode directly because of internal response cache.
|
|
45
38
|
if (this._request.getETAGValidationStatus() === StatusCodes.NOT_MODIFIED) {
|
|
46
39
|
this._response.setStatusCode(StatusCodes.NOT_MODIFIED)
|