@sap/cds 7.5.2 → 7.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 +79 -22
- package/app/index.js +1 -1
- package/lib/auth/index.js +3 -0
- package/lib/compile/extend.js +9 -4
- package/lib/compile/for/lean_drafts.js +3 -4
- package/lib/compile/load.js +11 -15
- package/lib/compile/minify.js +2 -4
- package/lib/compile/to/sql.js +6 -4
- package/lib/compile/to/srvinfo.js +25 -3
- package/lib/compile/to/yaml.js +1 -1
- package/lib/dbs/cds-deploy.js +7 -13
- package/lib/env/defaults.js +1 -10
- package/lib/env/schemas/cds-package.js +27 -0
- package/lib/env/schemas/cds-rc.js +693 -0
- package/lib/env/schemas/index.js +6 -4
- package/lib/i18n/localize.js +15 -1
- package/lib/index.js +40 -47
- package/lib/log/cds-error.js +6 -0
- package/lib/ql/Query.js +2 -1
- package/lib/ql/cds-ql.js +1 -2
- package/lib/ql/infer.js +0 -2
- package/lib/req/request.js +3 -6
- package/lib/srv/middlewares/trace.js +2 -2
- package/lib/srv/protocols/hcql.js +44 -30
- package/lib/srv/protocols/http.js +60 -0
- package/lib/srv/protocols/index.js +0 -7
- package/lib/srv/protocols/odata-v4.js +8 -2
- package/lib/srv/srv-api.js +129 -62
- package/lib/srv/srv-handlers.js +0 -1
- package/lib/srv/srv-models.js +1 -0
- package/lib/utils/cds-test.js +1 -1
- package/lib/utils/cds-utils.js +26 -0
- package/lib/utils/check-version.js +10 -13
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +22 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +3 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +89 -21
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/boundToCQN.js +4 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +1 -24
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +1 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/ApplyParser.js +3 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/TrustedResourceJsonSerializer.js +7 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +0 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +2 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +17 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +22 -2
- package/libx/_runtime/cds-services/services/utils/columns.js +1 -2
- package/libx/_runtime/common/aspects/Association.js +17 -9
- package/libx/_runtime/common/generic/crud.js +13 -22
- package/libx/_runtime/common/generic/etag.js +1 -1
- package/libx/_runtime/common/generic/input.js +9 -1
- package/libx/_runtime/common/generic/paging.js +3 -3
- package/libx/_runtime/common/generic/sorting.js +25 -15
- package/libx/_runtime/common/generic/stream.js +2 -16
- package/libx/_runtime/common/utils/copy.js +5 -0
- package/libx/_runtime/common/utils/cqn.js +1 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +4 -3
- package/libx/_runtime/common/utils/csn.js +0 -49
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +5 -5
- package/libx/_runtime/common/utils/generateOnCond.js +50 -25
- package/libx/_runtime/common/utils/resolveView.js +5 -44
- package/libx/_runtime/common/utils/rewriteAsterisks.js +17 -4
- package/libx/_runtime/common/utils/stream.js +16 -15
- package/libx/_runtime/common/utils/streamProp.js +25 -22
- package/libx/_runtime/db/Service.js +27 -8
- package/libx/_runtime/db/generic/input.js +6 -1
- package/libx/_runtime/db/generic/rewrite.js +3 -2
- package/libx/_runtime/db/query/read.js +15 -5
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +0 -11
- package/libx/_runtime/db/utils/columns.js +1 -0
- package/libx/_runtime/db/utils/stream.js +41 -0
- package/libx/_runtime/fiori/generic/read.js +2 -1
- package/libx/_runtime/fiori/generic/readOverDraft.js +1 -1
- package/libx/_runtime/fiori/lean-draft.js +216 -59
- package/libx/_runtime/hana/Service.js +1 -1
- package/libx/_runtime/hana/execute.js +53 -14
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +34 -15
- package/libx/_runtime/remote/Service.js +2 -1
- package/libx/_runtime/remote/utils/client.js +1 -1
- package/libx/_runtime/sqlite/Service.js +1 -1
- package/libx/_runtime/sqlite/execute.js +17 -5
- package/libx/odata/afterburner.js +58 -19
- package/libx/odata/cqn2odata.js +6 -8
- package/libx/odata/create.js +44 -0
- package/libx/odata/delete.js +25 -0
- package/libx/odata/error.js +8 -3
- package/libx/odata/metadata.js +6 -8
- package/libx/odata/service-document.js +1 -1
- package/libx/odata/update.js +110 -0
- package/libx/odata/utils.js +9 -6
- package/libx/outbox/index.js +48 -78
- package/libx/rest/RestAdapter.js +0 -3
- package/package.json +1 -1
- package/lib/env/schemas/cds-package.json +0 -17
- package/lib/env/schemas/cds-rc.json +0 -740
- package/lib/ql/STREAM.js +0 -90
package/lib/i18n/localize.js
CHANGED
|
@@ -140,7 +140,10 @@ function bundle4 (model, locale) {
|
|
|
140
140
|
if (!folders.length) return bundles[locale] = {}
|
|
141
141
|
|
|
142
142
|
const {i18n} = cds.env
|
|
143
|
-
let bundle = null
|
|
143
|
+
let bundle = Object.create(null)
|
|
144
|
+
bundle.toJSON = jsonWithAllProps // allows JSON.stringify with all inherited props
|
|
145
|
+
|
|
146
|
+
let locales = (
|
|
144
147
|
locale === i18n.fallback_bundle ? [ i18n.fallback_bundle ] :
|
|
145
148
|
locale === i18n.default_language ? [ i18n.fallback_bundle, i18n.default_language ] :
|
|
146
149
|
[ i18n.fallback_bundle, i18n.default_language, locale ]
|
|
@@ -158,6 +161,7 @@ function bundle4 (model, locale) {
|
|
|
158
161
|
Object.assign (b, next)
|
|
159
162
|
}
|
|
160
163
|
}
|
|
164
|
+
|
|
161
165
|
return bundles[locale] = bundle
|
|
162
166
|
}
|
|
163
167
|
|
|
@@ -255,3 +259,13 @@ function escapeXmlAttr (str) {
|
|
|
255
259
|
}
|
|
256
260
|
|
|
257
261
|
const escapeJson = str => str.replace(/"/g, '\\"')
|
|
262
|
+
|
|
263
|
+
function jsonWithAllProps() {
|
|
264
|
+
const res = {}
|
|
265
|
+
for (let key in this) {
|
|
266
|
+
if (typeof this[key] !== 'function') {
|
|
267
|
+
res[key] = this[key]
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return res
|
|
271
|
+
}
|
package/lib/index.js
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
|
-
if (process.env.CDS_STRICT_NODE_VERSION !== 'false') require('./utils/check-version')
|
|
1
|
+
if (process.env.CDS_STRICT_NODE_VERSION !== 'false') require ('./utils/check-version')
|
|
2
|
+
!(global.__cds_loaded_from ??= new Set).add(__filename) // track from where we loaded cds
|
|
3
|
+
|
|
4
|
+
/** @typedef { import('../apis/linked.js').LinkedCSN } Linked */
|
|
5
|
+
/** @typedef { import('./srv/srv-api') } Service */
|
|
2
6
|
|
|
3
7
|
const { EventEmitter } = require('node:events')
|
|
4
8
|
const { extend, lazify } = require('./lazy')
|
|
5
9
|
|
|
6
|
-
const cds = module.exports = new class cds extends EventEmitter {
|
|
10
|
+
const cds = module.exports = global.cds = new class cds extends EventEmitter {
|
|
11
|
+
|
|
12
|
+
async emit (eve, ...args) {
|
|
13
|
+
if (eve === 'served') for (let l of this.listeners(eve)) await l.call(this,...args)
|
|
14
|
+
else return super.emit (eve, ...args)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** CLI args */ cli = { command:'', options:{}, argv:[] }
|
|
18
|
+
/** Working dir */ root = process.cwd()
|
|
19
|
+
/** @type Linked */ model = undefined
|
|
20
|
+
/** @type Service */ db = undefined
|
|
7
21
|
|
|
8
22
|
// Configuration & Information
|
|
9
23
|
get requires() { return super.requires = this.env.requires._resolved() }
|
|
@@ -12,8 +26,6 @@ const cds = module.exports = new class cds extends EventEmitter {
|
|
|
12
26
|
get env() { return super.env = require('./env/cds-env').for('cds',this.root) }
|
|
13
27
|
get home() { return super.home = __dirname.slice(0,-4) }
|
|
14
28
|
get schema() { return super.schema = require('./env/schemas') } // REVISIT: Better move that to cds-dk?
|
|
15
|
-
cli = { command:'', options:{}, argv:[] }
|
|
16
|
-
root = process.cwd()
|
|
17
29
|
|
|
18
30
|
// Loading and Compiling Models
|
|
19
31
|
get compiler() { return super.compiler = require('./compile/cdsc') }
|
|
@@ -26,10 +38,9 @@ const cds = module.exports = new class cds extends EventEmitter {
|
|
|
26
38
|
get extend() { return super.extend = require('./compile/extend') }
|
|
27
39
|
get deploy() { return super.deploy = require('./dbs/cds-deploy') }
|
|
28
40
|
get localize() { return super.localize = require('./i18n/localize') }
|
|
29
|
-
/** @type {{definitions:{},extensions:[]}} */ model = undefined
|
|
30
|
-
/** @type Service */ db = undefined
|
|
31
41
|
|
|
32
42
|
// Model Reflection, Builtin types and classes
|
|
43
|
+
get entities() { return this.db?.entities || this.model?.entities }
|
|
33
44
|
get reflect() { return super.reflect = this.linked }
|
|
34
45
|
get linked() { return super.linked = require('./linked/models') }
|
|
35
46
|
get infer() { return super.infer = require('./ql/infer') }
|
|
@@ -47,10 +58,10 @@ const cds = module.exports = new class cds extends EventEmitter {
|
|
|
47
58
|
})}
|
|
48
59
|
|
|
49
60
|
// Providing and Consuming Services
|
|
50
|
-
/** @type {
|
|
51
|
-
|
|
52
|
-
_pending: {value:{}}
|
|
53
|
-
|
|
61
|
+
/** @type { Record<string,Service> } */ services = Object.defineProperties (
|
|
62
|
+
new class { *[Symbol.iterator](){ for (let e in this) yield this[e] } },
|
|
63
|
+
{ _pending: {value:{}} }
|
|
64
|
+
)
|
|
54
65
|
get server() { return super.server = require('../server') }
|
|
55
66
|
get serve() { return super.serve = require('./srv/cds-serve') }
|
|
56
67
|
get connect() { return super.connect = require('./srv/cds-connect') }
|
|
@@ -60,14 +71,7 @@ const cds = module.exports = new class cds extends EventEmitter {
|
|
|
60
71
|
get odata() { return super.odata = require('../libx/odata') }
|
|
61
72
|
get auth() { return super.auth = require('./auth') }
|
|
62
73
|
|
|
63
|
-
// awaiting async functions for some events
|
|
64
|
-
async emit (eve, ...args) {
|
|
65
|
-
if (eve === 'served') for (let l of this.listeners(eve)) await l.call(this,...args)
|
|
66
|
-
else return super.emit (eve, ...args)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
74
|
// Core Services API
|
|
70
|
-
/** @typedef {import './srv/srv-api'} Service */
|
|
71
75
|
get Service() { return super.Service = require('./srv/srv-api') }
|
|
72
76
|
get EventContext() { return super.EventContext = require('./req/context') }
|
|
73
77
|
get Request() { return super.Request = require('./req/request') }
|
|
@@ -85,7 +89,6 @@ const cds = module.exports = new class cds extends EventEmitter {
|
|
|
85
89
|
get context() { return this._context._for(this) }
|
|
86
90
|
set context(_) { this._context._for(this,_) }
|
|
87
91
|
get spawn() { return super.spawn = this._context.spawn }
|
|
88
|
-
tx(..._) { return (this.db || this.Service.prototype) .tx (..._) }
|
|
89
92
|
|
|
90
93
|
// Helpers
|
|
91
94
|
get utils() { return super.utils = require('./utils/cds-utils') }
|
|
@@ -94,31 +97,29 @@ const cds = module.exports = new class cds extends EventEmitter {
|
|
|
94
97
|
get test() { return super.test = require('./utils/cds-test') }
|
|
95
98
|
get log() { return super.log = require('./log/cds-log') }
|
|
96
99
|
get debug() { return super.debug = this.log.debug }
|
|
97
|
-
get lazify(){ return lazify }
|
|
98
|
-
get lazified(){ return lazify }
|
|
99
|
-
clone(
|
|
100
|
+
get lazify() { return lazify }
|
|
101
|
+
get lazified() { return lazify }
|
|
102
|
+
get clone() { return super.clone = this.utils.clone }
|
|
100
103
|
exit(code){ return cds.shutdown ? cds.shutdown() : process.exit(code) }
|
|
101
104
|
|
|
102
105
|
// Querying and Databases
|
|
103
|
-
get ql()
|
|
104
|
-
|
|
105
|
-
run
|
|
106
|
-
foreach
|
|
107
|
-
stream
|
|
108
|
-
read
|
|
109
|
-
create
|
|
110
|
-
insert
|
|
111
|
-
update
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
106
|
+
get ql() { return super.ql = require('./ql/cds-ql') }
|
|
107
|
+
tx (..._) { return (this.db || this.Service.prototype) .tx (..._) }
|
|
108
|
+
run (..._) { return (this.db || this.error._no_primary_db).run(..._) }
|
|
109
|
+
foreach (..._) { return (this.db || this.error._no_primary_db).foreach(..._) }
|
|
110
|
+
stream (..._) { return (this.db || this.error._no_primary_db).stream(..._) }
|
|
111
|
+
read (..._) { return (this.db || this.error._no_primary_db).read(..._) }
|
|
112
|
+
create (..._) { return (this.db || this.error._no_primary_db).create(..._) }
|
|
113
|
+
insert (..._) { return (this.db || this.error._no_primary_db).insert(..._) }
|
|
114
|
+
update (..._) { return (this.db || this.error._no_primary_db).update(..._) }
|
|
115
|
+
upsert (..._) { return (this.db || this.error._no_primary_db).upsert(..._) }
|
|
116
|
+
delete (..._) { return (this.db || this.error._no_primary_db).delete(..._) }
|
|
117
|
+
disconnect (..._) { return (this.db || this.error._no_primary_db).disconnect(..._) }
|
|
115
118
|
|
|
116
119
|
// legacy and to be moved stuff -> hidden for tools in cds.__proto__
|
|
117
|
-
/** @deprecated */
|
|
118
|
-
get build() { return
|
|
119
|
-
|
|
120
|
-
Either update @sap/cds-dk to version 7 or downgrade @sap/cds to version 6 instead.\
|
|
121
|
-
')}
|
|
120
|
+
/** @deprecated */ transaction (..._) { return (this.db||this.error._no_primary_db).transaction(..._) }
|
|
121
|
+
/** @deprecated */ get build() { return super.build = this.error._outdated_dk }
|
|
122
|
+
/** @deprecated */ get in() { return super.in = cwd => !cwd ? this : {__proto__:this, cwd, env: this.env.for('cds',cwd) } }
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
// add global cds.ql commands
|
|
@@ -128,7 +129,6 @@ extend (global) .with (class {
|
|
|
128
129
|
static get UPSERT() { return cds.ql.UPSERT }
|
|
129
130
|
static get UPDATE() { return cds.ql.UPDATE }
|
|
130
131
|
static get DELETE() { return cds.ql.DELETE }
|
|
131
|
-
static get STREAM() { return cds.ql.STREAM }
|
|
132
132
|
static get CREATE() { return cds.ql.CREATE }
|
|
133
133
|
static get DROP() { return cds.ql.DROP }
|
|
134
134
|
static get CDL() { return cds.parse.CDL }
|
|
@@ -136,15 +136,8 @@ extend (global) .with (class {
|
|
|
136
136
|
static get CXL() { return cds.parse.CXL }
|
|
137
137
|
})
|
|
138
138
|
|
|
139
|
-
// can be used to later check that one has only one cds object
|
|
140
|
-
;(global.__cds_loaded_from ??= new Set).add(__filename)
|
|
141
|
-
global.cds = cds // REVISIT: using global.cds seems wrong
|
|
142
|
-
|
|
143
139
|
// install jest util if jest is defined
|
|
144
140
|
if (process.env.CDS_JEST_MEM_FIX && typeof jest !== 'undefined') require('./utils/jest.js')
|
|
145
141
|
|
|
146
142
|
// Allow for import cds from '@sap/cds' without esModuleInterop
|
|
147
|
-
Object.defineProperties(module.exports, {
|
|
148
|
-
default: { value: module.exports },
|
|
149
|
-
__esModule: { value: true },
|
|
150
|
-
})
|
|
143
|
+
Object.defineProperties(module.exports, { default: {value:module.exports}, __esModule: {value:true} })
|
package/lib/log/cds-error.js
CHANGED
|
@@ -85,3 +85,9 @@ exports._no_primary_db = new Proxy ({},{ get: function fn(_,p) { error (`Not con
|
|
|
85
85
|
cds ${process.argv[2]} --in-memory` : ''}`
|
|
86
86
|
|
|
87
87
|
,{},fn) }})
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
exports._outdated_dk = () => error `
|
|
91
|
+
This application uses @sap/cds version >= 7, which is not compatible with the installed @sap/cds-dk version 6.
|
|
92
|
+
Either update @sap/cds-dk to version 7 or downgrade @sap/cds to version 6 instead.
|
|
93
|
+
`
|
package/lib/ql/Query.js
CHANGED
|
@@ -33,7 +33,8 @@ class Query {
|
|
|
33
33
|
get then() {
|
|
34
34
|
const srv = this._srv || cds.db || cds.error `Can't execute query as no primary database is connected.`
|
|
35
35
|
const q = new AsyncResource('await cds.query')
|
|
36
|
-
|
|
36
|
+
// Temporary solution for cds.stream in .then. Remove with the next major release.
|
|
37
|
+
return (r,e) => q.runInAsyncScope (srv.run, srv, this) .then(rt => { rt = this._stream && rt ? Object.values(rt)[0] : rt; r(rt) }, e)
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
_target4 (...args) {
|
package/lib/ql/cds-ql.js
CHANGED
package/lib/ql/infer.js
CHANGED
package/lib/req/request.js
CHANGED
|
@@ -41,8 +41,7 @@ class Request extends require('./event') {
|
|
|
41
41
|
if (q.INSERT) return this._set ('path', _path4 (q.INSERT,'into'))
|
|
42
42
|
if (q.UPSERT) return this._set ('path', _path4 (q.UPSERT,'into'))
|
|
43
43
|
if (q.UPDATE) return this._set ('path', _path4 (q.UPDATE,'entity'))
|
|
44
|
-
if (q.DELETE) return this._set ('path', _path4 (q.DELETE,'from'))
|
|
45
|
-
if (q.STREAM) return this._set ('path', _path4 (q.STREAM,'from') || _path4 (q.STREAM,'into'))
|
|
44
|
+
if (q.DELETE) return this._set ('path', _path4 (q.DELETE,'from'))
|
|
46
45
|
}
|
|
47
46
|
const {_} = this
|
|
48
47
|
if (_.target) return this._set ('path', _.target.name)
|
|
@@ -66,8 +65,7 @@ class Request extends require('./event') {
|
|
|
66
65
|
q?.INSERT?.into ||
|
|
67
66
|
q?.UPSERT?.into ||
|
|
68
67
|
q?.UPDATE?.entity ||
|
|
69
|
-
q?.DELETE?.from
|
|
70
|
-
q?.STREAM?.from || q?.STREAM?.into
|
|
68
|
+
q?.DELETE?.from
|
|
71
69
|
)
|
|
72
70
|
|
|
73
71
|
const {target} = this; if (!target) return undefined
|
|
@@ -171,8 +169,7 @@ const Http2Crud = {
|
|
|
171
169
|
DELETE: 'DELETE',
|
|
172
170
|
}
|
|
173
171
|
|
|
174
|
-
const SQL2Crud = {
|
|
175
|
-
STREAM: 'STREAM',
|
|
172
|
+
const SQL2Crud = {
|
|
176
173
|
SELECT: 'READ',
|
|
177
174
|
INSERT: 'CREATE',
|
|
178
175
|
UPSERT: 'UPSERT',
|
|
@@ -55,9 +55,9 @@ function _instrument_cds_services (_get_perf) {
|
|
|
55
55
|
const perf = _get_perf(req)
|
|
56
56
|
if (perf) {
|
|
57
57
|
const pe = perf.log (this.name, '-', req.event, req.path||'')
|
|
58
|
-
var _done =
|
|
58
|
+
var _done = () => perf.done(pe)
|
|
59
59
|
}
|
|
60
|
-
return handle.apply (this, arguments) .
|
|
60
|
+
return handle.apply (this, arguments) .finally (_done)
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -1,37 +1,51 @@
|
|
|
1
|
-
const cds = require('../../index'), { decodeURIComponent } = cds.utils
|
|
2
|
-
const LOG = cds.log('hcql')
|
|
3
1
|
const express = require('express') // eslint-disable-line cds/no-missing-dependencies
|
|
2
|
+
const cds = require('../../index')
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
return express.Router()
|
|
8
|
-
.use(express.json()) //> for application/json -> cqn
|
|
9
|
-
.use(express.text()) //> for text/plain -> cql -> cqn
|
|
10
|
-
|
|
11
|
-
/** Returns CSN schema in response to /<srv>/$csn requests */
|
|
12
|
-
.get('/\\$csn', (_, res) => {
|
|
13
|
-
let csn = cds.minify (cds.model, { service: srv.name })
|
|
14
|
-
res.json(csn)
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
/** Convenience route for REST-style request formats like that: */
|
|
18
|
-
.get('/:entity/:id?(%20:tail)?', (req, _, next) => {
|
|
19
|
-
let { entity, id, tail } = req.params, q = SELECT.from(entity, id)
|
|
20
|
-
if (is_string(req.body)) tail = req.body
|
|
21
|
-
else if (is_array(req.body)) q.columns(req.body)
|
|
22
|
-
else Object.assign(q.SELECT, req.body)
|
|
23
|
-
if (tail) q = { SELECT: { ...CQL(`SELECT from _ ${tail}`).SELECT, ...q.SELECT } }
|
|
24
|
-
req.body = q; next() // delegating to main handler
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
/** The actual protocol adapter. */
|
|
28
|
-
.use((req, res, next) => {
|
|
29
|
-
let q = req.body; if (is_string(q)) q = CQL(q)
|
|
30
|
-
LOG.info (req.method, decodeURIComponent(req.originalUrl), { ...q })
|
|
31
|
-
return srv.run(q).then(r => res.json(r)).catch(next)
|
|
32
|
-
})
|
|
4
|
+
class HCQLAdapter extends require('./http') {
|
|
33
5
|
|
|
6
|
+
schema4 (srv) {
|
|
7
|
+
return cds.minify (cds.model, { service: srv.name })
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
router4 (srv) { return super.router4 (srv)
|
|
11
|
+
|
|
12
|
+
/** Return CSN schema in response to /<srv>/$csn requests */
|
|
13
|
+
.get('/\\$csn', (_, res) => res.json (this.schema4(srv)))
|
|
14
|
+
|
|
15
|
+
.use(express.json()) //> for application/json -> cqn
|
|
16
|
+
.use(express.text()) //> for text/plain -> cql -> cqn
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Convenience route for REST-style request formats like that:
|
|
20
|
+
* GET /browse/Books { ID, title, author.name as author } where stock < 100
|
|
21
|
+
* GET /browse/Books/201 { ID, title, author.name as author }
|
|
22
|
+
*/
|
|
23
|
+
.get('/:entity/:id?(%20:tail)?', (req, _, next) => {
|
|
24
|
+
let { entity, id, tail } = req.params, q = SELECT.from(entity, id)
|
|
25
|
+
if (is_string(req.body)) tail = req.body
|
|
26
|
+
else if (is_array(req.body)) q.columns(req.body)
|
|
27
|
+
else Object.assign(q.SELECT, req.body)
|
|
28
|
+
if (tail) q = { SELECT: { ...CQL(`SELECT from _ ${tail}`).SELECT, ...q.SELECT } }
|
|
29
|
+
req.body = q; next() // delegating to main handler
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The actual protocol adapter, handling all requests.
|
|
34
|
+
*/
|
|
35
|
+
.use((req, res, next) => {
|
|
36
|
+
let q = this.query4(req)
|
|
37
|
+
this.log(req,q)
|
|
38
|
+
return srv.run(q).then(r => res.json(r)).catch(next)
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
query4 (req) {
|
|
43
|
+
if (typeof req.body === 'string') return cds.parse.cql (req.body)
|
|
44
|
+
return req.body //> a plain CQN object
|
|
45
|
+
}
|
|
34
46
|
}
|
|
35
47
|
|
|
36
48
|
const is_string = x => typeof x === 'string'
|
|
37
49
|
const is_array = Array.isArray
|
|
50
|
+
|
|
51
|
+
module.exports = HCQLAdapter
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const express = require('express') // eslint-disable-line cds/no-missing-dependencies
|
|
2
|
+
const cds = require('../../index')
|
|
3
|
+
const { inspect } = require('util')
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
module.exports = class HttpAdapter {
|
|
7
|
+
|
|
8
|
+
constructor (srv) {
|
|
9
|
+
this.kind = this.constructor.name.replace(/Adapter$/,'').toLowerCase()
|
|
10
|
+
this.log = cds.log (this.kind)
|
|
11
|
+
return this.router4 (srv)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
router4 (srv) {
|
|
15
|
+
return express.Router()
|
|
16
|
+
.use(this.early_access_check4(srv))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
log (req, cqn) {
|
|
20
|
+
this.log.info (req.method, decodeURIComponent(req.originalUrl), inspect(cqn,{colors:true,depth:11}))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Does early checks on required roles to reject early */
|
|
24
|
+
early_access_check4 (srv) {
|
|
25
|
+
|
|
26
|
+
// Resolve required roles statically once....
|
|
27
|
+
const d = srv.definition
|
|
28
|
+
const roles = d['@requires'] || d['@restrict']?.map(r => r.to).flat()
|
|
29
|
+
|| cds.env.requires.auth?.restrict_all_services !== false && ['authenticated-user']
|
|
30
|
+
|
|
31
|
+
// ... and return a handler function accordingly -> PROBLEM: Extensibility
|
|
32
|
+
if (!roles) return (req, res, next) => next() //> no handlers required
|
|
33
|
+
|
|
34
|
+
const required_roles = Array.isArray(roles) ? roles : [roles]
|
|
35
|
+
return function early_access_check (req, res, next) {
|
|
36
|
+
let u = req.user; if (!u?.is) u = new cds.User(u) // revisit
|
|
37
|
+
if (required_roles.some(r => u.is(r))) return next()
|
|
38
|
+
// Following demonstrates how to directly send responses from here...
|
|
39
|
+
// However, in order to allow others to plug in error handlers throwing errors is better.
|
|
40
|
+
// For example, also for ourselves to obfucscate error details in production mode.
|
|
41
|
+
// throw cds.error ({ status: 403, code: 'REQUIRES_AUTH_USER', details: `Requires any of [ ${roles} ]` })
|
|
42
|
+
if (!u._is_anonymous) return res.status(403).send(`User '${u.id}' is lacking required roles: [ ${roles} ]`)
|
|
43
|
+
else if (!req._login) return res.status(401).send('Requires authenticated user')
|
|
44
|
+
else return req._login()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/*
|
|
50
|
+
function MTXRouter (srv) {
|
|
51
|
+
this.routers = {
|
|
52
|
+
t1_fta: 'new HCQLAdapter(srv.for(t1))',
|
|
53
|
+
t1_fta_ftb: 'new HCQLAdapter(srv.for(t1))',
|
|
54
|
+
t2: 'new HCQLAdapter(srv.for(t2))',
|
|
55
|
+
}
|
|
56
|
+
return express.Router().use((req,res,next) => {
|
|
57
|
+
return this.routers[req.tenant].handle(req,res,next)
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
*/
|
|
@@ -185,12 +185,5 @@ const _slugified = name => (
|
|
|
185
185
|
)
|
|
186
186
|
|
|
187
187
|
|
|
188
|
-
const using_old_paths = (path) => `PLEASE NOTE:\n
|
|
189
|
-
With @sap/cds version 7, default service paths have changed to '${path}/<srv>'.
|
|
190
|
-
If you use SAP Fiori Elements, make sure to adapt the 'dataSources.uri' paths
|
|
191
|
-
in 'manifest.json' files accordingly. For more information, see the release notes at
|
|
192
|
-
https://cap.cloud.sap/docs/releases/jun23.
|
|
193
|
-
`
|
|
194
|
-
|
|
195
188
|
module.exports = new Protocols
|
|
196
189
|
if (!cds.requires.middlewares) require('./_legacy')
|
|
@@ -29,10 +29,16 @@ module.exports = function ODataAdapter(srv) {
|
|
|
29
29
|
router.use(/^\/$/, require('../../../libx/odata/service-document')(srv))
|
|
30
30
|
router.use('/\\$metadata', require('../../../libx/odata/metadata')(srv))
|
|
31
31
|
router.get('*', require('../../../libx/odata/read')(srv))
|
|
32
|
+
router.delete('*', require('../../../libx/odata/delete')(srv))
|
|
33
|
+
// REVISIT do we want to build our own body parser logic?
|
|
34
|
+
router.use(express.json())
|
|
35
|
+
router.post('*', require('../../../libx/odata/create')(srv))
|
|
36
|
+
router.put('*', require('../../../libx/odata/update')(srv))
|
|
37
|
+
router.patch('*', require('../../../libx/odata/update')(srv))
|
|
32
38
|
router.use(require('../../../libx/odata/error')(srv))
|
|
39
|
+
} else {
|
|
40
|
+
router.use(libx.to.odata_v4(srv))
|
|
33
41
|
}
|
|
34
42
|
|
|
35
|
-
router.use(libx.to.odata_v4(srv))
|
|
36
|
-
|
|
37
43
|
return router
|
|
38
44
|
}
|