@sap/cds 7.2.1 → 7.3.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 +168 -126
- package/README.md +1 -1
- package/apis/core.d.ts +6 -4
- package/apis/services.d.ts +24 -4
- package/apis/test.d.ts +24 -10
- package/bin/serve.js +4 -3
- package/lib/auth/ias-auth.js +7 -8
- package/lib/compile/cdsc.js +5 -7
- package/lib/compile/etc/csv.js +22 -11
- package/lib/compile/for/lean_drafts.js +1 -1
- package/lib/dbs/cds-deploy.js +1 -2
- package/lib/env/cds-env.js +26 -20
- package/lib/env/defaults.js +4 -3
- package/lib/env/schema.js +9 -0
- package/lib/i18n/localize.js +83 -77
- package/lib/index.js +6 -2
- package/lib/linked/classes.js +13 -13
- package/lib/plugins.js +41 -45
- package/lib/req/user.js +2 -2
- package/lib/srv/protocols/_legacy.js +0 -1
- package/lib/srv/protocols/odata-v4.js +4 -0
- package/lib/utils/axios.js +7 -1
- package/lib/utils/cds-test.js +140 -133
- package/lib/utils/cds-utils.js +10 -3
- package/lib/utils/check-version.js +6 -0
- package/lib/utils/data.js +19 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +20 -19
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +10 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +2 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/ValueConverter.js +0 -14
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/core/OdataRequest.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/BatchRequestListBuilder.js +5 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/handler/MetadataHandler.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/handler/ServiceHandler.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/DispatcherCommand.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/utils/BufferedWriter.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +1 -1
- package/libx/_runtime/common/composition/update.js +18 -2
- package/libx/_runtime/common/error/frontend.js +46 -34
- package/libx/_runtime/common/generic/auth/capabilities.js +33 -14
- package/libx/_runtime/common/generic/input.js +1 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +3 -3
- package/libx/_runtime/db/query/update.js +48 -30
- package/libx/_runtime/fiori/lean-draft.js +2 -3
- package/libx/_runtime/hana/conversion.js +3 -2
- package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +1 -1
- package/libx/_runtime/messaging/outbox/utils.js +1 -1
- package/libx/_runtime/remote/Service.js +1 -17
- package/libx/_runtime/remote/utils/client.js +3 -3
- package/libx/_runtime/remote/utils/data.js +5 -7
- package/libx/odata/{grammar.pegjs → grammar.peggy} +1 -1
- package/libx/odata/metadata.js +121 -0
- package/libx/odata/parser.js +1 -1
- package/libx/odata/service-document.js +61 -0
- package/libx/odata/utils.js +102 -48
- package/libx/rest/RestAdapter.js +2 -2
- package/libx/rest/middleware/error.js +1 -1
- package/package.json +1 -1
package/libx/odata/utils.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { toBase64url } = require('../_runtime/common/utils/binary')
|
|
2
2
|
const cds = require('../_runtime/cds')
|
|
3
3
|
|
|
4
|
-
const MATH_FUNC = {
|
|
4
|
+
const MATH_FUNC = { round: 1, floor: 1, ceiling: 1 }
|
|
5
5
|
|
|
6
6
|
const getSafeNumber = str => {
|
|
7
7
|
const n = Number(str)
|
|
@@ -50,56 +50,110 @@ const _getElement = (csnTarget, key) => {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
const _v2 = (val, element) => {
|
|
54
|
+
switch (element.type) {
|
|
55
|
+
case 'cds.UUID':
|
|
56
|
+
return `guid'${val}'`
|
|
57
|
+
// binaries
|
|
58
|
+
case 'cds.Binary':
|
|
59
|
+
case 'cds.LargeBinary':
|
|
60
|
+
return `binary'${toBase64url(val)}'`
|
|
61
|
+
// integers
|
|
62
|
+
case 'cds.UInt8':
|
|
63
|
+
case 'cds.Int16':
|
|
64
|
+
case 'cds.Int32':
|
|
65
|
+
case 'cds.Integer':
|
|
66
|
+
return val
|
|
67
|
+
// big integers
|
|
68
|
+
case 'cds.Int64':
|
|
69
|
+
case 'cds.Integer64':
|
|
70
|
+
// inofficial flag to skip appending "L"
|
|
71
|
+
return cds.env.remote?.skip_v2_appendix ? val : `${val}L`.replace(/ll$/i, 'L')
|
|
72
|
+
// floating point numbers
|
|
73
|
+
case 'cds.Decimal':
|
|
74
|
+
// inofficial flag to skip appending "m"
|
|
75
|
+
return cds.env.remote?.skip_v2_appendix ? val : `${val}m`.replace(/mm$/i, 'm')
|
|
76
|
+
case 'cds.Double':
|
|
77
|
+
// inofficial flag to skip appending "d"
|
|
78
|
+
return cds.env.remote?.skip_v2_appendix ? val : `${val}d`.replace(/dd$/i, 'd')
|
|
79
|
+
// dates et al
|
|
80
|
+
case 'cds.Date':
|
|
81
|
+
return element['@odata.Type'] === 'Edm.DateTimeOffset'
|
|
82
|
+
? `datetimeoffset'${val}T00:00:00'`
|
|
83
|
+
: `datetime'${val}T00:00:00'`
|
|
84
|
+
case 'cds.DateTime':
|
|
85
|
+
return element['@odata.Type'] === 'Edm.DateTimeOffset' ? `datetimeoffset'${val}'` : `datetime'${val}'`
|
|
86
|
+
case 'cds.Time':
|
|
87
|
+
return `time'${_PT(val.split(':'))}'`
|
|
88
|
+
case 'cds.Timestamp':
|
|
89
|
+
return element['@odata.Type'] === 'Edm.DateTime' ? `datetime'${val}'` : `datetimeoffset'${val}'`
|
|
90
|
+
// bool
|
|
91
|
+
case 'cds.Boolean':
|
|
92
|
+
return val
|
|
93
|
+
// strings + default to string representation
|
|
94
|
+
case 'cds.String':
|
|
95
|
+
case 'cds.LargeString':
|
|
96
|
+
default:
|
|
97
|
+
return `'${val}'`
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const _v4 = (val, element) => {
|
|
102
|
+
switch (element.type) {
|
|
103
|
+
case 'cds.UUID':
|
|
104
|
+
return val
|
|
105
|
+
// binary
|
|
106
|
+
case 'cds.Binary':
|
|
107
|
+
case 'cds.LargeBinary':
|
|
108
|
+
return `binary'${toBase64url(val)}'`
|
|
109
|
+
// integers
|
|
110
|
+
case 'cds.UInt8':
|
|
111
|
+
case 'cds.Int16':
|
|
112
|
+
case 'cds.Int32':
|
|
113
|
+
case 'cds.Integer':
|
|
114
|
+
return val
|
|
115
|
+
// big integers
|
|
116
|
+
case 'cds.Int64':
|
|
117
|
+
case 'cds.Integer64':
|
|
118
|
+
return val
|
|
119
|
+
// floating point numbers
|
|
120
|
+
case 'cds.Decimal':
|
|
121
|
+
case 'cds.Double':
|
|
122
|
+
return val
|
|
123
|
+
// dates et al
|
|
124
|
+
case 'cds.DateTime':
|
|
125
|
+
case 'cds.Date':
|
|
126
|
+
case 'cds.Timestamp':
|
|
127
|
+
case 'cds.Time':
|
|
128
|
+
return val
|
|
129
|
+
// bool
|
|
130
|
+
case 'cds.Boolean':
|
|
131
|
+
return val
|
|
132
|
+
// strings + default to string representation
|
|
133
|
+
case 'cds.String':
|
|
134
|
+
case 'cds.LargeString':
|
|
135
|
+
default:
|
|
136
|
+
return `'${val}'`
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
53
140
|
const formatVal = (val, elementName, csnTarget, kind, func) => {
|
|
54
141
|
if (val === null || val === 'null') return 'null'
|
|
55
142
|
if (typeof val === 'boolean') return val
|
|
56
|
-
if (typeof val === '
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
case 'cds.Binary':
|
|
67
|
-
case 'cds.LargeBinary':
|
|
68
|
-
return `binary'${toBase64url(val)}'`
|
|
69
|
-
case 'cds.Date':
|
|
70
|
-
return element['@odata.Type'] === 'Edm.DateTimeOffset'
|
|
71
|
-
? `datetimeoffset'${val}T00:00:00'`
|
|
72
|
-
: `datetime'${val}T00:00:00'`
|
|
73
|
-
case 'cds.DateTime':
|
|
74
|
-
return element['@odata.Type'] === 'Edm.DateTimeOffset' ? `datetimeoffset'${val}'` : `datetime'${val}'`
|
|
75
|
-
case 'cds.Time':
|
|
76
|
-
return `time'${_PT(val.split(':'))}'`
|
|
77
|
-
case 'cds.Timestamp':
|
|
78
|
-
return element['@odata.Type'] === 'Edm.DateTime' ? `datetime'${val}'` : `datetimeoffset'${val}'`
|
|
79
|
-
case 'cds.UUID':
|
|
80
|
-
return `guid'${val}'`
|
|
81
|
-
default:
|
|
82
|
-
return `'${val}'`
|
|
83
|
-
}
|
|
84
|
-
} else {
|
|
85
|
-
switch (element.type) {
|
|
86
|
-
case 'cds.Binary':
|
|
87
|
-
case 'cds.LargeBinary':
|
|
88
|
-
return `binary'${toBase64url(val)}'`
|
|
89
|
-
case 'cds.Decimal':
|
|
90
|
-
case 'cds.Integer64':
|
|
91
|
-
case 'cds.Int64':
|
|
92
|
-
case 'cds.Boolean':
|
|
93
|
-
case 'cds.DateTime':
|
|
94
|
-
case 'cds.Date':
|
|
95
|
-
case 'cds.Timestamp':
|
|
96
|
-
case 'cds.Time':
|
|
97
|
-
case 'cds.UUID':
|
|
98
|
-
return val
|
|
99
|
-
default:
|
|
100
|
-
return _isTimestamp(val) ? val : `'${val}'` // Why are we checking strings for timestamps? --> expensive
|
|
101
|
-
}
|
|
143
|
+
if (typeof val === 'string') {
|
|
144
|
+
if (!csnTarget && UUID.test(val)) return kind === 'odata-v2' ? `guid'${val}'` : val
|
|
145
|
+
if (func in MATH_FUNC) return val
|
|
146
|
+
}
|
|
147
|
+
if (typeof val === 'number') val = getSafeNumber(val)
|
|
148
|
+
if (!csnTarget) {
|
|
149
|
+
if (typeof val !== 'string') return val
|
|
150
|
+
// REVISIT: why do we need to check strings for timestamps?
|
|
151
|
+
if (_isTimestamp(val)) return val
|
|
152
|
+
return `'${val}'`
|
|
102
153
|
}
|
|
154
|
+
const element = _getElement(csnTarget, elementName)
|
|
155
|
+
if (!element?.type) return typeof val === 'string' ? `'${val}'` : val
|
|
156
|
+
return kind === 'odata-v2' ? _v2(val, element) : _v4(val, element)
|
|
103
157
|
}
|
|
104
158
|
|
|
105
159
|
const skipToken = (token, cqn) => {
|
|
@@ -135,7 +189,7 @@ const skipToken = (token, cqn) => {
|
|
|
135
189
|
}
|
|
136
190
|
|
|
137
191
|
if (cqn.SELECT.where) {
|
|
138
|
-
cqn.SELECT.where = [{ xpr: [
|
|
192
|
+
cqn.SELECT.where = [{ xpr: [...cqn.SELECT.where] }, 'and', { xpr }]
|
|
139
193
|
} else {
|
|
140
194
|
cqn.SELECT.where = [{ xpr }]
|
|
141
195
|
}
|
package/libx/rest/RestAdapter.js
CHANGED
|
@@ -65,8 +65,8 @@ const RestAdapter = function (srv) {
|
|
|
65
65
|
|
|
66
66
|
// check @requires as soon as possible (DoS)
|
|
67
67
|
router.use((req, res, next) => {
|
|
68
|
-
//
|
|
69
|
-
if (!req.user) req.user = new cds.User.default
|
|
68
|
+
// ensure there always is a user going forward (not always the case with old or custom auth)
|
|
69
|
+
if (!req.user) req.user = new cds.User.default()
|
|
70
70
|
|
|
71
71
|
// check @restrict and @requires as soon as possible (DoS)
|
|
72
72
|
if (!accessRestrictions.some(r => req.user.is(r))) {
|
|
@@ -44,7 +44,7 @@ module.exports = (err, req, res, next) => {
|
|
|
44
44
|
let ctx = cds.context
|
|
45
45
|
if (!ctx) {
|
|
46
46
|
// > error before req was dispatched
|
|
47
|
-
ctx = new cds.Request({ req, res: req.res, user: req.user || new cds.User.
|
|
47
|
+
ctx = new cds.Request({ req, res: req.res, user: req.user || new cds.User.default })
|
|
48
48
|
for (const each of srv._handlers._error) each.handler.call(srv, err, ctx)
|
|
49
49
|
} else if (ctx._tx?._done !== 'rolled back') {
|
|
50
50
|
for (const each of srv._handlers._error) each.handler.call(srv, err, ctx)
|