@sap/cds 7.9.2 → 8.0.3
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 +139 -3656
- package/_i18n/i18n_en_US_saptrc.properties +113 -0
- package/_i18n/i18n_zh_CN.properties +7 -4
- package/app/index.css +129 -0
- package/app/index.html +16 -64
- package/app/index.js +14 -9
- package/bin/args.js +34 -0
- package/bin/serve.js +18 -24
- package/bin/test.js +97 -0
- package/common.cds +5 -12
- package/eslint.config.mjs +133 -0
- package/lib/auth/basic-auth.js +16 -20
- package/lib/auth/dummy-auth.js +1 -1
- package/lib/auth/ias-auth.js +12 -30
- package/lib/auth/index.js +1 -14
- package/lib/auth/jwt-auth.js +14 -30
- package/lib/compile/cds-compile.js +1 -2
- package/lib/compile/cdsc.js +21 -26
- package/lib/compile/etc/_localized.js +1 -6
- package/lib/compile/etc/csv.js +1 -1
- package/lib/compile/etc/properties.js +1 -1
- package/lib/compile/for/java.js +1 -1
- package/lib/compile/for/lean_drafts.js +4 -6
- package/lib/compile/for/nodejs.js +1 -1
- package/lib/compile/parse.js +4 -0
- package/lib/compile/resolve.js +4 -4
- package/lib/compile/to/edm-files.js +16 -23
- package/lib/compile/to/hana.js +27 -0
- package/lib/compile/to/json.js +1 -1
- package/lib/compile/to/sql.js +5 -1
- package/lib/compile/to/srvinfo.js +1 -1
- package/lib/compile/to/yaml.js +3 -3
- package/lib/dbs/cds-deploy.js +4 -2
- package/lib/env/cds-env.js +10 -14
- package/lib/env/cds-requires.js +29 -13
- package/lib/env/defaults.js +46 -16
- package/lib/env/plugins.js +1 -1
- package/lib/env/schemas/cds-rc.js +8 -4
- package/lib/env/schemas/index.js +7 -7
- package/lib/env/serviceBindings.js +1 -1
- package/lib/index.js +12 -10
- package/lib/lazy.js +1 -1
- package/lib/linked/classes.js +36 -8
- package/lib/linked/entities.js +2 -10
- package/lib/linked/models.js +2 -1
- package/lib/linked/validate.js +292 -0
- package/lib/log/cds-error.js +0 -6
- package/lib/log/cds-log.js +3 -3
- package/lib/log/format/json.js +1 -1
- package/lib/log/service/index.js +0 -1
- package/lib/plugins.js +3 -3
- package/lib/ql/Query.js +2 -10
- package/lib/ql/SELECT.js +1 -1
- package/lib/ql/Whereable.js +3 -2
- package/lib/req/cds-context.js +14 -25
- package/lib/req/context.js +23 -25
- package/lib/req/request.js +1 -34
- package/lib/req/user.js +47 -35
- package/lib/srv/bindings.js +1 -1
- package/lib/srv/cds-connect.js +4 -4
- package/lib/srv/cds-serve.js +2 -2
- package/lib/srv/factory.js +1 -1
- package/lib/srv/middlewares/cds-context.js +11 -22
- package/lib/srv/middlewares/ctx-model.js +2 -3
- package/lib/srv/middlewares/errors.js +41 -8
- package/lib/srv/middlewares/index.js +3 -3
- package/lib/srv/middlewares/trace.js +0 -2
- package/lib/srv/protocols/hcql.js +15 -10
- package/lib/srv/protocols/http.js +44 -49
- package/lib/srv/protocols/index.js +1 -23
- package/lib/srv/protocols/odata-v4.js +12 -74
- package/lib/srv/protocols/rest.js +1 -13
- package/lib/srv/srv-api.js +0 -20
- package/lib/srv/srv-dispatch.js +3 -2
- package/lib/srv/srv-handlers.js +22 -11
- package/lib/srv/srv-methods.js +2 -2
- package/lib/srv/srv-models.js +3 -36
- package/lib/test/expect.js +343 -0
- package/lib/test/index.js +2 -0
- package/lib/test/reporter.js +176 -0
- package/lib/utils/axios.js +10 -9
- package/lib/utils/cds-test.js +86 -37
- package/lib/utils/cds-utils.js +54 -7
- package/lib/utils/check-version.js +0 -4
- package/lib/utils/colors.js +49 -0
- package/lib/utils/data.js +5 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +2 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +3 -30
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +6 -12
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +4 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +12 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +2 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/AbstractEdmStructuredType.js +1 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +5 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ContextURLFactory.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +9 -43
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +8 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +4 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +1 -3
- package/libx/_runtime/cds-services/util/assert.js +1 -1
- package/libx/_runtime/cds.js +10 -3
- package/libx/_runtime/common/Service.js +12 -32
- package/libx/_runtime/common/aspects/any.js +1 -0
- package/libx/_runtime/common/code-ext/execute.js +1 -1
- package/libx/_runtime/common/code-ext/worker.js +0 -1
- package/libx/_runtime/common/composition/data.js +0 -1
- package/libx/_runtime/common/composition/delete.js +0 -1
- package/libx/_runtime/common/composition/insert.js +2 -2
- package/libx/_runtime/common/composition/tree.js +0 -1
- package/libx/_runtime/common/composition/update.js +3 -3
- package/libx/_runtime/common/error/frontend.js +21 -12
- package/libx/_runtime/common/error/log.js +36 -0
- package/libx/_runtime/common/error/utils.js +2 -5
- package/libx/_runtime/common/generic/auth/autoexpose.js +18 -17
- package/libx/_runtime/common/generic/auth/expand.js +1 -1
- package/libx/_runtime/common/generic/auth/readOnly.js +1 -2
- package/libx/_runtime/common/generic/auth/restrict.js +23 -42
- package/libx/_runtime/common/generic/auth/restrictions.js +2 -7
- package/libx/_runtime/common/generic/auth/utils.js +91 -88
- package/libx/_runtime/common/generic/crud.js +6 -5
- package/libx/_runtime/common/generic/etag.js +7 -12
- package/libx/_runtime/common/generic/input.js +70 -68
- package/libx/_runtime/common/generic/paging.js +1 -0
- package/libx/_runtime/common/generic/sorting.js +1 -0
- package/libx/_runtime/common/generic/temporal.js +8 -2
- package/libx/_runtime/common/i18n/index.js +1 -1
- package/libx/_runtime/common/i18n/messages.properties +3 -1
- package/libx/_runtime/common/utils/binary.js +8 -2
- package/libx/_runtime/common/utils/compareJson.js +5 -1
- package/libx/_runtime/common/utils/copy.js +6 -11
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +16 -14
- package/libx/_runtime/common/utils/differ.js +3 -6
- package/libx/_runtime/common/utils/keys.js +77 -18
- package/libx/_runtime/common/utils/postProcess.js +12 -15
- package/libx/_runtime/common/utils/propagateForeignKeys.js +0 -1
- package/libx/_runtime/common/utils/resolveView.js +2 -3
- package/libx/_runtime/common/utils/restrictions.js +45 -17
- package/libx/_runtime/common/utils/rewriteAsterisks.js +1 -8
- package/libx/_runtime/common/utils/stream.js +3 -16
- package/libx/_runtime/common/utils/streamProp.js +8 -18
- package/libx/_runtime/common/utils/structured.js +1 -1
- package/libx/_runtime/common/utils/ucsn.js +0 -2
- package/libx/_runtime/db/Service.js +0 -72
- package/libx/_runtime/db/data-conversion/post-processing.js +0 -1
- package/libx/_runtime/db/expand/expandCQNToJoin.js +9 -9
- package/libx/_runtime/db/expand/rawToExpanded.js +0 -8
- package/libx/_runtime/db/generic/input.js +3 -8
- package/libx/_runtime/db/generic/rewrite.js +27 -4
- package/libx/_runtime/db/query/read.js +2 -2
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +0 -1
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -1
- package/libx/_runtime/db/utils/columns.js +2 -6
- package/libx/_runtime/fiori/lean-draft.js +138 -56
- package/libx/_runtime/hana/Service.js +0 -1
- package/libx/_runtime/hana/driver.js +1 -1
- package/libx/_runtime/hana/dynatrace.js +1 -2
- package/libx/_runtime/hana/pool.js +11 -21
- package/libx/_runtime/hana/streaming.js +0 -1
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +0 -1
- package/libx/_runtime/messaging/common-utils/authorizedRequest.js +1 -1
- package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -33
- package/libx/_runtime/messaging/event-broker.js +0 -12
- package/libx/_runtime/messaging/file-based.js +3 -3
- package/libx/_runtime/messaging/http-utils/token.js +1 -1
- package/libx/_runtime/messaging/kafka.js +2 -2
- package/libx/_runtime/messaging/redis-messaging.js +0 -1
- package/libx/_runtime/remote/Service.js +25 -25
- package/libx/_runtime/remote/utils/client.js +4 -5
- package/libx/_runtime/remote/utils/cloudSdkProvider.js +0 -3
- package/libx/_runtime/remote/utils/data.js +0 -1
- package/libx/_runtime/sqlite/Service.js +1 -2
- package/libx/_runtime/ucl/Service.js +37 -78
- package/libx/common/assert/index.js +22 -21
- package/libx/common/assert/type-relaxed.js +39 -0
- package/libx/common/assert/utils.js +3 -2
- package/libx/common/assert/validation.js +3 -8
- package/libx/common/utils/index.js +5 -0
- package/libx/common/utils/path.js +51 -0
- package/libx/odata/ODataAdapter.js +126 -0
- package/libx/odata/index.js +15 -2
- package/libx/odata/middleware/batch.js +261 -72
- package/libx/odata/middleware/body-parser.js +33 -0
- package/libx/odata/middleware/create.js +44 -59
- package/libx/odata/middleware/delete.js +23 -12
- package/libx/odata/middleware/error.js +30 -6
- package/libx/odata/middleware/metadata.js +38 -26
- package/libx/odata/middleware/operation.js +93 -69
- package/libx/odata/middleware/parse.js +6 -8
- package/libx/odata/middleware/read.js +117 -93
- package/libx/odata/middleware/service-document.js +22 -19
- package/libx/odata/middleware/stream.js +54 -56
- package/libx/odata/middleware/update.js +79 -87
- package/libx/odata/parse/afterburner.js +191 -175
- package/libx/odata/parse/cqn2odata.js +8 -8
- package/libx/odata/parse/grammar.peggy +27 -20
- package/libx/odata/parse/multipartToJson.js +17 -9
- package/libx/odata/parse/parser.js +1 -1
- package/libx/odata/utils/etag.js +14 -6
- package/libx/odata/utils/index.js +84 -12
- package/libx/odata/utils/metadata.js +161 -0
- package/libx/odata/utils/postProcess.js +89 -0
- package/libx/odata/utils/readAfterWrite.js +134 -17
- package/libx/odata/utils/result.js +36 -142
- package/libx/outbox/index.js +5 -4
- package/libx/rest/RestAdapter.js +115 -182
- package/libx/rest/middleware/create.js +28 -24
- package/libx/rest/middleware/delete.js +7 -10
- package/libx/rest/middleware/error.js +19 -16
- package/libx/rest/middleware/operation.js +48 -41
- package/libx/rest/middleware/parse.js +128 -126
- package/libx/rest/middleware/read.js +20 -27
- package/libx/rest/middleware/update.js +26 -31
- package/package.json +16 -12
- package/server.js +4 -2
- package/tasks/enterprise-messaging-deploy.js +1 -1
- package/apis/cds.d.ts +0 -3
- package/apis/core.d.ts +0 -21
- package/apis/cqn.d.ts +0 -18
- package/apis/csn.d.ts +0 -21
- package/apis/events.d.ts +0 -18
- package/apis/internal/inference.d.ts +0 -18
- package/apis/linked.d.ts +0 -18
- package/apis/log.d.ts +0 -20
- package/apis/models.d.ts +0 -18
- package/apis/ql.d.ts +0 -18
- package/apis/reflect.d.ts +0 -32
- package/apis/server.d.ts +0 -18
- package/apis/services.d.ts +0 -22
- package/bin/cds-serve.js +0 -56
- package/lib/compile/to/gql.js +0 -15
- package/lib/srv/protocols/_legacy.js +0 -44
- package/lib/utils/jest.js +0 -43
- package/libx/_runtime/auth/index.js +0 -193
- package/libx/_runtime/auth/strategies/JWT.js +0 -37
- package/libx/_runtime/auth/strategies/basic.js +0 -20
- package/libx/_runtime/auth/strategies/dummy.js +0 -14
- package/libx/_runtime/auth/strategies/ias-auth.js +0 -1
- package/libx/_runtime/auth/strategies/mock.js +0 -77
- package/libx/_runtime/auth/strategies/xssecUtils.js +0 -93
- package/libx/_runtime/auth/strategies/xsuaa.js +0 -38
- package/libx/_runtime/common/perf/index.js +0 -19
- package/libx/_runtime/common/utils/ensureIEEE754.js +0 -29
- package/libx/_runtime/fiori/draft.js +0 -2
- package/libx/_runtime/fiori/generic/activate.js +0 -190
- package/libx/_runtime/fiori/generic/before.js +0 -201
- package/libx/_runtime/fiori/generic/cancel.js +0 -19
- package/libx/_runtime/fiori/generic/delete.js +0 -21
- package/libx/_runtime/fiori/generic/edit.js +0 -157
- package/libx/_runtime/fiori/generic/index.js +0 -25
- package/libx/_runtime/fiori/generic/new.js +0 -82
- package/libx/_runtime/fiori/generic/patch.js +0 -101
- package/libx/_runtime/fiori/generic/prepare.js +0 -57
- package/libx/_runtime/fiori/generic/read.js +0 -1340
- package/libx/_runtime/fiori/generic/readOverDraft.js +0 -146
- package/libx/_runtime/fiori/utils/csn.js +0 -13
- package/libx/_runtime/fiori/utils/delete.js +0 -114
- package/libx/_runtime/fiori/utils/handler.js +0 -264
- package/libx/_runtime/fiori/utils/lockInfo.js +0 -27
- package/libx/_runtime/fiori/utils/req.js +0 -23
- package/libx/_runtime/fiori/utils/stream.js +0 -36
- package/libx/_runtime/fiori/utils/where.js +0 -254
- package/libx/_runtime/index.js +0 -22
- package/libx/odata/utils/handler.js +0 -120
- package/libx/odata/utils/metaInfo.js +0 -410
- package/libx/odata/utils/path.js +0 -75
- package/libx/rest/RestRequest.js +0 -32
- package/libx/rest/index.js +0 -3
- package/libx/rest/readme.md +0 -1
- /package/libx/common/assert/{type.js → type-strict.js} +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
const { cds } = global
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
|
|
3
|
+
const strictTypeCheckers = require('./type-strict')
|
|
4
|
+
const relaxedTypeCheckers = require('./type-relaxed')
|
|
5
|
+
let typeCheckers
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const { checkMandatory, checkEnum, checkRange, checkFormat } = require('./validation')
|
|
8
|
+
const { getNested, getTarget, resolveCDSType, resolveSegment } = require('./utils')
|
|
8
9
|
|
|
9
10
|
const _no_op = () => {}
|
|
10
11
|
|
|
@@ -91,6 +92,10 @@ function _process(obj, def, errs, opts) {
|
|
|
91
92
|
continue
|
|
92
93
|
}
|
|
93
94
|
|
|
95
|
+
if (ele['@cds.api.ignore']) {
|
|
96
|
+
opts._handle_unknown(obj, k, def, errs)
|
|
97
|
+
continue
|
|
98
|
+
}
|
|
94
99
|
if (ele.isAssociation) {
|
|
95
100
|
const keys = ele.keys?.map(k => k.ref[0]) || Object.keys(ele._target.keys)
|
|
96
101
|
opts.path.push(ele.is2many || Object.keys(keys).length ? { assoc: k, keys } : k)
|
|
@@ -114,7 +119,13 @@ function _process(obj, def, errs, opts) {
|
|
|
114
119
|
opts.path.pop()
|
|
115
120
|
continue
|
|
116
121
|
}
|
|
117
|
-
if (ele instanceof cds.builtin.classes.array) {
|
|
122
|
+
if (ele instanceof cds.builtin.classes.array && v !== undefined) {
|
|
123
|
+
if (!Array.isArray(v)) {
|
|
124
|
+
// REVISIT: allow null if served via REST or OData v4.02
|
|
125
|
+
const target = getTarget(opts.path, k)
|
|
126
|
+
errs.push(new cds.error('ASSERT_ARRAY', { target, statusCode: 400, code: '400' }))
|
|
127
|
+
continue
|
|
128
|
+
}
|
|
118
129
|
for (let i = 0; i < v.length; i++) {
|
|
119
130
|
opts.path.push({ prop: k, index: i })
|
|
120
131
|
const _def = ele.items?.__proto__.elements ? ele.items.__proto__ : ele.__proto__
|
|
@@ -138,26 +149,14 @@ function _process(obj, def, errs, opts) {
|
|
|
138
149
|
if (typeChecker) {
|
|
139
150
|
if (v == null) continue
|
|
140
151
|
|
|
141
|
-
// if used in protocol adapter, adjust val/ checker if necessary
|
|
142
|
-
if (opts.http) {
|
|
143
|
-
if (typeof v !== 'boolean') {
|
|
144
|
-
if (type === 'cds.Decimal') v = getNormalizedDecimal(v)
|
|
145
|
-
else if (type === 'cds.Int64') v = String(v)
|
|
146
|
-
else if (NUMBER_TYPES.has(type)) v = Number(v)
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// use relaxed uuid check if not in strict mode
|
|
151
|
-
if (type === 'cds.UUID' && !opts.strict) typeChecker = typeCheckers['relaxed.UUID']
|
|
152
|
-
|
|
153
152
|
// type check
|
|
154
153
|
// REVISIT: all checkers should add errors themselves!
|
|
155
|
-
if (type === 'cds.Decimal')
|
|
154
|
+
if (type === 'cds.Decimal' && opts.strict) {
|
|
156
155
|
typeChecker(v, ele, errs, opts.path, k) //> _checkDecimal adds error itself
|
|
157
|
-
else if (!typeChecker(v, ele)
|
|
156
|
+
} else if (!typeChecker(v, ele)) {
|
|
158
157
|
errs.push(
|
|
159
158
|
new cds.error('ASSERT_DATA_TYPE', {
|
|
160
|
-
args: [typeof
|
|
159
|
+
args: [typeof v === 'string' ? `"${v}"` : v, ele._type],
|
|
161
160
|
target: getTarget(opts.path, k),
|
|
162
161
|
statusCode: 400,
|
|
163
162
|
code: '400'
|
|
@@ -169,7 +168,7 @@ function _process(obj, def, errs, opts) {
|
|
|
169
168
|
if (obj[k] !== v) obj[k] = v
|
|
170
169
|
|
|
171
170
|
// @assert
|
|
172
|
-
if (ele['@assert.
|
|
171
|
+
if (ele['@assert.range'] && ele.enum) checkEnum(v, ele, errs, opts.path, k)
|
|
173
172
|
if (ele['@assert.range']) checkRange(v, ele, errs, opts.path, k)
|
|
174
173
|
if (ele['@assert.format']) checkFormat(v, ele, errs, opts.path, k)
|
|
175
174
|
// REVISIT: @assert.target? -> no because async, but maybe return the necessary query to execute?
|
|
@@ -212,6 +211,8 @@ module.exports = (data, definition, options = {}) => {
|
|
|
212
211
|
options.path ??= []
|
|
213
212
|
|
|
214
213
|
// materialize what is done ...
|
|
214
|
+
// ... re type checks
|
|
215
|
+
typeCheckers = options.strict ? strictTypeCheckers : relaxedTypeCheckers
|
|
215
216
|
// ... in case of unknown elements
|
|
216
217
|
if (options.strict) options._handle_unknown = _reject_unknown
|
|
217
218
|
else if (options.filter) options._handle_unknown = _filter_unknown
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const { Readable } = require('stream')
|
|
2
|
+
|
|
3
|
+
const _isString = v => typeof v === 'string'
|
|
4
|
+
|
|
5
|
+
const _isBoolean = v => typeof v === 'boolean'
|
|
6
|
+
|
|
7
|
+
const _isNumber = v => typeof v === 'number'
|
|
8
|
+
|
|
9
|
+
const _isNumberish = v => !!Number(v) || v === 0 //> string representation of number is ok, e.g., '1e3'
|
|
10
|
+
|
|
11
|
+
const _isDate = v => !!Date.parse(v)
|
|
12
|
+
|
|
13
|
+
const _isTime = v => !!Date.parse('2000-01-01T' + v)
|
|
14
|
+
|
|
15
|
+
const _isBuffer = v => Buffer.isBuffer(v) || v.type === 'Buffer' || _isString(v) //> base64 encoded string is ok as well
|
|
16
|
+
|
|
17
|
+
const _isStreamOrBuffer = v => v instanceof Readable || _isBuffer(v)
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
'cds.UUID': _isString,
|
|
21
|
+
'cds.Boolean': _isBoolean,
|
|
22
|
+
'cds.Integer': _isNumber,
|
|
23
|
+
'cds.UInt8': _isNumber,
|
|
24
|
+
'cds.Int16': _isNumber,
|
|
25
|
+
'cds.Int32': _isNumber,
|
|
26
|
+
'cds.Integer64': _isNumberish,
|
|
27
|
+
'cds.Int64': _isNumberish,
|
|
28
|
+
'cds.Decimal': _isNumberish,
|
|
29
|
+
'cds.DecimalFloat': _isNumber,
|
|
30
|
+
'cds.Double': _isNumber,
|
|
31
|
+
'cds.Date': _isDate,
|
|
32
|
+
'cds.Time': _isTime,
|
|
33
|
+
'cds.DateTime': _isDate,
|
|
34
|
+
'cds.Timestamp': _isDate,
|
|
35
|
+
'cds.String': _isString,
|
|
36
|
+
'cds.Binary': _isBuffer,
|
|
37
|
+
'cds.LargeString': _isString,
|
|
38
|
+
'cds.LargeBinary': _isStreamOrBuffer
|
|
39
|
+
}
|
|
@@ -102,8 +102,9 @@ function resolveSegment(prev, obj, def) {
|
|
|
102
102
|
keys.push(`${k}=false`) //> always false if not in obj as it must be a draft activate
|
|
103
103
|
else keys.push(`${k}=null`)
|
|
104
104
|
} else {
|
|
105
|
-
const
|
|
106
|
-
|
|
105
|
+
const cdsType = resolveCDSType(def.elements[k])
|
|
106
|
+
const odataType = def.elements[k]['@odata.Type']
|
|
107
|
+
if (!odataType && cdsType === 'cds.String' || odataType === 'Edm.String') val = `'${val}'`
|
|
107
108
|
// TODO: more proper val encoding based on type
|
|
108
109
|
keys.push(`${k}=${val}`)
|
|
109
110
|
}
|
|
@@ -6,7 +6,7 @@ const {
|
|
|
6
6
|
'cds.DateTime': checkISODateTime,
|
|
7
7
|
'cds.Timestamp': checkISOTimestamp,
|
|
8
8
|
'cds.String': checkString
|
|
9
|
-
} = require('./type')
|
|
9
|
+
} = require('./type-strict')
|
|
10
10
|
const { getTarget, resolveCDSType } = require('./utils')
|
|
11
11
|
|
|
12
12
|
const _isNavigationColumn = (col, as) => col.ref?.length > 1 && (col.as === as || col.ref[col.ref.length - 1] === as)
|
|
@@ -14,7 +14,7 @@ const _isNavigationColumn = (col, as) => col.ref?.length > 1 && (col.as === as |
|
|
|
14
14
|
// REVISIT: mandatory is actually not the same as not null or empty string
|
|
15
15
|
const _isNotFilled = val => val === null || val === undefined || (typeof val === 'string' && val.trim() === '')
|
|
16
16
|
|
|
17
|
-
const _getEnumElement = ele => ((ele['@assert.range'] && ele.enum)
|
|
17
|
+
const _getEnumElement = ele => ((ele['@assert.range'] && ele.enum) ? ele.enum : undefined)
|
|
18
18
|
|
|
19
19
|
const _enumValues = ele => {
|
|
20
20
|
return Object.keys(ele).map(enumKey => {
|
|
@@ -73,12 +73,7 @@ const checkMandatory = (v, ele, errs, path, k) => {
|
|
|
73
73
|
const checkEnum = (v, ele, errs, path, k) => {
|
|
74
74
|
const enumElements = _getEnumElement(ele)
|
|
75
75
|
const enumValues = enumElements && _enumValues(enumElements)
|
|
76
|
-
|
|
77
|
-
if (ele._type in { 'cds.Decimal': 1, 'cds.Int64': 1 }) {
|
|
78
|
-
return enumValues.map(ev => String(ev)).includes(String(v))
|
|
79
|
-
} else return enumValues.includes(v)
|
|
80
|
-
}
|
|
81
|
-
if (enumElements && !includes(enumValues, v)) {
|
|
76
|
+
if (enumElements && !enumValues.some(ev => ev == v)) { //> use == for automatic type coercion
|
|
82
77
|
const args =
|
|
83
78
|
typeof v === 'string'
|
|
84
79
|
? ['"' + v + '"', enumValues.map(ele => '"' + ele + '"').join(', ')]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const cds = require('../../_runtime/cds')
|
|
2
|
+
const { where2obj } = require('../../_runtime/common/utils/cqn')
|
|
3
|
+
const { getKeysForNavigationFromRefPath } = require('../../_runtime/common/utils/keys')
|
|
4
|
+
|
|
5
|
+
// REVISIT: do we already have something like this _without using okra api_?
|
|
6
|
+
// REVISIT: should we still support process.env.CDS_FEATURES_PARAMS? probably nobody uses it...
|
|
7
|
+
const getKeysAndParamsFromPath = (from, srv) => {
|
|
8
|
+
if (!from.ref || !from.ref.length) return {}
|
|
9
|
+
|
|
10
|
+
const keys = {}
|
|
11
|
+
const params = []
|
|
12
|
+
|
|
13
|
+
const model = cds.context.model ?? srv.model
|
|
14
|
+
|
|
15
|
+
let cur = model.definitions
|
|
16
|
+
let lastElement
|
|
17
|
+
|
|
18
|
+
for (let i = 0; i < from.ref.length; i++) {
|
|
19
|
+
const ref = from.ref[i]
|
|
20
|
+
const id = ref.id || ref
|
|
21
|
+
lastElement = cur[id]
|
|
22
|
+
const target = cur[id]._target ?? lastElement
|
|
23
|
+
cur = target.elements
|
|
24
|
+
|
|
25
|
+
if (i === from.ref.length - 1) {
|
|
26
|
+
// last element
|
|
27
|
+
if (ref.where) {
|
|
28
|
+
const seg_keys = where2obj(ref.where)
|
|
29
|
+
Object.assign(keys, seg_keys)
|
|
30
|
+
params[i] = seg_keys.ID && Object.keys(seg_keys).length === 1 ? seg_keys.ID : seg_keys
|
|
31
|
+
}
|
|
32
|
+
if (lastElement.isAssociation && from.ref.length > 1) {
|
|
33
|
+
// add keys for navigation from path
|
|
34
|
+
const rootRef = from.ref[0]
|
|
35
|
+
const rootTarget = model.definitions[rootRef.id || rootRef]
|
|
36
|
+
const seg_keys = getKeysForNavigationFromRefPath(from.ref, rootTarget)
|
|
37
|
+
// only take if a known property
|
|
38
|
+
for (const k in seg_keys) if (k in cur) keys[k] = seg_keys[k]
|
|
39
|
+
}
|
|
40
|
+
} else if (ref.where) {
|
|
41
|
+
const seg_keys = where2obj(ref.where)
|
|
42
|
+
params[i] = seg_keys.ID && Object.keys(seg_keys).length === 1 ? seg_keys.ID : seg_keys
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return { keys, params }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = {
|
|
50
|
+
getKeysAndParamsFromPath
|
|
51
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
const HttpAdapter = require('../../lib/srv/protocols/http')
|
|
2
|
+
const cds = require('../../lib')
|
|
3
|
+
const LOG = cds.log('odata')
|
|
4
|
+
|
|
5
|
+
const operation4 = require('./middleware/operation')
|
|
6
|
+
const create4 = require('./middleware/create')
|
|
7
|
+
const stream4 = require('./middleware/stream')
|
|
8
|
+
const read4 = require('./middleware/read')
|
|
9
|
+
const update4 = require('./middleware/update')
|
|
10
|
+
const delete4 = require('./middleware/delete')
|
|
11
|
+
const error4 = require('./middleware/error')
|
|
12
|
+
const bodyParser4 = require('./middleware/body-parser')
|
|
13
|
+
|
|
14
|
+
// REVISIT: copied from lib/req/request.js
|
|
15
|
+
const Http2Crud = { POST: 'CREATE', GET: 'READ', PUT: 'UPDATE', PATCH: 'UPDATE', DELETE: 'DELETE' }
|
|
16
|
+
|
|
17
|
+
const { isStream } = require('./utils')
|
|
18
|
+
|
|
19
|
+
class ODataAdapter extends HttpAdapter {
|
|
20
|
+
log(req) {
|
|
21
|
+
// REVISIT: this impl recreates the behavior of the old adapter, but is not very clean
|
|
22
|
+
|
|
23
|
+
// req.__proto__.method is set in case of upsert
|
|
24
|
+
if (req.__proto__.method in { PUT: 1, PATCH: 1 }) return // REVISIT: voodoo magic
|
|
25
|
+
|
|
26
|
+
if (req._subrequest)
|
|
27
|
+
//> req._subrequest is set for batch subrequests
|
|
28
|
+
LOG._info && LOG.info('>', Http2Crud[req.method], req.path, Object.keys(req.query).length ? { ...req.query } : '')
|
|
29
|
+
else super.log(req)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// early_access_check4(srv) { // REVISIT: let's remove that!
|
|
33
|
+
// const super_early_access_check = super.early_access_check4(srv)
|
|
34
|
+
// // REVISIT: We should remove the protectMetadata option in cds8, and always just do the right thing -> DOUBLE CHECK: Bad fiori behavior on 403ers
|
|
35
|
+
// return function early_access_check(req, res, next) {
|
|
36
|
+
// if (cds.env.odata.protectMetadata === false && (req.path === '/' || req.path === '/$metadata')) { // REVISIT: do that statically!
|
|
37
|
+
// // > nothing to do
|
|
38
|
+
// return next()
|
|
39
|
+
// }
|
|
40
|
+
|
|
41
|
+
// // REVISIT: Why exactly was that required?
|
|
42
|
+
// // > It was wrong, as it requested a login for any service operation, even though only one action was restricted (submitOrder)
|
|
43
|
+
// // we need to challenge in case of $batch requests if the service has restrictions
|
|
44
|
+
// // const user = cds.context.user
|
|
45
|
+
// // if (user._is_anonymous && req.path === '/$batch' && containsAnyRestrictions(srv)) {
|
|
46
|
+
// // if (!req._login) throw cds.error({ code: '401', statusCode: 401 })
|
|
47
|
+
// // return req._login()
|
|
48
|
+
// // }
|
|
49
|
+
|
|
50
|
+
// return super_early_access_check(req, res, next)
|
|
51
|
+
// }
|
|
52
|
+
// }
|
|
53
|
+
|
|
54
|
+
get router() {
|
|
55
|
+
const jsonBodyParser = bodyParser4(this)
|
|
56
|
+
return (
|
|
57
|
+
super.router
|
|
58
|
+
.use(function odata_version(req, res, next) {
|
|
59
|
+
res.set('OData-Version', '4.0')
|
|
60
|
+
next()
|
|
61
|
+
})
|
|
62
|
+
// REVISIT: add middleware for negative cases?
|
|
63
|
+
// service root
|
|
64
|
+
.use(/^\/$/, require('./middleware/service-document')(this))
|
|
65
|
+
.use('/\\$metadata', require('./middleware/metadata')(this))
|
|
66
|
+
// parse
|
|
67
|
+
.use(require('./middleware/parse')(this))
|
|
68
|
+
.use(function odata_streams(req, res, next) {
|
|
69
|
+
if (req.method === 'PUT' && isStream(req._query)) {
|
|
70
|
+
req.body = { value: req }
|
|
71
|
+
return next()
|
|
72
|
+
}
|
|
73
|
+
if (req.method === 'POST' && req.headers['content-type']?.match(/multipart\/mixed/)) {
|
|
74
|
+
return next()
|
|
75
|
+
}
|
|
76
|
+
if (req.method in { POST: 1, PUT: 1, PATCH: 1 } && req.headers['content-type']) {
|
|
77
|
+
const parts = req.headers['content-type'].split(';')
|
|
78
|
+
// header ending with semicolon is not allowed
|
|
79
|
+
if (!parts[0].match(/^application\/json$/) || parts[1] === '') {
|
|
80
|
+
throw cds.error('415', { statusCode: 415, code: '415' }) // FIXME: use res.status
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// POST with empty body is allowed by actions
|
|
84
|
+
if (req.method in { PUT: 1, PATCH: 1 }) {
|
|
85
|
+
if (req.headers['content-length'] === '0') {
|
|
86
|
+
res.status(400).json({ error: { message: 'Expected non-empty body', statusCode: 400, code: '400' } })
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return jsonBodyParser(req, res, next)
|
|
92
|
+
})
|
|
93
|
+
// batch
|
|
94
|
+
.post('/\\$batch', require('./middleware/batch')(this))
|
|
95
|
+
// handle
|
|
96
|
+
// REVISIT: with old adapter, we return 405 for HEAD requests -> check OData spec
|
|
97
|
+
.head('*', (_, res) => res.sendStatus(405))
|
|
98
|
+
.post('*', operation4(this), create4(this))
|
|
99
|
+
.get('*', operation4(this), stream4(this), read4(this))
|
|
100
|
+
.put('*', update4(this), create4(this, 'upsert'))
|
|
101
|
+
.patch('*', update4(this), create4(this, 'upsert'))
|
|
102
|
+
.delete('*', delete4(this))
|
|
103
|
+
// error
|
|
104
|
+
.use(error4(this))
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
request4(args) {
|
|
109
|
+
return new NoaRequest(args)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// REVISIT: ugly hack -> eliminate
|
|
114
|
+
class NoaRequest extends cds.Request {
|
|
115
|
+
// REVISIT: all usages of .protocol are very bad style, violating modularization
|
|
116
|
+
get protocol() {
|
|
117
|
+
return 'odata'
|
|
118
|
+
}
|
|
119
|
+
// AFC uses unofficial req._queryOptions -> which is bad! -> should eliminate
|
|
120
|
+
get _queryOptions() {
|
|
121
|
+
cds.utils.deprecated({ kind: '', old: 'req._queryOptions', new: 'req._.req.query' })
|
|
122
|
+
return this.req?.query
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
module.exports = Object.assign(ODataAdapter, { NoaRequest })
|
package/libx/odata/index.js
CHANGED
|
@@ -78,7 +78,12 @@ const _2query = cqn => {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
const enhanceCqn = (cqn, options) => {
|
|
81
|
-
if (options.afterburner)
|
|
81
|
+
if (options.afterburner) {
|
|
82
|
+
const { service, protocol } = options
|
|
83
|
+
let { model, definition: { name: namespace } } = service // prettier-ignore
|
|
84
|
+
if (service.isExtensible) model = cds.context?.model || model
|
|
85
|
+
cqn = options.afterburner(cqn, model, namespace, protocol)
|
|
86
|
+
}
|
|
82
87
|
|
|
83
88
|
const query = _2query(cqn)
|
|
84
89
|
|
|
@@ -101,8 +106,16 @@ module.exports = {
|
|
|
101
106
|
|
|
102
107
|
url = decodeURIComponent(url)
|
|
103
108
|
|
|
109
|
+
// REVISIT: compat for bad url in mtxs tests (cf. #957)
|
|
110
|
+
if (url.match(/\?\?/)) {
|
|
111
|
+
const split = url.split('?')
|
|
112
|
+
url = split.shift() + '?'
|
|
113
|
+
while (split[0] === '') split.shift()
|
|
114
|
+
url += split.join('?')
|
|
115
|
+
}
|
|
116
|
+
|
|
104
117
|
options = options === 'strict' ? { strict } : options.strict ? { ...options, strict } : options
|
|
105
|
-
if (options.service) Object.assign(options, { minimal: true, afterburner
|
|
118
|
+
if (options.service?.model) Object.assign(options, { minimal: true, afterburner })
|
|
106
119
|
options.safeNumber = safeNumber
|
|
107
120
|
options.skipToken = require('./utils').skipToken
|
|
108
121
|
|