@sap/cds 7.3.1 → 7.4.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 +69 -3
- package/_i18n/i18n_es_MX.properties +110 -0
- package/apis/cds.d.ts +13 -12
- package/apis/core.d.ts +27 -108
- package/apis/cqn.d.ts +15 -18
- package/apis/csn.d.ts +95 -60
- package/apis/env.d.ts +25 -0
- package/apis/events.d.ts +125 -0
- package/apis/{reflect.d.ts → linked.d.ts} +29 -38
- package/apis/models.d.ts +60 -45
- package/apis/ql.d.ts +19 -5
- package/apis/{serve.d.ts → server.d.ts} +59 -33
- package/apis/services.d.ts +76 -147
- package/apis/test.d.ts +1 -1
- package/bin/serve.js +3 -0
- package/lib/compile/cds-compile.js +2 -2
- package/lib/compile/etc/csv.js +2 -1
- package/lib/compile/to/edm.js +8 -3
- package/lib/compile/to/gql.js +4 -0
- package/lib/dbs/cds-deploy.js +52 -4
- package/lib/env/cds-requires.js +27 -15
- package/lib/env/defaults.js +1 -0
- package/lib/env/schemas/index.js +10 -0
- package/lib/index.js +7 -4
- package/lib/linked/models.js +8 -5
- package/lib/ql/CREATE.js +2 -0
- package/lib/ql/DELETE.js +1 -0
- package/lib/ql/DROP.js +2 -0
- package/lib/ql/INSERT.js +2 -22
- package/lib/ql/Query.js +59 -22
- package/lib/ql/SELECT.js +5 -0
- package/lib/ql/STREAM.js +2 -0
- package/lib/ql/UPDATE.js +2 -0
- package/lib/ql/UPSERT.js +3 -1
- package/lib/ql/cds-ql.js +21 -5
- package/lib/ql/infer.js +129 -0
- package/lib/req/cds-context.js +8 -5
- package/lib/srv/cds-connect.js +3 -1
- package/lib/utils/axios.js +4 -2
- package/lib/utils/data.js +3 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +12 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +27 -9
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/batch/BatchProcessor.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/TrustedResourceJsonSerializer.js +8 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +11 -8
- package/libx/_runtime/common/code-ext/worker.js +5 -16
- package/libx/_runtime/common/generic/auth/capabilities.js +11 -2
- package/libx/_runtime/common/i18n/messages.properties +1 -0
- package/libx/_runtime/common/utils/postProcessing.js +1 -1
- package/libx/_runtime/common/utils/resolveView.js +28 -9
- package/libx/{common → _runtime/common}/utils/ucsn.js +19 -11
- package/libx/_runtime/db/expand/expandCQNToJoin.js +6 -6
- package/libx/_runtime/db/expand/rawToExpanded.js +4 -4
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +6 -1
- package/libx/_runtime/db/sql-builder/UpdateBuilder.js +6 -1
- package/libx/_runtime/db/sql-builder/dollar.js +7 -7
- package/libx/_runtime/fiori/generic/activate.js +2 -2
- package/libx/_runtime/fiori/generic/edit.js +25 -45
- package/libx/_runtime/fiori/generic/read.js +3 -5
- package/libx/_runtime/fiori/lean-draft.js +171 -84
- package/libx/_runtime/fiori/utils/delete.js +7 -1
- package/libx/_runtime/fiori/utils/handler.js +4 -6
- package/libx/_runtime/fiori/utils/lockInfo.js +27 -0
- package/libx/_runtime/fiori/utils/where.js +20 -1
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +3 -2
- package/libx/_runtime/messaging/Outbox.js +12 -47
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +1 -3
- package/libx/_runtime/messaging/common-utils/authorizedRequest.js +3 -0
- package/libx/_runtime/messaging/common-utils/connections.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging.js +12 -13
- package/libx/_runtime/messaging/file-based.js +7 -5
- package/libx/_runtime/messaging/redis-messaging.js +10 -11
- package/libx/_runtime/messaging/service.js +12 -26
- package/libx/_runtime/remote/Service.js +52 -36
- package/libx/_runtime/remote/utils/client.js +24 -125
- package/libx/odata/afterburner.js +16 -6
- package/libx/odata/grammar.peggy +26 -7
- package/libx/odata/metadata.js +18 -1
- package/libx/odata/parser.js +1 -1
- package/libx/odata/service-document.js +0 -1
- package/libx/odata/utils.js +19 -3
- package/libx/{_runtime/messaging/outbox/utils.js → outbox/index.js} +94 -24
- package/libx/rest/middleware/parse.js +1 -1
- package/package.json +2 -2
- package/apis/connect.d.ts +0 -39
- package/bin/utils/modules.js +0 -7
- package/bin/utils/term.js +0 -56
- package/lib/env/schema.js +0 -9
- package/lib/linked/queries.js +0 -41
- package/lib/srv/protocols/odata-v2-proxy.js +0 -3699
- package/libx/common/asserts.js +0 -0
- package/libx/common/crud.js +0 -0
- package/libx/common/etag.js +0 -0
- package/libx/common/localized.js +0 -0
- package/libx/common/managed.js +0 -0
- package/libx/common/paging.js +0 -0
- package/libx/common/readme.md +0 -4
- package/libx/common/sorting.js +0 -0
- package/libx/common/temporal.js +0 -0
- package/libx/connect/auth.js +0 -0
- package/libx/connect/perf.js +0 -0
- package/libx/connect/readme.md +0 -3
- package/libx/fiori/draft/readme.md +0 -1
- package/libx/fiori/readme.md +0 -1
- package/libx/hana/readme.md +0 -1
- package/libx/msg/readme.md +0 -3
- package/libx/readme.md +0 -1
- package/libx/sqlite/readme.md +0 -1
- /package/libx/_runtime/{messaging/common-utils → common/utils}/waitingTime.js +0 -0
- /package/libx/{_runtime/messaging/outbox → outbox}/OutboxRunner.js +0 -0
|
@@ -7,41 +7,34 @@ const { convertV2ResponseData, deepSanitize, convertV2PayloadData } = require('.
|
|
|
7
7
|
|
|
8
8
|
let _cloudSdk
|
|
9
9
|
|
|
10
|
-
const PPPD = {
|
|
11
|
-
POST: 1,
|
|
12
|
-
PUT: 1,
|
|
13
|
-
PATCH: 1,
|
|
14
|
-
DELETE: 1
|
|
15
|
-
}
|
|
16
|
-
|
|
10
|
+
const PPPD = { POST: 1, PUT: 1, PATCH: 1, DELETE: 1 }
|
|
17
11
|
const KINDS_SUPPORTING_BATCH = { odata: 1, 'odata-v2': 1, 'odata-v4': 1 }
|
|
18
12
|
|
|
19
13
|
const _sanitizeHeaders = headers => {
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
// REVISIT: is this in-place modification intended?
|
|
15
|
+
if (headers?.authorization) headers.authorization = headers.authorization.split(' ')[0] + ' ***'
|
|
22
16
|
return headers
|
|
23
17
|
}
|
|
24
18
|
|
|
25
19
|
const _executeHttpRequest = async ({ requestConfig, destination, destinationOptions, jwt, csrf, csrfInBatch }) => {
|
|
26
|
-
const { executeHttpRequestWithOrigin } =
|
|
27
|
-
const destinationName = typeof destination === 'string' && destination
|
|
20
|
+
const { executeHttpRequestWithOrigin } = _getCloudSdk()
|
|
28
21
|
|
|
29
|
-
if (
|
|
30
|
-
destination = {
|
|
22
|
+
if (typeof destination === 'string') {
|
|
23
|
+
destination = {
|
|
24
|
+
destinationName: destination,
|
|
25
|
+
...destinationOptions,
|
|
26
|
+
...{ jwt: destinationOptions?.jwt !== undefined ? destinationOptions.jwt : jwt }
|
|
27
|
+
}
|
|
28
|
+
if (destination.jwt !== undefined && !destination.jwt) delete destination.jwt // don't pass any value
|
|
31
29
|
} else if (destination.forwardAuthToken) {
|
|
32
30
|
destination = {
|
|
33
31
|
...destination,
|
|
34
32
|
headers: destination.headers ? { ...destination.headers } : {},
|
|
35
33
|
authentication: 'NoAuthentication'
|
|
36
34
|
}
|
|
37
|
-
|
|
38
35
|
delete destination.forwardAuthToken
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
destination.headers.authorization = `Bearer ${jwt}`
|
|
42
|
-
} else {
|
|
43
|
-
LOG._warn && LOG.warn('Missing JWT token for forwardAuthToken')
|
|
44
|
-
}
|
|
36
|
+
if (jwt) destination.headers.authorization = `Bearer ${jwt}`
|
|
37
|
+
else LOG._warn && LOG.warn('Missing JWT token for forwardAuthToken')
|
|
45
38
|
}
|
|
46
39
|
|
|
47
40
|
let requestOptions
|
|
@@ -64,62 +57,20 @@ const _executeHttpRequest = async ({ requestConfig, destination, destinationOpti
|
|
|
64
57
|
const maxBodyLength = cds.env?.remote?.max_body_length
|
|
65
58
|
requestConfig = {
|
|
66
59
|
...requestConfig,
|
|
67
|
-
headers: {
|
|
68
|
-
custom: { ...requestConfig.headers }
|
|
69
|
-
},
|
|
60
|
+
headers: { custom: { ...requestConfig.headers } },
|
|
70
61
|
...(maxBodyLength && { maxBodyLength })
|
|
71
62
|
}
|
|
72
63
|
|
|
73
64
|
return executeHttpRequestWithOrigin(destination, requestConfig, requestOptions)
|
|
74
65
|
}
|
|
75
66
|
|
|
76
|
-
const
|
|
67
|
+
const _getCloudSdk = () => {
|
|
77
68
|
if (_cloudSdk) return _cloudSdk
|
|
78
69
|
// eslint-disable-next-line cds/no-missing-dependencies -- needs to be added by app dev
|
|
79
70
|
_cloudSdk = require('@sap-cloud-sdk/http-client')
|
|
80
71
|
return _cloudSdk
|
|
81
72
|
}
|
|
82
73
|
|
|
83
|
-
const getDestination = (name, credentials) => {
|
|
84
|
-
// Cloud SDK wants property "queryParameters" but we have documented "queries"
|
|
85
|
-
if (credentials.queries && !credentials.queryParameters) {
|
|
86
|
-
credentials.queryParameters = credentials.queries
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return { name, ...credentials }
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* @param {import('@sap-cloud-sdk/connectivity').DestinationFetchOptions} [options]
|
|
94
|
-
* @param {string} [jwt]
|
|
95
|
-
* @returns {import('@sap-cloud-sdk/connectivity').DestinationFetchOptions}
|
|
96
|
-
*/
|
|
97
|
-
const resolveDestinationOptions = function (options, jwt) {
|
|
98
|
-
if (!options && !jwt) return
|
|
99
|
-
|
|
100
|
-
const resolvedOptions = Object.assign({}, options ?? {})
|
|
101
|
-
resolvedOptions.jwt = jwt
|
|
102
|
-
|
|
103
|
-
if (options?.selectionStrategy) {
|
|
104
|
-
resolvedOptions.selectionStrategy = options.selectionStrategy
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return resolvedOptions
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const getKind = options => {
|
|
111
|
-
const kind = (options.credentials && options.credentials.kind) || options.kind
|
|
112
|
-
if (typeof kind === 'object') {
|
|
113
|
-
const k = Object.keys(kind).find(
|
|
114
|
-
key => key === 'odata' || key === 'odata-v4' || key === 'odata-v2' || key === 'rest'
|
|
115
|
-
)
|
|
116
|
-
// odata-v4 is equivalent of odata
|
|
117
|
-
return k === 'odata-v4' ? 'odata' : k
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return kind
|
|
121
|
-
}
|
|
122
|
-
|
|
123
74
|
/**
|
|
124
75
|
* Rest Client
|
|
125
76
|
*/
|
|
@@ -278,21 +229,10 @@ const _getSanitizedError = (e, reqOptions, options = { suppressRemoteResponseBod
|
|
|
278
229
|
}
|
|
279
230
|
|
|
280
231
|
// eslint-disable-next-line complexity
|
|
281
|
-
const run = async (
|
|
282
|
-
requestConfig,
|
|
283
|
-
{
|
|
284
|
-
destination,
|
|
285
|
-
jwt,
|
|
286
|
-
kind,
|
|
287
|
-
resolvedTarget,
|
|
288
|
-
returnType,
|
|
289
|
-
suppressRemoteResponseBody,
|
|
290
|
-
destinationOptions,
|
|
291
|
-
csrf,
|
|
292
|
-
csrfInBatch
|
|
293
|
-
}
|
|
294
|
-
) => {
|
|
232
|
+
const run = async (requestConfig, options) => {
|
|
295
233
|
let response
|
|
234
|
+
|
|
235
|
+
const { destination, destinationOptions, jwt, csrf, csrfInBatch, suppressRemoteResponseBody } = options
|
|
296
236
|
try {
|
|
297
237
|
response = await _executeHttpRequest({
|
|
298
238
|
requestConfig,
|
|
@@ -368,31 +308,19 @@ const run = async (
|
|
|
368
308
|
}
|
|
369
309
|
}
|
|
370
310
|
|
|
311
|
+
const { kind, resolvedTarget, returnType } = options
|
|
371
312
|
if (kind === 'odata-v4') return _purgeODataV4(response.data)
|
|
372
313
|
if (kind === 'odata-v2') return _purgeODataV2(response.data, resolvedTarget, returnType, requestConfig.headers)
|
|
373
314
|
if (kind === 'odata') {
|
|
374
315
|
if (typeof response.data !== 'object') return response.data
|
|
375
316
|
// try to guess if we need to purge v2 or v4
|
|
376
|
-
if (response.data.d)
|
|
377
|
-
return _purgeODataV2(response.data, resolvedTarget, returnType, requestConfig.headers)
|
|
378
|
-
}
|
|
379
|
-
|
|
317
|
+
if (response.data.d) return _purgeODataV2(response.data, resolvedTarget, returnType, requestConfig.headers)
|
|
380
318
|
return _purgeODataV4(response.data)
|
|
381
319
|
}
|
|
382
320
|
|
|
383
321
|
return response.data
|
|
384
322
|
}
|
|
385
323
|
|
|
386
|
-
const getJwt = req => {
|
|
387
|
-
const headers = req?.context?.headers
|
|
388
|
-
if (headers?.authorization) {
|
|
389
|
-
const token = headers.authorization.match(/^bearer (.+)/i)
|
|
390
|
-
if (token) return token[1]
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
return null
|
|
394
|
-
}
|
|
395
|
-
|
|
396
324
|
const _cqnToReqOptions = (query, service, req) => {
|
|
397
325
|
const { kind, model } = service
|
|
398
326
|
const method = req.method
|
|
@@ -459,8 +387,8 @@ const getReqOptions = (req, query, service) => {
|
|
|
459
387
|
typeof query === 'object'
|
|
460
388
|
? _cqnToReqOptions(query, service, req)
|
|
461
389
|
: typeof query === 'string'
|
|
462
|
-
|
|
463
|
-
|
|
390
|
+
? _stringToReqOptions(query, req.data, req.target)
|
|
391
|
+
: _pathToReqOptions(req.method, req.path, req.data, req.target, service.name)
|
|
464
392
|
|
|
465
393
|
if (service.kind === 'odata-v2' && req.event === 'READ' && reqOptions.url?.match(/(\/any\()|(\/all\()/)) {
|
|
466
394
|
req.reject(501, 'Lambda expressions are not supported in OData v2')
|
|
@@ -528,41 +456,12 @@ const getReqOptions = (req, query, service) => {
|
|
|
528
456
|
if (service.path) reqOptions.url = `${encodeURI(service.path)}${reqOptions.url}`
|
|
529
457
|
|
|
530
458
|
// set axios responseType to 'arraybuffer' if returning binary in rest
|
|
531
|
-
if (req._binary)
|
|
532
|
-
reqOptions.responseType = 'arraybuffer'
|
|
533
|
-
}
|
|
459
|
+
if (req._binary) reqOptions.responseType = 'arraybuffer'
|
|
534
460
|
|
|
535
461
|
return reqOptions
|
|
536
462
|
}
|
|
537
463
|
|
|
538
|
-
const getAdditionalOptions = (
|
|
539
|
-
req,
|
|
540
|
-
destination,
|
|
541
|
-
kind,
|
|
542
|
-
resolvedTarget,
|
|
543
|
-
returnType,
|
|
544
|
-
destinationOptions,
|
|
545
|
-
csrf,
|
|
546
|
-
csrfInBatch
|
|
547
|
-
) => {
|
|
548
|
-
const jwt = getJwt(req)
|
|
549
|
-
const additionalOptions = {
|
|
550
|
-
destination,
|
|
551
|
-
kind,
|
|
552
|
-
resolvedTarget,
|
|
553
|
-
returnType,
|
|
554
|
-
destinationOptions,
|
|
555
|
-
csrf,
|
|
556
|
-
csrfInBatch
|
|
557
|
-
}
|
|
558
|
-
if (jwt) additionalOptions.jwt = jwt
|
|
559
|
-
return additionalOptions
|
|
560
|
-
}
|
|
561
|
-
|
|
562
464
|
module.exports = {
|
|
563
|
-
getKind,
|
|
564
465
|
run,
|
|
565
|
-
getReqOptions
|
|
566
|
-
getDestination,
|
|
567
|
-
getAdditionalOptions
|
|
466
|
+
getReqOptions
|
|
568
467
|
}
|
|
@@ -125,12 +125,15 @@ function getResolvedElement(entity, { ref }) {
|
|
|
125
125
|
return element
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
const forbidden = { '(': 1, and: 1, or: 1, not: 1, ')': 1 }
|
|
129
|
+
|
|
128
130
|
function _processWhere(where, entity) {
|
|
129
131
|
for (let i = 0; i < where.length; i++) {
|
|
130
132
|
const ref = where[i]
|
|
133
|
+
const operator = where[i + 1]
|
|
131
134
|
const val = where[i + 2]
|
|
132
135
|
|
|
133
|
-
if (ref
|
|
136
|
+
if (ref in forbidden || val in forbidden || ref.func) {
|
|
134
137
|
continue
|
|
135
138
|
}
|
|
136
139
|
if (ref.xpr) {
|
|
@@ -138,6 +141,11 @@ function _processWhere(where, entity) {
|
|
|
138
141
|
continue
|
|
139
142
|
}
|
|
140
143
|
|
|
144
|
+
if (operator in forbidden) {
|
|
145
|
+
// xpr check needs to be done first, else it could happen, that we ignore xpr OR xpr
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
|
|
141
149
|
let valIndex = -1
|
|
142
150
|
let refIndex = -1
|
|
143
151
|
if (typeof val === 'object') {
|
|
@@ -165,19 +173,21 @@ function _processWhere(where, entity) {
|
|
|
165
173
|
function _convertVal(element, value) {
|
|
166
174
|
if (value === null) return value
|
|
167
175
|
switch (element._type) {
|
|
168
|
-
case 'cds.Integer':
|
|
169
176
|
case 'cds.UInt8':
|
|
177
|
+
case 'cds.Integer':
|
|
170
178
|
case 'cds.Int16':
|
|
171
179
|
case 'cds.Int32':
|
|
180
|
+
if (!/^-?\d+$/.test(value)) throw new Error('Not a valid integer')
|
|
172
181
|
// eslint-disable-next-line no-case-declarations
|
|
173
182
|
const n = Number(value)
|
|
174
|
-
if (Number.isSafeInteger(n))
|
|
175
|
-
throw new Error('Not a
|
|
183
|
+
if (!Number.isSafeInteger(n)) throw new Error('Not a valid integer')
|
|
184
|
+
if (element._type === 'cds.UInt8' && n < 0) throw new Error('Not a positive integer')
|
|
185
|
+
return n
|
|
176
186
|
|
|
177
187
|
case 'cds.String':
|
|
178
|
-
case 'cds.LargeString':
|
|
188
|
+
case 'cds.LargeString':
|
|
179
189
|
return String(value)
|
|
180
|
-
case 'cds.Double':
|
|
190
|
+
case 'cds.Double':
|
|
181
191
|
return parseFloat(value)
|
|
182
192
|
case 'cds.Decimal':
|
|
183
193
|
case 'cds.DecimalFloat':
|
package/libx/odata/grammar.peggy
CHANGED
|
@@ -36,9 +36,25 @@
|
|
|
36
36
|
// we keep that here to allow for usage in https://peggyjs.org/online
|
|
37
37
|
const safeNumber =
|
|
38
38
|
options.safeNumber ||
|
|
39
|
-
function (
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
function (inputString) {
|
|
40
|
+
if (typeof inputString !== 'string') return inputString
|
|
41
|
+
// Try to parse the input string as a floating-point number using parseFloat
|
|
42
|
+
const parsedFloat = parseFloat(inputString)
|
|
43
|
+
|
|
44
|
+
// Check if the parsed value is not NaN and is equal to the original input string
|
|
45
|
+
if (!isNaN(parsedFloat) && String(parsedFloat) === inputString) {
|
|
46
|
+
return parsedFloat
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Try to parse the input string as an integer using parseInt
|
|
50
|
+
const parsedInt = parseInt(inputString);
|
|
51
|
+
// special case like '3.00000000000001', the precision is not lost and string is returned
|
|
52
|
+
if (!isNaN(parsedInt) && String(parsedInt) === inputString.replace(/^-?\d+\.0+$/, inputString.split('.')[0])) {
|
|
53
|
+
return parsedInt
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// If none of the above conditions are met, return the input string as is
|
|
57
|
+
return inputString
|
|
42
58
|
}
|
|
43
59
|
const skipToken = options.skipToken
|
|
44
60
|
const standardBase64 =
|
|
@@ -380,7 +396,9 @@
|
|
|
380
396
|
"$skip=" o val:skip { _setLimitOffset(val) } /
|
|
381
397
|
"$search=" o s:search { if (s) SELECT.search = s } /
|
|
382
398
|
"$count=" o count /
|
|
383
|
-
"$apply=" o trafos:transformations { return trafos }
|
|
399
|
+
"$apply=" o trafos:transformations { return trafos } /
|
|
400
|
+
//Workaround to support empty expand even if not OData compliant old adapter supported it and did not crash
|
|
401
|
+
"$expand=" {return null}
|
|
384
402
|
|
|
385
403
|
temporal = ("$at" / "$from" / "$toInclusive" / "$to") "=" date
|
|
386
404
|
|
|
@@ -600,7 +618,8 @@
|
|
|
600
618
|
aliasedParamVal = val / jsonObject / jsonArray / "[" list:innerList "]" { return { list } }
|
|
601
619
|
|
|
602
620
|
custom
|
|
603
|
-
= [a-zA-Z0-9-_.~]+ "=" [^&]*
|
|
621
|
+
= [a-zA-Z0-9-_.~]+ ("=" [^&]*)?
|
|
622
|
+
|
|
604
623
|
aliasedParam "an aliased parameter (@param)" = "@" i:identifier { return "@" + i }
|
|
605
624
|
aliasedParamEqualsVal = alias:aliasedParam "=" !aliasedParam value:aliasedParamVal {
|
|
606
625
|
_replaceAliased(SELECT, alias, value);
|
|
@@ -655,9 +674,9 @@
|
|
|
655
674
|
}
|
|
656
675
|
|
|
657
676
|
val
|
|
658
|
-
= val:
|
|
659
|
-
/ val:time {return {val}}
|
|
677
|
+
= val:bool {return {val}}
|
|
660
678
|
/ val:date {return {val}}
|
|
679
|
+
/ val:time {return {val}}
|
|
661
680
|
/ val:guid {return {val}}
|
|
662
681
|
/ val:number {return typeof val === 'number' ? {val} : { val, literal:'number' }}
|
|
663
682
|
/ val:string {return {val}}
|
package/libx/odata/metadata.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const cds = require('../../lib')
|
|
2
2
|
const LOG = cds.log('odata')
|
|
3
3
|
const crypto = require('crypto')
|
|
4
|
+
const { join } = require ('path')
|
|
4
5
|
|
|
5
6
|
const _requestedFormat = (queryOption, header) => {
|
|
6
7
|
if (queryOption) return queryOption.match(/json/i) ? 'json' : 'xml'
|
|
@@ -44,6 +45,12 @@ const generateEtag = s => {
|
|
|
44
45
|
|
|
45
46
|
const odata_error = (code, message) => ({ error: { code, message } })
|
|
46
47
|
|
|
48
|
+
const mpSupportsEmptyLocale = () => {
|
|
49
|
+
const pkg = require(join('@sap/cds-mtxs', 'package.json'))
|
|
50
|
+
const version = pkg.version.match(/^(\d+\.)?(\d+\.)?(\*|\d+)$/).map(Number)
|
|
51
|
+
return version[1] > 1 || (version[1] === 1 && version[2] >= 12)
|
|
52
|
+
}
|
|
53
|
+
|
|
47
54
|
module.exports = srv =>
|
|
48
55
|
async function metadata(req, res, next) {
|
|
49
56
|
if (req.path === '/$metadata') {
|
|
@@ -85,10 +92,20 @@ module.exports = srv =>
|
|
|
85
92
|
)
|
|
86
93
|
|
|
87
94
|
try {
|
|
88
|
-
|
|
95
|
+
let edmx
|
|
96
|
+
// REVISIT: remove check later
|
|
97
|
+
if (mpSupportsEmptyLocale()) {
|
|
98
|
+
edmx = metadataCache.edm || await mps.getEdmx({ tenant, model: srv.model, service: srv.definition.name })
|
|
99
|
+
metadataCache.edm = edmx
|
|
100
|
+
const extBundle = cds.env.requires.extensibility && await mps.getI18n({ tenant, locale })
|
|
101
|
+
edmx = cds.localize(srv.model, locale, edmx, extBundle)
|
|
102
|
+
} else {
|
|
103
|
+
edmx = await mps.getEdmx({ tenant, model: srv.model, service: srv.definition.name, locale })
|
|
104
|
+
}
|
|
89
105
|
metadataCache.xmlEtag[locale] = generateEtag(edmx)
|
|
90
106
|
res.set('Content-Type', 'application/xml')
|
|
91
107
|
res.send(edmx)
|
|
108
|
+
return
|
|
92
109
|
} catch (e) {
|
|
93
110
|
if (LOG._error) {
|
|
94
111
|
e.message = 'Unable to get EDMX for tenant ' + tenant + ' due to error: ' + e.message
|