@sap/cds 5.5.3 → 5.6.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 +134 -1
- package/apis/services.d.ts +27 -1
- package/app/index.js +22 -11
- package/bin/build/buildTaskFactory.js +1 -1
- package/bin/build/provider/buildTaskProviderInternal.js +1 -1
- package/bin/build/provider/fiori/index.js +1 -1
- package/bin/build/provider/hana/2migration.js +8 -7
- package/bin/build/provider/java-cf/index.js +1 -1
- package/bin/deploy/to-hana/hana.js +1 -17
- package/common.cds +8 -0
- package/lib/compile/index.js +1 -1
- package/lib/compile/to/sql.js +22 -2
- package/lib/connect/bindings.js +2 -1
- package/lib/connect/index.js +1 -1
- package/lib/core/infer.js +1 -1
- package/lib/core/reflect.js +3 -1
- package/lib/env/index.js +175 -41
- package/lib/env/requires.js +24 -3
- package/lib/i18n/localize.js +33 -5
- package/lib/index.js +7 -6
- package/lib/log/format/kibana.js +6 -2
- package/lib/ql/DELETE.js +1 -1
- package/lib/ql/INSERT.js +1 -1
- package/lib/ql/Query.js +13 -10
- package/lib/ql/SELECT.js +15 -8
- package/lib/ql/UPDATE.js +1 -1
- package/lib/ql/Whereable.js +5 -0
- package/lib/req/context.js +87 -37
- package/lib/req/{impl.js → request.js} +1 -1
- package/lib/req/{res.js → response.js} +0 -0
- package/lib/serve/Service-api.js +1 -1
- package/lib/serve/Service-dispatch.js +12 -2
- package/lib/serve/Service-handlers.js +21 -7
- package/lib/serve/Service-methods.js +1 -1
- package/lib/serve/Transaction.js +7 -6
- package/lib/serve/index.js +1 -1
- package/lib/utils/axios.js +7 -0
- package/lib/utils/data.js +1 -1
- package/libx/_runtime/audit/Service.js +18 -18
- package/libx/_runtime/audit/generic/personal/access.js +1 -1
- package/libx/_runtime/audit/generic/personal/modification.js +3 -2
- package/libx/_runtime/audit/generic/personal/utils.js +23 -63
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +6 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +37 -35
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +3 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +5 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +13 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +84 -34
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +10 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +9 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +8 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +13 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +11 -95
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/ResourcePathParser.js +17 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +6 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +3 -34
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +3 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +48 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +10 -5
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/handlers/update.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +1 -3
- package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +9 -2
- package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +20 -21
- package/libx/_runtime/cds-services/services/utils/columns.js +6 -1
- package/libx/_runtime/cds-services/services/utils/compareJson.js +1 -8
- package/libx/_runtime/cds-services/services/utils/differ.js +7 -26
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +2 -4
- package/libx/_runtime/cds-services/util/assert.js +29 -13
- package/libx/_runtime/cds.js +2 -1
- package/libx/_runtime/common/aspects/Association.js +72 -0
- package/libx/_runtime/common/aspects/any.js +8 -45
- package/libx/_runtime/common/aspects/entity.js +0 -1
- package/libx/_runtime/common/aspects/relation.js +40 -0
- package/libx/_runtime/common/aspects/utils.js +73 -1
- package/libx/_runtime/common/auth/strategies/utils/uaa.js +10 -14
- package/libx/_runtime/common/composition/data.js +3 -2
- package/libx/_runtime/common/composition/delete.js +3 -1
- package/libx/_runtime/common/composition/tree.js +23 -18
- package/libx/_runtime/common/composition/utils.js +34 -8
- package/libx/_runtime/common/error/frontend.js +6 -1
- package/libx/_runtime/common/generic/auth.js +15 -13
- package/libx/_runtime/common/generic/crud.js +2 -2
- package/libx/_runtime/common/generic/etag.js +11 -8
- package/libx/_runtime/common/generic/input.js +3 -3
- package/libx/_runtime/common/generic/paging.js +9 -5
- package/libx/_runtime/common/generic/put.js +3 -2
- package/libx/_runtime/common/generic/sorting.js +3 -3
- package/libx/_runtime/common/generic/temporal.js +3 -3
- package/libx/_runtime/common/toggles/alpha.js +1 -1
- package/libx/_runtime/common/utils/cqn.js +20 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +125 -139
- package/libx/_runtime/common/utils/csn.js +50 -52
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +41 -176
- package/libx/_runtime/common/utils/generateOnCond.js +40 -70
- package/libx/_runtime/common/utils/{enrichWithKeysFromWhere.js → keys.js} +29 -28
- package/libx/_runtime/common/utils/postProcessing.js +3 -0
- package/libx/_runtime/common/utils/propagateForeignKeys.js +84 -0
- package/libx/_runtime/common/utils/resolveStructured.js +1 -1
- package/libx/_runtime/common/utils/resolveView.js +19 -9
- package/libx/_runtime/common/utils/rewriteAsterisks.js +94 -0
- package/libx/_runtime/common/utils/search2cqn4sql.js +9 -8
- package/libx/_runtime/common/utils/template.js +54 -46
- package/libx/_runtime/db/Service.js +9 -2
- package/libx/_runtime/db/expand/expandCQNToJoin.js +10 -24
- package/libx/_runtime/db/expand/rawToExpanded.js +2 -1
- package/libx/_runtime/db/generic/create.js +1 -0
- package/libx/_runtime/db/generic/input.js +7 -11
- package/libx/_runtime/db/generic/integrity.js +2 -2
- package/libx/_runtime/db/generic/rewrite.js +2 -5
- package/libx/_runtime/db/generic/update.js +1 -0
- package/libx/_runtime/db/query/read.js +10 -5
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +6 -0
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +7 -2
- package/libx/_runtime/db/sql-builder/annotations.js +1 -0
- package/libx/_runtime/db/utils/columns.js +14 -43
- package/libx/_runtime/db/utils/deep.js +5 -7
- package/libx/_runtime/fiori/generic/activate.js +3 -2
- package/libx/_runtime/fiori/generic/before.js +2 -2
- package/libx/_runtime/fiori/generic/cancel.js +3 -2
- package/libx/_runtime/fiori/generic/delete.js +3 -2
- package/libx/_runtime/fiori/generic/edit.js +2 -2
- package/libx/_runtime/fiori/generic/new.js +2 -2
- package/libx/_runtime/fiori/generic/patch.js +2 -2
- package/libx/_runtime/fiori/generic/prepare.js +2 -2
- package/libx/_runtime/fiori/generic/read.js +17 -63
- package/libx/_runtime/fiori/generic/readOverDraft.js +4 -4
- package/libx/_runtime/fiori/uiflex/extensibility/index.cds +15 -0
- package/libx/_runtime/fiori/uiflex/extensibility/index.js +148 -0
- package/libx/_runtime/fiori/uiflex/handler/transformREAD.js +119 -0
- package/libx/_runtime/fiori/uiflex/handler/transformRESULT.js +43 -0
- package/libx/_runtime/fiori/uiflex/handler/transformWRITE.js +62 -0
- package/libx/_runtime/fiori/uiflex/index.js +35 -0
- package/libx/_runtime/fiori/uiflex/utils.js +78 -0
- package/libx/_runtime/fiori/utils/handler.js +3 -13
- package/libx/_runtime/fiori/utils/where.js +6 -1
- package/libx/_runtime/hana/Service.js +5 -2
- package/libx/_runtime/hana/execute.js +1 -1
- package/libx/_runtime/hana/pool.js +12 -11
- package/libx/_runtime/hana/search2cqn4sql.js +34 -43
- package/libx/_runtime/hana/searchToContains.js +3 -3
- package/libx/_runtime/index.js +5 -2
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +1 -1
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +16 -3
- package/libx/_runtime/messaging/common-utils/connections.js +11 -14
- package/libx/_runtime/messaging/common-utils/naming-conventions.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -1
- package/libx/_runtime/messaging/enterprise-messaging.js +1 -1
- package/libx/_runtime/messaging/message-queuing.js +18 -0
- package/libx/_runtime/remote/Service.js +14 -2
- package/libx/_runtime/remote/utils/client-types.d.ts +7 -0
- package/libx/_runtime/remote/utils/client.js +117 -23
- package/libx/_runtime/sqlite/Service.js +4 -3
- package/libx/_runtime/sqlite/convertAssocToOneManaged.js +1 -3
- package/libx/_runtime/sqlite/execute.js +1 -1
- package/libx/gql/GraphQLAdapter.js +33 -0
- package/libx/gql/constants/adapter.js +69 -0
- package/libx/gql/constants/cds.js +18 -0
- package/libx/gql/constants/graphql.js +33 -0
- package/libx/gql/resolvers/crud/create.js +15 -0
- package/libx/gql/resolvers/crud/delete.js +24 -0
- package/libx/gql/resolvers/crud/index.js +6 -0
- package/libx/gql/resolvers/crud/read.js +25 -0
- package/libx/gql/resolvers/crud/update.js +31 -0
- package/libx/gql/resolvers/crud/utils/index.js +36 -0
- package/libx/gql/resolvers/field.js +5 -0
- package/libx/gql/resolvers/index.js +7 -0
- package/libx/gql/resolvers/mutation.js +23 -0
- package/libx/gql/resolvers/parse/ast/enrich.js +51 -0
- package/libx/gql/resolvers/parse/ast/fragment.js +11 -0
- package/libx/gql/resolvers/parse/ast/fromObject.js +39 -0
- package/libx/gql/resolvers/parse/ast/index.js +3 -0
- package/libx/gql/resolvers/parse/ast/meta.js +4 -0
- package/libx/gql/resolvers/parse/ast/variable.js +7 -0
- package/libx/gql/resolvers/parse/ast2cqn/columns.js +42 -0
- package/libx/gql/resolvers/parse/ast2cqn/entries.js +31 -0
- package/libx/gql/resolvers/parse/ast2cqn/index.js +8 -0
- package/libx/gql/resolvers/parse/ast2cqn/limit.js +6 -0
- package/libx/gql/resolvers/parse/ast2cqn/orderBy.js +24 -0
- package/libx/gql/resolvers/parse/ast2cqn/utils/index.js +3 -0
- package/libx/gql/resolvers/parse/ast2cqn/where.js +70 -0
- package/libx/gql/resolvers/parse/utils/index.js +8 -0
- package/libx/gql/resolvers/query.js +13 -0
- package/libx/gql/resolvers/root.js +34 -0
- package/libx/gql/schema/generate.js +18 -0
- package/libx/gql/schema/index.js +5 -0
- package/libx/gql/schema/mutation.js +76 -0
- package/libx/gql/schema/query.js +108 -0
- package/libx/gql/schema/typeDefMap.js +45 -0
- package/libx/gql/schema/utils/index.js +54 -0
- package/libx/gql/utils/index.js +12 -0
- package/libx/{_runtime/odata/cqn2odata.js → odata/cqn2odata/index.js} +39 -100
- package/libx/odata/index.js +80 -0
- package/libx/odata/odata2cqn/afterburner.js +170 -0
- package/libx/{_runtime/odata/odata2cqn.pegjs → odata/odata2cqn/grammar.pegjs} +102 -123
- package/libx/odata/odata2cqn/index.js +3 -0
- package/libx/odata/odata2cqn/parser.js +1 -0
- package/libx/odata/utils/index.js +64 -0
- package/libx/rest/RestAdapter.js +101 -0
- package/libx/rest/RestRequest.js +30 -0
- package/libx/rest/index.js +3 -0
- package/libx/rest/middleware/auth.js +22 -0
- package/libx/rest/middleware/content.js +15 -0
- package/libx/rest/middleware/create.js +40 -0
- package/libx/rest/middleware/delete.js +20 -0
- package/libx/rest/middleware/error.js +56 -0
- package/libx/rest/middleware/operation.js +39 -0
- package/libx/rest/middleware/parse.js +90 -0
- package/libx/rest/middleware/read.js +29 -0
- package/libx/rest/middleware/update.js +42 -0
- package/libx/rest/utils/data.js +65 -0
- package/package.json +4 -1
- package/server.js +42 -29
- package/lib/req/cls.js +0 -39
- package/libx/_runtime/cds-services/services/utils/diff.js +0 -53
- package/libx/_runtime/cds-services/util/auditlog.js +0 -247
- package/libx/_runtime/cds-services/util/xsenv.js +0 -51
- package/libx/_runtime/common/utils/backlinks.js +0 -83
- package/libx/_runtime/common/utils/rewriteAsterisk.js +0 -72
- package/libx/_runtime/odata/index.js +0 -55
- package/libx/_runtime/odata/odata2cqn.js +0 -1
- package/libx/_runtime/odata/readToCqn.js +0 -129
- package/libx/_runtime/remote/cqn2odata/index.js +0 -2
package/lib/req/context.js
CHANGED
|
@@ -12,41 +12,20 @@ class EventContext {
|
|
|
12
12
|
|
|
13
13
|
toString() { return `${this.event} ${this.path}` }
|
|
14
14
|
|
|
15
|
-
static new (data) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// REVISIT: Do we have to propagate hidden subdomain as well?
|
|
21
|
-
// let subdomain = _ && _.req && _.req && _.req.authInfo && _.req.authInfo.getSubdomain()
|
|
22
|
-
// if (subdomain) {
|
|
23
|
-
// if (!data._) data._ = {}
|
|
24
|
-
// if (!data._.req) data._.req = {}
|
|
25
|
-
// if (!data._.req.authInfo) data._.req.authInfo = { setSubdomain(v){ this.subdomain = v } }
|
|
26
|
-
// if (!data._.req.authInfo.getSubdomain()) data._.req.authInfo.setSubdomain (subdomain)
|
|
27
|
-
// }
|
|
28
|
-
} else if (data && 'user' in data) {
|
|
29
|
-
data = { user:1, ...data } // IMPORTANT: ensure user is first to be assigned to call setter
|
|
15
|
+
static new (data={}, base = cds.context) {
|
|
16
|
+
if (data && base && features.cds_tx_inheritance) {
|
|
17
|
+
let u = _inherit('user', u => typeof u === 'object' ? Object.create(u) : u)
|
|
18
|
+
if (!u || !u.tenant) _inherit('tenant')
|
|
19
|
+
if (!u || !u.locale) _inherit('locale')
|
|
30
20
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const fn = async ()=> {
|
|
36
|
-
// create a new transaction for each run of the background job
|
|
37
|
-
// which inherits from the current event context by default
|
|
38
|
-
const tx = cds.context = cds.tx({...o})
|
|
39
|
-
try {
|
|
40
|
-
await background_job(tx)
|
|
41
|
-
await tx.commit()
|
|
42
|
-
} catch(e) {
|
|
43
|
-
console.trace (`ERROR occured in background job:`, e) // eslint-disable-line no-console
|
|
44
|
-
await tx.rollback()
|
|
45
|
-
}
|
|
21
|
+
function _inherit (p,fn) {
|
|
22
|
+
if (p in data) return data[p]
|
|
23
|
+
let pd = Reflect.getOwnPropertyDescriptor(base,p); if (!pd) return
|
|
24
|
+
return data[p] = fn ? fn(pd.value) : pd.value
|
|
46
25
|
}
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
|
|
26
|
+
// IMPORTANT: ensure user is first to be assigned to call setter_
|
|
27
|
+
if (data.user) data = { user:1, ...data }
|
|
28
|
+
return new this (data)
|
|
50
29
|
}
|
|
51
30
|
|
|
52
31
|
constructor(_={}) {
|
|
@@ -107,7 +86,7 @@ class EventContext {
|
|
|
107
86
|
if (t) super.tenant = this.user.tenant = t
|
|
108
87
|
}
|
|
109
88
|
get tenant() {
|
|
110
|
-
return this.tenant = this.
|
|
89
|
+
return this.tenant = this.user.tenant || this._propagated('tenant')
|
|
111
90
|
}
|
|
112
91
|
|
|
113
92
|
set user(u) {
|
|
@@ -124,7 +103,7 @@ class EventContext {
|
|
|
124
103
|
if (l) super.locale = this.user.locale = l
|
|
125
104
|
}
|
|
126
105
|
get locale() {
|
|
127
|
-
return this.locale = this.
|
|
106
|
+
return this.locale = this.user.locale || this._propagated('locale')
|
|
128
107
|
}
|
|
129
108
|
|
|
130
109
|
get timestamp() {
|
|
@@ -177,7 +156,78 @@ class EventContext {
|
|
|
177
156
|
}
|
|
178
157
|
}
|
|
179
158
|
|
|
180
|
-
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Continuation Local Storage for cds.context
|
|
162
|
+
*/
|
|
163
|
+
const cls = (cds,v) => {
|
|
164
|
+
|
|
165
|
+
if (cds.env.features.cls) {
|
|
166
|
+
|
|
167
|
+
const { executionAsyncResource:current, createHook } = module.require ('async_hooks')
|
|
168
|
+
const _context = Symbol('cds.context')
|
|
169
|
+
|
|
170
|
+
createHook ({ init(id,t,tid, res) {
|
|
171
|
+
const cr = current(); if (!cr) return
|
|
172
|
+
const ctx = cr[_context]
|
|
173
|
+
if (ctx) res[_context] = ctx
|
|
174
|
+
}}).enable()
|
|
175
|
+
|
|
176
|
+
Reflect.defineProperty (cds,'context',{ enumerable:1,
|
|
177
|
+
set(v) {
|
|
178
|
+
const cr = current(); if (!cr) return
|
|
179
|
+
const ctx = typeof v !== 'object' ? v : v.context || (v instanceof EventContext ? v : EventContext.new(v,false))
|
|
180
|
+
cr[_context] = ctx
|
|
181
|
+
},
|
|
182
|
+
get() {
|
|
183
|
+
const cr = current(); if (!cr) return undefined
|
|
184
|
+
return cr[_context]
|
|
185
|
+
},
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
} else {
|
|
189
|
+
|
|
190
|
+
Reflect.defineProperty (cds,'context',{ enumerable:1,
|
|
191
|
+
get:()=> undefined,
|
|
192
|
+
set:()=> {},
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return v ? cds.context = v : cds.context
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
function cds_spawn (o,fn) {
|
|
202
|
+
if (typeof o === 'function') [ fn, o ] = [ o, fn ]
|
|
203
|
+
if (!o) o = {}
|
|
204
|
+
const em = new EventEmitter()
|
|
205
|
+
const fx = async ()=> {
|
|
206
|
+
// create a new transaction for each run of the background job
|
|
207
|
+
// which inherits from the current event context by default
|
|
208
|
+
const tx = cds.context = cds.tx({...o})
|
|
209
|
+
try {
|
|
210
|
+
const res = await fn(tx)
|
|
211
|
+
await tx.commit()
|
|
212
|
+
for (const handler of em.listeners('succeeded')) await handler(res)
|
|
213
|
+
for (const handler of em.listeners('done')) await handler()
|
|
214
|
+
} catch(e) {
|
|
215
|
+
console.trace (`ERROR occured in background job:`, e) // eslint-disable-line no-console
|
|
216
|
+
await tx.rollback()
|
|
217
|
+
for (const handler of em.listeners('failed')) await handler(e)
|
|
218
|
+
for (const handler of em.listeners('done')) await handler()
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const timer = (
|
|
222
|
+
(o && o.after) ? setTimeout (fx, o.after) :
|
|
223
|
+
(o && o.every) ? setInterval (fx, o.every) :
|
|
224
|
+
setImmediate (fx) )
|
|
225
|
+
return Object.assign(em, { timer })
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
|
|
181
229
|
module.exports = Object.assign (EventContext,{
|
|
182
|
-
/** @type {(cds,v)=>EventContext} */ for:
|
|
230
|
+
/** @type {(cds,v)=>EventContext} */ for: cls,
|
|
231
|
+
spawn: cds_spawn,
|
|
232
|
+
propagateHeaders: [ 'x-correlation-id' ]
|
|
183
233
|
})
|
|
File without changes
|
package/lib/serve/Service-api.js
CHANGED
|
@@ -77,7 +77,7 @@ class Service extends require('./Service-handlers') {
|
|
|
77
77
|
get namespace() {
|
|
78
78
|
return super.namespace = this.definition && this.definition.name
|
|
79
79
|
|| this.model && this.model.namespace
|
|
80
|
-
|| this
|
|
80
|
+
|| !(this instanceof cds.DatabaseService) && !/\W/.test(this.name) && this.name || undefined
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
get operations() { return super.operations = _reflect (this, d => d.kind === 'action' || d.kind === 'function') }
|
|
@@ -4,7 +4,7 @@ const cds = require ('../index')
|
|
|
4
4
|
* The default implementation of the `srv.dispatch(req)` ensures everything
|
|
5
5
|
* is prepared before calling `srv.handle(req)`
|
|
6
6
|
* @typedef {import('./Service-api')} Service
|
|
7
|
-
* @typedef {import('../req/
|
|
7
|
+
* @typedef {import('../req/request')} Request
|
|
8
8
|
* @this {Service}
|
|
9
9
|
* @param {Request} req
|
|
10
10
|
* @returns {Promise} resolving to the outcome/return value of last .on handler
|
|
@@ -32,7 +32,17 @@ exports.dispatch = async function dispatch (req) { //NOSONAR
|
|
|
32
32
|
|
|
33
33
|
// Ensure target and fqns
|
|
34
34
|
if (!req.target) _ensure_target (this,req)
|
|
35
|
-
if (
|
|
35
|
+
if (typeof req.query === 'object') {
|
|
36
|
+
if (req.query._target !== req.target) Object.defineProperty (req.query,'_target',{ value:req.target, configurable:true, writable:true })
|
|
37
|
+
if (!req.query._srv) Object.defineProperty (req.query,'_srv',{ value:this, configurable:true, writable:true })
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// REVISIT: Ensure req._.req and req._.res in case of srv.run(query)?!
|
|
41
|
+
/*
|
|
42
|
+
if (this instanceof cds.ApplicationService && !req._.req) {
|
|
43
|
+
// TODO: add req and res to req._ from tx
|
|
44
|
+
}
|
|
45
|
+
*/
|
|
36
46
|
|
|
37
47
|
return this.handle(req)
|
|
38
48
|
}
|
|
@@ -3,7 +3,7 @@ const cds = require('..'), {expected} = cds.error
|
|
|
3
3
|
class EventHandlers {
|
|
4
4
|
|
|
5
5
|
constructor (name) {
|
|
6
|
-
this._handlers = {
|
|
6
|
+
this._handlers = { _initial:[], before:[], on:[], after:[], _error:[] }
|
|
7
7
|
this.name = name
|
|
8
8
|
}
|
|
9
9
|
|
|
@@ -77,17 +77,31 @@ const _register = function (srv, phase, event, path, handler) { //NOSONAR
|
|
|
77
77
|
|
|
78
78
|
// Finally register with a filter function to match requests to be handled
|
|
79
79
|
const _handlers = srv._handlers [event === 'error' ? '_error' : (handler._initial ? '_initial' : phase)] // REVISIT: remove _initial handlers
|
|
80
|
-
_handlers.push (
|
|
81
|
-
event && path ? (req) => (event === req.event) && (path === req.path || path === req.entity) :
|
|
82
|
-
event ? (req) => (event === req.event) :
|
|
83
|
-
path ? (req) => (path === req.path || path === req.entity) :
|
|
84
|
-
() => true
|
|
85
|
-
)})
|
|
80
|
+
_handlers.push (new EventHandler (phase, event, path, handler))
|
|
86
81
|
|
|
87
82
|
if (phase === 'on') cds.emit('subscribe',srv,event) //> inform messaging service
|
|
88
83
|
return srv
|
|
89
84
|
}
|
|
90
85
|
|
|
86
|
+
|
|
87
|
+
class EventHandler {
|
|
88
|
+
constructor (phase, event, path, handler) {
|
|
89
|
+
this[phase] = event || '*'
|
|
90
|
+
if (path) this.path = path
|
|
91
|
+
this.handler = handler
|
|
92
|
+
Object.defineProperties (this, { // non-enumerable properties to improve debugging
|
|
93
|
+
_initial: { value: handler._initial },
|
|
94
|
+
for: { value:
|
|
95
|
+
event && path ? (req) => (event === req.event) && (path === req.path || path === req.entity) :
|
|
96
|
+
event ? (req) => (event === req.event) :
|
|
97
|
+
path ? (req) => (path === req.path || path === req.entity) :
|
|
98
|
+
/* else: */ () => true
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
91
105
|
const is_impl = x => typeof x === 'function' && !(x.prototype && /^class\b/.test(x))
|
|
92
106
|
const is_array = Array.isArray
|
|
93
107
|
const AlternativeEvents = {
|
|
@@ -47,7 +47,7 @@ const add_handler_for = (srv, def) => {
|
|
|
47
47
|
|
|
48
48
|
// object variant of params, ensure at least one param is present in args[0]
|
|
49
49
|
// REVISIT: still not bullet proof, but parameters might be optional
|
|
50
|
-
if (typeof args[0] === 'object' && args.length === 1 && Object.keys(def.params).some(p => p in args[0])) {
|
|
50
|
+
if ( args[0] !== null && typeof args[0] === 'object' && args.length === 1 && Object.keys(def.params).some(p => p in args[0])) {
|
|
51
51
|
const params = args.shift()
|
|
52
52
|
for (const p in def.params) {
|
|
53
53
|
data[p] = params[p]
|
package/lib/serve/Transaction.js
CHANGED
|
@@ -6,12 +6,18 @@ const _context = Symbol()
|
|
|
6
6
|
* a new Transaction as a derivate of the `srv` (i.e. {__proto__:srv})
|
|
7
7
|
* @returns { Transaction & import('./Service-api') }
|
|
8
8
|
*/
|
|
9
|
-
module.exports = function tx (req,
|
|
9
|
+
module.exports = function tx (req,fn) { const srv = this
|
|
10
10
|
if (srv.context) return srv
|
|
11
11
|
if (!req) {
|
|
12
12
|
// called as srv.tx() -> new root transaction
|
|
13
13
|
return RootTransaction.for (srv, Context.new())
|
|
14
14
|
}
|
|
15
|
+
if (typeof req === 'function') [ req, fn ] = [ Context.new(), req ]
|
|
16
|
+
if (typeof fn === 'function') {
|
|
17
|
+
// auto-committed transaction, i.e. cds.tx (tx => {...})
|
|
18
|
+
const tx = srv.tx(req)
|
|
19
|
+
return Promise.resolve(tx).then(fn) .then (tx.commit,tx.rollback)
|
|
20
|
+
}
|
|
15
21
|
if (req instanceof Context) {
|
|
16
22
|
// called for a nested req -> nested tx
|
|
17
23
|
if (req.context !== req) return NestedTransaction.for (srv, req.context)
|
|
@@ -20,11 +26,6 @@ module.exports = function tx (req,o) { const srv = this
|
|
|
20
26
|
// called for a top-level req -> root tx
|
|
21
27
|
else return RootTransaction.for (srv, req)
|
|
22
28
|
}
|
|
23
|
-
if (typeof req === 'function') {
|
|
24
|
-
// auto-committed transaction, i.e. cds.tx (tx => {...})
|
|
25
|
-
const tx = srv.tx(o)
|
|
26
|
-
return Promise.resolve().then (()=> req(tx)) .then (tx.commit,tx.rollback)
|
|
27
|
-
}
|
|
28
29
|
if (req[_context]) {
|
|
29
30
|
// called again for an arbitrary context object -> see below
|
|
30
31
|
return NestedTransaction.for (srv, req[_context])
|
package/lib/serve/index.js
CHANGED
package/lib/utils/axios.js
CHANGED
|
@@ -36,6 +36,13 @@ const _args = (args) => {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
const _error = (e) => {
|
|
39
|
+
if (e.code === 'ECONNREFUSED' && e.port === 80 /*unchanged default port*/) {
|
|
40
|
+
// retain original error properties (code,...)
|
|
41
|
+
e = Object.assign(new Error(e.message +
|
|
42
|
+
'\nIt seems that the server was not started. Make sure to call \'cds.test(...)\' or \'cds.test.run(...)\'.'),e)
|
|
43
|
+
e.stack = null // stack is just clutter here
|
|
44
|
+
throw e
|
|
45
|
+
}
|
|
39
46
|
if (!e.response) throw e
|
|
40
47
|
if (!e.response.data) throw e
|
|
41
48
|
if (!e.response.data.error) throw new Error(e.message + '\n\n' + e.response.data)
|
package/lib/utils/data.js
CHANGED
|
@@ -6,7 +6,7 @@ class DataUtil {
|
|
|
6
6
|
if (!db) db = await cds.connect.to('db')
|
|
7
7
|
if (!this._deletes) {
|
|
8
8
|
this._deletes = []
|
|
9
|
-
for (const entity of db.model.
|
|
9
|
+
for (const entity of db.model.each('entity')) {
|
|
10
10
|
if (!entity.query && entity['@cds.persistence.skip'] !== true) {
|
|
11
11
|
this._deletes.push(cds.ql.DELETE.from(entity))
|
|
12
12
|
}
|
|
@@ -3,6 +3,13 @@ const LOG = cds.log('audit-log')
|
|
|
3
3
|
|
|
4
4
|
const v2utils = require('./utils/v2')
|
|
5
5
|
|
|
6
|
+
const ANONYMOUS = 'anonymous'
|
|
7
|
+
|
|
8
|
+
const _getTenantAndUser = () => ({
|
|
9
|
+
user: (cds.context && cds.context.user && cds.context.user.id) || ANONYMOUS,
|
|
10
|
+
tenant: (cds.context && cds.context.tenant) || ANONYMOUS
|
|
11
|
+
})
|
|
12
|
+
|
|
6
13
|
module.exports = class AuditLogService extends cds.MessagingService {
|
|
7
14
|
async init() {
|
|
8
15
|
// call MessagingService's init, which handles outboxing
|
|
@@ -13,7 +20,8 @@ module.exports = class AuditLogService extends cds.MessagingService {
|
|
|
13
20
|
this.ready = !!this.alc
|
|
14
21
|
}
|
|
15
22
|
|
|
16
|
-
async emit(
|
|
23
|
+
async emit(first, second) {
|
|
24
|
+
const { event, data } = typeof first === 'object' ? first : { event: first, data: second }
|
|
17
25
|
if (!this.options.outbox) return this.send(event, data)
|
|
18
26
|
|
|
19
27
|
if (this.ready && this[event]) {
|
|
@@ -33,10 +41,7 @@ module.exports = class AuditLogService extends cds.MessagingService {
|
|
|
33
41
|
async dataAccessLog({ accesses }) {
|
|
34
42
|
if (!this.ready) throw new Error('AuditLogService not connected')
|
|
35
43
|
|
|
36
|
-
const {
|
|
37
|
-
tenant,
|
|
38
|
-
user: { id: user }
|
|
39
|
-
} = cds.context
|
|
44
|
+
const { tenant, user } = _getTenantAndUser()
|
|
40
45
|
|
|
41
46
|
// build the logs
|
|
42
47
|
const { entries, errors } = v2utils.buildDataAccessLogs(this.alc, accesses, tenant, user)
|
|
@@ -59,10 +64,7 @@ module.exports = class AuditLogService extends cds.MessagingService {
|
|
|
59
64
|
async dataModificationLog({ modifications }) {
|
|
60
65
|
if (!this.ready) throw new Error('AuditLogService not connected')
|
|
61
66
|
|
|
62
|
-
const {
|
|
63
|
-
tenant,
|
|
64
|
-
user: { id: user }
|
|
65
|
-
} = cds.context
|
|
67
|
+
const { tenant, user } = _getTenantAndUser()
|
|
66
68
|
|
|
67
69
|
// build the logs
|
|
68
70
|
const { entries, errors } = v2utils.buildDataModificationLogs(this.alc, modifications, tenant, user)
|
|
@@ -87,8 +89,9 @@ module.exports = class AuditLogService extends cds.MessagingService {
|
|
|
87
89
|
// cds.context not always set on auth-related errors -> try to extract from data
|
|
88
90
|
let user, tenant
|
|
89
91
|
if (cds.context) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
const tenantAndUser = _getTenantAndUser()
|
|
93
|
+
tenant = tenantAndUser.tenant
|
|
94
|
+
user = tenantAndUser.user
|
|
92
95
|
} else {
|
|
93
96
|
try {
|
|
94
97
|
const parsed = JSON.parse(data)
|
|
@@ -101,10 +104,10 @@ module.exports = class AuditLogService extends cds.MessagingService {
|
|
|
101
104
|
delete parsed.user
|
|
102
105
|
}
|
|
103
106
|
data = JSON.stringify(parsed)
|
|
104
|
-
} catch (e) {
|
|
105
|
-
// ignore
|
|
106
|
-
}
|
|
107
|
+
} catch (e) {}
|
|
107
108
|
}
|
|
109
|
+
if (!tenant) tenant = ANONYMOUS
|
|
110
|
+
if (!user) user = ANONYMOUS
|
|
108
111
|
|
|
109
112
|
// build the log
|
|
110
113
|
const entry = v2utils.buildSecurityLog(this.alc, action, data, tenant, user)
|
|
@@ -117,10 +120,7 @@ module.exports = class AuditLogService extends cds.MessagingService {
|
|
|
117
120
|
async configChangeLog({ action, success, configurations }) {
|
|
118
121
|
if (!this.ready) throw new Error('AuditLogService not connected')
|
|
119
122
|
|
|
120
|
-
const {
|
|
121
|
-
tenant,
|
|
122
|
-
user: { id: user }
|
|
123
|
-
} = cds.context
|
|
123
|
+
const { tenant, user } = _getTenantAndUser()
|
|
124
124
|
|
|
125
125
|
// build the logs
|
|
126
126
|
const { entries, errors } = v2utils.buildConfigChangeLogs(this.alc, configurations, tenant, user)
|
|
@@ -40,7 +40,7 @@ const _processorFnAccess = (accessLogs, model, req) => {
|
|
|
40
40
|
element.parent['@PersonalData.EntitySemantics'] === 'DataSubjectDetails' &&
|
|
41
41
|
accessLog.dataSubject.id.length === 0 // > id still an array -> promise not yet set
|
|
42
42
|
) {
|
|
43
|
-
addDataSubjectForDetailsEntity(row, accessLog, req, entity, model)
|
|
43
|
+
addDataSubjectForDetailsEntity(row, accessLog, req, entity, model, element)
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -41,7 +41,8 @@ const _getOldAndNew = (action, row, key) => {
|
|
|
41
41
|
const _addAttribute = (log, action, row, key) => {
|
|
42
42
|
if (!log.attributes.find(ele => ele.name === key)) {
|
|
43
43
|
const { oldValue, newValue } = _getOldAndNew(action, row, key)
|
|
44
|
-
if (oldValue !== newValue)
|
|
44
|
+
if (oldValue !== newValue)
|
|
45
|
+
log.attributes.push({ name: key, oldValue: String(oldValue), newValue: String(newValue) })
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
48
|
|
|
@@ -75,7 +76,7 @@ const _processorFnModification = (modificationLogs, model, req, beforeWrite) =>
|
|
|
75
76
|
element.parent['@PersonalData.EntitySemantics'] === 'DataSubjectDetails' &&
|
|
76
77
|
modificationLog.dataSubject.id.length === 0 // > id still an array -> promise not yet set
|
|
77
78
|
) {
|
|
78
|
-
addDataSubjectForDetailsEntity(row, modificationLog, req, entity, model)
|
|
79
|
+
addDataSubjectForDetailsEntity(row, modificationLog, req, entity, model, element)
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
82
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const cds = require('../../../cds')
|
|
2
2
|
|
|
3
3
|
const { getDataSubject } = require('../../../common/utils/csn')
|
|
4
|
-
const { getBackLinks, isSelfManaged } = require('../../../common/utils/backlinks')
|
|
5
4
|
|
|
6
5
|
const WRITE = { CREATE: 1, UPDATE: 1, DELETE: 1 }
|
|
7
6
|
const ASPECTS = { CREATE: '_auditCreate', READ: '_auditRead', UPDATE: '_auditUpdate', DELETE: '_auditDelete' }
|
|
@@ -23,8 +22,11 @@ const getPick = event => {
|
|
|
23
22
|
const categories = []
|
|
24
23
|
if (!element.isAssociation && element.key) categories.push('ObjectID')
|
|
25
24
|
if (
|
|
26
|
-
element
|
|
27
|
-
element
|
|
25
|
+
element['@PersonalData.FieldSemantics'] === 'DataSubjectID' &&
|
|
26
|
+
// item element of arrayed element has no parent, but
|
|
27
|
+
// at the moment annotation on item level is not supported
|
|
28
|
+
element.parent &&
|
|
29
|
+
element.parent['@PersonalData.EntitySemantics'] === 'DataSubject'
|
|
28
30
|
)
|
|
29
31
|
categories.push('DataSubjectID')
|
|
30
32
|
if (event in WRITE && element['@PersonalData.IsPotentiallyPersonal']) categories.push('IsPotentiallyPersonal')
|
|
@@ -55,14 +57,15 @@ const createLogEntry = (logs, entity, row) => {
|
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
const addObjectID = (log, row, key) => {
|
|
58
|
-
if (!log.dataObject.id.find(ele => ele.keyName === key))
|
|
60
|
+
if (!log.dataObject.id.find(ele => ele.keyName === key))
|
|
61
|
+
log.dataObject.id.push({ keyName: key, value: String(row[key]) })
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
const addDataSubject = (log, row, key, entity) => {
|
|
62
65
|
if (!log.dataSubject.type) log.dataSubject.type = entity.name
|
|
63
66
|
if (!log.dataSubject.id.find(ele => ele.key === key)) {
|
|
64
67
|
const value = row[key] || (row._old && row._old[key])
|
|
65
|
-
log.dataSubject.id.push({ keyName: key, value })
|
|
68
|
+
log.dataSubject.id.push({ keyName: key, value: String(value) })
|
|
66
69
|
}
|
|
67
70
|
}
|
|
68
71
|
|
|
@@ -77,82 +80,39 @@ const _addKeysToWhere = (child, row) => {
|
|
|
77
80
|
return keysWithValue
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
const backlinks = getBackLinks(assoc)
|
|
83
|
-
let parentName, childName
|
|
84
|
-
if (assoc.isComposition && assoc.is2one && !assoc.on) {
|
|
85
|
-
// look for foreign keys in parent
|
|
86
|
-
parentName = parent.name
|
|
87
|
-
childName = child.name
|
|
88
|
-
} else if (assoc.is2many && isSelfManaged(assoc)) {
|
|
89
|
-
// look for foreign keys in child
|
|
90
|
-
parentName = child.name
|
|
91
|
-
childName = parent.name
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
backlinks.forEach(el => {
|
|
95
|
-
if (foreignKeys.length > 0) foreignKeys.push('and')
|
|
96
|
-
foreignKeys.push({ ref: [parentName, el.entityKey] }, '=', {
|
|
97
|
-
ref: [childName, el.targetKey]
|
|
98
|
-
})
|
|
99
|
-
})
|
|
100
|
-
return foreignKeys
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const _buildWhere = (child, dataSubjectInfo, row) => {
|
|
104
|
-
const where = []
|
|
105
|
-
where.push(
|
|
106
|
-
..._addForeignKeysToWhere(dataSubjectInfo.entity, child, dataSubjectInfo.assoc),
|
|
107
|
-
'and',
|
|
108
|
-
..._addKeysToWhere(child, row)
|
|
109
|
-
)
|
|
110
|
-
return where
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const _buildSubSelect = (child, dataSubjectInfo, row) => {
|
|
114
|
-
let previousCqn
|
|
83
|
+
const _buildSubSelect = (model, child, { element, up }, row, previousCqn) => {
|
|
84
|
+
const entity = element.parent
|
|
115
85
|
const childCqn = SELECT.from(child.name)
|
|
116
86
|
.columns(Object.keys(child.keys))
|
|
117
|
-
.where(
|
|
118
|
-
if (
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const nextEl = dataSubjectInfo.previous[i + 1]
|
|
123
|
-
const args = nextEl
|
|
124
|
-
? [nextEl.entity, el.entity, nextEl.assoc]
|
|
125
|
-
: [dataSubjectInfo.entity, el.entity, dataSubjectInfo.assoc]
|
|
126
|
-
|
|
127
|
-
currentCQN = SELECT.from(el.entity.name)
|
|
128
|
-
.columns(Object.keys(el.entity.keys))
|
|
129
|
-
.where([..._addForeignKeysToWhere(...args), 'and', 'exists', previousCqn || childCqn])
|
|
130
|
-
|
|
131
|
-
previousCqn = currentCQN
|
|
132
|
-
})
|
|
87
|
+
.where(entity._relations[element.name].join(child.name, entity.name))
|
|
88
|
+
if (previousCqn) {
|
|
89
|
+
childCqn.where('exists', previousCqn)
|
|
90
|
+
} else {
|
|
91
|
+
childCqn.where(_addKeysToWhere(child, row))
|
|
133
92
|
}
|
|
134
|
-
return
|
|
93
|
+
if (up) return _buildSubSelect(model, entity, up, {}, childCqn)
|
|
94
|
+
return childCqn
|
|
135
95
|
}
|
|
136
96
|
|
|
137
|
-
const _getDataSubjectIdPromise = (child, dataSubjectInfo, row, req) => {
|
|
97
|
+
const _getDataSubjectIdPromise = (child, dataSubjectInfo, row, req, model) => {
|
|
138
98
|
const root = dataSubjectInfo.entity
|
|
139
99
|
const cqn = SELECT.from(root.name)
|
|
140
100
|
.columns(Object.keys(root.keys))
|
|
141
|
-
.where(['exists', _buildSubSelect(child, dataSubjectInfo, row)])
|
|
101
|
+
.where(['exists', _buildSubSelect(model, child, dataSubjectInfo.up, row)])
|
|
142
102
|
return cds
|
|
143
103
|
.tx(req)
|
|
144
104
|
.run(cqn)
|
|
145
105
|
.then(res => {
|
|
146
106
|
const id = []
|
|
147
|
-
for (const k in res[0]) id.push({ keyName: k, value: res[0][k] })
|
|
107
|
+
for (const k in res[0]) id.push({ keyName: k, value: String(res[0][k]) })
|
|
148
108
|
return id
|
|
149
109
|
})
|
|
150
110
|
}
|
|
151
111
|
|
|
152
|
-
const addDataSubjectForDetailsEntity = (row, log, req, entity, model) => {
|
|
112
|
+
const addDataSubjectForDetailsEntity = (row, log, req, entity, model, element) => {
|
|
153
113
|
const role = entity['@PersonalData.DataSubjectRole']
|
|
154
114
|
|
|
155
|
-
const dataSubjectInfo = getDataSubject(entity, model, role)
|
|
115
|
+
const dataSubjectInfo = getDataSubject(entity, model, role, element)
|
|
156
116
|
|
|
157
117
|
log.dataSubject.type = dataSubjectInfo.entity.name
|
|
158
118
|
|
|
@@ -165,7 +125,7 @@ const addDataSubjectForDetailsEntity = (row, log, req, entity, model) => {
|
|
|
165
125
|
if (!req.context._audit.dataSubjects.has(mapKey)) req.context._audit.dataSubjects.set(mapKey, new Map())
|
|
166
126
|
const map = req.context._audit.dataSubjects.get(mapKey)
|
|
167
127
|
if (map.has(role)) log.dataSubject.id = map.get(role)
|
|
168
|
-
else map.set(role, _getDataSubjectIdPromise(entity, dataSubjectInfo, row, req))
|
|
128
|
+
else map.set(role, _getDataSubjectIdPromise(entity, dataSubjectInfo, row, req, model))
|
|
169
129
|
}
|
|
170
130
|
|
|
171
131
|
const resolveDataSubjectPromises = async logs => {
|
|
@@ -3,6 +3,8 @@ const LOG = cds.log('odata')
|
|
|
3
3
|
|
|
4
4
|
const OData = require('./OData')
|
|
5
5
|
|
|
6
|
+
const { alias2ref } = require('../../../common/utils/csn')
|
|
7
|
+
|
|
6
8
|
function _createNewService(name, csn, defaultOptions) {
|
|
7
9
|
const reflectedModel = cds.linked(cds.compile.for.odata(csn))
|
|
8
10
|
const options = Object.assign({}, defaultOptions, { reflectedModel })
|
|
@@ -13,6 +15,8 @@ function _createNewService(name, csn, defaultOptions) {
|
|
|
13
15
|
service._isExtended = true
|
|
14
16
|
|
|
15
17
|
const edm = cds.compile.to.edm(csn, { service: name })
|
|
18
|
+
alias2ref(service, edm)
|
|
19
|
+
|
|
16
20
|
const odataService = new OData(edm, csn, options)
|
|
17
21
|
odataService.addCDSServiceToChannel(service)
|
|
18
22
|
|
|
@@ -84,6 +88,7 @@ class Dispatcher {
|
|
|
84
88
|
* @returns {Promise}
|
|
85
89
|
*/
|
|
86
90
|
async dispatch(req, res) {
|
|
91
|
+
// here, req is express' req -> req.tenant not available
|
|
87
92
|
if (cds._mtxEnabled && req.user && req.user.tenant) {
|
|
88
93
|
// enable mtx, if not done yet
|
|
89
94
|
if (!this._extMap) {
|
|
@@ -95,6 +100,7 @@ class Dispatcher {
|
|
|
95
100
|
|
|
96
101
|
const { alpha_toggles: alphaToggles } = cds.env.features
|
|
97
102
|
|
|
103
|
+
// here, req is express' req -> req.tenant not available
|
|
98
104
|
const hash = alphaToggles ? cds.mtx._getHash(req) : req.user.tenant
|
|
99
105
|
|
|
100
106
|
// add hash to map, if not done yet
|