@sap/cds 9.6.3 → 9.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 +55 -0
- package/bin/serve.js +38 -26
- package/lib/compile/for/flows.js +92 -19
- package/lib/compile/for/lean_drafts.js +0 -47
- package/lib/compile/for/nodejs.js +47 -14
- package/lib/compile/for/odata.js +20 -0
- package/lib/compile/load.js +22 -25
- package/lib/compile/minify.js +29 -11
- package/lib/compile/parse.js +1 -1
- package/lib/compile/resolve.js +133 -76
- package/lib/compile/to/csn.js +2 -2
- package/lib/dbs/cds-deploy.js +48 -43
- package/lib/env/cds-env.js +6 -0
- package/lib/env/cds-requires.js +9 -3
- package/lib/index.js +3 -1
- package/lib/plugins.js +1 -1
- package/lib/req/request.js +2 -2
- package/lib/srv/bindings.js +17 -5
- package/lib/srv/middlewares/auth/index.js +7 -5
- package/lib/srv/protocols/hcql.js +8 -3
- package/lib/srv/protocols/index.js +1 -0
- package/lib/utils/cds-utils.js +28 -1
- package/lib/utils/colors.js +1 -1
- package/libx/_runtime/common/generic/assert.js +1 -7
- package/libx/_runtime/common/generic/flows.js +14 -4
- package/libx/_runtime/common/utils/resolveView.js +4 -0
- package/libx/_runtime/fiori/lean-draft.js +8 -3
- package/libx/_runtime/messaging/common-utils/authorizedRequest.js +4 -0
- package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +12 -12
- package/libx/_runtime/messaging/enterprise-messaging.js +1 -1
- package/libx/_runtime/messaging/http-utils/token.js +18 -3
- package/libx/_runtime/messaging/message-queuing.js +7 -7
- package/libx/_runtime/remote/Service.js +3 -1
- package/libx/_runtime/remote/utils/client.js +1 -0
- package/libx/_runtime/remote/utils/query.js +0 -1
- package/libx/odata/middleware/batch.js +128 -112
- package/libx/odata/middleware/error.js +7 -3
- package/libx/odata/parse/afterburner.js +10 -11
- package/libx/odata/parse/grammar.peggy +4 -2
- package/libx/odata/parse/parser.js +1 -1
- package/libx/odata/utils/odataBind.js +8 -2
- package/libx/queue/index.js +3 -1
- package/package.json +4 -7
- package/srv/outbox.cds +1 -1
- package/srv/ucl-service.cds +3 -5
- package/bin/colors.js +0 -2
- package/libx/_runtime/.eslintrc +0 -14
package/lib/compile/minify.js
CHANGED
|
@@ -26,20 +26,20 @@ class Minifier {
|
|
|
26
26
|
// If o.services is specified, only keep matching services and their children
|
|
27
27
|
const rx = o.services == 'all' || o.services == '/all/i' ? {test:()=>true} : o.services
|
|
28
28
|
for (let [s,d] of all) if (d.kind === 'service' && rx.test(s)) {
|
|
29
|
-
this.keep
|
|
29
|
+
this.keep(s,d); children (s, (c,d) => this.keep(c,d))
|
|
30
30
|
}
|
|
31
31
|
} else {
|
|
32
32
|
// Otherwise first mark all external services and their children as initially skipped
|
|
33
33
|
for (let [s,d] of all) if (d.kind === 'service' && _skip_service(s,d)) {
|
|
34
|
-
skipped[s] = 0; children (s, (c,d) => d.kind in events ? this.keep
|
|
34
|
+
skipped[s] = 0; children (s, (c,d) => d.kind in events ? this.keep(c,d,s) : skipped[c] = s) // used later on in this.keep()
|
|
35
35
|
}
|
|
36
36
|
// Then keep all own services and their children
|
|
37
37
|
for (let [s,d] of all) if (d.kind === 'service' && !(s in skipped)) {
|
|
38
|
-
this.keep
|
|
38
|
+
this.keep(s,d); children (s, (c,d) => d.kind in keep ? this.keep(c,d) : skipped[c] = 0)
|
|
39
39
|
}
|
|
40
40
|
// Also keep remaining non-service entities
|
|
41
41
|
for (let [e,d] of all) if (d.kind === 'entity') {
|
|
42
|
-
e in kept || e in skipped || _skip_entity(e,d) || this.keep
|
|
42
|
+
e in kept || e in skipped || _skip_entity(e,d) || this.keep(e,d)
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -58,8 +58,8 @@ class Minifier {
|
|
|
58
58
|
if (d.target) this.keep (d.target) // has to go first w/o return for redirected targets
|
|
59
59
|
if (d.type in this.defs) return this.keep (d.type) // return to avoid endless recursion
|
|
60
60
|
if (d.type?.ref) return this.keep (d.type.ref[0]) // return to avoid endless recursion
|
|
61
|
-
if (d.projection) this.view (d.projection)
|
|
62
|
-
if (d.query) this.view (d.query)
|
|
61
|
+
if (d.projection) this.view (d.projection,d)
|
|
62
|
+
if (d.query) this.view (d.query,d)
|
|
63
63
|
if (d.items) this.walk (d.items)
|
|
64
64
|
if (d.returns) this.walk (d.returns)
|
|
65
65
|
for (let e in d.elements) this.walk (d.elements[e])
|
|
@@ -69,20 +69,38 @@ class Minifier {
|
|
|
69
69
|
// Note: this ^^^^^^^^^^^^ is required for cdsc.recompile; with delete d.includes, redirects in AFC broke
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
view (q) {
|
|
72
|
+
view (q,v) {
|
|
73
73
|
if (q.SELECT) q = q.SELECT // i.e. entity as select from ...
|
|
74
74
|
if (q.mixin) for (let e in q.mixin) this.walk (q.mixin[e])
|
|
75
|
-
if (q.from?.ref)
|
|
75
|
+
if (q.from?.ref) {
|
|
76
|
+
let r = q.from.ref[0]; if (r.id) r = r.id; if (r in this.kept) return
|
|
77
|
+
if (v?.['@cds.minify'] === 'unused-elements') return this.keep_columns (q)
|
|
78
|
+
else return this.keep (r) // keep sources of views
|
|
79
|
+
}
|
|
76
80
|
if (q.from?.join) return q.from.args.forEach (from => this.view ({from}))
|
|
77
81
|
if (q.SET) return q.SET.args.forEach (q => this.view (q.SELECT||q))
|
|
78
|
-
function _source (r) { return r.id || r }
|
|
79
82
|
}
|
|
80
83
|
|
|
81
|
-
|
|
84
|
+
keep_columns (q) {
|
|
85
|
+
let r = q.from.ref[0]; if (r.id) r = r.id; if (r in this.kept) return
|
|
86
|
+
let e = this.defs[r]; if (!e) return
|
|
87
|
+
let all = e.elements, kept = {}
|
|
88
|
+
if (q.columns) _keep (q.columns)
|
|
89
|
+
if (q.where) _keep (q.where)
|
|
90
|
+
e.elements = kept
|
|
91
|
+
return this.keep (r,e)
|
|
92
|
+
function _keep (xx) { for (let x of xx) {
|
|
93
|
+
if (x.func) { _keep (x.args); continue }
|
|
94
|
+
if (x.xpr) { _keep (x.xpr); continue }
|
|
95
|
+
if (x.ref) { let [r] = x.ref; if (r in all) kept[r] = all[r] }
|
|
96
|
+
}}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
keep (n,d,parent) {
|
|
82
100
|
if (n in this.kept) return; else d ??= this.defs[n]
|
|
83
101
|
if (d) this.walk (this.kept[n] = d, n); else return
|
|
84
102
|
let texts = this.defs[n+'.texts']; if (texts) this.keep (n+'.texts', texts)
|
|
85
|
-
let
|
|
103
|
+
let p = parent ?? this.skipped[n]; if (p) this.keep(p) // keep initially skipped services
|
|
86
104
|
}
|
|
87
105
|
|
|
88
106
|
}
|
package/lib/compile/parse.js
CHANGED
|
@@ -128,7 +128,7 @@ const native = {
|
|
|
128
128
|
SESSION_USER : { func: 'session_user' },
|
|
129
129
|
SYSUUID : { func: 'sysuuid' },
|
|
130
130
|
}
|
|
131
|
-
const is_cqn = x => typeof x === 'object' && (
|
|
131
|
+
const is_cqn = x => x !== null && typeof x === 'object' && (
|
|
132
132
|
'ref' in x ||
|
|
133
133
|
'val' in x ||
|
|
134
134
|
'xpr' in x ||
|
package/lib/compile/resolve.js
CHANGED
|
@@ -1,109 +1,166 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
const suffixes = [ '.csn', '.cds', sep+'index.csn', sep+'index.cds', sep+'csn.json' ]
|
|
4
|
-
|
|
1
|
+
const resolve = module.exports = exports = cds_resolve
|
|
2
|
+
const cds = require('..'), {fs,path} = cds.utils
|
|
5
3
|
|
|
6
4
|
/**
|
|
7
|
-
* Resolves given model references to an array of absolute filenames.
|
|
8
|
-
* For the model references, all these are accepted:
|
|
9
|
-
* - with suffix or without → will append `.csn|cds`, `/index.csn|cds`
|
|
10
|
-
* - absolute refs like `@sap/cds/common`
|
|
11
|
-
* - local refs with leading `.` or without, e.g. `srv/cat-service`
|
|
12
|
-
* - directory names → will fetch all contained `.csn` and `.cds` files
|
|
13
|
-
* - arrays of any of the above
|
|
14
|
-
* @returns and array of absolute filenames
|
|
15
|
-
*/
|
|
16
|
-
|
|
5
|
+
* Resolves given model references to an array of absolute filenames.
|
|
6
|
+
* For the model references, all these are accepted:
|
|
7
|
+
* - with suffix or without → will append `.csn|cds`, `/index.csn|cds`
|
|
8
|
+
* - absolute refs like `@sap/cds/common`
|
|
9
|
+
* - local refs with leading `.` or without, e.g. `srv/cat-service`
|
|
10
|
+
* - directory names → will fetch all contained `.csn` and `.cds` files
|
|
11
|
+
* - arrays of any of the above
|
|
12
|
+
* @returns and array of absolute filenames
|
|
13
|
+
*/
|
|
14
|
+
function cds_resolve (refs,o) {
|
|
15
|
+
if (refs?._resolved) return refs //> already resolved
|
|
16
|
+
let o2 = resolve.options(o)
|
|
17
|
+
if (Array.isArray(refs)) return resolve.many (refs.flat(),o2)
|
|
18
|
+
else return resolve.single (refs,o2)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
resolve.locations = (model='*', o) => resolve(model, { dry:true, ...o })
|
|
22
|
+
|
|
23
|
+
resolve.options = function (options) {
|
|
24
|
+
|
|
25
|
+
const o = options === false ? { dry:true } : {...options}
|
|
26
|
+
const cwd = o.root ??= cds.root
|
|
27
|
+
|
|
28
|
+
// return cached prior result, if any
|
|
29
|
+
const caches = o.cache || exports.cache
|
|
30
|
+
if (cwd in caches) return { ...caches[cwd], ...o }
|
|
31
|
+
else caches[cwd] = { paths: [ cwd ], cached:{} }
|
|
32
|
+
|
|
33
|
+
// prepare module lookup paths
|
|
34
|
+
const paths = [ cwd ]
|
|
35
|
+
const node_modules = (o.env||cds.env).cdsc.moduleLookupDirectories
|
|
36
|
+
const a = cwd.split(path.sep), n = a.length
|
|
37
|
+
for (let each of node_modules) paths.push (
|
|
38
|
+
...a.map ((_,i,a)=> a.slice(0,n-i).join(path.sep) + path.sep + each)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
// cache and return
|
|
42
|
+
const cache = caches[cwd] = { paths, cached:{} }
|
|
43
|
+
return { ...o, ...cache }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
resolve.all = function (o) {
|
|
48
|
+
|
|
49
|
+
const _required = env => Object.values(env.requires) .map (r => r.model) .filter(x=>x) .flat()
|
|
50
|
+
const env = o.env || cds.env
|
|
51
|
+
|
|
52
|
+
// resolve(...,false) => return cds.env.roots + all required models
|
|
53
|
+
if (o.dry) return [ ...env.roots, ...new Set(_required(env)) ]
|
|
54
|
+
|
|
55
|
+
// return cached prior result, if any
|
|
56
|
+
const {cached} = o; if ('*' in cached) return cached['*']
|
|
57
|
+
else cached['*'] = [] // important to avoid endless recursion on '*'
|
|
58
|
+
|
|
59
|
+
// resolve all roots, plus all cds.required.models (unless csn.json already there)
|
|
60
|
+
const files = resolve.many (env.roots,o) || []
|
|
61
|
+
const is_csn_json = files.length === 1 && files[0].endsWith('csn.json')
|
|
62
|
+
if (!is_csn_json) files.push (...resolve.many (_required(env),o)||[])
|
|
63
|
+
|
|
64
|
+
// cache and return resolved files
|
|
65
|
+
return cached['*'] = _resolved (files)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
resolve.many = function (models, o) {
|
|
70
|
+
const resolved = _distinct(models) .reduce ((p,n) => p.concat (resolve.single(n,o)||[]), [])
|
|
71
|
+
return o.dry ? _distinct(resolved.flat()) : _resolved (resolved)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
resolve.single = function (model,o) {
|
|
76
|
+
|
|
17
77
|
if (!model || model === '--') return
|
|
18
|
-
if (model
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return o.dry || o === false ? [...new Set(resolved.flat())] : _resolved (resolved)
|
|
23
|
-
}
|
|
24
|
-
if (model.endsWith('/*')) return _resolve_subdirs_in(model,o,this)
|
|
78
|
+
if (model == '*') return resolve.all(o)
|
|
79
|
+
|
|
80
|
+
// handle subdirectory patterns like 'app/*', 'srv/*', 'fts/*'
|
|
81
|
+
if (model.endsWith('/*')) return resolve.nested (model,o)
|
|
25
82
|
|
|
26
|
-
|
|
27
|
-
const
|
|
83
|
+
// check cache, and return if already resolved
|
|
84
|
+
const {cached} = o, local = path.resolve (o.root, model)
|
|
28
85
|
let id = model.startsWith('.') ? local : model
|
|
29
|
-
if (id in cached)
|
|
86
|
+
if (id in cached) return cached[id]
|
|
30
87
|
|
|
31
88
|
// expand @sap/cds by cds.home
|
|
32
|
-
if (id.startsWith('@sap/cds/')) id =
|
|
89
|
+
if (id.startsWith('@sap/cds/')) id = cds.home + id.slice(8)
|
|
33
90
|
|
|
34
91
|
// fetch file with .cds/.csn suffix as is
|
|
35
92
|
if (/\.(csn|cds)$/.test(id)) try {
|
|
36
|
-
return cached[id] = _resolved ([ _resolve (id,
|
|
93
|
+
return cached[id] = _resolved ([ _resolve (id,o) ])
|
|
37
94
|
} catch {/* ignored */}
|
|
38
95
|
|
|
39
96
|
// try to resolve file with one of the suffixes
|
|
40
|
-
for (let tail of o.suffixes || suffixes) try {
|
|
41
|
-
return cached[id] = _resolved ([ _resolve (id+tail,
|
|
97
|
+
for (let tail of o.suffixes || exports.suffixes) try {
|
|
98
|
+
return cached[id] = _resolved ([ _resolve (id+tail,o) ])
|
|
42
99
|
} catch {/* ignored */}
|
|
43
100
|
|
|
44
|
-
// fetch all in a
|
|
101
|
+
// fetch all in a folder
|
|
45
102
|
if (o.all !== false) try {
|
|
46
|
-
|
|
47
|
-
for (let f of files) if (f.endsWith('.csn')) {
|
|
48
|
-
all.push (unique[f.slice(0,-4)] = join(local,f))
|
|
49
|
-
}
|
|
50
|
-
for (let f of files) if (f.endsWith('.cds')) {
|
|
51
|
-
unique[f.slice(0,-4)] || all.push (join(local,f))
|
|
52
|
-
}
|
|
53
|
-
return cached[id] = _resolved (all)
|
|
103
|
+
return cached[id] = _resolved (resolve.folder (local,o))
|
|
54
104
|
} catch {/* ignored */}
|
|
55
105
|
|
|
56
106
|
// fetch file without suffix
|
|
57
107
|
if (o.any !== false && !id.endsWith('/')) try { // NOTE: this also finds .js files!
|
|
58
|
-
return cached[id] = _resolved ([ _resolve (id,
|
|
108
|
+
return cached[id] = _resolved ([ _resolve (id,o) ])
|
|
59
109
|
} catch {/* ignored */}
|
|
60
110
|
|
|
111
|
+
// not found
|
|
112
|
+
return cached[id] = null
|
|
61
113
|
}
|
|
62
114
|
|
|
63
115
|
|
|
64
|
-
|
|
116
|
+
resolve.folder = function (folder) {
|
|
117
|
+
const files = fs.readdirSync(folder), all=[], unique={}
|
|
118
|
+
for (let f of files) if (f.endsWith('.csn')) {
|
|
119
|
+
all.push (unique[f.slice(0,-4)] = path.join(folder,f))
|
|
120
|
+
}
|
|
121
|
+
for (let f of files) if (f.endsWith('.cds')) {
|
|
122
|
+
unique[f.slice(0,-4)] || all.push (path.join(folder,f))
|
|
123
|
+
}
|
|
124
|
+
return all
|
|
125
|
+
}
|
|
65
126
|
|
|
66
127
|
|
|
67
|
-
|
|
68
|
-
const _resolve = require('module')._resolveFilename
|
|
128
|
+
resolve.nested = function (pattern,o) {
|
|
69
129
|
|
|
70
|
-
|
|
71
|
-
const {
|
|
72
|
-
const cache = o.cache || exports.cache
|
|
73
|
-
const cached = cache['*']; if (cached) return cached
|
|
74
|
-
cache['*'] = [] // important to avoid endless recursion on '*'
|
|
75
|
-
const sources = cds.resolve (roots,o) || []
|
|
76
|
-
if (!(sources.length === 1 && sources[0].endsWith('csn.json'))) // REVISIT: why is that? -> pre-compiled gen/csn.json?
|
|
77
|
-
sources.push (...cds.resolve (_required(cds,o.env),o)||[])
|
|
78
|
-
return cache['*'] = _resolved (sources)
|
|
79
|
-
}
|
|
130
|
+
// return cached prior result, if any
|
|
131
|
+
const {cached} = o; if (!o.dry && pattern in cached) return cached[pattern]
|
|
80
132
|
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
const dirs = readdirSync(dir) .filter (e => cds.utils.isdir(dir+sep+e)) .map (e => folder+sep+e+sep)
|
|
87
|
-
if (o.dry || o === false) return dirs
|
|
88
|
-
return cache[pattern] = cds.resolve (dirs,o) || undefined
|
|
89
|
-
} catch(e) {
|
|
90
|
-
if (e.code === 'ENOENT')
|
|
91
|
-
return cache[pattern] = undefined
|
|
92
|
-
}
|
|
93
|
-
}
|
|
133
|
+
// fetch all subdirectories matching the pattern
|
|
134
|
+
const folder = pattern.slice(0,-2), dir = path.resolve (o.root, folder)
|
|
135
|
+
try { var nested = fs.readdirSync(dir) .filter (d => is_dir(path.join(dir,d))) .map (d => path.join(folder,d,path.sep)) }
|
|
136
|
+
catch(e) { if (e.code === 'ENOENT') return cached[pattern] = null }
|
|
94
137
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
paths.push(...a.map ((_,i,a)=> a.slice(0,n-i).join(sep)+sep+mld))
|
|
102
|
-
}
|
|
103
|
-
return cache[dir] = { paths, cached:{} }
|
|
138
|
+
// don't resolve, if resolve(...,false)
|
|
139
|
+
if (o.dry) return nested
|
|
140
|
+
|
|
141
|
+
// resolve files for those dirs
|
|
142
|
+
const files = nested?.map (d => resolve.single (d,o)).flat()
|
|
143
|
+
return cached[pattern] = _resolved (files.filter(x=>x))
|
|
104
144
|
}
|
|
105
145
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
146
|
+
|
|
147
|
+
resolve.module = function (module_name, o = resolve.options()) {
|
|
148
|
+
try { return _resolve (module_name,o) }
|
|
149
|
+
catch { return null }
|
|
109
150
|
}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
exports.suffixes = [ // always .csn before .cds to prefer compiled files
|
|
154
|
+
'.csn',
|
|
155
|
+
'.cds',
|
|
156
|
+
path.sep+'index.csn',
|
|
157
|
+
path.sep+'index.cds',
|
|
158
|
+
path.sep+'csn.json'
|
|
159
|
+
]
|
|
160
|
+
exports.cache = {}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
const _resolve = require('module')._resolveFilename
|
|
164
|
+
const _resolved = files => !files?.length ? null : Object.defineProperty (_distinct(files), '_resolved', {value:true})
|
|
165
|
+
const _distinct = files => [...new Set (files)]
|
|
166
|
+
const is_dir = file => fs.statSync(file).isDirectory()
|
package/lib/compile/to/csn.js
CHANGED
|
@@ -36,8 +36,8 @@ function cds_compile_to_csn (model, options, _flavor) {
|
|
|
36
36
|
// REVISIT: experimental implementation to detect external APIs
|
|
37
37
|
for (let each in csn.definitions) {
|
|
38
38
|
const d = csn.definitions[each]
|
|
39
|
-
if (d.kind === 'service' && cds.requires[each]?.external && (!o.mocked || cds.requires[each].credentials)) {
|
|
40
|
-
Object.defineProperty (d,'@cds.external', { value: cds.requires[each].kind || true })
|
|
39
|
+
if (d.kind === 'service' && !d['@cds.external'] && cds.requires[each]?.external && (!o.mocked || cds.requires[each].credentials)) {
|
|
40
|
+
Object.defineProperty (d,'@cds.external', { value: !!cds.requires[each].kind || true })
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
if (!csn.meta) csn.meta = {}
|
package/lib/dbs/cds-deploy.js
CHANGED
|
@@ -275,53 +275,55 @@ deploy.prepare = async function (csn, srces) {
|
|
|
275
275
|
}
|
|
276
276
|
|
|
277
277
|
|
|
278
|
-
/**
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
found[path.join(subdir,fx)] = e.name
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
// fetching init.js files -> Note: after .csv files to have that on top, when processing in .reverse order
|
|
278
|
+
/**
|
|
279
|
+
* Resolve initial data resources for given model.
|
|
280
|
+
* Found resources are executed in reverse order of discovery!
|
|
281
|
+
*/
|
|
282
|
+
deploy.resources = async function (csn,o) {
|
|
283
|
+
|
|
284
|
+
const { fs, isdir, isfile } = cds.utils, ts = process.env.CDS_TYPESCRIPT
|
|
285
|
+
const subfolders = ['data','csv']
|
|
286
|
+
const filetypes = ['.csv','.json']
|
|
287
|
+
const found={}, neighborhood = new Set
|
|
288
|
+
|
|
289
|
+
// fetching csvs in models' neighborhood ...
|
|
290
|
+
if (!csn?.definitions) csn = await cds.load (csn||'*') .then (cds.minify)
|
|
291
|
+
for (let each of csn.$sources) {
|
|
292
|
+
let d = path.dirname (each)
|
|
293
|
+
if (d === cds.home || neighborhood.has(d)) continue; else neighborhood.add (d)
|
|
294
|
+
for (let data of subfolders) await fetch_csvs_in (path.join (d,data))
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// fetching csvs in db/data and test/data
|
|
298
|
+
let d = cds.env.requires.db?.data || (
|
|
299
|
+
// REVISIT: fallback for some very special cds build and mtxs tests with mocked cds.env?
|
|
300
|
+
o?.testdata ? ['db/data','test/data'] : ['db/data']
|
|
301
|
+
)
|
|
302
|
+
if (d) for (let each of d) await fetch_csvs_in (each)
|
|
303
|
+
|
|
304
|
+
// fetching init.js files in models' neighborhood ...
|
|
305
|
+
for (let folder of neighborhood) {
|
|
311
306
|
const init_js = ts && isfile(folder,'init.ts') || isfile(folder,'init.js')
|
|
312
307
|
if (init_js) found[init_js] = '*'
|
|
313
308
|
}
|
|
314
309
|
return found
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
310
|
+
|
|
311
|
+
async function fetch_csvs_in (dir) {
|
|
312
|
+
const subdir = isdir(dir); if (!subdir) return
|
|
313
|
+
const files = await fs.promises.readdir (subdir)
|
|
314
|
+
files.forEach (fx => { if (fx[0] !== '-') {
|
|
315
|
+
const ext = path.extname(fx); if (!filetypes.includes(ext)) return
|
|
316
|
+
const f = fx.slice(0,-ext.length)
|
|
317
|
+
// ignore '_texts.csv' if there is any '_texts_<lang>.csv'
|
|
318
|
+
if (/[._]texts$/.test(f) && files.some(g => g.startsWith(f+'_')))
|
|
319
|
+
return DEBUG?.(`ignoring '${fx}' in favor of translated ones`)
|
|
320
|
+
const e = _entity4(f,csn); if (!e || e['@cds.persistence.skip'] === true) return
|
|
321
|
+
if (cds.env.features.deploy_data_onconflict === 'replace' && !/[._]texts_/.test(f))
|
|
322
|
+
for (let any in found) if (found[any] === e.name)
|
|
323
|
+
return DEBUG?.(`Conflict for '${e.name}': replacing '${local(any)}' with '${local(path.join(subdir,fx))}'`)
|
|
324
|
+
found[path.join(subdir,fx)] = e.name
|
|
325
|
+
}})
|
|
326
|
+
}
|
|
325
327
|
}
|
|
326
328
|
|
|
327
329
|
|
|
@@ -335,6 +337,9 @@ deploy.include_external_entities_in = function (csn) {
|
|
|
335
337
|
DEBUG?.('including mocked', each)
|
|
336
338
|
delete def['@cds.persistence.skip']
|
|
337
339
|
}
|
|
340
|
+
// DON'T CLEANUP: Please keep these comments in for future reference!
|
|
341
|
+
// REVISIT: if (def['@cds.external'] === 2) delete def['@cds.external']
|
|
342
|
+
// REVISIT: if (def['@cds.external'] === 2) def['@cds.external'] = 1
|
|
338
343
|
}
|
|
339
344
|
deploy.exclude_external_entities_in (csn)
|
|
340
345
|
return csn
|
package/lib/env/cds-env.js
CHANGED
|
@@ -395,6 +395,12 @@ class Config {
|
|
|
395
395
|
_fetch ({ type: conf.dialect || conf.kind })
|
|
396
396
|
|
|
397
397
|
function _fetch (predicate) {
|
|
398
|
+
if (Array.isArray(predicate)) {
|
|
399
|
+
for (const p of predicate) {
|
|
400
|
+
const found = _fetch(p)
|
|
401
|
+
if (found) return found
|
|
402
|
+
}
|
|
403
|
+
}
|
|
398
404
|
const filters = []
|
|
399
405
|
for (let k in predicate) {
|
|
400
406
|
const v = predicate[k]; if (!v) continue
|
package/lib/env/cds-requires.js
CHANGED
|
@@ -119,21 +119,27 @@ const _services = {
|
|
|
119
119
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
const _db_data = {
|
|
123
|
+
'[development]': { data: [ 'db/data', 'db/csv', 'test/data' ] },
|
|
124
|
+
'[production]': { data: [ 'db/data', 'db/csv' ] },
|
|
125
|
+
}
|
|
122
126
|
|
|
123
127
|
const _databases = {
|
|
124
128
|
|
|
125
|
-
"db-defaults": { kind: 'sql' },
|
|
129
|
+
"db-defaults": { kind: 'sql', ..._db_data }, //> applied only for cds.requires.db: true
|
|
130
|
+
|
|
126
131
|
"sql": {
|
|
127
132
|
'[development]': { kind: 'sqlite', credentials: { url: ':memory:' } },
|
|
128
133
|
'[production]': { kind: 'hana' },
|
|
129
134
|
},
|
|
130
135
|
|
|
131
136
|
"sqlite": {
|
|
132
|
-
impl: '@cap-js/sqlite',
|
|
133
|
-
|
|
137
|
+
impl: '@cap-js/sqlite', credentials: { url: 'db.sqlite' },
|
|
138
|
+
..._db_data,
|
|
134
139
|
},
|
|
135
140
|
"hana": {
|
|
136
141
|
impl: '@cap-js/hana',
|
|
142
|
+
..._db_data,
|
|
137
143
|
},
|
|
138
144
|
"hana-cloud": {
|
|
139
145
|
kind: 'hana', "deploy-format": "hdbtable",
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
if (process.env.CDS_STRICT_NODE_VERSION !== 'false') require('./utils/version').check()
|
|
2
|
-
! (global.__cds_loaded_from ??= new Set).add(__filename.toLowerCase()) // track from where we loaded cds, lowercase to avoid path duplicates on Windows
|
|
3
2
|
|
|
4
3
|
const { AsyncLocalStorage } = require ('async_hooks')
|
|
5
4
|
const context = new AsyncLocalStorage
|
|
@@ -152,3 +151,6 @@ function G (p,v) { Object.defineProperty (global, p, { value:v, enumerable:1,con
|
|
|
152
151
|
|
|
153
152
|
// Allow for `import cds from '@sap/cds'` without `esModuleInterop` in tsconfig.json
|
|
154
153
|
Object.defineProperties (exports, { default: {value:cds}, __esModule: {value:true} })
|
|
154
|
+
|
|
155
|
+
// track from where we loaded cds, lowercase to avoid path duplicates on Windows
|
|
156
|
+
!(global.__cds_loaded_from ??= new Set).add(cds.home.toLowerCase())
|
package/lib/plugins.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const DEBUG =
|
|
1
|
+
const DEBUG = /\b(y|all|plugins)\b/.test(process.env.DEBUG) ? console : undefined // eslint-disable-line no-console
|
|
2
2
|
const cds = require('.')
|
|
3
3
|
const prio_plugins = {
|
|
4
4
|
'@sap/cds-mtxs': true, // plugins may register handlers for mtxs services
|
package/lib/req/request.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const cds = require('../index')
|
|
2
|
-
const { Responses, Errors
|
|
2
|
+
const { Responses, Errors } = require('./response')
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Class Request represents requests received via synchronous protocols.
|
|
@@ -110,7 +110,7 @@ class Request extends require('./event') {
|
|
|
110
110
|
delete err.stack
|
|
111
111
|
throw err
|
|
112
112
|
}
|
|
113
|
-
let e =
|
|
113
|
+
let e = this._errors.add (4, ...args)
|
|
114
114
|
if (!('stack' in e)) Error.captureStackTrace (e = Object.assign(new Error,e), this.reject)
|
|
115
115
|
if (!('message' in e)) e.message = String (e.code || e.status)
|
|
116
116
|
throw e
|
package/lib/srv/bindings.js
CHANGED
|
@@ -11,25 +11,31 @@ class Bindings {
|
|
|
11
11
|
#bound = {}
|
|
12
12
|
|
|
13
13
|
then (r,e) {
|
|
14
|
+
const info = ()=> LOG.info ('using bindings from:', { registry })
|
|
15
|
+
if (cds.watched) cds.prependOnceListener ('connect',info); else info()
|
|
14
16
|
delete Bindings.prototype.then // only once per process
|
|
15
|
-
cds.prependOnceListener ('connect', ()=> LOG.info ('connect using bindings from:', { registry }))
|
|
16
17
|
cds.once('listening', server => this.export (cds.service.providers, server.url))
|
|
17
18
|
return this.import() .then (r,e)
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
bind (service) {
|
|
21
22
|
let required = cds.requires [service]
|
|
22
|
-
let binding = this.provides [
|
|
23
|
-
|
|
23
|
+
let binding = this.provides [service]
|
|
24
|
+
|
|
25
|
+
if (binding?.endpoints && !required?.credentials) {
|
|
26
|
+
// > Re-route requests to the mock service running locally
|
|
27
|
+
|
|
24
28
|
const server = this.servers [binding.server]
|
|
25
29
|
const kind = [ required?.kind, 'hcql', 'rest', 'odata' ].find (k => k in binding.endpoints)
|
|
26
30
|
const path = binding.endpoints [kind]
|
|
31
|
+
|
|
27
32
|
// in case of cds.requires.Foo = { ... }
|
|
28
33
|
if (typeof required === 'object') required.credentials = {
|
|
29
34
|
...required.credentials,
|
|
30
35
|
...binding.credentials,
|
|
31
36
|
url: server.url + path
|
|
32
37
|
}
|
|
38
|
+
|
|
33
39
|
// in case of cds.requires.Foo = true
|
|
34
40
|
else required = cds.requires[service] = cds.env.requires[service] = {
|
|
35
41
|
...cds.requires.kinds [binding.kind],
|
|
@@ -38,12 +44,18 @@ class Bindings {
|
|
|
38
44
|
url: server.url + path
|
|
39
45
|
}
|
|
40
46
|
}
|
|
47
|
+
|
|
41
48
|
required.kind = kind
|
|
49
|
+
|
|
42
50
|
// REVISIT: temporary fix to inherit kind as well for mocked odata services
|
|
43
51
|
// otherwise mocking with two services does not work for kind:odata-v2
|
|
44
52
|
if (kind === 'odata-v2' || kind === 'odata-v4') required.kind = 'odata'
|
|
53
|
+
|
|
54
|
+
// Finally, ensure the changes are mirrored in cds.requires overlays as well
|
|
55
|
+
if (required.service) cds.requires[required.service] = required
|
|
45
56
|
}
|
|
46
|
-
|
|
57
|
+
|
|
58
|
+
return this.#bound [service] = required
|
|
47
59
|
}
|
|
48
60
|
|
|
49
61
|
// used by cds.connect
|
|
@@ -74,7 +86,7 @@ class Bindings {
|
|
|
74
86
|
|
|
75
87
|
async import() {
|
|
76
88
|
await this.load()
|
|
77
|
-
for (let each in cds.requires) this.bind (each)
|
|
89
|
+
for (let each in cds.env.requires) this.bind (each)
|
|
78
90
|
return this
|
|
79
91
|
}
|
|
80
92
|
|
|
@@ -29,14 +29,16 @@ module.exports = function auth_factory (o) {
|
|
|
29
29
|
// from cds.requires.auth in the log or error output below.
|
|
30
30
|
|
|
31
31
|
// try resolving the impl, throw if not found
|
|
32
|
-
const config = { kind
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
const config = { kind }
|
|
33
|
+
try { impl = require.resolve (impl, {paths:[cds.root]}) } catch {
|
|
34
|
+
try { impl = require.resolve ('./'+impl, {paths:[cds.root]}) } catch {
|
|
35
|
+
throw cds.error `Didn't find auth implementation for ${config}`
|
|
36
|
+
}
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
// load the auth middleware from the resolved path
|
|
39
|
-
|
|
40
|
+
const builtin = cds.utils.path.join (cds.home,'lib')
|
|
41
|
+
if (!impl.match(builtin)) config.impl = cds.utils.local(impl)
|
|
40
42
|
cds.log().info ('using auth strategy', config)
|
|
41
43
|
let auth = require (impl)
|
|
42
44
|
|