@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
package/lib/srv/srv-api.js
CHANGED
|
@@ -149,23 +149,3 @@ const is_rest = x => x && typeof x === 'string' && x[0] === '/'
|
|
|
149
149
|
const is_query = x => x && x.bind || is_array(x) && !x.raw
|
|
150
150
|
const is_array = (x) => Array.isArray(x) && !x.raw
|
|
151
151
|
const is_object = (x) => typeof x === 'object'
|
|
152
|
-
|
|
153
|
-
// Deprecated
|
|
154
|
-
/** @deprecated: To be removed with the next major release */
|
|
155
|
-
Service.prototype.stream = cds.utils.deprecated (function (...args) {
|
|
156
|
-
if (is_query(args[0])) {
|
|
157
|
-
Object.defineProperty(args[0], '_stream', { value: true, enumerable: false })
|
|
158
|
-
if (cds.env.features.stream_compat)
|
|
159
|
-
Object.defineProperty(args[0], '_streaming', { value: true, enumerable: false })
|
|
160
|
-
return this.run(...args).then(result => {
|
|
161
|
-
if (!result) return result
|
|
162
|
-
return Array.isArray(result) ? Object.values(result[0])[0] : Object.values(result)[0]})
|
|
163
|
-
}
|
|
164
|
-
const q = (args ? SELECT.one.columns(args) : SELECT.one).bind(this)
|
|
165
|
-
Object.defineProperty(q, '_stream', { value: true, enumerable: false })
|
|
166
|
-
if (cds.env.features.stream_compat) Object.defineProperty(q, '_streaming', { value: true, enumerable: false })
|
|
167
|
-
return q
|
|
168
|
-
}, {
|
|
169
|
-
old: 'srv.stream()',
|
|
170
|
-
use: 'srv.read()'
|
|
171
|
-
})
|
package/lib/srv/srv-dispatch.js
CHANGED
|
@@ -15,8 +15,9 @@ exports.dispatch = async function dispatch (req) { //NOSONAR
|
|
|
15
15
|
if (!this.context) return this.run (tx => tx.dispatch(req))
|
|
16
16
|
if (!req.tx) req.tx = this // `this` is a tx from now on...
|
|
17
17
|
|
|
18
|
-
//
|
|
19
|
-
|
|
18
|
+
// REVISIT: remove together with okra
|
|
19
|
+
// Inform potential listeners
|
|
20
|
+
let _is_root = req.constructor.name in { ODataRequest:1 }
|
|
20
21
|
if (_is_root) req._.req.emit ('dispatch',req)
|
|
21
22
|
|
|
22
23
|
// Handle batches of queries
|
package/lib/srv/srv-handlers.js
CHANGED
|
@@ -4,7 +4,13 @@ const LOG = cds.log()
|
|
|
4
4
|
class EventHandlers {
|
|
5
5
|
|
|
6
6
|
constructor (name) {
|
|
7
|
-
this._handlers = {
|
|
7
|
+
this._handlers = {
|
|
8
|
+
/** @type {EventHandler[]} */ _initial:[],
|
|
9
|
+
/** @type {EventHandler[]} */ before:[],
|
|
10
|
+
/** @type {EventHandler[]} */ on:[],
|
|
11
|
+
/** @type {EventHandler[]} */ after:[],
|
|
12
|
+
/** @type {EventHandler[]} */ _error:[]
|
|
13
|
+
}
|
|
8
14
|
this.name = name
|
|
9
15
|
}
|
|
10
16
|
|
|
@@ -68,9 +74,12 @@ const _register = function (srv, phase, event, path, handler) { //NOSONAR
|
|
|
68
74
|
for (let each of ['CREATE','UPDATE']) _register (srv, phase, each, path, handler)
|
|
69
75
|
return this
|
|
70
76
|
}
|
|
71
|
-
else if (
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
else if (phase === 'after' && ( event === 'each' //> srv.after ('each', Book, b => ...) // event 'each' => READ each
|
|
78
|
+
|| event === 'READ' && path?.is_singular //> srv.after ('READ', Book, b => ...) // Book is a singular def from cds-typer
|
|
79
|
+
|| event === 'READ' && /^\(?each\b/.test(handler) //> srv.after ('READ', Book, each => ...) // handler's first param is named 'each'
|
|
80
|
+
)) {
|
|
81
|
+
event = 'READ' // override event='each' to 'READ'
|
|
82
|
+
const h=handler; handler = (rows,req) => is_array(rows) ? rows.forEach (r => h(r,req)) : rows && h(rows,req)
|
|
74
83
|
}
|
|
75
84
|
else if (typeof event === 'object') {
|
|
76
85
|
// extract action name from an action definition's fqn
|
|
@@ -91,7 +100,7 @@ const _register = function (srv, phase, event, path, handler) { //NOSONAR
|
|
|
91
100
|
if (!path.startsWith(srv.name+'.')) path = `${srv.name}.${path}`
|
|
92
101
|
}
|
|
93
102
|
|
|
94
|
-
if (cds.env.fiori.
|
|
103
|
+
if (cds.env.fiori.draft_compat) {
|
|
95
104
|
const entity = path && srv.model?.definitions[path.name || path]
|
|
96
105
|
if (['PATCH', 'CANCEL', 'NEW'].includes(event)) {
|
|
97
106
|
// delegate to drafts
|
|
@@ -120,14 +129,16 @@ class EventHandler {
|
|
|
120
129
|
this.handler = handler
|
|
121
130
|
Object.defineProperties (this, { // non-enumerable properties to improve debugging
|
|
122
131
|
_initial: { value: handler._initial },
|
|
123
|
-
for: { value:
|
|
124
|
-
event && path ? (req) => (event === req.event) && (path === req.path || path === req.entity) :
|
|
125
|
-
event ? (req) => (event === req.event) :
|
|
126
|
-
path ? (req) => (path === req.path || path === req.entity) :
|
|
127
|
-
/* else: */ () => true
|
|
128
|
-
}
|
|
132
|
+
for: { value: this.for(event,path) }
|
|
129
133
|
})
|
|
130
134
|
}
|
|
135
|
+
/** Factory for the actual filter method this.for, assigned above */
|
|
136
|
+
for (event, path) {
|
|
137
|
+
if (event && path) return req => event === req.event && (path === req.path || path === req.entity)
|
|
138
|
+
if (event) return req => event === req.event
|
|
139
|
+
if (path) return req => path === req.path || path === req.entity
|
|
140
|
+
else return () => true
|
|
141
|
+
}
|
|
131
142
|
}
|
|
132
143
|
|
|
133
144
|
|
package/lib/srv/srv-methods.js
CHANGED
|
@@ -67,7 +67,7 @@ const add_handler_for = (srv, def) => {
|
|
|
67
67
|
req.params = [ args.shift() ] // second argument is the target's primary key
|
|
68
68
|
}
|
|
69
69
|
const {params} = target ? target.actions[event] : def
|
|
70
|
-
if (params) req.data =
|
|
70
|
+
if (params) req.data = _named(args,params) || _positional(args,params)
|
|
71
71
|
|
|
72
72
|
// ensure legacy compat, keys in req.data
|
|
73
73
|
if(cds.env.features.keys_in_data_compat && target) {
|
|
@@ -104,5 +104,5 @@ const _named = (args, declared) => {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
const _positional = (args, declared) => Object.keys(declared).reduce (
|
|
107
|
-
(data,k,i) => { data[k] = args[i]; return data }, {}
|
|
107
|
+
(data,k,i) => { if (args[i] !== undefined) data[k] = args[i]; return data }, {}
|
|
108
108
|
)
|
package/lib/srv/srv-models.js
CHANGED
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
// REVISIT: all edmx caches also have to be hooked in here
|
|
3
3
|
|
|
4
4
|
const cds = require ('../index')
|
|
5
|
-
const LOG = cds.log()
|
|
6
|
-
|
|
7
|
-
const {normalizeError} = require('../../libx/_runtime/common/error/frontend')
|
|
8
|
-
const getError = require('../../libx/_runtime/common/error')
|
|
9
5
|
|
|
10
6
|
/**
|
|
11
7
|
* Implements a static cache for all tenant/features-specific models.
|
|
@@ -15,34 +11,6 @@ const getError = require('../../libx/_runtime/common/error')
|
|
|
15
11
|
*/
|
|
16
12
|
class ExtendedModels {
|
|
17
13
|
|
|
18
|
-
/**
|
|
19
|
-
* Returns an express middleware to be used with the given srv to set cds.context.model.
|
|
20
|
-
* Uses `this.model4(...)` to get the respective tenant/features-specific models.
|
|
21
|
-
* @returns {(req,res)=>{}}
|
|
22
|
-
*/
|
|
23
|
-
static middleware4 (srv) {
|
|
24
|
-
if (!(srv instanceof cds.ApplicationService)) return [] //> no middleware to add // REVISIT: move to `srv.isExtensible`
|
|
25
|
-
if (!srv.isExtensible) return [] //> no middleware to add
|
|
26
|
-
else return async function cds_context_model (req,res,next) {
|
|
27
|
-
if (!req.tenant && !req.user?.tenant && !req.features && !req.user?.features) return next()
|
|
28
|
-
const ctx = cds.context = cds.EventContext.for({ http: { req, res } })
|
|
29
|
-
ctx.user = req.user // REVISIT: should move to auth middleware?
|
|
30
|
-
try {
|
|
31
|
-
ctx.model = req.__model = await ExtendedModels.model4 (ctx.tenant, ctx.features)
|
|
32
|
-
} catch (e) {
|
|
33
|
-
LOG.error (Object.assign(e, { message: 'Unable to get service from service map due to error: ' + e.message }))
|
|
34
|
-
|
|
35
|
-
// return 503 to client
|
|
36
|
-
// REVISIT: Error handling!
|
|
37
|
-
const { error } = normalizeError(getError(Object.assign(e, { statusCode: 503 })), req)
|
|
38
|
-
|
|
39
|
-
return res.status(503).json({ error })
|
|
40
|
-
}
|
|
41
|
-
next()
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
14
|
/**
|
|
47
15
|
* Returns the model to use for given tenant and features.
|
|
48
16
|
* Loaded models are cached, with eviction on inactivity of tenants,
|
|
@@ -87,7 +55,7 @@ class ExtendedModels {
|
|
|
87
55
|
* @returns {string} of the form `<tenant>:<comma-separated-features>`
|
|
88
56
|
*/
|
|
89
57
|
key4 (tenant, features) {
|
|
90
|
-
return `${tenant||''}:${features
|
|
58
|
+
return `${tenant||''}:${features?.$hash}`
|
|
91
59
|
}
|
|
92
60
|
|
|
93
61
|
|
|
@@ -158,7 +126,7 @@ class ExtendedModels {
|
|
|
158
126
|
}
|
|
159
127
|
|
|
160
128
|
|
|
161
|
-
/** The cache instance used by `
|
|
129
|
+
/** The cache instance used by `model4()`. */
|
|
162
130
|
static cache = new ExtendedModels
|
|
163
131
|
|
|
164
132
|
/** Time interval in ms to check for new extensions and refresh models, if so. */
|
|
@@ -176,7 +144,6 @@ module.exports = ExtendedModels
|
|
|
176
144
|
const extensibility = cds.requires.extensibility
|
|
177
145
|
if (!extensibility) {
|
|
178
146
|
ExtendedModels.prototype.at = function (key) { return this[key] }
|
|
179
|
-
if (!cds.requires.toggles) ExtendedModels.middleware4 = ()=> []
|
|
180
147
|
}
|
|
181
148
|
|
|
182
149
|
|
|
@@ -189,7 +156,7 @@ const _get_model4 = async (tenant, features) => {
|
|
|
189
156
|
if (cds.edmxs) await cds.compile.to.edmx.files ( csn,
|
|
190
157
|
// adding tenant and features to runtime model, for later use when fetching generated files
|
|
191
158
|
nsn.tenant = tenant,
|
|
192
|
-
nsn.features = features
|
|
159
|
+
nsn.features = features?.$hash
|
|
193
160
|
)
|
|
194
161
|
return nsn
|
|
195
162
|
}
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
const { inspect } = require('node:util')
|
|
2
|
+
const format = x => inspect(
|
|
3
|
+
is.error(x) ? x.message
|
|
4
|
+
: typeof x === 'object' && 'status' in x && 'body' in x ? { status: x.status, body: x.body }
|
|
5
|
+
: typeof x === 'object' && 'status' in x && 'data' in x ? { status: x.status, data: x.data }
|
|
6
|
+
: x,
|
|
7
|
+
{ colors: true, sorted: true, depth: 11 }
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
const expect = module.exports = actual => {
|
|
11
|
+
const chainable = function (x) { return this.call(x) }; delete chainable.length
|
|
12
|
+
return Object.setPrototypeOf(chainable, new Assertion(actual))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const is = new class {
|
|
16
|
+
Array = Array.isArray
|
|
17
|
+
Error = x => x instanceof Error || x?.stack && x.message
|
|
18
|
+
Symbol = x => typeof x === 'symbol'
|
|
19
|
+
Object = x => typeof x === 'object' // && x && !is.array(x)
|
|
20
|
+
String = x => typeof x === 'string' || x instanceof String
|
|
21
|
+
Number = x => typeof x === 'number' || x instanceof Number
|
|
22
|
+
Boolean = x => typeof x === 'boolean' || x instanceof Boolean
|
|
23
|
+
Promise = x => x instanceof Promise
|
|
24
|
+
RegExp = x => x instanceof RegExp
|
|
25
|
+
Date = x => x instanceof Date
|
|
26
|
+
Set = x => x instanceof Set
|
|
27
|
+
Map = x => x instanceof Map
|
|
28
|
+
array = this.Array
|
|
29
|
+
error = this.Error
|
|
30
|
+
symbol = this.Symbol
|
|
31
|
+
object = this.Object
|
|
32
|
+
string = this.String
|
|
33
|
+
number = this.Number
|
|
34
|
+
boolean = this.Boolean
|
|
35
|
+
promise = this.Promise
|
|
36
|
+
regexp = this.RegExp
|
|
37
|
+
date = this.Date
|
|
38
|
+
set = this.Set
|
|
39
|
+
map = this.Map
|
|
40
|
+
/** Jest-style any matcher */
|
|
41
|
+
any = expect.any = type => {
|
|
42
|
+
if (type === undefined) return () => true
|
|
43
|
+
else return this [type.name || type] || (x => x instanceof type)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Core {
|
|
49
|
+
|
|
50
|
+
constructor (actual) { this._ = actual }
|
|
51
|
+
|
|
52
|
+
/** The central method to throw an AssertionError. */
|
|
53
|
+
expected ([a, be, ...etc], ...args) {
|
|
54
|
+
const raw = [a, (this._not ? ' NOT' : '') + be, ...etc]
|
|
55
|
+
const err = new expected({ raw }, ...args)
|
|
56
|
+
// err.operator = be.trim().replace(/^to /,'')
|
|
57
|
+
// err.expected = args[1]
|
|
58
|
+
// err.actual = args[0]
|
|
59
|
+
throw err
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
should ([be, ...etc], ...args) {
|
|
63
|
+
return this.expected(['', ' to ' + be, ...etc], this._, ...args)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The central method to check assertions. */
|
|
67
|
+
assert (check, _fail = () => false) {
|
|
68
|
+
const outcome = check(this._)
|
|
69
|
+
if (this._not ? outcome : !outcome) return _fail(outcome)
|
|
70
|
+
else return this
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
instanceof (x) {
|
|
74
|
+
return this.assert(a => a instanceof x) || this.should`be an instance of ${x.name || x}`
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
kindof (x) {
|
|
78
|
+
return this.assert(is.any(x)) || this.should`be kind of ${x?.name || x}`
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
equals (x, _fail = () => this.should`strictly equal ${x}`) {
|
|
82
|
+
if (typeof x === 'function') return this.assert(x)
|
|
83
|
+
if (this._deep) return this.eqls(x)
|
|
84
|
+
return this.assert(a => a === x, _fail)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
eqls (x, _fail = () => this.should`deeply equal ${x}`) {
|
|
88
|
+
if (typeof x === 'function') return this.assert(x)
|
|
89
|
+
return this.assert(a => compare(a, x, true), _fail)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
subset (x, _fail = () => this.should`contain subset ${x}`) {
|
|
93
|
+
return this.assert(a => {
|
|
94
|
+
if (is.array(a) && is.array(x)) return x.every(x => a.some(o => compare(o, x, false)))
|
|
95
|
+
if (is.array(a) && !is.array(x)) return a.some(o => compare(o, x, false))
|
|
96
|
+
else return compare(a, x, false)
|
|
97
|
+
}, _fail)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
matches (x, _fail = () => this.should`match ${x}`) {
|
|
101
|
+
return this.assert(a => {
|
|
102
|
+
if (is.regexp(x)) return x.test(a)
|
|
103
|
+
if (is.string(x)) return a.includes?.(x)
|
|
104
|
+
if (is.object(x)) return this.subset(x) && !this._not //> to avoid doubled not
|
|
105
|
+
// if (is.array(x)) return x.every(x => a.includes(x))
|
|
106
|
+
}, _fail)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
includes (x, _fail = () => this.should`include ${x}`) {
|
|
110
|
+
return this.assert(a => {
|
|
111
|
+
if (!a) expected`an array or string or set or object but got ${a}`
|
|
112
|
+
if (is.string(a)) return a.includes(x)
|
|
113
|
+
if (is.array(a)) return a.includes(x) || this._deep && a.some(o => compare(o, x, true))
|
|
114
|
+
if (is.set(a)) return a.has(x)
|
|
115
|
+
if (this._deep && is.object(a)) return compare(a, x, true)
|
|
116
|
+
}, _fail)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
oneOf (x, _fail = () => this.should`be one of ${x}`) {
|
|
120
|
+
return this.assert(a => x.includes(a), _fail)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
throws (x, _fail = () => this.should`throw ${x}`) {
|
|
124
|
+
if (is.promise(this._)) return this.rejectsWith(x)
|
|
125
|
+
return this.assert(a => {
|
|
126
|
+
if (typeof a === 'function') try { a(); return false } catch (err) { if (!x) return true; else a = err }
|
|
127
|
+
if (typeof x.test === 'function') return x.test(a)
|
|
128
|
+
if (typeof x === 'function') return x(a)
|
|
129
|
+
if (typeof x === 'string') return a == x || a.code == x || a.message?.includes(x)
|
|
130
|
+
if (typeof x === 'object') return compare(a, x, false)
|
|
131
|
+
}, _fail)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
rejectsWith (x) {
|
|
135
|
+
if (this._not) return Promise.resolve(this._).catch(
|
|
136
|
+
e => expected`promise to be fulfilled but it was rejected with ${e}`
|
|
137
|
+
)
|
|
138
|
+
else return Promise.resolve(this._).then(
|
|
139
|
+
y => expected`promise to be rejected but it was fulfilled with ${y}`,
|
|
140
|
+
e => {
|
|
141
|
+
if (x) expect(e).throws(x, () => expected`promise to be rejected with ${x} but got ${e}`)
|
|
142
|
+
return e
|
|
143
|
+
}
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
length (ln) {
|
|
148
|
+
return this.assert(a => (a.length ?? String(a).length) === ln, () => this.should`have length ${ln}`)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
property (p, v) {
|
|
152
|
+
const has = !this._own ? (a, p) => p in a : Reflect.getOwnPropertyDescriptor
|
|
153
|
+
const get = (a, p) => has(a, p) ? a[p] : $not_found, $not_found = {}
|
|
154
|
+
const y = this.assert(() => true) && !this._nested ? get(this._, p) : p.split('.').reduce((a, p) => get(a, p), this._)
|
|
155
|
+
if (y === $not_found) return this._not || (this._nested
|
|
156
|
+
? this.should`have nested property ${p}`
|
|
157
|
+
: this.should`have property ${p}`)
|
|
158
|
+
const that = Object.assign(expect(), this, { _: y })
|
|
159
|
+
if (v !== undefined) return that.eqls(v, () => this._nested
|
|
160
|
+
? this.should`have nested property ${p} with value ${v}`
|
|
161
|
+
: this.should`have property ${p} with value ${v}`)
|
|
162
|
+
return that
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
keys (...keys) {
|
|
166
|
+
if (is.array(keys[0])) keys = keys[0]
|
|
167
|
+
return this.assert(a => keys.every(k => k in a)) || this.should`have all keys ${keys}`
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
gt (x) { return this.assert(a => a > x) || this.should`be > ${x}` }
|
|
171
|
+
lt (x) { return this.assert(a => a < x) || this.should`be < ${x}` }
|
|
172
|
+
gte (x) { return this.assert(a => a >= x) || this.should`be >= ${x}` }
|
|
173
|
+
lte (x) { return this.assert(a => a <= x) || this.should`be <= ${x}` }
|
|
174
|
+
within (x, y) { return this.assert(a => x <= a && a <= y) || this.should`be within ${[x, y]}` }
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class Chai extends Core {
|
|
179
|
+
|
|
180
|
+
// linguistic chaining
|
|
181
|
+
|
|
182
|
+
get to () { return this }
|
|
183
|
+
get be () { this.call = this.equals; return this }
|
|
184
|
+
get is () { this.call = this.equals; return this }
|
|
185
|
+
get at () { return this }
|
|
186
|
+
get of () { return this }
|
|
187
|
+
get and () { return this }
|
|
188
|
+
get but () { return this }
|
|
189
|
+
get has () { this.call = this.property; return this }
|
|
190
|
+
get have () { this.call = this.property; return this }
|
|
191
|
+
get that () { return this }
|
|
192
|
+
get does () { return this }
|
|
193
|
+
get with () { return this }
|
|
194
|
+
get also () { return this }
|
|
195
|
+
get still () { return this }
|
|
196
|
+
get which () { return this }
|
|
197
|
+
get eventually () {
|
|
198
|
+
this.assert = (fn, _fail) => Promise.resolve(this._).then(a => expect(a).assert(fn, _fail))
|
|
199
|
+
return this
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// flags changing behaviour of subsequent methods in the chain
|
|
203
|
+
|
|
204
|
+
get not () { this._not = true; return this }
|
|
205
|
+
get own () { this._own = true; return this }
|
|
206
|
+
get deep () { this._deep = true; return this }
|
|
207
|
+
get nested () { this._nested = true; return this }
|
|
208
|
+
get ordered () { return unsupported() }
|
|
209
|
+
get any () { return unsupported() }
|
|
210
|
+
get all () { return this }
|
|
211
|
+
|
|
212
|
+
get undefined () { return this.assert(a => a === undefined) || this.should`be undefined` }
|
|
213
|
+
get exist () { return this.assert(a => a != undefined) || this.should`exist` }
|
|
214
|
+
get truthy () { return this.assert(a => !!a) || this.should`be truthy` }
|
|
215
|
+
get falsy () { return this.assert(a => !a) || this.should`be falsy` }
|
|
216
|
+
get null () { return this.assert(a => a === null) || this.should`be ${null}` }
|
|
217
|
+
get true () { return this.assert(a => a === true) || this.should`be ${true}` }
|
|
218
|
+
get false () { return this.assert(a => a === false) || this.should`be ${false}` }
|
|
219
|
+
get empty () { return this.assert(a => !a?.length === 0 || Object.keys(a).length === 0) || this.should`be empty` }
|
|
220
|
+
get NaN () { return this.assert(a => isNaN(a)) || this.should`be ${NaN}` }
|
|
221
|
+
get ok () { return this.truthy }
|
|
222
|
+
|
|
223
|
+
get containSubset () { return this.subset }
|
|
224
|
+
get contains () { return this.includes }
|
|
225
|
+
get contain () { return this.includes }
|
|
226
|
+
get include () { return this.includes }
|
|
227
|
+
get match () { return this.matches }
|
|
228
|
+
get equal () { return this.equals }
|
|
229
|
+
get eq () { return this.equals }
|
|
230
|
+
get eql () { return this.eqls }
|
|
231
|
+
get exists () { return this.defined }
|
|
232
|
+
get lengthOf () { return this.length }
|
|
233
|
+
get instanceOf () { return this.instanceof }
|
|
234
|
+
get kindOf () { return this.kindof }
|
|
235
|
+
get kind () { return this.kindof }
|
|
236
|
+
get an () { this.call = this.kindof; return this }
|
|
237
|
+
get a () { this.call = this.kindof; return this }
|
|
238
|
+
get key () { return this.keys }
|
|
239
|
+
|
|
240
|
+
get below () { return this.lt }
|
|
241
|
+
get above () { return this.gt }
|
|
242
|
+
get most () { return this.lte }
|
|
243
|
+
get least () { return this.gte }
|
|
244
|
+
get lessThan () { return this.lt }
|
|
245
|
+
get greaterThan () { return this.gt }
|
|
246
|
+
get lessThanOrEqual () { return this.lte }
|
|
247
|
+
get greaterThanOrEqual () { return this.gte }
|
|
248
|
+
|
|
249
|
+
get throw () { return this.throws }
|
|
250
|
+
get fulfilled () { return this.not.rejectsWith() }
|
|
251
|
+
get rejected () { return this.rejectsWith() }
|
|
252
|
+
get rejectedWith () { return this.rejectsWith }
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class Jest extends Chai {
|
|
257
|
+
|
|
258
|
+
get rejects () { return this }
|
|
259
|
+
get toBe () { return this.equals }
|
|
260
|
+
get toEqual () { return this.eqls }
|
|
261
|
+
get toMatch () { return this.matches }
|
|
262
|
+
get toMatchObject () { return this.matches }
|
|
263
|
+
get toContain () { return this.includes }
|
|
264
|
+
get toThrow () { return this.throws }
|
|
265
|
+
get toThrowError () { return this.throws }
|
|
266
|
+
get toBeGreaterThan () { return this.gt }
|
|
267
|
+
get toBeLessThan () { return this.lt }
|
|
268
|
+
get toBeGreaterThanOrEqual () { return this.gte }
|
|
269
|
+
get toBeLessThanOrEqual () { return this.lte }
|
|
270
|
+
get toHaveProperty () { return this.nested.property }
|
|
271
|
+
get toHaveLength () { return this.length }
|
|
272
|
+
|
|
273
|
+
toBeFalsy () { return this.falsy }
|
|
274
|
+
toBeTruthy () { return this.truthy }
|
|
275
|
+
toBeDefined () { return this.defined }
|
|
276
|
+
toBeUndefined () { return this.undefined }
|
|
277
|
+
toBeInstanceOf () { return this.instanceof }
|
|
278
|
+
toMatchSnapshot () { unsupported('toMatchSnapshot') }
|
|
279
|
+
|
|
280
|
+
static expect() {
|
|
281
|
+
expect.stringMatching = x => a => (is.regexp(x) ? x : RegExp(x)).test?.(a)
|
|
282
|
+
expect.arrayContaining = x => a => x.every(e => a.includes(e))
|
|
283
|
+
expect.any = is.any
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
Jest.expect()
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
class Assertion extends Jest {
|
|
290
|
+
toString() { return `[ expect: ${format(this._)} ]` }
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
class AssertionError extends Error {
|
|
295
|
+
constructor (m, caller = Assertion.prototype.should) { Error.captureStackTrace (super(m), caller) }
|
|
296
|
+
get caller() { return Assertion.prototype.should }
|
|
297
|
+
get code() { return 'ERR_ASSERTION' }
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// function AssertionError(m){
|
|
301
|
+
// Error.captureStackTrace (this,this.caller)
|
|
302
|
+
// this.message = m
|
|
303
|
+
// }
|
|
304
|
+
// AssertionError.prototype = Object.create (Error.prototype, {constructor:{value: AssertionError }})
|
|
305
|
+
// AssertionError.__proto__ = Error
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
expect.fail = function (actual, expected, message) {
|
|
309
|
+
if (arguments.length === 1) throw new AssertionError (actual, expect.fail)
|
|
310
|
+
if (arguments.length === 3) throw Object.assign (new AssertionError (message, expect.fail), { expected, actual })
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function expected (strings, ...args) {
|
|
314
|
+
const err = new AssertionError ('expected ' + String.raw(strings, ...args.map(format)))
|
|
315
|
+
if (new.target) return err; else throw err
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function unsupported (method) {
|
|
319
|
+
const ignore = unsupported.skip ??= (process.env._chest_skip || '')?.split(',').reduce((p, c) => (p[c] = 1, p), {})
|
|
320
|
+
if (!method) return new Error(`unsupported`)
|
|
321
|
+
if (method in ignore) return () => { }
|
|
322
|
+
else throw new Error(`
|
|
323
|
+
Method expect .${method}() is not yet supported.
|
|
324
|
+
Use --skip ${method} to skip checks.
|
|
325
|
+
`)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function compare (a, b, strict) {
|
|
329
|
+
if (a == b) return true
|
|
330
|
+
return function _recurse (a, b) {
|
|
331
|
+
if (!a || typeof a !== 'object') return false
|
|
332
|
+
if (!b || typeof b !== 'object') return false
|
|
333
|
+
if (strict)
|
|
334
|
+
for (let k of Object.keys(a)) if (!(k in b))
|
|
335
|
+
return false
|
|
336
|
+
for (let k in b) {
|
|
337
|
+
const v = a[k], x = b[k]; if (v === x) continue
|
|
338
|
+
if (typeof x === 'function') { if (x(v)) continue; else return false }
|
|
339
|
+
if (!_recurse(v, x)) return false
|
|
340
|
+
}
|
|
341
|
+
return true
|
|
342
|
+
}(a, b)
|
|
343
|
+
}
|