@sap/cds 6.0.4 → 6.1.2
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 +180 -18
- package/apis/cds.d.ts +11 -7
- package/apis/log.d.ts +124 -0
- package/apis/ql.d.ts +72 -15
- package/apis/services.d.ts +13 -2
- package/bin/build/buildTaskHandler.js +5 -2
- package/bin/build/constants.js +4 -1
- package/bin/build/provider/buildTaskHandlerEdmx.js +11 -39
- package/bin/build/provider/buildTaskHandlerFeatureToggles.js +14 -34
- package/bin/build/provider/buildTaskHandlerInternal.js +56 -4
- package/bin/build/provider/buildTaskProviderInternal.js +22 -14
- package/bin/build/provider/hana/index.js +12 -9
- package/bin/build/provider/java/index.js +18 -8
- package/bin/build/provider/mtx/index.js +7 -4
- package/bin/build/provider/mtx/resourcesTarBuilder.js +60 -35
- package/bin/build/provider/mtx-extension/index.js +57 -0
- package/bin/build/provider/mtx-sidecar/index.js +46 -18
- package/bin/build/provider/nodejs/index.js +34 -13
- package/bin/deploy/to-hana/cfUtil.js +7 -2
- package/bin/deploy/to-hana/hana.js +20 -25
- package/bin/deploy/to-hana/hdiDeployUtil.js +13 -2
- package/bin/serve.js +7 -4
- package/lib/compile/{index.js → cds-compile.js} +0 -0
- package/lib/compile/extend.js +15 -5
- package/lib/compile/minify.js +1 -15
- package/lib/compile/parse.js +1 -1
- package/lib/compile/resolve.js +2 -2
- package/lib/compile/to/srvinfo.js +6 -4
- package/lib/{deploy.js → dbs/cds-deploy.js} +9 -8
- package/lib/env/{index.js → cds-env.js} +1 -17
- package/lib/env/{requires.js → cds-requires.js} +24 -3
- package/lib/env/defaults.js +7 -1
- package/lib/env/schemas/cds-package.json +11 -0
- package/lib/env/schemas/cds-rc.json +614 -0
- package/lib/index.js +19 -16
- package/lib/log/{errors.js → cds-error.js} +1 -1
- package/lib/log/{index.js → cds-log.js} +0 -0
- package/lib/log/format/kibana.js +19 -1
- package/lib/ql/Query.js +9 -3
- package/lib/ql/SELECT.js +2 -2
- package/lib/ql/UPDATE.js +2 -2
- package/lib/ql/{index.js → cds-ql.js} +4 -10
- package/lib/req/context.js +49 -17
- package/lib/req/locale.js +5 -1
- package/lib/{serve → srv}/adapters.js +23 -19
- package/lib/{connect → srv}/bindings.js +0 -0
- package/lib/{connect/index.js → srv/cds-connect.js} +1 -1
- package/lib/{serve/index.js → srv/cds-serve.js} +0 -0
- package/lib/{serve → srv}/factory.js +1 -1
- package/lib/{serve/Service-api.js → srv/srv-api.js} +22 -6
- package/lib/{serve/Service-dispatch.js → srv/srv-dispatch.js} +13 -8
- package/lib/{serve/Service-handlers.js → srv/srv-handlers.js} +10 -0
- package/lib/{serve/Service-methods.js → srv/srv-methods.js} +10 -8
- package/lib/srv/srv-models.js +207 -0
- package/lib/{serve/Transaction.js → srv/srv-tx.js} +57 -40
- package/lib/utils/{tests.js → cds-test.js} +2 -2
- package/lib/utils/cds-utils.js +146 -0
- package/lib/utils/index.js +2 -145
- package/lib/utils/jest.js +43 -0
- package/lib/utils/resources/index.js +15 -25
- package/lib/utils/resources/tar.js +18 -41
- package/libx/_runtime/auth/index.js +14 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +7 -19
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +6 -19
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +3 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/PrimitiveValueDecoder.js +0 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +3 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +38 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +1 -3
- package/libx/_runtime/cds-services/services/utils/differ.js +4 -0
- package/libx/_runtime/cds-services/util/errors.js +1 -29
- package/libx/_runtime/common/i18n/messages.properties +2 -1
- package/libx/_runtime/common/perf/index.js +10 -15
- package/libx/_runtime/common/utils/binary.js +3 -4
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +0 -1
- package/libx/_runtime/common/utils/entityFromCqn.js +8 -5
- package/libx/_runtime/common/utils/keys.js +14 -6
- package/libx/_runtime/common/utils/resolveView.js +1 -1
- package/libx/_runtime/common/utils/template.js +1 -1
- package/libx/_runtime/db/Service.js +2 -14
- package/libx/_runtime/db/expand/expandCQNToJoin.js +28 -25
- package/libx/_runtime/db/expand/rawToExpanded.js +7 -6
- package/libx/_runtime/db/generic/input.js +8 -1
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -1
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +37 -18
- package/libx/_runtime/extensibility/activate.js +47 -47
- package/libx/_runtime/extensibility/add.js +22 -13
- package/libx/_runtime/extensibility/addExtension.js +17 -13
- package/libx/_runtime/extensibility/defaults.js +25 -30
- package/libx/_runtime/extensibility/handler/transformREAD.js +20 -18
- package/libx/_runtime/extensibility/linter/allowlist_checker.js +373 -0
- package/libx/_runtime/extensibility/linter/annotations_checker.js +113 -0
- package/libx/_runtime/extensibility/linter/checker_base.js +20 -0
- package/libx/_runtime/extensibility/linter/namespace_checker.js +180 -0
- package/libx/_runtime/extensibility/linter.js +32 -0
- package/libx/_runtime/extensibility/push.js +77 -20
- package/libx/_runtime/extensibility/service.js +29 -12
- package/libx/_runtime/extensibility/token.js +57 -0
- package/libx/_runtime/extensibility/utils.js +8 -6
- package/libx/_runtime/extensibility/validation.js +6 -9
- package/libx/_runtime/fiori/generic/new.js +0 -11
- package/libx/_runtime/fiori/utils/where.js +1 -1
- package/libx/_runtime/hana/Service.js +0 -1
- package/libx/_runtime/hana/conversion.js +12 -1
- package/libx/_runtime/hana/customBuilder/CustomFunctionBuilder.js +4 -3
- package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +5 -0
- package/libx/_runtime/hana/pool.js +6 -10
- package/libx/_runtime/hana/search2Contains.js +0 -5
- package/libx/_runtime/hana/search2cqn4sql.js +1 -0
- package/libx/_runtime/messaging/common-utils/authorizedRequest.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +1 -2
- package/libx/_runtime/messaging/outbox/utils.js +1 -1
- package/libx/_runtime/messaging/service.js +11 -6
- package/libx/_runtime/remote/utils/data.js +5 -0
- package/libx/_runtime/sqlite/Service.js +7 -6
- package/libx/_runtime/sqlite/execute.js +41 -28
- package/libx/odata/afterburner.js +79 -2
- package/libx/odata/cqn2odata.js +15 -9
- package/libx/odata/grammar.pegjs +157 -76
- package/libx/odata/index.js +9 -3
- package/libx/odata/parser.js +1 -1
- package/libx/odata/utils.js +39 -5
- package/libx/rest/RestAdapter.js +3 -7
- package/libx/rest/middleware/delete.js +4 -5
- package/libx/rest/middleware/parse.js +3 -2
- package/package.json +3 -3
- package/server.js +1 -1
- package/srv/extensibility-service.cds +6 -3
- package/srv/model-provider.cds +3 -1
- package/srv/model-provider.js +86 -106
- package/srv/mtx.js +7 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +0 -240
|
@@ -60,7 +60,7 @@ exports.expected = ([,type], arg) => {
|
|
|
60
60
|
//
|
|
61
61
|
|
|
62
62
|
exports._duplicate_cds = (...locations) => {
|
|
63
|
-
const { local } = require('../utils')
|
|
63
|
+
const { local } = require('../utils/cds-utils')
|
|
64
64
|
throw error `Duplicate @sap/cds/common!
|
|
65
65
|
|
|
66
66
|
There are duplicate versions of @sap/cds loaded from these locations:
|
|
File without changes
|
package/lib/log/format/kibana.js
CHANGED
|
@@ -56,6 +56,24 @@ module.exports = (module, level, ...args) => {
|
|
|
56
56
|
if (cf.length) toLog['#cf'] = { string: cf }
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
const getCircularReplacer = () => {
|
|
60
|
+
const seen = new WeakSet()
|
|
61
|
+
return (key, value) => {
|
|
62
|
+
if (typeof value === "object" && value !== null) {
|
|
63
|
+
if (seen.has(value)) {
|
|
64
|
+
return 'cyclic'
|
|
65
|
+
}
|
|
66
|
+
seen.add(value)
|
|
67
|
+
}
|
|
68
|
+
return value
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
59
72
|
// return array with the stringified toLog (to avoid multiple log lines) as the sole element
|
|
60
|
-
|
|
73
|
+
try {
|
|
74
|
+
return [JSON.stringify(toLog)]
|
|
75
|
+
} catch (e) {
|
|
76
|
+
// try again with removed circular references
|
|
77
|
+
return [JSON.stringify(toLog, getCircularReplacer())]
|
|
78
|
+
}
|
|
61
79
|
}
|
package/lib/ql/Query.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
const { AsyncResource } = require('async_hooks')
|
|
1
2
|
const { inspect } = require('util')
|
|
2
3
|
const cds = require('../index')
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
class Query {
|
|
5
6
|
|
|
6
7
|
constructor(_={}) { this[this.cmd] = _ }
|
|
7
8
|
|
|
@@ -16,8 +17,10 @@ module.exports = class Query {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
/** Turns all queries into Thenables which execute with primary db by default */
|
|
19
|
-
then
|
|
20
|
-
|
|
20
|
+
get then() {
|
|
21
|
+
const srv = this._srv || cds.db || cds.error `Can't execute query as no primary database is connected.`
|
|
22
|
+
const q = new AsyncResource('await cds.query')
|
|
23
|
+
return (r,e) => q.runInAsyncScope (srv.run, srv, this) .then (r,e)
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
/** Beautifies output in REPL */
|
|
@@ -87,3 +90,6 @@ const _target4 = (target, arg2) => target && (
|
|
|
87
90
|
)
|
|
88
91
|
|
|
89
92
|
const _name = cds.env.sql.names === 'quoted' ? n =>`"${n}"` : n => n.replace(/[.:]/g,'_')
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
module.exports = Query
|
package/lib/ql/SELECT.js
CHANGED
|
@@ -94,7 +94,7 @@ module.exports = class SELECT extends Whereable {
|
|
|
94
94
|
return Object.defineProperty(this, '_where_or_having', { value: 'on', configurable: true })
|
|
95
95
|
}
|
|
96
96
|
on (...args) {
|
|
97
|
-
if (!this.SELECT.from || !this.SELECT.from.join)
|
|
97
|
+
if (!this.SELECT.from || !this.SELECT.from.join || !this.SELECT.from.args) // `join` can also be a function, e.g. in SELECT.from(SELECT.from('foo'))
|
|
98
98
|
throw new Error(`Invalid call of "SELECT.on()" without prior call of "SELECT.join()"`)
|
|
99
99
|
return this._where (args,'and','on')
|
|
100
100
|
}
|
|
@@ -116,7 +116,7 @@ module.exports = class SELECT extends Whereable {
|
|
|
116
116
|
|
|
117
117
|
limit (rows, offset) {
|
|
118
118
|
if (is_number(rows) || rows) this.SELECT.limit = rows.rows ? rows : { rows: {val:rows} }
|
|
119
|
-
if (is_number(offset)) this.SELECT.limit.offset = { val: offset }
|
|
119
|
+
if (is_number(offset)) (this.SELECT.limit = (this.SELECT.limit || {})) .offset = { val: offset }
|
|
120
120
|
return this
|
|
121
121
|
}
|
|
122
122
|
|
package/lib/ql/UPDATE.js
CHANGED
|
@@ -54,8 +54,8 @@ module.exports = class UPDATE extends Whereable {
|
|
|
54
54
|
const o = args[0]
|
|
55
55
|
for (let col in o) {
|
|
56
56
|
let op = '=', v = o[col]
|
|
57
|
-
if (typeof v === 'object' && !(v === null || v.map || v.pipe || v instanceof Buffer || v instanceof Date)) {
|
|
58
|
-
let o = Object.keys(v)[0]
|
|
57
|
+
if (typeof v === 'object' && !(v === null || v.map || v.pipe || v instanceof Buffer || v instanceof Date || v instanceof RegExp)) {
|
|
58
|
+
let o = Object.keys(v)[0] //|| this._expected `${{v}} to be an object with an operator as single key`
|
|
59
59
|
if (o in operators) v = v[op=o]
|
|
60
60
|
}
|
|
61
61
|
_add (this, col, op, v && (v.val !== undefined || v.ref || v.xpr || v.func || v.SELECT) ? v : {val:v})
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const cds = require('../index')
|
|
2
|
+
const Query = require('./Query')
|
|
2
3
|
require = path => { // eslint-disable-line no-global-assign
|
|
3
4
|
const clazz = module.require (path); if (!clazz._api) return clazz
|
|
4
5
|
Object.defineProperty (clazz.prototype, 'cmd', { value: path.match(/\w+$/)[0] })
|
|
@@ -6,12 +7,14 @@ require = path => { // eslint-disable-line no-global-assign
|
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
module.exports = Object.assign (_deprecated_srv_ql, { cdr: true,
|
|
10
|
+
Query,
|
|
9
11
|
SELECT: require('./SELECT'),
|
|
10
12
|
INSERT: require('./INSERT'),
|
|
11
13
|
UPDATE: require('./UPDATE'),
|
|
12
14
|
DELETE: require('./DELETE'),
|
|
13
15
|
CREATE: require('./CREATE'),
|
|
14
16
|
DROP: require('./DROP'),
|
|
17
|
+
clone(q) { return Query.prototype.clone.call(q) }
|
|
15
18
|
})
|
|
16
19
|
|
|
17
20
|
function _deprecated_srv_ql() { // eslint-disable-next-line no-console
|
|
@@ -22,18 +25,9 @@ function _deprecated_srv_ql() { // eslint-disable-next-line no-console
|
|
|
22
25
|
return module.exports
|
|
23
26
|
}
|
|
24
27
|
|
|
25
|
-
if (cds.env.features.cls && cds.env.features.debug_queries) {
|
|
26
|
-
const Query = require('./Query'), { then } = Query.prototype
|
|
27
|
-
const { AsyncResource } = require('async_hooks')
|
|
28
|
-
Object.defineProperty (Query.prototype,'then',{ get(){
|
|
29
|
-
const q = new AsyncResource('cds.Query')
|
|
30
|
-
return (r,e) => q.runInAsyncScope (then,this,r,e)
|
|
31
|
-
}})
|
|
32
|
-
}
|
|
33
|
-
|
|
34
28
|
module.exports._reset = ()=>{ // for strange tests only
|
|
35
29
|
const _name = cds.env.sql.names === 'quoted' ? n =>`"${n}"` : n => n.replace(/[.:]/g,'_')
|
|
36
|
-
Object.defineProperty (
|
|
30
|
+
Object.defineProperty (Query.prototype,'valueOf',{ configurable:1, value: function(cmd=this.cmd) {
|
|
37
31
|
return `${cmd} ${_name(this._target.name)} `
|
|
38
32
|
}})
|
|
39
33
|
return this
|
package/lib/req/context.js
CHANGED
|
@@ -12,11 +12,15 @@ const { EventEmitter } = require('events')
|
|
|
12
12
|
class EventContext {
|
|
13
13
|
|
|
14
14
|
/** Creates a new instance that inherits from cds.context */
|
|
15
|
-
static for (_) {
|
|
15
|
+
static for (_,_as_root) {
|
|
16
16
|
const ctx = new this (_)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
const base = cds.context
|
|
18
|
+
if (base) {
|
|
19
|
+
ctx._set('_propagated', base) // we inherit from former cds.currents
|
|
20
|
+
if (!_as_root) {
|
|
21
|
+
if (!ctx.context) ctx._set('context', base.context) // all transaction handling works with root contexts
|
|
22
|
+
if (!ctx.tx && base.tx) ctx.tx = base.tx
|
|
23
|
+
}
|
|
20
24
|
}
|
|
21
25
|
return ctx
|
|
22
26
|
}
|
|
@@ -38,7 +42,7 @@ class EventContext {
|
|
|
38
42
|
//
|
|
39
43
|
|
|
40
44
|
get emitter() {
|
|
41
|
-
return this.context._emitter ||
|
|
45
|
+
return this.context._emitter || this.context._set('_emitter', new EventEmitter)
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
async emit (event,...args) {
|
|
@@ -104,6 +108,24 @@ class EventContext {
|
|
|
104
108
|
|| this.hasOwnProperty('locale') && this.locale // eslint-disable-line no-prototype-builtins
|
|
105
109
|
}
|
|
106
110
|
|
|
111
|
+
get _features() {
|
|
112
|
+
return super._features = this._propagated._features || _features4 (this.http?.req?.features || this.user?.features || this.http?.req?.user?.features)
|
|
113
|
+
}
|
|
114
|
+
get features() {
|
|
115
|
+
return super.features = this._features || noFeatures
|
|
116
|
+
}
|
|
117
|
+
set features(v) {
|
|
118
|
+
super.features = _features4(v)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
get model() {
|
|
122
|
+
const m = this._propagated.model || this.http?.req.__model // IMPORTANT: Never use that anywhere else
|
|
123
|
+
return this._set('model',m)
|
|
124
|
+
}
|
|
125
|
+
set model(m) {
|
|
126
|
+
super.model = m
|
|
127
|
+
}
|
|
128
|
+
|
|
107
129
|
get timestamp() {
|
|
108
130
|
return super.timestamp = this._propagated.timestamp || new Date
|
|
109
131
|
}
|
|
@@ -139,30 +161,40 @@ class EventContext {
|
|
|
139
161
|
*/
|
|
140
162
|
set tx (tx) {
|
|
141
163
|
Object.defineProperty (this,'tx',{value:tx}) //> allowed only once!
|
|
142
|
-
const
|
|
143
|
-
if (
|
|
144
|
-
if (!this.hasOwnProperty('context')) this.context =
|
|
145
|
-
|
|
164
|
+
const root = tx.context?.context
|
|
165
|
+
if (root && root !== this) {
|
|
166
|
+
if (!this.hasOwnProperty('context')) this.context = root // eslint-disable-line no-prototype-builtins
|
|
146
167
|
if (features.assert_integrity && features.assert_integrity_type == 'RT') {
|
|
147
|
-
const reqs =
|
|
168
|
+
const reqs = root._children || root._set('_children', {})
|
|
148
169
|
const all = reqs[tx.name] || (reqs[tx.name] = [])
|
|
149
170
|
all.push(this)
|
|
150
171
|
}
|
|
151
172
|
}
|
|
152
173
|
}
|
|
153
174
|
get _tx() { return this.tx } // REVISIT: for compatibility to bade usages of req._tx
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
/** REVISIT: remove -> @deprecated */
|
|
157
|
-
set _model(m){ Object.defineProperty(this,'_model',{value:m}) }
|
|
158
|
-
get _model() {
|
|
159
|
-
return this._model = this.tx && this.tx.model || this._propagated._model
|
|
160
|
-
}
|
|
161
175
|
}
|
|
162
176
|
|
|
163
177
|
const _TENANT_LOCALE = { tenant:1, locale:2 }
|
|
164
178
|
const _anonymous = new cds.User.default
|
|
165
179
|
|
|
180
|
+
const _features4 = features => { // normalizes features to an object
|
|
181
|
+
if (!features) return
|
|
182
|
+
if (features === '*') return allFeatures
|
|
183
|
+
const o = (
|
|
184
|
+
Array.isArray(features) ? features.reduce((fts,f)=>{ fts[f] = true; return fts },{}) :
|
|
185
|
+
typeof features === 'object' ? Object.fromEntries (Object.entries(features).filter(([,v])=>v)) :
|
|
186
|
+
(''+features).split(',').reduce((fts,f)=>{ fts[f] = true; return fts },{})
|
|
187
|
+
)
|
|
188
|
+
return Object.defineProperty (o,'$hash',$hash)
|
|
189
|
+
}
|
|
190
|
+
const $hash = {
|
|
191
|
+
get() { return this.$hash = Object.keys(this).join(',') },
|
|
192
|
+
set(v){ Object.defineProperty(this,'$hash',{value:v}) },
|
|
193
|
+
configurable:true
|
|
194
|
+
}
|
|
195
|
+
const allFeatures = new Proxy ({'*':true},{ has:() => true, get:(_,p) => p === '$hash' ? '*' : true })
|
|
196
|
+
const noFeatures = {__proto__:{ $hash:'' }}
|
|
197
|
+
|
|
166
198
|
EventContext.prototype._set('_propagated', Object.seal({}))
|
|
167
199
|
EventContext.propagateHeaders = [ 'x-correlation-id' ]
|
|
168
200
|
module.exports = EventContext
|
package/lib/req/locale.js
CHANGED
|
@@ -4,7 +4,11 @@ const INCLUDE_LIST = i18n.preserved_locales.reduce((p,n)=>{
|
|
|
4
4
|
p[n]=n; p[n.toUpperCase()]=n; return p
|
|
5
5
|
},{
|
|
6
6
|
en_US_x_saptrc: 'en_US_saptrc',
|
|
7
|
-
en_US_x_sappsd: 'en_US_sappsd'
|
|
7
|
+
en_US_x_sappsd: 'en_US_sappsd',
|
|
8
|
+
en_US_x_saprigi: 'en_US_saprigi',
|
|
9
|
+
'1Q': 'en_US_saptrc',
|
|
10
|
+
'2Q': 'en_US_sappsd',
|
|
11
|
+
'3Q': 'en_US_saprigi'
|
|
8
12
|
})
|
|
9
13
|
|
|
10
14
|
const from_req = req => req && (
|
|
@@ -32,22 +32,11 @@ class ProtocolAdapter {
|
|
|
32
32
|
/**
|
|
33
33
|
* Mounts the adapter to an express app.
|
|
34
34
|
*/
|
|
35
|
-
in (app
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
lib.perf (app)
|
|
41
|
-
app.use (path, (req,_,next) => {
|
|
42
|
-
if (req.user?.tenant && !cds.context?.tenant) cds.context = { user: req.user } // REVISIT: should move to auth middleware
|
|
43
|
-
LOG && LOG (req.method, decodeURI(req.originalUrl), req.body||'')
|
|
44
|
-
if (/\$batch/.test(req.url)) req.on ('dispatch', (req) => {
|
|
45
|
-
LOG && LOG ('>', req.event, decodeURI(req._path), req._query||'')
|
|
46
|
-
if (DEBUG && req.query) DEBUG (req.query)
|
|
47
|
-
})
|
|
48
|
-
next()
|
|
49
|
-
})
|
|
50
|
-
app.use (path, this)
|
|
35
|
+
in (app) {
|
|
36
|
+
const srv = this.service
|
|
37
|
+
app.use (srv.path+'/webapp/', (_,res)=> res.sendStatus(404))
|
|
38
|
+
const cds_context_model = require('./srv-models')
|
|
39
|
+
app.use (srv.path, logger, lib.perf, lib.auth(srv), cds_context_model.middleware4(srv), this)
|
|
51
40
|
return srv
|
|
52
41
|
}
|
|
53
42
|
|
|
@@ -72,10 +61,25 @@ class ProtocolAdapter {
|
|
|
72
61
|
|
|
73
62
|
const _protocol4 = (srv) => {
|
|
74
63
|
const {to} = srv.options; if (to) return to
|
|
75
|
-
|
|
76
|
-
return !def ? default_protocol : def['@protocol'] || def['@rest'] && 'rest' || def['@odata'] && 'odata_v4' || default_protocol
|
|
64
|
+
return _protocol4Service(srv.definition)
|
|
77
65
|
}
|
|
78
66
|
|
|
67
|
+
const _protocol4Service = (service) => {
|
|
68
|
+
return !service ? default_protocol : service['@protocol'] || service['@rest'] && 'rest' || service['@odata'] && 'odata_v4' || default_protocol
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
const LOG = cds.log(), DEBUG = cds.debug('server')
|
|
73
|
+
const logger = function cap_req_logger (req,_,next) {
|
|
74
|
+
LOG && LOG (req.method, decodeURI(req.originalUrl), req.body||'')
|
|
75
|
+
if (/\$batch/.test(req.url)) req.on ('dispatch', (req) => {
|
|
76
|
+
LOG && LOG ('>', req.event, decodeURI(req._path), req._query||'')
|
|
77
|
+
if (DEBUG && req.query) DEBUG (req.query)
|
|
78
|
+
})
|
|
79
|
+
next()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
79
83
|
const default_protocol = 'odata_v4'
|
|
80
84
|
const _prototype = Object.getOwnPropertyDescriptors (ProtocolAdapter.prototype)
|
|
81
|
-
module.exports = { ProtocolAdapter }
|
|
85
|
+
module.exports = { ProtocolAdapter, _protocol4Service }
|
|
File without changes
|
|
@@ -18,7 +18,7 @@ const connect = module.exports = async function cds_connect (options) {
|
|
|
18
18
|
* or with options configured in cds.env.requires.<datasource>.
|
|
19
19
|
* @param {string} [datasource]
|
|
20
20
|
* @param {{ kind?:String, impl?:String }} [options]
|
|
21
|
-
* @returns { Promise<import('
|
|
21
|
+
* @returns { Promise<import('./srv-api')> }
|
|
22
22
|
*/
|
|
23
23
|
connect.to = async (datasource, options) => {
|
|
24
24
|
let Service = cds.service.factory, _done = x=>x
|
|
File without changes
|
|
@@ -2,7 +2,7 @@ const cds = require('..'), { path, isfile } = cds.utils
|
|
|
2
2
|
const paths = Array.from (new Set ([ cds.root, ...require.resolve.paths('x') ]))
|
|
3
3
|
const DEBUG = cds.debug('srv.factory'); DEBUG && DEBUG ({ 'cds.root':cds.root, paths })
|
|
4
4
|
|
|
5
|
-
/** @typedef {import('./
|
|
5
|
+
/** @typedef {import('./srv-api')} Service @type { (()=>Service) & (new()=>Service) } */
|
|
6
6
|
const ServiceFactory = function (name, model, options) { //NOSONAR
|
|
7
7
|
|
|
8
8
|
const o = { ...options } // avoid changing shared options
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
const cds = require('..'), { Event, Request } = cds
|
|
2
|
-
const add_methods_to = require ('./
|
|
2
|
+
const add_methods_to = require ('./srv-methods')
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
class Service extends require('./
|
|
5
|
+
class Service extends require('./srv-handlers') {
|
|
6
6
|
|
|
7
7
|
constructor (name, model, o) {
|
|
8
8
|
if (is_object(name)) {
|
|
9
9
|
[ model, o ] = [ name, model ]
|
|
10
|
-
let srv = cds.linked(model).services[0] || cds.error.expected `${{
|
|
10
|
+
let srv = cds.linked(model).services[0] || cds.error.expected `${{model}} passed as first argument to be a CSN with a single service definition`
|
|
11
11
|
name = srv.name
|
|
12
12
|
}
|
|
13
13
|
super (name || new.target.name) .options = o || (o={})
|
|
@@ -56,6 +56,14 @@ class Service extends require('./Service-handlers') {
|
|
|
56
56
|
* Querying API to send synchronous requests...
|
|
57
57
|
*/
|
|
58
58
|
run (query, data) {
|
|
59
|
+
if (typeof query === 'function') {
|
|
60
|
+
const ctx = cds.context, fn = query
|
|
61
|
+
if (ctx?.tx && !ctx.tx._done) {
|
|
62
|
+
return fn (this.tx(ctx)) // with nested tx
|
|
63
|
+
} else {
|
|
64
|
+
return this.tx (fn) // with root tx
|
|
65
|
+
}
|
|
66
|
+
}
|
|
59
67
|
const req = new Request ({ query, data })
|
|
60
68
|
return this.dispatch (req)
|
|
61
69
|
}
|
|
@@ -94,6 +102,13 @@ class Service extends require('./Service-handlers') {
|
|
|
94
102
|
get events() { return super.events = _reflect (this, d => d.kind === 'event') }
|
|
95
103
|
get types() { return super.types = _reflect (this, d => !d.kind || d.kind === 'type') }
|
|
96
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Flag to control whether this service is extensible.
|
|
107
|
+
* Can be overridden by subclasses.
|
|
108
|
+
* REVISIT cds.xt name check should move to respective services
|
|
109
|
+
*/
|
|
110
|
+
get isExtensible() { return !this.name?.startsWith('cds.xt.') && this.model === cds.model}
|
|
111
|
+
|
|
97
112
|
/**
|
|
98
113
|
* Subclasses may override this to free private resources
|
|
99
114
|
*/
|
|
@@ -107,10 +122,11 @@ class Service extends require('./Service-handlers') {
|
|
|
107
122
|
|
|
108
123
|
}
|
|
109
124
|
|
|
110
|
-
const { dispatch, handle } = require('./
|
|
111
|
-
Service.prototype.
|
|
125
|
+
const { dispatch, handle } = require('./srv-dispatch')
|
|
126
|
+
Service.prototype.tx = require('./srv-tx')
|
|
112
127
|
Service.prototype.handle = handle
|
|
113
|
-
Service.prototype.
|
|
128
|
+
Service.prototype.dispatch = dispatch
|
|
129
|
+
Service.prototype.transaction = Service.prototype.tx
|
|
114
130
|
Service.prototype._implicit_next = cds.env.features.implicit_next
|
|
115
131
|
Service.prototype._is_service_instance = Service._is_service_class = true //> for factory
|
|
116
132
|
module.exports = Service
|
|
@@ -3,7 +3,7 @@ const cds = require ('../index')
|
|
|
3
3
|
/**
|
|
4
4
|
* The default implementation of the `srv.dispatch(req)` ensures everything
|
|
5
5
|
* is prepared before calling `srv.handle(req)`
|
|
6
|
-
* @typedef {import('./
|
|
6
|
+
* @typedef {import('./srv-api')} Service
|
|
7
7
|
* @typedef {import('../req/request')} Request
|
|
8
8
|
* @this {Service}
|
|
9
9
|
* @param {Request} req
|
|
@@ -13,18 +13,23 @@ exports.dispatch = async function dispatch (req) { //NOSONAR
|
|
|
13
13
|
|
|
14
14
|
// Ensure we are in a proper transaction
|
|
15
15
|
if (!this.context) {
|
|
16
|
-
const
|
|
17
|
-
if (
|
|
18
|
-
|
|
16
|
+
const ctx = cds.context
|
|
17
|
+
if (ctx?.tx && !ctx.tx._done) {
|
|
18
|
+
return this.tx (ctx) .dispatch(req) // with nested tx
|
|
19
|
+
} else {
|
|
20
|
+
return this.tx (tx => tx.dispatch(req)) // with root tx
|
|
21
|
+
}
|
|
19
22
|
}
|
|
20
23
|
if (!req.tx) req.tx = this // `this` is a tx from now on...
|
|
21
24
|
|
|
22
25
|
// Inform potential listeners // REVISIT: -> this should move into protocol adapters
|
|
23
|
-
|
|
26
|
+
let _is_root = req.constructor.name in { ODataRequest:1, RestRequest:2 }
|
|
27
|
+
if (_is_root) req._.req.emit ('dispatch',req)
|
|
24
28
|
|
|
25
29
|
// Handle batches of queries
|
|
26
|
-
if (_is_array(req.query))
|
|
27
|
-
|
|
30
|
+
if (_is_array(req.query)) return Promise.all (req.query.map (
|
|
31
|
+
q => this.dispatch ({ query:q, context: req.context, __proto__:req })
|
|
32
|
+
))
|
|
28
33
|
|
|
29
34
|
// Ensure target and fqns
|
|
30
35
|
if (!req.target) _ensure_target (this,req)
|
|
@@ -88,7 +93,6 @@ exports.handle = async function handle (req) {
|
|
|
88
93
|
}
|
|
89
94
|
|
|
90
95
|
|
|
91
|
-
const _is_root = (req) => /OData|REST/i.test(req.constructor.name)
|
|
92
96
|
const _is_array = Array.isArray
|
|
93
97
|
const _dummy = ()=>{} // REVISIT: required for some messaging tests which obviously still expect and call next()
|
|
94
98
|
|
|
@@ -107,6 +111,7 @@ const _ensure_target = (srv,req) => {
|
|
|
107
111
|
|
|
108
112
|
const _ensure_fqn = (x,p,srv, name = x[p]) => {
|
|
109
113
|
if (typeof name === 'string') {
|
|
114
|
+
if (srv instanceof cds.DatabaseService) return
|
|
110
115
|
if (srv.model && name in srv.model.definitions) return
|
|
111
116
|
if (name.startsWith(srv.namespace)) return
|
|
112
117
|
if (name.endsWith('_drafts')) return // REVISIT: rather fix test/fiori/localized-draft.test.js ?
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const cds = require('..'), {expected} = cds.error
|
|
2
|
+
const LOG = cds.log()
|
|
2
3
|
|
|
3
4
|
class EventHandlers {
|
|
4
5
|
|
|
@@ -41,6 +42,15 @@ const _register = function (srv, phase, event, path, handler) { //NOSONAR
|
|
|
41
42
|
|
|
42
43
|
if (!handler) [ handler, path ] = [ path ] // argument path is optional
|
|
43
44
|
if (typeof handler !== 'function') expected `${{handler}} to be a function`
|
|
45
|
+
if (handler._is_stub) {
|
|
46
|
+
LOG.warn (`\n
|
|
47
|
+
WARNING: You are trying to register a frameworks-generated stub method for
|
|
48
|
+
custom action/function '${event}' in implementation of service '${srv.name}'.
|
|
49
|
+
We're ignoring that as we already registered the according handler.
|
|
50
|
+
Please fix your implementation, i.e., just don't register that handler.
|
|
51
|
+
`)
|
|
52
|
+
return srv
|
|
53
|
+
}
|
|
44
54
|
|
|
45
55
|
// Canonicalize event argument
|
|
46
56
|
if (!event || event === '*') event = undefined
|
|
@@ -3,19 +3,21 @@ const LOG = cds.log('cds-app-service-methods')
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
module.exports = (srv) => {
|
|
6
|
-
if (
|
|
6
|
+
if (srv.model && ( //> we only support that for app services
|
|
7
7
|
srv instanceof cds.ApplicationService ||
|
|
8
8
|
srv instanceof cds.RemoteService ||
|
|
9
9
|
srv._add_stub_methods
|
|
10
|
-
))
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
)) {
|
|
11
|
+
for (const each of srv.operations) {
|
|
12
|
+
add_handler_for (srv, each)
|
|
13
|
+
}
|
|
14
|
+
for (const each of srv.entities) {
|
|
15
|
+
for (const a in each.actions) {
|
|
16
|
+
add_handler_for (srv, each.actions[a])
|
|
17
|
+
}
|
|
17
18
|
}
|
|
18
19
|
}
|
|
20
|
+
return srv
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
const add_handler_for = (srv, def) => {
|