@sap/cds 5.7.3 → 5.8.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 +111 -0
- package/app/fiori/routes.js +1 -1
- package/bin/deploy/to-hana/cfUtil.js +251 -138
- package/bin/deploy/to-hana/gitUtil.js +55 -0
- package/bin/deploy/to-hana/hana.js +92 -93
- package/bin/deploy/to-hana/hdiDeployUtil.js +42 -27
- package/bin/deploy/to-hana/index.js +14 -13
- package/bin/mtx/in-cds.js +1 -0
- package/bin/serve.js +1 -1
- package/bin/version.js +1 -0
- package/lib/compile/cdsc.js +0 -6
- package/lib/compile/minify.js +1 -1
- package/lib/compile/resolve.js +1 -1
- package/lib/compile/to/srvinfo.js +1 -1
- package/lib/core/classes.js +21 -1
- package/lib/env/index.js +3 -2
- package/lib/env/requires.js +4 -0
- package/lib/i18n/localize.js +5 -8
- package/lib/index.js +1 -0
- package/lib/log/errors.js +1 -1
- package/lib/ql/SELECT.js +2 -2
- package/lib/req/cds-context.js +1 -1
- package/lib/req/context.js +1 -1
- package/lib/serve/Transaction.js +9 -5
- package/lib/serve/index.js +13 -21
- package/lib/utils/tests.js +90 -66
- package/libx/_runtime/audit/generic/personal/modification.js +0 -8
- package/libx/_runtime/auth/index.js +7 -6
- package/libx/_runtime/auth/strategies/dwc.js +43 -0
- package/libx/_runtime/auth/utils.js +24 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +11 -38
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +12 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +7 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +24 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +43 -38
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +11 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +13 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/boundToCQN.js +1 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/deleteToCQN.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/orderByToCQN.js +9 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +17 -30
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +12 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/AbstractEdmStructuredType.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriHelper.js +7 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriTokenizer.js +5 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/PrimitiveValueDecoder.js +19 -47
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/ValueConverter.js +4 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +7 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/CommandExecutor.js +0 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/ConditionalRequestControlCommand.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ContextURLFactory.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ErrorJsonSerializer.js +2 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/TrustedResourceJsonSerializer.js +2 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/ConditionalRequestValidator.js +6 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +18 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +41 -17
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +1 -17
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +80 -21
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +47 -10
- package/libx/_runtime/cds-services/adapter/rest/Rest.js +22 -1
- package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +8 -3
- package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +3 -0
- package/libx/_runtime/cds-services/services/Service.js +1 -1
- package/libx/_runtime/cds-services/services/utils/columns.js +5 -1
- package/libx/_runtime/cds-services/services/utils/compareJson.js +15 -16
- package/libx/_runtime/cds-services/services/utils/differ.js +2 -8
- package/libx/_runtime/common/aspects/Association.js +16 -0
- package/libx/_runtime/common/composition/data.js +28 -37
- package/libx/_runtime/common/composition/delete.js +107 -58
- package/libx/_runtime/common/composition/index.js +3 -3
- package/libx/_runtime/common/composition/insert.js +14 -27
- package/libx/_runtime/common/composition/update.js +39 -34
- package/libx/_runtime/common/error/frontend.js +19 -5
- package/libx/_runtime/common/generic/auth.js +20 -85
- package/libx/_runtime/common/generic/crud.js +22 -1
- package/libx/_runtime/common/i18n/messages.properties +2 -1
- package/libx/_runtime/common/utils/cqn.js +2 -6
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +95 -122
- package/libx/_runtime/common/utils/csn.js +15 -4
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +18 -1
- package/libx/_runtime/common/utils/keys.js +2 -1
- package/libx/_runtime/common/utils/path.js +1 -1
- package/libx/_runtime/common/utils/resolveView.js +12 -4
- package/libx/_runtime/common/utils/rewriteAsterisks.js +27 -13
- package/libx/_runtime/common/utils/search2cqn4sql.js +11 -6
- package/libx/_runtime/common/utils/structured.js +11 -5
- package/libx/_runtime/common/utils/vcap.js +27 -10
- package/libx/_runtime/db/data-conversion/post-processing.js +42 -35
- package/libx/_runtime/db/expand/expand-v2.js +21 -12
- package/libx/_runtime/db/expand/expandCQNToJoin.js +57 -29
- package/libx/_runtime/db/expand/index.js +3 -0
- package/libx/_runtime/db/generic/create.js +0 -10
- package/libx/_runtime/db/generic/index.js +3 -0
- package/libx/_runtime/db/generic/read.js +2 -24
- package/libx/_runtime/db/generic/rewrite.js +1 -3
- package/libx/_runtime/db/generic/update.js +1 -1
- package/libx/_runtime/db/query/delete.js +10 -4
- package/libx/_runtime/db/query/insert.js +3 -4
- package/libx/_runtime/db/query/read.js +4 -1
- package/libx/_runtime/db/query/update.js +5 -5
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +9 -2
- package/libx/_runtime/db/sql-builder/FunctionBuilder.js +3 -0
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +7 -3
- package/libx/_runtime/db/sql-builder/index.js +3 -0
- package/libx/_runtime/db/utils/columns.js +5 -2
- package/libx/_runtime/db/utils/deep.js +6 -8
- package/libx/_runtime/db/utils/generateAliases.js +56 -6
- package/libx/_runtime/fiori/generic/before.js +73 -49
- package/libx/_runtime/fiori/generic/edit.js +14 -18
- package/libx/_runtime/fiori/generic/patch.js +8 -11
- package/libx/_runtime/fiori/generic/read.js +22 -20
- package/libx/_runtime/fiori/generic/readOverDraft.js +1 -4
- package/libx/_runtime/fiori/utils/handler.js +1 -11
- package/libx/_runtime/hana/Service.js +1 -1
- package/libx/_runtime/hana/conversion.js +12 -1
- package/libx/_runtime/hana/execute.js +31 -16
- package/libx/_runtime/hana/localized.js +1 -1
- package/libx/_runtime/hana/search.js +3 -3
- package/libx/_runtime/hana/search2cqn4sql.js +23 -25
- package/libx/_runtime/hana/searchToContains.js +1 -1
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +4 -2
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +0 -1
- package/libx/_runtime/messaging/enterprise-messaging.js +1 -0
- package/libx/_runtime/messaging/file-based.js +3 -1
- package/libx/_runtime/messaging/service.js +16 -7
- package/libx/_runtime/remote/utils/client.js +37 -20
- package/libx/_runtime/remote/utils/data.js +53 -12
- package/libx/_runtime/sqlite/Service.js +1 -1
- package/libx/_runtime/sqlite/conversion.js +10 -0
- package/libx/_runtime/sqlite/localized.js +1 -1
- package/libx/_runtime/types/api.js +2 -2
- package/libx/gql/resolvers/crud/update.js +8 -5
- package/libx/gql/resolvers/parse/ast/enrich.js +1 -0
- package/libx/odata/afterburner.js +29 -6
- package/libx/odata/cqn2odata.js +9 -0
- package/libx/odata/grammar.pegjs +50 -22
- package/libx/odata/index.js +2 -2
- package/libx/odata/parser.js +1 -1
- package/libx/odata/utils.js +2 -2
- package/libx/rest/RestAdapter.js +29 -1
- package/libx/rest/middleware/auth.js +1 -3
- package/libx/rest/middleware/parse.js +1 -0
- package/package.json +1 -1
- package/server.js +1 -1
- package/bin/deploy/to-hana/logger.js +0 -27
- package/bin/deploy/to-hana/runCommand.js +0 -113
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +0 -37
- package/libx/_runtime/common/utils/auth.js +0 -16
|
@@ -22,12 +22,13 @@ class UriHelper {
|
|
|
22
22
|
return uriLiteral.substring(1, uriLiteral.length - 1).replace(REGEXP_TWO_SINGLE_QUOTES, "'")
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
if (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
if (edmType === EdmPrimitiveTypeKind.Binary) {
|
|
26
|
+
// convert the URL-safe base64 encoding to the standard variant (with padding, if necessary)
|
|
27
|
+
let val = uriLiteral.substring(uriLiteral.indexOf("'") + 1, uriLiteral.length - 1).replace(/_/g, '/').replace(/-/g, '+')
|
|
28
|
+
return val.padEnd(val.length + val.length % 4, '=')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (edmType === EdmPrimitiveTypeKind.Duration || edmType.getKind() === EdmTypeKind.ENUM || edmType.getName().startsWith('Geo')) {
|
|
31
32
|
return uriLiteral.substring(uriLiteral.indexOf("'") + 1, uriLiteral.length - 1)
|
|
32
33
|
}
|
|
33
34
|
|
|
@@ -19,11 +19,8 @@ const GUID_VALUE_REGEXP = new RegExp(
|
|
|
19
19
|
'^(?:' + HEX_DIGIT + '{8}-' + HEX_DIGIT + '{4}-' + HEX_DIGIT + '{4}-' + HEX_DIGIT + '{4}-' + HEX_DIGIT + '{12})'
|
|
20
20
|
)
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
const BASE64B8 = BASE64 + '[AQgw](?:==)?'
|
|
25
|
-
const BINARY = '(?:' + BASE64 + '{4})*(?:' + BASE64B16 + '|' + BASE64B8 + ')?'
|
|
26
|
-
const BINARY_VALUE_REGEXP = new RegExp("^[Bb][Ii][Nn][Aa][Rr][Yy]'" + BINARY + "'")
|
|
22
|
+
// regex for url-safe base64
|
|
23
|
+
const BINARY_VALUE_REGEXP = /^[Bb][Ii][Nn][Aa][Rr][Yy]'(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{4}|[A-Za-z0-9-_]{3}=|[A-Za-z0-9-_]{2}={2}|[A-Za-z0-9-_]{2,3})'/
|
|
27
24
|
|
|
28
25
|
const UNSIGNED_INTEGER_VALUE_REGEXP = new RegExp('^\\d+')
|
|
29
26
|
const INTEGER_VALUE_REGEXP = new RegExp('^[-+]?\\d+')
|
|
@@ -684,8 +681,8 @@ class UriTokenizer {
|
|
|
684
681
|
* @returns {boolean} whether the constant has been found at the current index
|
|
685
682
|
* @private
|
|
686
683
|
*/
|
|
687
|
-
_nextConstant (constant) {
|
|
688
|
-
if (this._parseString.startsWith(constant, this._index)) {
|
|
684
|
+
_nextConstant (constant, caseInsensitive) {
|
|
685
|
+
if ((caseInsensitive ? this._parseString.toLowerCase() : this._parseString).startsWith(constant, this._index)) {
|
|
689
686
|
this._index += constant.length
|
|
690
687
|
return true
|
|
691
688
|
}
|
|
@@ -784,7 +781,7 @@ class UriTokenizer {
|
|
|
784
781
|
* @private
|
|
785
782
|
*/
|
|
786
783
|
_nextMethod (methodName) {
|
|
787
|
-
return this._nextConstant(methodName) && this._nextCharacter('(')
|
|
784
|
+
return this._nextConstant(methodName, true) && this._nextCharacter('(')
|
|
788
785
|
}
|
|
789
786
|
|
|
790
787
|
/**
|
|
@@ -67,6 +67,16 @@ const MULTI_LINE_STRING_VALIDATION = new RegExp('^' + SRID + 'MultiLineString\\(
|
|
|
67
67
|
const MULTI_POLYGON_VALIDATION = new RegExp('^' + SRID + 'MultiPolygon\\((' + MULTI_POLYGON + ')\\)$', 'i')
|
|
68
68
|
const COLLECTION_VALIDATION = new RegExp('^' + SRID + 'Collection\\((' + MULTI_GEO_LITERAL + ')\\)$', 'i')
|
|
69
69
|
|
|
70
|
+
const BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}={2})$/
|
|
71
|
+
|
|
72
|
+
function _getBase64(val) {
|
|
73
|
+
// convert url-safe to standard base64 (with padding, if necessary)
|
|
74
|
+
val = val.replace(/_/g, '/').replace(/-/g, '+')
|
|
75
|
+
val = val.padEnd(val.length + val.length % 4, '=')
|
|
76
|
+
if (!val.match(BASE64)) return
|
|
77
|
+
return val
|
|
78
|
+
}
|
|
79
|
+
|
|
70
80
|
/**
|
|
71
81
|
* The primitive-value decoder decodes primitive values, using OData V4 primitive types.
|
|
72
82
|
* The following mapping of V2 and V4 primitive types is assumed:
|
|
@@ -148,7 +158,9 @@ class PrimitiveValueDecoder {
|
|
|
148
158
|
const type = propertyOrReturnType.getType()
|
|
149
159
|
|
|
150
160
|
if (type === EdmPrimitiveTypeKind.Binary) {
|
|
151
|
-
|
|
161
|
+
const val = _getBase64(jsonValue)
|
|
162
|
+
if (!val) throw new IllegalArgumentError('The value for Edm.Binary is not valid base64 content.')
|
|
163
|
+
return val
|
|
152
164
|
}
|
|
153
165
|
|
|
154
166
|
let value = jsonValue
|
|
@@ -284,12 +296,9 @@ class PrimitiveValueDecoder {
|
|
|
284
296
|
}
|
|
285
297
|
|
|
286
298
|
if (type === EdmPrimitiveTypeKind.Binary) {
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
propertyOrReturnType.getType().getMaxLength()) ||
|
|
291
|
-
null
|
|
292
|
-
return this._decodeBinary(value, maxLength)
|
|
299
|
+
const val = _getBase64(value)
|
|
300
|
+
if (!val) throw new IllegalArgumentError('The value for Edm.Binary is not valid base64 content.')
|
|
301
|
+
return val
|
|
293
302
|
}
|
|
294
303
|
|
|
295
304
|
if (type === EdmPrimitiveTypeKind.Int64 || type === EdmPrimitiveTypeKind.Decimal) {
|
|
@@ -362,14 +371,9 @@ class PrimitiveValueDecoder {
|
|
|
362
371
|
if (type.getKind() === EdmTypeKind.DEFINITION) type = type.getUnderlyingType()
|
|
363
372
|
|
|
364
373
|
if (type === EdmPrimitiveTypeKind.Binary) {
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
(propertyOrReturnType.getType().getKind() === EdmTypeKind.DEFINITION &&
|
|
369
|
-
propertyOrReturnType.getType().getMaxLength()) ||
|
|
370
|
-
null
|
|
371
|
-
this._validator.validateBinary(valueBuffer, maxLength)
|
|
372
|
-
return valueBuffer
|
|
374
|
+
const val = _getBase64(valueString)
|
|
375
|
+
if (!val) throw new IllegalArgumentError('The value for Edm.Binary is not valid base64 content.')
|
|
376
|
+
return val
|
|
373
377
|
}
|
|
374
378
|
|
|
375
379
|
let decoded
|
|
@@ -661,38 +665,6 @@ class PrimitiveValueDecoder {
|
|
|
661
665
|
)
|
|
662
666
|
}
|
|
663
667
|
}
|
|
664
|
-
|
|
665
|
-
/**
|
|
666
|
-
* Decode an OData JSON representation of a binary value into its JavaScript value.
|
|
667
|
-
* @param {string} value the JSON value
|
|
668
|
-
* @param {?(number|string)} maxLength the value of the Maxlength facet
|
|
669
|
-
* @returns {Buffer} the JavaScript value
|
|
670
|
-
* @private
|
|
671
|
-
*/
|
|
672
|
-
_decodeBinary (value, maxLength) {
|
|
673
|
-
const valueBuffer = Buffer.from(value, 'base64')
|
|
674
|
-
// The method Buffer.from(...) does not throw an error on invalid input;
|
|
675
|
-
// it simply returns the result of the conversion of the content up to the first error.
|
|
676
|
-
// So we check if the length is correct, taking padding characters into account (see RFC 4648).
|
|
677
|
-
// Newline or other whitespace characters are not allowed according to the OData JSON format specification.
|
|
678
|
-
let length = (value.length * 3) / 4 // Four base64 characters result in three octets.
|
|
679
|
-
if (value.length % 4) {
|
|
680
|
-
// The length is not a multiple of four as it should be.
|
|
681
|
-
length =
|
|
682
|
-
3 * Math.floor(value.length / 4) +
|
|
683
|
-
// The remainder (due to missing padding characters) will result in one or two octets.
|
|
684
|
-
Math.ceil((value.length % 4) / 2)
|
|
685
|
-
} else {
|
|
686
|
-
// Padding characters reduce the amount of expected octets.
|
|
687
|
-
if (value.endsWith('==')) length--
|
|
688
|
-
if (value.endsWith('=')) length--
|
|
689
|
-
}
|
|
690
|
-
if (valueBuffer.length < length) {
|
|
691
|
-
throw new IllegalArgumentError('The value for Edm.Binary is not valid base64 content.')
|
|
692
|
-
}
|
|
693
|
-
this._validator.validateBinary(valueBuffer, maxLength)
|
|
694
|
-
return valueBuffer
|
|
695
|
-
}
|
|
696
668
|
}
|
|
697
669
|
|
|
698
670
|
module.exports = PrimitiveValueDecoder
|
package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/ValueConverter.js
CHANGED
|
@@ -186,19 +186,12 @@ class ValueConverter {
|
|
|
186
186
|
|
|
187
187
|
/**
|
|
188
188
|
* Converts value to the value of Edm.Binary type.
|
|
189
|
-
* @param {Buffer} value - value, which should be converted
|
|
190
|
-
* @param {number} [maxLength] - value of MaxLength facet
|
|
189
|
+
* @param {Buffer|string} value - value, which should be converted
|
|
191
190
|
* @returns {string} Base64 string
|
|
192
191
|
*/
|
|
193
|
-
convertBinary (value
|
|
194
|
-
|
|
195
|
-
return (
|
|
196
|
-
value
|
|
197
|
-
.toString('base64')
|
|
198
|
-
// Convert the standard base64 encoding to the URL-safe variant.
|
|
199
|
-
.replace(PLUS_REGEXP, '-')
|
|
200
|
-
.replace(SLASH_REGEXP, '_')
|
|
201
|
-
)
|
|
192
|
+
convertBinary (value) {
|
|
193
|
+
// convert the standard base64 encoding to the URL-safe variant
|
|
194
|
+
return (Buffer.isBuffer(value) ? value.toString('base64') : value).replace(/\+/g, '-').replace(/\//g, '_')
|
|
202
195
|
}
|
|
203
196
|
|
|
204
197
|
/**
|
|
@@ -43,11 +43,13 @@ class ResourceJsonDeserializer {
|
|
|
43
43
|
* @throws {DeserializationError} if provided data can not be deserialized
|
|
44
44
|
*/
|
|
45
45
|
deserializeEntity (edmType, value, expand, additionalInformation) {
|
|
46
|
+
let data
|
|
46
47
|
try {
|
|
47
|
-
|
|
48
|
+
data = JSON.parse(value)
|
|
48
49
|
this._deserializeStructuralType(edmType, data, null, false, expand, additionalInformation)
|
|
49
50
|
return data
|
|
50
51
|
} catch (e) {
|
|
52
|
+
e._data = data
|
|
51
53
|
if (e instanceof DeserializationError) throw e
|
|
52
54
|
throw new DeserializationError('An error occurred during deserialization of the entity.', e)
|
|
53
55
|
}
|
|
@@ -82,6 +84,7 @@ class ResourceJsonDeserializer {
|
|
|
82
84
|
}
|
|
83
85
|
return tempData
|
|
84
86
|
} catch (e) {
|
|
87
|
+
e._data = tempData
|
|
85
88
|
if (e instanceof DeserializationError) throw e
|
|
86
89
|
throw new DeserializationError('An error occurred during deserialization of the collection.', e)
|
|
87
90
|
}
|
|
@@ -129,6 +132,7 @@ class ResourceJsonDeserializer {
|
|
|
129
132
|
try {
|
|
130
133
|
return this._deserializePrimitive(edmProperty, tempData)
|
|
131
134
|
} catch (e) {
|
|
135
|
+
e._data = tempData
|
|
132
136
|
if (e instanceof DeserializationError) throw e
|
|
133
137
|
throw new DeserializationError('An error occurred during deserialization of the property.', e)
|
|
134
138
|
}
|
|
@@ -155,6 +159,7 @@ class ResourceJsonDeserializer {
|
|
|
155
159
|
try {
|
|
156
160
|
return this._deserializePrimitive(edmProperty, tempData)
|
|
157
161
|
} catch (e) {
|
|
162
|
+
e._data = tempData
|
|
158
163
|
if (e instanceof DeserializationError) throw e
|
|
159
164
|
throw new DeserializationError('An error occurred during deserialization of the property.', e)
|
|
160
165
|
}
|
|
@@ -174,6 +179,7 @@ class ResourceJsonDeserializer {
|
|
|
174
179
|
try {
|
|
175
180
|
return this._deserializeReference(edmType, tempData)
|
|
176
181
|
} catch (e) {
|
|
182
|
+
e._data = tempData
|
|
177
183
|
if (e instanceof DeserializationError) throw e
|
|
178
184
|
throw new DeserializationError('An error occurred during deserialization of the reference.', e)
|
|
179
185
|
}
|
package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/CommandExecutor.js
CHANGED
|
@@ -82,9 +82,6 @@ class CommandExecutor {
|
|
|
82
82
|
}
|
|
83
83
|
}, this._error)
|
|
84
84
|
} catch (innerError) {
|
|
85
|
-
if (innerError.__crashOnError) {
|
|
86
|
-
throw innerError
|
|
87
|
-
}
|
|
88
85
|
if (this._runTimeMeasurement && description) this._runTimeMeasurement.getChild(description).stop()
|
|
89
86
|
callback(innerError)
|
|
90
87
|
}
|
|
@@ -4,7 +4,6 @@ const commons = require('../../odata-commons')
|
|
|
4
4
|
const StatusCodes = commons.http.HttpStatusCode.StatusCodes
|
|
5
5
|
const RepresentationKinds = commons.format.RepresentationKind.Kinds
|
|
6
6
|
const Command = require('./Command')
|
|
7
|
-
const InternalServerError = require('../errors/InternalServerError')
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* The `next` callback to be called upon finish execution.
|
|
@@ -281,7 +281,7 @@ class ContextURLFactory {
|
|
|
281
281
|
// If there is one '*' selected, the context URL contains only '*'.
|
|
282
282
|
if (isAll) {
|
|
283
283
|
value = ['*']
|
|
284
|
-
} else if (type && type.getKind() === EdmTypeKind.ENTITY && isKeyRequired) {
|
|
284
|
+
} else if (type && type.getKind() === EdmTypeKind.ENTITY && isKeyRequired && !cds.env.features.odata_new_parser) {
|
|
285
285
|
for (const keyName of type.getKeyPropertyRefs().keys()) {
|
|
286
286
|
if (!value.includes(keyName)) value.push(keyName)
|
|
287
287
|
}
|
|
@@ -352,7 +352,7 @@ class ContextURLFactory {
|
|
|
352
352
|
// Define a local function because navigation properties must be searched recursively.
|
|
353
353
|
const getExpandPaths = structuredType => {
|
|
354
354
|
let structureResult = []
|
|
355
|
-
for (const name of structuredType.getNavigationProperties().keys()) structureResult.push(name)
|
|
355
|
+
for (const name of structuredType.getNavigationProperties(true).keys()) structureResult.push(name)
|
|
356
356
|
for (const [name, property] of structuredType.getProperties()) {
|
|
357
357
|
if (property.getType().getKind() === EdmTypeKind.COMPLEX) {
|
|
358
358
|
for (const innerPath of getExpandPaths(property.getType())) {
|
|
@@ -472,11 +472,8 @@ class TrustedResourceJsonSerializer {
|
|
|
472
472
|
if (type === EdmPrimitiveTypeKind.Decimal || type === EdmPrimitiveTypeKind.Int64) {
|
|
473
473
|
result = this._formatParams.getIEEE754Setting() ? String(propertyValue) : Number(propertyValue)
|
|
474
474
|
} else if (type === EdmPrimitiveTypeKind.Binary) {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
// Convert the standard base64 encoding to the URL-safe variant.
|
|
478
|
-
.replace(new RegExp('\\+', 'g'), '-')
|
|
479
|
-
.replace(new RegExp('/', 'g'), '_')
|
|
475
|
+
// convert the standard base64 encoding to the URL-safe variant
|
|
476
|
+
result = (Buffer.isBuffer(propertyValue) ? propertyValue.toString('base64') : propertyValue).replace(/\+/g, '-').replace(/\//g, '_')
|
|
480
477
|
}
|
|
481
478
|
break
|
|
482
479
|
case EdmTypeKind.COMPLEX:
|
|
@@ -5,7 +5,6 @@ const HttpMethods = commons.http.HttpMethod.Methods
|
|
|
5
5
|
const ResourceKinds = commons.uri.UriResource.ResourceKind
|
|
6
6
|
const PreconditionFailedError = require('../errors/PreconditionFailedError')
|
|
7
7
|
const PreconditionRequiredError = require('../errors/PreconditionRequiredError')
|
|
8
|
-
const ConflictError = require('../errors/ConflictError')
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
10
|
* Class to validate conditional requests.
|
|
@@ -23,11 +22,12 @@ class ConditionalRequestValidator {
|
|
|
23
22
|
preValidate (ifMatch, ifNoneMatch, method, isConcurrentResource) {
|
|
24
23
|
if (isConcurrentResource) {
|
|
25
24
|
if (method !== HttpMethods.GET && !ifMatch && !ifNoneMatch) throw new PreconditionRequiredError()
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (ifMatch || ifNoneMatch) {
|
|
29
|
+
// Careless clients send this also for DELETE and POST, other careless clients send the star in double-quotes.
|
|
30
|
+
if ([HttpMethods.POST].includes(method)) {
|
|
31
31
|
if (
|
|
32
32
|
(ifMatch && ifMatch.trim() !== '*' && ifMatch.trim() !== '"*"') ||
|
|
33
33
|
(ifNoneMatch && ifNoneMatch.trim() !== '*' && ifNoneMatch.trim() !== '"*"')
|
|
@@ -6,7 +6,7 @@ const Dispatcher = require('./Dispatcher')
|
|
|
6
6
|
const { alias2ref } = require('../../../common/utils/csn')
|
|
7
7
|
|
|
8
8
|
const to = service => {
|
|
9
|
-
const edm = cds.compile.to.edm(service.model, { service: service.definition.name })
|
|
9
|
+
const edm = service._edm || cds.compile.to.edm(service.model, { service: service.definition.name })
|
|
10
10
|
alias2ref(service, edm)
|
|
11
11
|
|
|
12
12
|
const odata = new OData(edm, service.model, service.options)
|
|
@@ -65,11 +65,25 @@ const _getParamData = parameters => {
|
|
|
65
65
|
|
|
66
66
|
return paramData
|
|
67
67
|
}
|
|
68
|
-
|
|
68
|
+
const _flattenStructureKeys = structureData => {
|
|
69
|
+
const result = {}
|
|
70
|
+
for (const prop in structureData) {
|
|
71
|
+
if (typeof structureData[prop] === 'object') {
|
|
72
|
+
const nested = _flattenStructureKeys(structureData[prop])
|
|
73
|
+
for (const key in nested) {
|
|
74
|
+
result[prop + '_' + key] = nested[key]
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
result[prop] = structureData[prop]
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return result
|
|
81
|
+
}
|
|
69
82
|
// works only for custom on condition working on keys with '=' operator
|
|
70
83
|
// and combination of multiple conditions connected with 'and'
|
|
71
84
|
const _addKeysToData = (navSourceKeyValues, onCondition, data) => {
|
|
72
|
-
|
|
85
|
+
const flattenKeys = _flattenStructureKeys(navSourceKeyValues)
|
|
86
|
+
for (const key in flattenKeys) {
|
|
73
87
|
// find index of source column
|
|
74
88
|
const sourceIndex = onCondition.findIndex(e => e.ref && e.ref[0] === 'source' && e.ref[1] === key)
|
|
75
89
|
if (sourceIndex === -1) {
|
|
@@ -82,7 +96,7 @@ const _addKeysToData = (navSourceKeyValues, onCondition, data) => {
|
|
|
82
96
|
const {
|
|
83
97
|
ref: [, target]
|
|
84
98
|
} = onCondition[sourceIndex + 1] === '=' ? onCondition[sourceIndex + 2] : onCondition[sourceIndex - 2]
|
|
85
|
-
data[target] =
|
|
99
|
+
data[target] = flattenKeys[key]
|
|
86
100
|
}
|
|
87
101
|
}
|
|
88
102
|
}
|
|
@@ -151,8 +165,7 @@ const _getCopiedData = (odataReq, streaming, lastSegment) => {
|
|
|
151
165
|
return data
|
|
152
166
|
}
|
|
153
167
|
|
|
154
|
-
|
|
155
|
-
return data
|
|
168
|
+
return Array.isArray(data) ? deepCopyArray(data) : deepCopyObject(data)
|
|
156
169
|
}
|
|
157
170
|
|
|
158
171
|
/**
|
|
@@ -4,6 +4,8 @@ const { isCustomOperation } = require('./request')
|
|
|
4
4
|
const expandToCQN = require('../odata-to-cqn/expandToCQN')
|
|
5
5
|
const QueryOptions = require('../okra/odata-server').QueryOptions
|
|
6
6
|
const { COMPLEX_PROPERTY, PRIMITIVE_PROPERTY } = require('../okra/odata-server').uri.UriResource.ResourceKind
|
|
7
|
+
const { mergeJson } = require('../../../services/utils/compareJson')
|
|
8
|
+
const { getColumns } = require('../../../services/utils/columns')
|
|
7
9
|
|
|
8
10
|
const _selectForFunction = (selectColumns, result, opReturnType) => {
|
|
9
11
|
if (!Array.isArray(result)) result = [result]
|
|
@@ -33,31 +35,51 @@ const _expand = (model, uriInfo, options) => {
|
|
|
33
35
|
return expandToCQN(model, expand, uriInfo.getFinalEdmType(), options)
|
|
34
36
|
}
|
|
35
37
|
|
|
38
|
+
const _compareKeys = (first, second) => key => {
|
|
39
|
+
const val1 = first[key]
|
|
40
|
+
const val2 = second[key]
|
|
41
|
+
if (Array.isArray(val1) || Buffer.isBuffer(val1)) return val1.every((_, i) => _compareKeys(val1, val2)(i))
|
|
42
|
+
if (val1 && typeof val1 === 'object') return Object.keys(val1).every(_compareKeys(val1, val2))
|
|
43
|
+
return val1 === val2
|
|
44
|
+
}
|
|
45
|
+
|
|
36
46
|
const _expandForFunction = async (uriInfo, result, req, srv, opReturnType) => {
|
|
37
47
|
const results = Array.isArray(result) ? result : [result]
|
|
38
|
-
|
|
39
|
-
const opReturnTypeName = typeof opReturnType === 'string' ? opReturnType : opReturnType.name
|
|
40
|
-
const isDraft = srv.model.definitions[opReturnTypeName] && srv.model.definitions[opReturnTypeName]._isDraftEnabled
|
|
41
|
-
|
|
48
|
+
const isDraft = opReturnType._isDraftEnabled
|
|
42
49
|
const isDraftActivate = isDraftActivateAction(req)
|
|
43
50
|
|
|
44
51
|
// REVISIT: what happens here exactly?
|
|
52
|
+
const selectQuery = SELECT.from(
|
|
53
|
+
isDraft && !isDraftActivate ? ensureDraftsSuffix(opReturnType.name) : opReturnType.name
|
|
54
|
+
)
|
|
55
|
+
const keys = getColumns(opReturnType, {
|
|
56
|
+
onlyNames: true,
|
|
57
|
+
removeIgnore: true,
|
|
58
|
+
filterDraft: !isDraft || isDraftActivate,
|
|
59
|
+
filterVirtual: true,
|
|
60
|
+
keysOnly: true
|
|
61
|
+
})
|
|
62
|
+
const expandCqn = _expand(srv.model, uriInfo, { rewriteAsterisks: true })
|
|
63
|
+
selectQuery.columns(expandCqn)
|
|
64
|
+
selectQuery.columns(keys)
|
|
45
65
|
for (const row of results) {
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if ((!isDraft || isDraftActivate) && key === 'IsActiveEntity') {
|
|
50
|
-
continue
|
|
51
|
-
}
|
|
52
|
-
selectQuery.where(key, '=', row[key])
|
|
66
|
+
const where = ['(']
|
|
67
|
+
for (const key of keys) {
|
|
68
|
+
where.push({ ref: [key] }, '=', { val: row[key] }, 'and')
|
|
53
69
|
}
|
|
70
|
+
where.pop() // last 'and'
|
|
71
|
+
where.push(')')
|
|
72
|
+
if (!selectQuery.SELECT.where) selectQuery.where(where)
|
|
73
|
+
else selectQuery.or(where)
|
|
74
|
+
}
|
|
75
|
+
const expandedResults = await cds.tx(req).run(selectQuery)
|
|
54
76
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (res) Object.assign(row, res[0])
|
|
77
|
+
for (let i = 0; i < results.length; i++) {
|
|
78
|
+
const result = results[i]
|
|
79
|
+
const res = expandedResults.find(r => keys.every(_compareKeys(result, r)))
|
|
80
|
+
if (res) results[i] = mergeJson(res, result, opReturnType)
|
|
60
81
|
}
|
|
82
|
+
return Array.isArray(result) ? results : results[0]
|
|
61
83
|
}
|
|
62
84
|
|
|
63
85
|
const _cleanupResult = (result, opReturnType) => {
|
|
@@ -89,14 +111,16 @@ const getActionOrFunctionReturnType = (pathSegments, definitions) => {
|
|
|
89
111
|
const actionAndFunctionQueries = async (req, odataReq, result, srv, opReturnType) => {
|
|
90
112
|
_cleanupResult(result, opReturnType)
|
|
91
113
|
|
|
114
|
+
// REVISIT consider $expand columns as inline content for $select
|
|
92
115
|
if (odataReq.getQueryOptions().$select) {
|
|
93
116
|
_selectForFunction(odataReq.getQueryOptions().$select.split(','), result, opReturnType)
|
|
94
117
|
}
|
|
95
118
|
|
|
96
119
|
// REVISIT: we need to read directly from db for this, which might not be there!
|
|
97
120
|
if (odataReq.getQueryOptions().$expand && cds.db) {
|
|
98
|
-
await _expandForFunction(odataReq.getUriInfo(), result, req, srv, opReturnType)
|
|
121
|
+
result = await _expandForFunction(odataReq.getUriInfo(), result, req, srv, opReturnType)
|
|
99
122
|
}
|
|
123
|
+
return result
|
|
100
124
|
}
|
|
101
125
|
|
|
102
126
|
const resolveStructuredName = (pathSegments, index, nameArr = []) => {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const {
|
|
2
|
-
QueryOptions,
|
|
3
2
|
uri: {
|
|
4
3
|
UriResource: {
|
|
5
4
|
ResourceKind: { ENTITY, ENTITY_COLLECTION }
|
|
@@ -60,22 +59,7 @@ const validateResourcePath = (odataReq, service) => {
|
|
|
60
59
|
}
|
|
61
60
|
}
|
|
62
61
|
|
|
63
|
-
/**
|
|
64
|
-
* Used for pagination, where the start of the collection is defined via skip token.
|
|
65
|
-
*
|
|
66
|
-
* @param {object} uriInfo
|
|
67
|
-
* @returns {number}
|
|
68
|
-
* @private
|
|
69
|
-
*/
|
|
70
|
-
const skipToken = uriInfo => {
|
|
71
|
-
const token = uriInfo.getQueryOption(QueryOptions.SKIPTOKEN)
|
|
72
|
-
|
|
73
|
-
// If given, the token is a string but needed as numeric value.
|
|
74
|
-
return token ? parseInt(token) : 0
|
|
75
|
-
}
|
|
76
|
-
|
|
77
62
|
module.exports = {
|
|
78
63
|
isCustomOperation,
|
|
79
|
-
validateResourcePath
|
|
80
|
-
skipToken
|
|
64
|
+
validateResourcePath
|
|
81
65
|
}
|