@sap/cds 6.6.1 → 6.7.0
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 +67 -3
- package/README.md +1 -1
- package/apis/connect.d.ts +11 -4
- package/apis/core.d.ts +1 -1
- package/apis/csn.d.ts +1 -0
- package/apis/internal/inference.d.ts +15 -2
- package/apis/log.d.ts +10 -0
- package/apis/serve.d.ts +4 -9
- package/apis/services.d.ts +86 -19
- package/bin/build/buildTaskEngine.js +16 -42
- package/bin/build/constants.js +4 -2
- package/bin/build/provider/buildTaskProviderInternal.js +117 -85
- package/bin/build/provider/hana/index.js +6 -1
- package/bin/build/provider/mtx-extension/index.js +74 -34
- package/bin/build/provider/mtx-sidecar/index.js +3 -3
- package/bin/build/provider/nodejs/index.js +2 -2
- package/bin/build/util.js +63 -14
- package/bin/cds-serve.js +6 -0
- package/bin/cds.js +20 -4
- package/bin/deploy/to-hana/cfUtil.js +15 -1
- package/bin/deploy/to-hana/hana.js +1 -1
- package/bin/deploy/to-hana/hdiDeployUtil.js +1 -1
- package/bin/mtx/in-cds.js +2 -9
- package/bin/plugins.js +31 -0
- package/bin/serve.js +12 -12
- package/lib/compile/etc/_localized.js +1 -1
- package/lib/compile/for/lean_drafts.js +22 -6
- package/lib/compile/for/nodejs.js +4 -1
- package/lib/compile/load.js +4 -2
- package/lib/core/index.js +35 -15
- package/lib/dbs/cds-deploy.js +129 -133
- package/lib/env/cds-env.js +25 -17
- package/lib/env/cds-requires.js +10 -40
- package/lib/env/compat.js +12 -0
- package/lib/env/defaults.js +17 -9
- package/lib/env/plugins.js +29 -0
- package/lib/env/schemas/cds-rc.json +14 -0
- package/lib/index.js +3 -0
- package/lib/log/cds-log.js +7 -4
- package/lib/ql/CREATE.js +1 -1
- package/lib/ql/DELETE.js +1 -1
- package/lib/ql/DROP.js +3 -3
- package/lib/ql/INSERT.js +1 -1
- package/lib/ql/Query.js +14 -6
- package/lib/ql/SELECT.js +8 -2
- package/lib/ql/UPDATE.js +1 -1
- package/lib/ql/Whereable.js +1 -1
- package/lib/ql/cds-ql.js +1 -9
- package/lib/req/cds-context.js +1 -4
- package/lib/req/request.js +63 -2
- package/lib/req/response.js +3 -2
- package/lib/srv/bindings.js +69 -71
- package/lib/srv/cds-connect.js +4 -1
- package/lib/srv/cds-serve.js +4 -0
- package/lib/srv/middlewares/index.js +37 -6
- package/lib/srv/protocols/_legacy.js +1 -1
- package/lib/srv/protocols/index.js +1 -1
- package/lib/srv/srv-api.js +4 -6
- package/lib/srv/srv-dispatch.js +4 -3
- package/lib/srv/srv-handlers.js +1 -1
- package/lib/srv/srv-methods.js +8 -2
- package/lib/utils/cds-test.js +4 -1
- package/libx/_runtime/audit/Service.js +8 -9
- package/libx/_runtime/audit/generic/personal/index.js +1 -1
- package/libx/_runtime/audit/generic/personal/utils.js +1 -1
- package/libx/_runtime/audit/utils/v2.js +17 -20
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +2 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +11 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +3 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +4 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/oDataConfiguration.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +1 -1
- package/libx/_runtime/cds-services/services/Service.js +1 -1
- package/libx/_runtime/cds-services/util/assert.js +41 -65
- package/libx/_runtime/common/code-ext/WorkerPool.js +90 -0
- package/libx/_runtime/common/code-ext/WorkerReq.js +0 -4
- package/libx/_runtime/common/code-ext/execute.js +28 -18
- package/libx/_runtime/common/code-ext/handlers.js +5 -4
- package/libx/_runtime/common/code-ext/worker.js +45 -3
- package/libx/_runtime/common/code-ext/workerQueryExecutor.js +8 -7
- package/libx/_runtime/common/composition/delete.js +1 -1
- package/libx/_runtime/common/composition/update.js +3 -5
- package/libx/_runtime/common/generic/auth/expand.js +1 -1
- package/libx/_runtime/common/generic/auth/readOnly.js +5 -4
- package/libx/_runtime/common/generic/auth/restrict.js +7 -2
- package/libx/_runtime/common/generic/crud.js +12 -1
- package/libx/_runtime/common/generic/etag.js +11 -3
- package/libx/_runtime/common/generic/input.js +8 -6
- package/libx/_runtime/common/generic/paging.js +25 -8
- package/libx/_runtime/common/generic/put.js +1 -1
- package/libx/_runtime/common/generic/sorting.js +0 -1
- package/libx/_runtime/common/i18n/messages.properties +1 -0
- package/libx/_runtime/common/utils/cqn.js +5 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +2 -2
- package/libx/_runtime/common/utils/resolveView.js +14 -10
- package/libx/_runtime/common/utils/rewriteAsterisks.js +2 -3
- package/libx/_runtime/common/utils/templateProcessor.js +15 -17
- package/libx/_runtime/common/utils/templateProcessorPathSerializer.js +18 -6
- package/libx/_runtime/db/Service.js +1 -0
- package/libx/_runtime/db/data-conversion/post-processing.js +0 -18
- package/libx/_runtime/db/expand/expand-v2.js +2 -2
- package/libx/_runtime/db/expand/rawToExpanded.js +6 -6
- package/libx/_runtime/db/generic/integrity.js +1 -1
- package/libx/_runtime/db/utils/columns.js +5 -5
- package/libx/_runtime/fiori/generic/activate.js +3 -3
- package/libx/_runtime/fiori/generic/edit.js +1 -1
- package/libx/_runtime/fiori/generic/new.js +4 -0
- package/libx/_runtime/fiori/lean-draft.js +138 -46
- package/libx/_runtime/hana/execute.js +3 -1
- package/libx/_runtime/hana/pool.js +10 -2
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +6 -1
- package/libx/_runtime/messaging/enterprise-messaging.js +1 -0
- package/libx/_runtime/remote/Service.js +16 -13
- package/libx/_runtime/remote/utils/client.js +6 -1
- package/libx/_runtime/sqlite/Service.js +5 -59
- package/libx/_runtime/sqlite/convertDraftAdminPathExpression.js +1 -0
- package/libx/_runtime/sqlite/customBuilder/CustomUpsertBuilder.js +2 -2
- package/libx/_runtime/sqlite/execute.js +3 -1
- package/libx/_runtime/types/api.js +12 -3
- package/libx/odata/afterburner.js +36 -0
- package/libx/odata/cqn2odata.js +1 -1
- package/libx/odata/grammar.pegjs +5 -3
- package/libx/odata/parser.js +1 -1
- package/libx/odata/utils.js +1 -1
- package/libx/rest/RestAdapter.js +1 -1
- package/libx/rest/RestRequest.js +1 -0
- package/package.json +5 -2
- package/libx/_runtime/common/code-ext/workerQuery.js +0 -45
- package/libx/_runtime/common/constants/limit.js +0 -12
- package/libx/_runtime/common/utils/page.js +0 -39
package/lib/ql/CREATE.js
CHANGED
|
@@ -10,7 +10,7 @@ module.exports = class Query extends require('./Query') {
|
|
|
10
10
|
if (elements)
|
|
11
11
|
this.CREATE.entity = { elements: elements, kind: 'entity', name:e }
|
|
12
12
|
else
|
|
13
|
-
this.CREATE.entity = e && e.elements ? e : this.
|
|
13
|
+
this.CREATE.entity = e && e.elements ? e : this._target4(e)
|
|
14
14
|
return this
|
|
15
15
|
}
|
|
16
16
|
|
package/lib/ql/DELETE.js
CHANGED
|
@@ -9,7 +9,7 @@ module.exports = class Query extends Whereable {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
from(entity, key) {
|
|
12
|
-
this.DELETE.from = this.
|
|
12
|
+
this.DELETE.from = this._target4 (...arguments) // supporting tts
|
|
13
13
|
if (key) this.byKey(key)
|
|
14
14
|
return this
|
|
15
15
|
}
|
package/lib/ql/DROP.js
CHANGED
|
@@ -7,17 +7,17 @@ module.exports = class Query extends require('./Query') {
|
|
|
7
7
|
})
|
|
8
8
|
}
|
|
9
9
|
entity(e) {
|
|
10
|
-
this.DROP.entity = this.
|
|
10
|
+
this.DROP.entity = this._target4 (e)
|
|
11
11
|
return this
|
|
12
12
|
}
|
|
13
13
|
table(e) {
|
|
14
14
|
const {DROP} = this
|
|
15
|
-
DROP.entity = DROP.table = this.
|
|
15
|
+
DROP.entity = DROP.table = this._target4 (e)
|
|
16
16
|
return this
|
|
17
17
|
}
|
|
18
18
|
view(e) {
|
|
19
19
|
const {DROP} = this
|
|
20
|
-
DROP.entity = DROP.view = this.
|
|
20
|
+
DROP.entity = DROP.view = this._target4 (e)
|
|
21
21
|
return this
|
|
22
22
|
}
|
|
23
23
|
}
|
package/lib/ql/INSERT.js
CHANGED
|
@@ -7,7 +7,7 @@ module.exports = class Query extends require('./Query') {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
into (entity, ...data) {
|
|
10
|
-
this[this.cmd].into = this.
|
|
10
|
+
this[this.cmd].into = this._target4 (...arguments) // supporting tts
|
|
11
11
|
if (data.length) this.entries(...data)
|
|
12
12
|
return this
|
|
13
13
|
}
|
package/lib/ql/Query.js
CHANGED
|
@@ -52,12 +52,6 @@ class Query {
|
|
|
52
52
|
|| this._expected `${{target}} to be an entity path string, a CSN definition, a {ref}, a {SELECT}, or a {SET}`
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
//> REVISIT: should we rather have consistent .from/.entity/.into in CQN?
|
|
56
|
-
_target_name4 (...args) {
|
|
57
|
-
const {ref, as} = this._target_ref4 (...args)
|
|
58
|
-
return ref.length === 1 && typeof ref[0] === 'string' && !as ? ref[0] : as ? {ref, as} : {ref}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
55
|
_expected (...args) {
|
|
62
56
|
return cds.error.expected (...args)
|
|
63
57
|
}
|
|
@@ -72,8 +66,15 @@ class Query {
|
|
|
72
66
|
return `${cmd} ${_name(this._target.name)} `
|
|
73
67
|
}
|
|
74
68
|
|
|
69
|
+
forSQL (db = cds.db || cds) { return _flat(db.cqn4sql(this)) }
|
|
70
|
+
toSQL (db = cds.db || cds) { return _2sql(db.cqn2sql(this)) }
|
|
71
|
+
toSql (db = cds.db || cds) { return this.toSQL(db).sql }
|
|
72
|
+
|
|
75
73
|
}
|
|
76
74
|
|
|
75
|
+
// skip .cqn property when in repl
|
|
76
|
+
const _2sql = cds.repl ? ({sql,values}) => ({sql,values}) : (x => x)
|
|
77
|
+
const _flat = Query.prototype.flat
|
|
77
78
|
|
|
78
79
|
const _target4 = (target, arg2) => target && (
|
|
79
80
|
typeof target === 'string' ? { name: target } :
|
|
@@ -85,5 +86,12 @@ const _target4 = (target, arg2) => target && (
|
|
|
85
86
|
|
|
86
87
|
const _name = cds.env.sql.names === 'quoted' ? n =>`"${n}"` : n => n.replace(/[.:]/g,'_')
|
|
87
88
|
|
|
89
|
+
Object.defineProperty (Query.prototype, '_target4', { value: (
|
|
90
|
+
cds.env.ql.quirks_mode === false ? Query.prototype._target_ref4
|
|
91
|
+
: function (...args) {
|
|
92
|
+
const {ref, as} = this._target_ref4 (...args)
|
|
93
|
+
return ref.length === 1 && typeof ref[0] === 'string' && !as ? ref[0] : as ? {ref, as} : {ref}
|
|
94
|
+
}
|
|
95
|
+
)})
|
|
88
96
|
|
|
89
97
|
module.exports = Query
|
package/lib/ql/SELECT.js
CHANGED
|
@@ -104,8 +104,14 @@ module.exports = class Query extends Whereable {
|
|
|
104
104
|
return this._where (args,'and','on')
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
having(...
|
|
108
|
-
return this._where (
|
|
107
|
+
having(...args) {
|
|
108
|
+
return this._where (args,'and','having')
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
search (...args) {
|
|
112
|
+
let _xpr=[]; for (let val of args) _xpr.push('or',{val})
|
|
113
|
+
this.SELECT.search = _xpr.slice(1)
|
|
114
|
+
return this
|
|
109
115
|
}
|
|
110
116
|
|
|
111
117
|
groupBy (...args) {
|
package/lib/ql/UPDATE.js
CHANGED
|
@@ -10,7 +10,7 @@ module.exports = class Query extends Whereable {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
entity (e, key) {
|
|
13
|
-
this.UPDATE.entity = this.
|
|
13
|
+
this.UPDATE.entity = this._target4 (...arguments) // supporting tts
|
|
14
14
|
if (key) this.byKey(key)
|
|
15
15
|
return this
|
|
16
16
|
}
|
package/lib/ql/Whereable.js
CHANGED
|
@@ -18,7 +18,7 @@ class Query extends require('./Query') {
|
|
|
18
18
|
error (`Invalid attempt to call '${this.cmd}.${and_or}()' before a prior call to '${this.cmd}.where()'`)
|
|
19
19
|
)
|
|
20
20
|
if (clause === 'on') _ = _.from
|
|
21
|
-
let left =
|
|
21
|
+
let left = _[clause]
|
|
22
22
|
if (!left) { //> .where() called first time
|
|
23
23
|
// SELECT.from `X` .where `x or y` .and `z` -> SELECT from X where (x or y) and z
|
|
24
24
|
if (pred.includes('or')) this._left_has_or = true
|
package/lib/ql/cds-ql.js
CHANGED
|
@@ -9,7 +9,7 @@ require = path => { // eslint-disable-line no-global-assign
|
|
|
9
9
|
}, api)
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
module.exports =
|
|
12
|
+
module.exports = {
|
|
13
13
|
Query, clone: (q,_) => Query.prototype.clone.call(q,_),
|
|
14
14
|
SELECT: require('./SELECT'),
|
|
15
15
|
INSERT: require('./INSERT'),
|
|
@@ -18,14 +18,6 @@ module.exports = Object.assign (_deprecated_srv_ql, { cdr: true,
|
|
|
18
18
|
DELETE: require('./DELETE'),
|
|
19
19
|
CREATE: require('./CREATE'),
|
|
20
20
|
DROP: require('./DROP'),
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
function _deprecated_srv_ql() { // eslint-disable-next-line no-console
|
|
24
|
-
console.trace(`
|
|
25
|
-
Method 'srv.ql(req)' is deprecated and superceded by 'cds.context'.
|
|
26
|
-
Please use global SELECT instead of 'const { SELECT } = srv.ql(req)'.
|
|
27
|
-
`)
|
|
28
|
-
return module.exports
|
|
29
21
|
}
|
|
30
22
|
|
|
31
23
|
module.exports._reset = ()=>{ // for strange tests only
|
package/lib/req/cds-context.js
CHANGED
|
@@ -15,12 +15,9 @@ module.exports = new class extends AsyncLocalStorage {
|
|
|
15
15
|
|
|
16
16
|
/** @returns {EventContext} */
|
|
17
17
|
_for (cds,v) {
|
|
18
|
-
Reflect.defineProperty (cds,'context', { enumerable:1, ...
|
|
18
|
+
Reflect.defineProperty (cds,'context', { enumerable:1, ... {
|
|
19
19
|
set:(v) => this.enterWith(v),
|
|
20
20
|
get:()=> this.getStore(),
|
|
21
|
-
} : {
|
|
22
|
-
get:()=> undefined,
|
|
23
|
-
set:()=> {},
|
|
24
21
|
}})
|
|
25
22
|
return cds.context = v // IMPORTANT: we need to set it initially, to get it all wired up correctly
|
|
26
23
|
}
|
package/lib/req/request.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { Responses, Errors } = require('./response')
|
|
2
|
+
const cds = require('../../lib')
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Class Request represents requests received via synchronous protocols.
|
|
@@ -56,6 +57,67 @@ class Request extends require('./event') {
|
|
|
56
57
|
return this._set ('data', {})
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
set subject(d) { if (d) super.subject = d }
|
|
61
|
+
get subject() {
|
|
62
|
+
const q = this.query
|
|
63
|
+
let subject = (
|
|
64
|
+
q?.SELECT?.from ||
|
|
65
|
+
q?.INSERT?.into ||
|
|
66
|
+
q?.UPSERT?.into ||
|
|
67
|
+
q?.UPDATE?.entity ||
|
|
68
|
+
q?.DELETE?.from
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
const {target} = this; if (!target) return undefined
|
|
72
|
+
|
|
73
|
+
// programmatic action calls as of now only in format /Books(201)/action
|
|
74
|
+
if (!subject) {
|
|
75
|
+
let where = []
|
|
76
|
+
for (const param of this.params) {
|
|
77
|
+
if (typeof param === 'object') {
|
|
78
|
+
for (const key in param) {
|
|
79
|
+
if (key in target.keys) {
|
|
80
|
+
if (where.length > 1) where.push('and')
|
|
81
|
+
where.push({ ref: [key] }, '=', { val: param[key] })
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
where.push({ ref: [Object.keys(target.keys)[0]] }, '=', { val: param })
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
subject = { id: target.name, where }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const ref = subject.ref || [ subject ]
|
|
92
|
+
|
|
93
|
+
if(!target.drafts || cds.env.fiori.lean_draft) return super.subject = { ref }
|
|
94
|
+
|
|
95
|
+
// special handling for old draft logic
|
|
96
|
+
let subjectRef = []
|
|
97
|
+
ref.forEach ((item, i) => {
|
|
98
|
+
// to one assoc & comp
|
|
99
|
+
if (typeof item === 'string') {
|
|
100
|
+
subjectRef.push(item)
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// create key value pair without IsActiveEntity & adjust root target
|
|
105
|
+
const keys = {}
|
|
106
|
+
let id = item.id
|
|
107
|
+
for (let j = 0; j < item?.where?.length; j = j + 4) {
|
|
108
|
+
const key = item.where[j].ref[0]
|
|
109
|
+
const value = item.where[j + 2].val
|
|
110
|
+
if (key !== 'IsActiveEntity') keys[key] = value
|
|
111
|
+
else if (i === 0 && key === 'IsActiveEntity' && value === false) id = item.id + '_drafts'
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// create copy
|
|
115
|
+
const {SELECT:{from:{ref}}} = SELECT.from(id,keys)
|
|
116
|
+
subjectRef.push(...ref)
|
|
117
|
+
})
|
|
118
|
+
return super.subject = {ref: subjectRef}
|
|
119
|
+
}
|
|
120
|
+
|
|
59
121
|
reply (results) { return this.results = results }
|
|
60
122
|
notify (...args) { return this._messages.add (1, ...args) }
|
|
61
123
|
info (...args) { return this._messages.add (2, ...args) }
|
|
@@ -116,8 +178,7 @@ const SQL2Crud = {
|
|
|
116
178
|
}
|
|
117
179
|
|
|
118
180
|
const Query2Crud = (q) => {
|
|
119
|
-
|
|
120
|
-
else for (let each in q) if (each in SQL2Crud) return SQL2Crud[each]
|
|
181
|
+
for (let each in q) if (each in SQL2Crud) return SQL2Crud[each]
|
|
121
182
|
}
|
|
122
183
|
|
|
123
184
|
const _path4 = (x,p) => {
|
package/lib/req/response.js
CHANGED
|
@@ -4,8 +4,9 @@ const cds = require ('../index')
|
|
|
4
4
|
* Messages Collector, used for `req.errors` and `req.messages`
|
|
5
5
|
*/
|
|
6
6
|
class Responses extends Array {
|
|
7
|
-
static get (severity, code, message, target, args) {
|
|
8
|
-
|
|
7
|
+
static get (severity, code, message, target, args, ...more) {
|
|
8
|
+
if (code?.raw) [ message, code ] = [ String.raw (code, message, target, args, ...more) ]
|
|
9
|
+
let e // be filled in below...
|
|
9
10
|
if (typeof code === 'object') e = code; else {
|
|
10
11
|
if (typeof code === 'number') e = { code }; else [ code, message, target, args, e ] = [ undefined, code, message, target, {} ]
|
|
11
12
|
if (typeof message === 'object') e = Object.assign(message,e); else {
|
package/lib/srv/bindings.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
const DEBUG = /\b(y|all|serve|bindings)\b/.test (process.env.DEBUG) && console.warn
|
|
2
|
-
// || console.debug
|
|
3
1
|
|
|
4
|
-
const cds = require ('..')
|
|
2
|
+
const cds = require ('..'), DEBUG = cds.debug('serve|bindings',{label:'cds'})
|
|
5
3
|
const { readFile, readFileSync, writeFile, writeFileSync } = require ('fs')
|
|
6
4
|
const [ read, write ] = [ readFile, writeFile ].map(require('util').promisify)
|
|
7
5
|
const registry = '~/.cds-services.json'
|
|
@@ -9,88 +7,88 @@ const registry = '~/.cds-services.json'
|
|
|
9
7
|
/** TODO: Add documentation */
|
|
10
8
|
module.exports = class Bindings {
|
|
11
9
|
|
|
12
|
-
|
|
10
|
+
static get registry(){ return registry }
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
static then(r,e) {
|
|
13
|
+
const LOG = cds.log('cds.serve', { prefix:'cds' })
|
|
14
|
+
const bindings = new Bindings
|
|
15
|
+
cds.prependOnceListener ('connect', ()=> LOG._info && LOG.info ('connect using bindings from:', { registry }))
|
|
16
|
+
cds.once('listening', ({url})=> bindings.export (cds.service.providers, url))
|
|
17
|
+
return bindings.import() .then (r,e)
|
|
18
|
+
}
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
constructor(url) {
|
|
21
|
+
this._source = require ('path') .resolve (cds.root, registry.replace(/^~/, require('os').homedir()))
|
|
22
|
+
this.cds = {provides:{}}
|
|
23
|
+
this.url = url
|
|
24
|
+
}
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
async load (sync) {
|
|
27
|
+
DEBUG?.('reading bindings from:', this._source)
|
|
28
|
+
try { Object.assign (this, JSON.parse (sync ? readFileSync (this._source) : await read (this._source))) }
|
|
29
|
+
catch (e) { /* ignored */ }
|
|
30
|
+
return this
|
|
31
|
+
}
|
|
32
|
+
async store (sync) {
|
|
33
|
+
DEBUG?.('writing bindings to:', this._source)
|
|
34
|
+
const json = JSON.stringify ({cds:this.cds},null,' ')
|
|
35
|
+
return sync ? writeFileSync (this._source, json) : write (this._source, json)
|
|
36
|
+
}
|
|
39
37
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
return this
|
|
38
|
+
async import() {
|
|
39
|
+
const required = cds.requires; if (!required) return this
|
|
40
|
+
const provided = (await this.load()) .cds.provides
|
|
41
|
+
for (let each in required) {
|
|
42
|
+
const req = required[each]; if (typeof req !== 'object') continue
|
|
43
|
+
const bound = provided [req.service||each]
|
|
44
|
+
if (bound) {
|
|
45
|
+
Object.assign (req.credentials || (req.credentials = {}), bound.credentials)
|
|
46
|
+
// REVISIT: temporary fix to inherit kind as well for mocked odata services
|
|
47
|
+
// otherwise mocking with two services does not work for kind:odata-v2
|
|
48
|
+
if (req.kind === 'odata-v2' || req.kind === 'odata-v4') req.kind = 'odata'
|
|
49
|
+
}
|
|
54
50
|
}
|
|
51
|
+
return this
|
|
52
|
+
}
|
|
55
53
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
}
|
|
54
|
+
async export (services, url) {
|
|
55
|
+
this.cleanup (this.url = url)
|
|
56
|
+
// register our services
|
|
57
|
+
const provides = this.cds.provides
|
|
58
|
+
for (let each of services) {
|
|
59
|
+
// if (each.name in cds.env.requires) continue
|
|
60
|
+
const options = each.options || {}
|
|
61
|
+
provides[each.name] = {
|
|
62
|
+
kind: options.to || 'odata',
|
|
63
|
+
credentials: {
|
|
64
|
+
...options.credentials,
|
|
65
|
+
url: url + each.path
|
|
70
66
|
}
|
|
71
|
-
|
|
72
|
-
return this.store()
|
|
67
|
+
}
|
|
73
68
|
}
|
|
69
|
+
process.on ('exit', ()=>this.purge())
|
|
70
|
+
return this.store()
|
|
71
|
+
}
|
|
74
72
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
73
|
+
purge() {
|
|
74
|
+
this.load(true)
|
|
75
|
+
DEBUG?.('purging bindings from:', this._source)
|
|
76
|
+
this.cleanup()
|
|
77
|
+
this.store(true)
|
|
78
|
+
}
|
|
81
79
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
return this
|
|
80
|
+
cleanup (url=this.url) {
|
|
81
|
+
// remove all services served at the same url
|
|
82
|
+
const all = this.cds.provides
|
|
83
|
+
for (let [key,srv] of Object.entries (all)) {
|
|
84
|
+
if (srv.credentials && srv.credentials.url && srv.credentials.url.startsWith(url)) delete all [key]
|
|
89
85
|
}
|
|
86
|
+
return this
|
|
87
|
+
}
|
|
90
88
|
}
|
|
91
89
|
|
|
92
90
|
const {NODE_ENV} = process.env
|
|
93
91
|
if (NODE_ENV === 'test' || global.it || cds.env.no_bindings) {
|
|
94
|
-
|
|
92
|
+
module['exports'] = { then: (r) => r() }
|
|
95
93
|
}
|
|
96
94
|
/* eslint no-console:off */
|
package/lib/srv/cds-connect.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const cds = require('..'), {one_model} = cds.env.features, LOG = cds.log('cds.connect')
|
|
2
2
|
const _pending = cds.services._pending || {} // used below to chain parallel connect.to(<same>)
|
|
3
|
+
const TRACE = cds.debug('trace')
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Connect to a service as primary datasource, i.e. cds.db.
|
|
@@ -21,6 +22,7 @@ const connect = module.exports = async function cds_connect (options) {
|
|
|
21
22
|
* @returns { Promise<import('./srv-api')> }
|
|
22
23
|
*/
|
|
23
24
|
connect.to = async (datasource, options) => {
|
|
25
|
+
TRACE?.time(`cds.connect ${datasource} `)
|
|
24
26
|
let Service = cds.service.factory, _done = x=>x
|
|
25
27
|
if (typeof datasource === 'object') [options,datasource] = [datasource]
|
|
26
28
|
else if (datasource) {
|
|
@@ -36,7 +38,7 @@ connect.to = async (datasource, options) => {
|
|
|
36
38
|
const m = await model4 (o)
|
|
37
39
|
// check if required service definition exists
|
|
38
40
|
const required = cds.requires[datasource]
|
|
39
|
-
if (required
|
|
41
|
+
if (required?.model?.length && datasource !== 'db' && !m.definitions[required.service||datasource]) {
|
|
40
42
|
LOG.error(`No service definition found for '${required.service || datasource}', as required by 'cds.requires.${datasource}':`, required)
|
|
41
43
|
throw new Error (`No service definition found for '${required.service || datasource}'`)
|
|
42
44
|
}
|
|
@@ -46,6 +48,7 @@ connect.to = async (datasource, options) => {
|
|
|
46
48
|
if (datasource === 'db') cds.db = srv
|
|
47
49
|
_done (cds.services[datasource] = srv)
|
|
48
50
|
if (!o.silent) cds.emit ('connect',srv)
|
|
51
|
+
TRACE?.timeEnd(`cds.connect ${datasource} `)
|
|
49
52
|
return srv
|
|
50
53
|
}
|
|
51
54
|
|
package/lib/srv/cds-serve.js
CHANGED
|
@@ -2,10 +2,13 @@ const cds = require ('..')
|
|
|
2
2
|
const { ProtocolAdapter } = cds.service.protocols
|
|
3
3
|
const { Service } = cds.service.factory
|
|
4
4
|
const _ready = Symbol(), _pending = cds.services._pending || {}
|
|
5
|
+
const TRACE = cds.debug('trace')
|
|
5
6
|
|
|
6
7
|
/** @param som - a service name or a model (name or csn) */
|
|
7
8
|
module.exports = function cds_serve (som, _options) { // NOSONAR
|
|
8
9
|
|
|
10
|
+
TRACE?.time(`cds.serve ${som} `)
|
|
11
|
+
|
|
9
12
|
if (som && typeof som === 'object' && !is_csn(som) && !is_files(som)) {
|
|
10
13
|
[som,_options] = [undefined,
|
|
11
14
|
som._is_service_instance ? { service:som, from:'*' } :
|
|
@@ -123,6 +126,7 @@ module.exports = function cds_serve (som, _options) { // NOSONAR
|
|
|
123
126
|
return chimera
|
|
124
127
|
}})
|
|
125
128
|
}
|
|
129
|
+
TRACE?.timeEnd('cds.serve '+som+' ')
|
|
126
130
|
return _resolve (response)
|
|
127
131
|
}, _error),
|
|
128
132
|
|
|
@@ -7,14 +7,45 @@ const trace = exports.trace = require('./trace')
|
|
|
7
7
|
|
|
8
8
|
// middlewares running before protocol adapters
|
|
9
9
|
exports.before = [
|
|
10
|
-
context
|
|
11
|
-
trace
|
|
12
|
-
auth
|
|
13
|
-
ctx_auth
|
|
14
|
-
ctx_model
|
|
15
|
-
]
|
|
10
|
+
context, // provides cds.context
|
|
11
|
+
trace, // provides detailed trace logs when DEBUG=trace
|
|
12
|
+
auth, // provides req.user & tenant
|
|
13
|
+
ctx_auth, // propagates auth results to cds.context
|
|
14
|
+
ctx_model, // fills in cds.context.model, in case of extensibility
|
|
15
|
+
].map(_instantiate)
|
|
16
16
|
|
|
17
17
|
// middlewares running after protocol adapters -> usually error middlewares
|
|
18
18
|
exports.after = [
|
|
19
19
|
errors(),
|
|
20
20
|
]
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Convenience method to add custom middlewares like so:
|
|
24
|
+
* ```js
|
|
25
|
+
* cds.middlewares.add (mymw, {at:0}) // to the front
|
|
26
|
+
* cds.middlewares.add (mymw, {at:2})
|
|
27
|
+
* cds.middlewares.add (mymw, {before:'auth'})
|
|
28
|
+
* cds.middlewares.add (mymw, {after:'auth'})
|
|
29
|
+
* cds.middlewares.add (mymw) // to the end
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
exports.add = (new_mw, { at: index, before, after, options }) => {
|
|
33
|
+
let mw = new_mw (options)
|
|
34
|
+
if (index) return exports.before.splice (index, 0, mw)
|
|
35
|
+
if (before) return exports.before.splice (index, _index4(before), mw)
|
|
36
|
+
if (after) return exports.before.splice (index, _index4(after)+1, mw)
|
|
37
|
+
else return exports.before.push(mw)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function _index4 (middleware) {
|
|
41
|
+
if (typeof middleware === 'string') middleware = exports[middleware]
|
|
42
|
+
if (!middleware) throw new Error (`Didn't find a middleware matching ${{middleware}}`)
|
|
43
|
+
const index = exports.before.findIndex(mw => mw.factory === before)
|
|
44
|
+
if (index === -1) throw new Error (`Didn't find ${{middleware}} in cds.middlewares.before`)
|
|
45
|
+
return index
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function _instantiate (factory,o) {
|
|
49
|
+
let mw = factory(o)
|
|
50
|
+
return mw && Object.assign(mw,{factory})
|
|
51
|
+
}
|
|
@@ -17,10 +17,10 @@ class LegacyProtocolAdapter extends ProtocolAdapter {
|
|
|
17
17
|
return super.serve (srv, app, { before: [
|
|
18
18
|
// async (req, res, next) => { await 1; next() }, // REVISIT: AsyncResource.bind() -> enable to break cds/tests/_runtime/odata/__tests__/integration/crud-with-mtx.test.js with existing, non-middleware mode, *w/o* fix to BufferedWriter
|
|
19
19
|
cds_context,
|
|
20
|
-
cap_req_logger,
|
|
21
20
|
libx.perf,
|
|
22
21
|
libx.auth(srv),
|
|
23
22
|
ctx_auth,
|
|
23
|
+
cap_req_logger,
|
|
24
24
|
cds_context_model.middleware4(srv)
|
|
25
25
|
], after:[] })
|
|
26
26
|
}
|
|
@@ -80,7 +80,7 @@ class ProtocolAdapter {
|
|
|
80
80
|
|
|
81
81
|
|
|
82
82
|
const protocols = Object.keys(ProtocolAdapter.init())
|
|
83
|
-
const protocol4 = (def, _default = protocols[0]) => def['@protocol'] || protocols.find(p => def['@'+p]) || _default
|
|
83
|
+
const protocol4 = (def, _default = protocols[0]) => def?.['@protocol'] || protocols.find(p => def['@'+p]) || _default
|
|
84
84
|
const is_global = adapter => adapter.length === 1 && !/^(function )?(\w+\s+)?\((srv|service)/.test(adapter)
|
|
85
85
|
|
|
86
86
|
module.exports = { ProtocolAdapter, protocol4 }
|
package/lib/srv/srv-api.js
CHANGED
|
@@ -63,11 +63,9 @@ class Service extends require('./srv-handlers') {
|
|
|
63
63
|
run (query, data) {
|
|
64
64
|
if (typeof query === 'function') {
|
|
65
65
|
const ctx = cds.context, fn = query
|
|
66
|
-
if (ctx?.tx && !ctx.tx._done)
|
|
67
|
-
return fn (this.tx(ctx)) // with nested tx
|
|
68
|
-
|
|
69
|
-
return this.tx (fn) // with root tx
|
|
70
|
-
}
|
|
66
|
+
if (ctx?.tx && !ctx.tx._done)
|
|
67
|
+
return fn (this.tx(ctx)) // run fn with nested tx
|
|
68
|
+
else return this.tx(fn) // run fn with root tx
|
|
71
69
|
}
|
|
72
70
|
const req = new Request ({ query, data })
|
|
73
71
|
return this.dispatch (req)
|
|
@@ -100,7 +98,7 @@ class Service extends require('./srv-handlers') {
|
|
|
100
98
|
get namespace() {
|
|
101
99
|
return super.namespace = this.definition && this.definition.name
|
|
102
100
|
|| this.model && this.model.namespace
|
|
103
|
-
|| !(this
|
|
101
|
+
|| !(this.isDatabaseService) && !/\W/.test(this.name) && this.name || undefined
|
|
104
102
|
}
|
|
105
103
|
|
|
106
104
|
get operations() { return super.operations = _reflect (this, d => d.kind === 'action' || d.kind === 'function') }
|
package/lib/srv/srv-dispatch.js
CHANGED
|
@@ -16,10 +16,11 @@ exports.dispatch = async function dispatch (req) { //NOSONAR
|
|
|
16
16
|
const ctx = cds.context
|
|
17
17
|
if (ctx?.tx && !ctx.tx._done) {
|
|
18
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
|
}
|
|
20
|
+
|
|
21
|
+
return this.tx (tx => tx.dispatch(req)) // with root tx
|
|
22
22
|
}
|
|
23
|
+
|
|
23
24
|
if (!req.tx) req.tx = this // `this` is a tx from now on...
|
|
24
25
|
|
|
25
26
|
// Inform potential listeners // REVISIT: -> this should move into protocol adapters
|
|
@@ -112,7 +113,7 @@ const _ensure_target = (srv,req) => {
|
|
|
112
113
|
|
|
113
114
|
const _ensure_fqn = (x,p,srv, name = x[p]) => {
|
|
114
115
|
if (typeof name === 'string') {
|
|
115
|
-
if (srv
|
|
116
|
+
if (srv.isDatabaseService) return
|
|
116
117
|
if (srv.model && name in srv.model.definitions) return
|
|
117
118
|
if (name.startsWith(srv.namespace)) return
|
|
118
119
|
if (name.endsWith('_drafts')) return // REVISIT: rather fix test/fiori/localized-draft.test.js ?
|
package/lib/srv/srv-handlers.js
CHANGED
|
@@ -91,7 +91,7 @@ const _register = function (srv, phase, event, path, handler) { //NOSONAR
|
|
|
91
91
|
if (!path.startsWith(srv.name+'.')) path = `${srv.name}.${path}`
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
if (cds.env.
|
|
94
|
+
if (cds.env.fiori.lean_draft && cds.env.fiori.draft_compat) {
|
|
95
95
|
const entity = path && srv.model?.definitions[path.name || path]
|
|
96
96
|
if (['PATCH', 'CANCEL', 'NEW'].includes(event)) {
|
|
97
97
|
// delegate to drafts
|
package/lib/srv/srv-methods.js
CHANGED
|
@@ -58,7 +58,10 @@ const add_handler_for = (srv, def) => {
|
|
|
58
58
|
`)
|
|
59
59
|
const stub = srv[event] = function (...args) {
|
|
60
60
|
const req = { event, data:{} }, $ = args[0]
|
|
61
|
-
const target =
|
|
61
|
+
const target = $ && (
|
|
62
|
+
this.model.definitions[ $.name ]
|
|
63
|
+
|| this.entities[ $.name?.replace(`${this.name}.`,'') || $ ]
|
|
64
|
+
)
|
|
62
65
|
if (target) { //> bound action/function?
|
|
63
66
|
req.target = target; args.shift() // first argument is the target entity name
|
|
64
67
|
req.params = [ args.shift() ] // second argument is the target's primary key
|
|
@@ -86,7 +89,10 @@ const add_handler_for = (srv, def) => {
|
|
|
86
89
|
|
|
87
90
|
return this.send (req)
|
|
88
91
|
}
|
|
89
|
-
stub
|
|
92
|
+
Object.defineProperties(stub,{
|
|
93
|
+
name: {value: /[^.]+$/.exec(srv.name)[0] +'.'+ event},
|
|
94
|
+
_is_stub: {value:true},
|
|
95
|
+
})
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
|
package/lib/utils/cds-test.js
CHANGED
|
@@ -164,4 +164,7 @@ const spy = (o,f) => {
|
|
|
164
164
|
|
|
165
165
|
|
|
166
166
|
/** @type Test & ()=>Test */
|
|
167
|
-
module.exports = Object.
|
|
167
|
+
module.exports = Object.assign (
|
|
168
|
+
Object.setPrototypeOf ((..._) => (new Test).run(..._), Test.prototype),
|
|
169
|
+
{ Test }
|
|
170
|
+
)
|