@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/dbs/cds-deploy.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
const cds = require('../index'), { local
|
|
1
|
+
const cds = require('../index'), { local } = cds.utils
|
|
2
|
+
const COLORS = !!process.stdout.isTTY && !!process.stderr.isTTY
|
|
3
|
+
const GREY = COLORS ? '\x1b[2m' : ''
|
|
4
|
+
const RESET = COLORS ? '\x1b[0m' : ''
|
|
2
5
|
const DEBUG = cds.debug('deploy')
|
|
3
|
-
|
|
4
|
-
const colors = !!process.stdout.isTTY && !!process.stderr.isTTY
|
|
5
|
-
const term = {
|
|
6
|
-
x1b2: colors ? '\x1b[2m' : '',
|
|
7
|
-
x1b0: colors ? '\x1b[0m' : '',
|
|
8
|
-
info: colors ? (s => term.x1b2 + s + term.x1b0) : (s => s)
|
|
9
|
-
}
|
|
6
|
+
const TRACE = cds.debug('trace')
|
|
10
7
|
|
|
11
8
|
/**
|
|
12
9
|
* Implementation of `cds.deploy` common to all databases.
|
|
@@ -18,18 +15,10 @@ exports = module.exports = function cds_deploy (model,options,csvs) { return {
|
|
|
18
15
|
|
|
19
16
|
/** @param {cds.Service} db */
|
|
20
17
|
async to (db, o = options || cds.options || {}) {
|
|
21
|
-
|
|
22
|
-
const LOG = o.silent || !cds.log('deploy')._info ? ()=>{} : console.log
|
|
18
|
+
TRACE?.time ('cds.deploy db ')
|
|
23
19
|
|
|
24
|
-
if (model
|
|
25
|
-
|
|
26
|
-
if (DEBUG) try {
|
|
27
|
-
DEBUG (`loaded model from ${model.$sources.length} file(s):\n${term.x1b2}`)
|
|
28
|
-
for (let each of model.$sources) console.log (' ', local(each))
|
|
29
|
-
} finally {
|
|
30
|
-
console.log (term.x1b0)
|
|
31
|
-
}
|
|
32
|
-
}
|
|
20
|
+
if (!model) throw new Error('Must provide a model or a path to model, received: ' + model)
|
|
21
|
+
if (model && !model.definitions) model = await cds.load(model).then(cds.minify)
|
|
33
22
|
|
|
34
23
|
if (o.mocked) exports.include_external_entities_in (model)
|
|
35
24
|
else exports.exclude_external_entities_in (model)
|
|
@@ -39,18 +28,18 @@ exports = module.exports = function cds_deploy (model,options,csvs) { return {
|
|
|
39
28
|
if (!db.model) db.model = model
|
|
40
29
|
|
|
41
30
|
// create tables & views...
|
|
31
|
+
const LOG = o.silent || !cds.log('deploy')._info ? ()=>{} : console.log
|
|
42
32
|
const any = await exports.create (db,model,o)
|
|
43
33
|
if (!any && !csvs) return db
|
|
44
34
|
|
|
45
35
|
// fill in initial data...
|
|
46
|
-
await exports.init (db,model,o,csvs, file => LOG(
|
|
47
|
-
term.info(` > init from ${local(file)}`)
|
|
48
|
-
))
|
|
36
|
+
await exports.init (db,model,o,csvs, file => LOG (GREY,` > init from ${local(file)}`, RESET))
|
|
49
37
|
|
|
50
38
|
// done
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
39
|
+
let url = db.url4 (cds.context?.tenant); if (url === ':memory:') url = 'in-memory database.'
|
|
40
|
+
LOG ('/> successfully deployed to', url, '\n')
|
|
41
|
+
|
|
42
|
+
TRACE?.timeEnd ('cds.deploy db ')
|
|
54
43
|
return db
|
|
55
44
|
},
|
|
56
45
|
|
|
@@ -105,127 +94,104 @@ exports.exclude_external_entities_in = function (csn) { // NOSONAR
|
|
|
105
94
|
}
|
|
106
95
|
}
|
|
107
96
|
|
|
108
|
-
function getSqls(db, csn, o, beforeCsn) {
|
|
109
|
-
const schemaEvo = (db.options?.schema_evolution === 'auto' || o.schema_evolution === 'auto')
|
|
110
|
-
if (schemaEvo) {
|
|
111
|
-
const { afterImage: afterCsn, drops, createsAndAlters: creas } = cds.compile.to.sql.delta (csn, o, beforeCsn);
|
|
112
|
-
if(beforeCsn === undefined) {
|
|
113
|
-
// If this is the first deployment done with automatic schema evolution, generate everything as if it was a drop create
|
|
114
|
-
// but set the afterCsn in the db so we can calculate a delta going forward
|
|
115
|
-
return { afterCsn, drops: creas.map (each => {
|
|
116
|
-
let [, kind, entity] = each.match(/^CREATE (TABLE|VIEW) "?([^\s"(]+)/im) || []
|
|
117
|
-
return `DROP ${kind} IF EXISTS ${entity};`
|
|
118
|
-
}).reverse(), creas };
|
|
119
|
-
}
|
|
120
|
-
return { afterCsn, drops, creas };
|
|
121
|
-
} else {
|
|
122
|
-
const creas = cds.compile.to.sql(csn, o);
|
|
123
|
-
const drops = creas.map (each => {
|
|
124
|
-
let [, kind, entity] = each.match(/^CREATE (TABLE|VIEW) "?([^\s"(]+)/im) || []
|
|
125
|
-
return `DROP ${kind} IF EXISTS ${entity};`
|
|
126
|
-
}).reverse();
|
|
127
|
-
return { afterCsn: {}, drops, creas };
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
97
|
|
|
131
|
-
exports.create = async function (db, csn=db.model, o) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
98
|
+
exports.create = async function cds_deploy_create (db, csn=db.model, o) {
|
|
99
|
+
|
|
100
|
+
let drops, creas, schevo = db.options?.schema_evolution === 'auto' || o.schema_evolution === 'auto'
|
|
101
|
+
if (schevo) {
|
|
102
|
+
// REVISIT: replace by db-specific ways to read/updated recent deployed model
|
|
103
|
+
let before = await db.run (async tx => { try {
|
|
104
|
+
let [{ csn }] = await tx.run('SELECT csn from cds_Model')
|
|
105
|
+
return JSON.parse(csn)
|
|
106
|
+
} catch(e) { if (!e.message.includes('no such table')) throw e
|
|
107
|
+
await tx.run(`CREATE table cds_Model (csn CLOB)`)
|
|
108
|
+
await tx.run(`INSERT into cds_Model values ('null')`)
|
|
109
|
+
}})
|
|
110
|
+
const { afterImage, drops:_drops, createsAndAlters:_creas } = cds.compile.to.sql.delta (csn, o, before)
|
|
111
|
+
creas = _creas.concat (o.dry ? [] : UPDATE('cds.Model').with({ csn: JSON.stringify(afterImage) }))
|
|
112
|
+
drops = before ? _drops : _drops4(_creas)
|
|
113
|
+
} else {
|
|
114
|
+
creas = cds.compile.to.sql (csn, o)
|
|
115
|
+
drops = _drops4(creas) .concat ('DROP table if exists cds_Model;')
|
|
137
116
|
}
|
|
117
|
+
if (!creas || creas.length === 0) return
|
|
138
118
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (e.message.includes('no such table: cds_Model')) {
|
|
145
|
-
await db.run('CREATE table cds_Model ( csn CLOB )')
|
|
146
|
-
await db.insert({csn:''}).into('cds.Model')
|
|
147
|
-
}
|
|
119
|
+
function _drops4 (creas) {
|
|
120
|
+
return creas.map (each => {
|
|
121
|
+
let [, kind, entity] = each.match(/^CREATE (TABLE|VIEW) ("[^"]+"|[^\s(]+)/im) || []
|
|
122
|
+
return `DROP ${kind} IF EXISTS ${entity};`
|
|
123
|
+
}).reverse()
|
|
148
124
|
}
|
|
149
125
|
|
|
150
|
-
const { afterCsn, drops, creas } = getSqls(db, csn, o, beforeCsn);
|
|
151
|
-
|
|
152
|
-
if (!creas || creas.length === 0) return
|
|
153
126
|
if (o.dry) {
|
|
154
|
-
console.log(); for (let each of drops) console.log(each
|
|
127
|
+
console.log(); for (let each of drops) console.log(each)
|
|
155
128
|
console.log(); for (let each of creas) console.log(each,'\n')
|
|
156
|
-
|
|
157
|
-
|
|
129
|
+
}
|
|
130
|
+
else return db.run (async tx => {
|
|
131
|
+
// This runs in a new transaction if called from CLI, while joining
|
|
132
|
+
// existing root tx, e.g. when called from DeploymentService.
|
|
133
|
+
if (!schevo && await tx.exists('sqlite.schema').where({name:'cds_xt_Extensions'})) {
|
|
134
|
+
// Poor man's schema evolution for extensions in drop-create deployments
|
|
135
|
+
drops = drops.filter(d => !d.includes('cds_xt_Extensions'))
|
|
136
|
+
creas = creas.filter(c => !c.includes('cds_xt_Extensions'))
|
|
137
|
+
}
|
|
138
|
+
// Set the context model while deploying for cqn42sql in new db layers
|
|
139
|
+
tx.model = cds.compile.for.nodejs(csn)
|
|
158
140
|
await tx.run(drops)
|
|
159
141
|
await tx.run(creas)
|
|
160
|
-
if (schemaEvo) {
|
|
161
|
-
await tx.update('cds.Model').with({ csn: JSON.stringify(afterCsn) })
|
|
162
|
-
}
|
|
163
142
|
return true
|
|
164
143
|
})
|
|
165
144
|
}
|
|
166
145
|
|
|
167
146
|
|
|
168
|
-
exports.init = (db, csn=db.model, o,
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const schemaEvo = db.options?.schema_evolution === 'auto' || o?.schema_evolution === 'auto'
|
|
172
|
-
const resources = await exports.resources(csn, {testdata: cds.env.features.test_data})
|
|
173
|
-
const inits=[]
|
|
174
|
-
|
|
175
|
-
if (csvs) {
|
|
176
|
-
const ccsn = cds.compile.for['nodejs'](csn) // compile to calculate keys for newly added entities
|
|
177
|
-
for(let [file,src] of Object.entries(csvs)) {
|
|
178
|
-
const entity = _entity4(path.basename(file, '.csv'), csn)
|
|
179
|
-
if (entity?.name) {
|
|
180
|
-
const q = INSERT_from_csv (entity.name,src,schemaEvo); if (!q) continue
|
|
181
|
-
if (db.kind === 'better-sqlite') _add_missing_pks2(q)
|
|
182
|
-
q._target = ccsn.definitions[entity.name]
|
|
183
|
-
inits.push (tx.run(q) .catch (e => {
|
|
184
|
-
throw Object.assign (e, { message: 'in cds.deploy(): ' + e.message +'\n'+ inspect(q) })
|
|
185
|
-
}))
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
} else {
|
|
189
|
-
for (let [file,e] of Object.entries(resources)) {
|
|
190
|
-
if (e === '*') { // init.js/ts
|
|
191
|
-
let x = await cds.utils._import(file); if (!x) continue
|
|
192
|
-
if (x.default) x = x.default // default ESM export
|
|
193
|
-
inits.push (!x.then && typeof x === 'function' ? x(tx,csn) : x)
|
|
194
|
-
log (file)
|
|
195
|
-
} else { // from .csv or .json
|
|
196
|
-
const INSERT_into = _from_csv_or_json [path.extname(file)]
|
|
197
|
-
const src = await read(file,'utf8'); if (!src) continue
|
|
198
|
-
const q = INSERT_into (e,src,schemaEvo); if (!q) continue
|
|
199
|
-
if (db.kind === 'better-sqlite') _add_missing_pks2(q)
|
|
200
|
-
if (cds.requires['cds.xt.ModelProviderService']?.kind === 'in-sidecar') q._target = csn.definitions[e]
|
|
201
|
-
log (file,e)
|
|
202
|
-
inits.push (tx.run(q) .catch (e => {
|
|
203
|
-
throw Object.assign (e, { message: 'in cds.deploy(): ' + e.message +'\n'+ inspect(q) })
|
|
204
|
-
}))
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
147
|
+
exports.init = async function cds_deploy_init (db, csn=db.model, o, srces, log=()=>{}) {
|
|
148
|
+
const t = cds.context?.tenant; if (t && t === cds.requires.multitenancy?.t0) return
|
|
149
|
+
return db.run (async tx => {
|
|
208
150
|
|
|
209
|
-
|
|
151
|
+
const m = tx.model = cds.compile.for.nodejs(csn) //> use correct model while deploying
|
|
152
|
+
const data = await exports.data (m,srces)
|
|
153
|
+
const query = _queries4 (db,m)
|
|
154
|
+
const INSERT_from = INSERT_from4 (db,o)
|
|
210
155
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
156
|
+
for await (let [ file, entity, src ] of data) {
|
|
157
|
+
log (file)
|
|
158
|
+
if (entity) {
|
|
159
|
+
const q = INSERT_from (file) .into (entity, src)
|
|
160
|
+
if (q) try { await tx.run (query(q)) } catch(e) {
|
|
161
|
+
throw Object.assign (e, { message: 'in cds.deploy(): ' + e.message +'\n'+ cds.utils.inspect(q) })
|
|
162
|
+
}
|
|
163
|
+
} else { //> init.js/ts case
|
|
164
|
+
if (typeof src === 'function') src(tx,csn)
|
|
219
165
|
}
|
|
220
166
|
}
|
|
221
|
-
}
|
|
167
|
+
})
|
|
168
|
+
}
|
|
222
169
|
|
|
223
|
-
|
|
170
|
+
|
|
171
|
+
/** Prepare input from .csv, .json, init.js, ... */
|
|
172
|
+
exports.data = async function cds_deploy_prepare_data (csn, srces) {
|
|
173
|
+
// In case of extension deployment .csv or .json input are provided through argument `srces`.
|
|
174
|
+
if (srces) return Object.entries(srces) .map (([file, src]) => {
|
|
175
|
+
let e = _entity4 (path.basename(file,'.csv'), csn)
|
|
176
|
+
return [ file, e, src ]
|
|
177
|
+
})
|
|
178
|
+
// If not, we load them from cds.deploy.resources(csn)
|
|
179
|
+
const resources = await exports.resources(csn, { testdata: cds.env.features.test_data })
|
|
180
|
+
return Object.entries(resources) .map (async ([file,e]) => {
|
|
181
|
+
if (e === '*') {
|
|
182
|
+
let init_js = await cds.utils._import (file)
|
|
183
|
+
return [ file, null, init_js.default || init_js ]
|
|
184
|
+
} else {
|
|
185
|
+
let src = await read (file, 'utf8')
|
|
186
|
+
return [ file, e, src ]
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
}
|
|
224
190
|
|
|
225
191
|
|
|
226
|
-
exports.resources = async function (csn, opts) {
|
|
192
|
+
exports.resources = async function cds_deploy_resources (csn, opts) {
|
|
227
193
|
if (!csn || !csn.definitions) csn = await cds.load (csn||'*') .then (cds.minify)
|
|
228
|
-
const folders = await
|
|
194
|
+
const folders = await cds_deploy_resources.folders(csn, opts)
|
|
229
195
|
const found={}, ts = process.env.CDS_TYPESCRIPT
|
|
230
196
|
for (let folder of folders) {
|
|
231
197
|
// fetching init.js files
|
|
@@ -237,7 +203,7 @@ exports.resources = async function (csn, opts) {
|
|
|
237
203
|
const files = await readdir (subdir)
|
|
238
204
|
for (let fx of files) {
|
|
239
205
|
if (fx[0] === '-') continue
|
|
240
|
-
const ext = path.extname(fx); if (ext in
|
|
206
|
+
const ext = path.extname(fx); if (ext in {'.csv':1,'.json':2}) {
|
|
241
207
|
const f = fx.slice(0,-ext.length)
|
|
242
208
|
if (/[._]texts$/.test(f) && files.some(g => g.startsWith(f+'_'))) {
|
|
243
209
|
// ignores 'Books_texts.csv/json' if there is any 'Books_texts_LANG.csv/json'
|
|
@@ -287,17 +253,47 @@ const _entity4 = (file,csn) => {
|
|
|
287
253
|
return entity.name ? entity : { name, __proto__:entity }
|
|
288
254
|
}
|
|
289
255
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
256
|
+
|
|
257
|
+
/** Prepare special handling for new db services */
|
|
258
|
+
const _queries4 = (db,csn) => !db.cqn2sql ? q => q : q => {
|
|
259
|
+
const { columns, rows } = q.INSERT || q.UPSERT; if (!columns) return q // REVISIT: .entries are covered by current runtime -> should eventually also be handled here
|
|
260
|
+
const entity = csn.definitions[q._target.name]
|
|
261
|
+
|
|
262
|
+
// Fill in missing primary keys...
|
|
263
|
+
const { uuid } = cds.utils
|
|
264
|
+
for (let k in entity.keys) if (entity.keys[k].isUUID && !columns.includes(k)) {
|
|
265
|
+
columns.push(k)
|
|
266
|
+
rows.forEach(row => row.push(uuid()))
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Fill in missing managed data...
|
|
270
|
+
const pseudos = { $user: 'anonymous', $now: (new Date).toISOString() }
|
|
271
|
+
for (let k in entity.elements) {
|
|
272
|
+
const managed = entity.elements[k]['@cds.on.insert']?.['=']
|
|
273
|
+
if (managed && !columns.includes(k)) {
|
|
274
|
+
columns.push(k)
|
|
275
|
+
rows.forEach(row => row.push(pseudos[managed]))
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return q
|
|
293
280
|
}
|
|
294
281
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
282
|
+
|
|
283
|
+
const INSERT_from4 = (db,o) => {
|
|
284
|
+
const schevo = db.options?.schema_evolution === 'auto' || o?.schema_evolution === 'auto'
|
|
285
|
+
const INSERT_into = (schevo ? UPSERT : INSERT).into
|
|
286
|
+
return (file) => ({
|
|
287
|
+
'.json': { into (entity, json) {
|
|
288
|
+
let records = JSON.parse(json)
|
|
289
|
+
if (records.length > 0) return INSERT_into(entity).entries(records)
|
|
290
|
+
}},
|
|
291
|
+
'.csv': { into (entity, csv) {
|
|
292
|
+
let [cols, ...rows] = cds.parse.csv(csv)
|
|
293
|
+
if (rows.length > 0) return INSERT_into(entity).columns(cols).rows(rows)
|
|
294
|
+
}},
|
|
295
|
+
}) [path.extname(file)]
|
|
298
296
|
}
|
|
299
297
|
|
|
300
|
-
const _from_csv_or_json = { '.json': INSERT_from_json, '.csv': INSERT_from_csv, }
|
|
301
298
|
const _skip = e => !e || e['@cds.persistence.skip'] === true
|
|
302
|
-
|
|
303
299
|
/* eslint-disable no-console */
|
package/lib/env/cds-env.js
CHANGED
|
@@ -32,6 +32,11 @@ class Config {
|
|
|
32
32
|
const { NODE_ENV, CDS_ENV } = process.env, profiles = []
|
|
33
33
|
if (NODE_ENV) profiles.push (NODE_ENV)
|
|
34
34
|
if (CDS_ENV) profiles.push (...CDS_ENV.split(/\s*,\s*/))
|
|
35
|
+
if (_home) try {
|
|
36
|
+
const { cds } = require(path.join(_home,'package.json'))
|
|
37
|
+
if (cds?.profiles) profiles.push(...cds.profiles)
|
|
38
|
+
if (cds?.profile) profiles.push(cds.profile)
|
|
39
|
+
} catch {/* ignore */}
|
|
35
40
|
if (!profiles.includes('production')) profiles.push('development')
|
|
36
41
|
this._profiles = new Set (profiles)
|
|
37
42
|
this._profiles._defined = new Set()
|
|
@@ -127,8 +132,9 @@ class Config {
|
|
|
127
132
|
* Retrieves the value for a config option, specified as a property path.
|
|
128
133
|
*/
|
|
129
134
|
get (option) {
|
|
130
|
-
if (!option)
|
|
131
|
-
|
|
135
|
+
if (!option) return
|
|
136
|
+
let path = option.includes('/') ? option.split('/') : option.split('.')
|
|
137
|
+
return path.reduce ((p,n)=> p && p[n], this)
|
|
132
138
|
}
|
|
133
139
|
|
|
134
140
|
get profiles() {
|
|
@@ -244,7 +250,7 @@ class Config {
|
|
|
244
250
|
}
|
|
245
251
|
|
|
246
252
|
_link_required_services () {
|
|
247
|
-
const { requires } = this; if (!requires) return
|
|
253
|
+
const { requires, _profiles } = this; if (!requires) return
|
|
248
254
|
const kinds = requires.kinds || {}
|
|
249
255
|
Object.defineProperty (requires, 'kinds', { value:kinds, enumerable:false }) // for cds env
|
|
250
256
|
// Object.setPrototypeOf (requires, kinds)
|
|
@@ -254,27 +260,28 @@ class Config {
|
|
|
254
260
|
if (!val || val._is_linked) return val
|
|
255
261
|
if (val === true) {
|
|
256
262
|
let x = kinds[key]
|
|
257
|
-
if (
|
|
258
|
-
else val = x
|
|
263
|
+
if (x) val = x; else return val
|
|
259
264
|
}
|
|
260
265
|
if (typeof val === 'string') {
|
|
261
266
|
let x = kinds[val] || kinds[val+'-'+key] || kinds[key+'-'+val]
|
|
262
|
-
if (
|
|
263
|
-
else val = { kind: val }
|
|
267
|
+
if (x) val = {kind:val}; else return val
|
|
264
268
|
}
|
|
265
|
-
|
|
266
|
-
if (
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
269
|
+
let k = val.kind, p, preset = kinds[p=k] || kinds[p=k+'-'+key] || kinds[p=key+'-'+k]
|
|
270
|
+
if (!preset?.$root) {
|
|
271
|
+
const preset1 = kinds[key]
|
|
272
|
+
if (typeof preset1 === 'object' && preset1 !== val) {
|
|
273
|
+
const top = val, base = _merge ({},_linked(key,preset1)), {kind} = base
|
|
274
|
+
val = _merge (base, top, false, false) // apply/override with top-level data
|
|
275
|
+
if (kind) val.kind = kind // but inherited kind wins
|
|
276
|
+
}
|
|
270
277
|
}
|
|
271
|
-
let k = val.kind, p, preset = k && kinds[p=k] || kinds[p=k+'-'+key] || kinds[p=key+'-'+k]
|
|
272
278
|
if (typeof preset === 'object' && preset !== val) {
|
|
273
|
-
const top = val, base = _merge ({},_linked(p,preset)), {kind} = base
|
|
274
|
-
val = _merge (base, top,
|
|
279
|
+
const top = val, base = _merge ({},_linked(p,preset), _profiles), {kind} = base
|
|
280
|
+
val = _merge (base, top, _profiles, false) // apply/override with top-level data
|
|
275
281
|
if (kind) val.kind = kind // but inherited kind wins
|
|
276
282
|
}
|
|
277
|
-
|
|
283
|
+
if (typeof val === 'object') Object.defineProperty (val, '_is_linked', {value:true})
|
|
284
|
+
return val
|
|
278
285
|
}
|
|
279
286
|
}
|
|
280
287
|
|
|
@@ -435,7 +442,8 @@ function _merge (dst, src, _profiles, _cloned, _profiles_only = false) {
|
|
|
435
442
|
const profile = p.slice(1,-1)
|
|
436
443
|
if (_profiles._defined) _profiles._defined.add (profile)
|
|
437
444
|
if (_profiles.has(profile)) {
|
|
438
|
-
|
|
445
|
+
let o = src[p]; if (typeof o === 'string') o = {kind:o}
|
|
446
|
+
profiled.push ({ profile, merge: () => _merge (dst, o, _profiles, _cloned, false)})
|
|
439
447
|
}
|
|
440
448
|
continue
|
|
441
449
|
}
|
package/lib/env/cds-requires.js
CHANGED
|
@@ -2,16 +2,8 @@ const _runtime = '@sap/cds/libx/_runtime'
|
|
|
2
2
|
|
|
3
3
|
exports = module.exports = {
|
|
4
4
|
|
|
5
|
-
middlewares:
|
|
6
|
-
"[
|
|
7
|
-
middlewares: true
|
|
8
|
-
},
|
|
9
|
-
|
|
10
|
-
"[schevo]": {
|
|
11
|
-
db: { schema_evolution: 'auto' },
|
|
12
|
-
},
|
|
13
|
-
|
|
14
|
-
db: undefined,
|
|
5
|
+
middlewares: undefined, "[middlewares]": { middlewares: true },
|
|
6
|
+
db: undefined, "[schevo]": { db: { schema_evolution: 'auto' }, },
|
|
15
7
|
messaging: undefined,
|
|
16
8
|
multitenancy: undefined,
|
|
17
9
|
extensibility: undefined,
|
|
@@ -127,19 +119,20 @@ const _databases = {
|
|
|
127
119
|
"sql": {
|
|
128
120
|
'[development]': { kind: 'sqlite', credentials: { url: ':memory:' } },
|
|
129
121
|
'[production]': { kind: 'hana' },
|
|
130
|
-
'[better-sqlite]': { kind: 'better-sqlite' },
|
|
131
122
|
},
|
|
132
|
-
"better-sqlite":
|
|
123
|
+
"better-sqlite": {
|
|
133
124
|
credentials: { url: ":memory:" },
|
|
134
125
|
impl: "@cap-js/sqlite",
|
|
135
|
-
|
|
136
|
-
|
|
126
|
+
kind: 'sqlite'
|
|
127
|
+
},
|
|
128
|
+
"sql-mt": { // For compatibility only
|
|
137
129
|
'[development]': { kind: 'sqlite' },
|
|
138
130
|
'[production]': { kind: 'hana-mt' },
|
|
139
131
|
},
|
|
140
132
|
"sqlite": _compat_to_use({
|
|
141
|
-
|
|
133
|
+
'[better-sqlite]': { impl: '@cap-js/sqlite' },
|
|
142
134
|
impl: `${_runtime}/sqlite/Service.js`,
|
|
135
|
+
credentials: { url: 'db.sqlite' },
|
|
143
136
|
}),
|
|
144
137
|
"hana": _compat_to_use ({
|
|
145
138
|
impl: `${_runtime}/hana/Service.js`,
|
|
@@ -246,37 +239,12 @@ const _platform_services = {
|
|
|
246
239
|
|
|
247
240
|
}
|
|
248
241
|
|
|
249
|
-
require = (id) => { // eslint-disable-line no-global-assign
|
|
250
|
-
try {
|
|
251
|
-
return module.require(id)
|
|
252
|
-
} catch(e) {
|
|
253
|
-
if (e.code !== 'MODULE_NOT_FOUND') throw e
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// REVISIT: we should have a real modular plugin technique for cds.env
|
|
258
|
-
const utils = require ('../utils/cds-utils')
|
|
259
|
-
const _mtxs = !utils._oldMtx()
|
|
260
|
-
// eslint-disable-next-line cds/no-missing-dependencies
|
|
261
|
-
&& require('@sap/cds-mtxs/lib/env-requires') || {
|
|
262
|
-
"toggles": {
|
|
263
|
-
model: "@sap/cds/srv/mtx"
|
|
264
|
-
},
|
|
265
|
-
"multitenancy": {
|
|
266
|
-
model: "@sap/cds/srv/mtx",
|
|
267
|
-
kind: "saas-registry",
|
|
268
|
-
},
|
|
269
|
-
"extensibility": {
|
|
270
|
-
model: "@sap/cds/srv/mtx",
|
|
271
|
-
},
|
|
272
|
-
}
|
|
273
242
|
|
|
274
243
|
exports.kinds = {
|
|
275
244
|
..._authentication_strategies,
|
|
276
245
|
..._databases,
|
|
277
246
|
..._services,
|
|
278
247
|
..._messaging,
|
|
279
|
-
..._mtxs,
|
|
280
248
|
..._platform_services,
|
|
281
249
|
}
|
|
282
250
|
|
|
@@ -285,3 +253,5 @@ function _compat_to_use(o) { return Object.defineProperties (o,{
|
|
|
285
253
|
// NOTE: Property .use is for compatibility only -> use .dialect instead!
|
|
286
254
|
use: { get(){ return this.dialect || this.kind }, configurable:true, enumerable:true },
|
|
287
255
|
})}
|
|
256
|
+
|
|
257
|
+
Object.defineProperty(exports,'_resolved',{value:exports._resolved,enumerable:false}) // hide it in outputs
|
package/lib/env/compat.js
CHANGED
|
@@ -89,4 +89,16 @@ module.exports = function (conf) {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
})
|
|
92
|
+
|
|
93
|
+
Object.defineProperties (conf.features, {
|
|
94
|
+
fiori_preview: {
|
|
95
|
+
get: ()=> _.fiori.preview,
|
|
96
|
+
set: (v) => { _.fiori.preview = v },
|
|
97
|
+
},
|
|
98
|
+
fiori_routes: {
|
|
99
|
+
get: ()=> _.fiori.routes,
|
|
100
|
+
set: (v) => { _.fiori.routes = v },
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
|
|
92
104
|
}
|
package/lib/env/defaults.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
const path = require('path')
|
|
2
|
-
const [,major,minor] = /v(\d+)\.(\d+)/.exec(process.version)
|
|
3
1
|
const production = process.env.NODE_ENV === 'production'
|
|
2
|
+
const path = require('path')
|
|
4
3
|
|
|
5
|
-
module.exports = {
|
|
4
|
+
const defaults = module.exports = {
|
|
6
5
|
|
|
7
6
|
requires: require('./cds-requires'),
|
|
8
7
|
|
|
@@ -13,10 +12,7 @@ module.exports = {
|
|
|
13
12
|
|
|
14
13
|
features: {
|
|
15
14
|
folders: 'fts/*', // where to find feature toggles -> switch on by default when released
|
|
16
|
-
cls: major > 12 || major == 12 && minor >= 18,
|
|
17
15
|
live_reload: !production,
|
|
18
|
-
fiori_preview: !production,
|
|
19
|
-
fiori_routes: !production,
|
|
20
16
|
in_memory_db: !production,
|
|
21
17
|
test_data: !production,
|
|
22
18
|
test_mocks: !production,
|
|
@@ -30,10 +26,20 @@ module.exports = {
|
|
|
30
26
|
assert_integrity: false,
|
|
31
27
|
cds_tx_protection: true,
|
|
32
28
|
cds_tx_inheritance: true,
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
fiori: {
|
|
32
|
+
preview: !production,
|
|
33
|
+
routes: !production,
|
|
33
34
|
lean_draft: false,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
35
|
+
draft_compat: false,
|
|
36
|
+
'[better-sqlite]': { lean_draft: true },
|
|
37
|
+
'[lean-draft]': { lean_draft: true },
|
|
38
|
+
'[draft-compat]': { draft_compat: true },
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
ql: {
|
|
42
|
+
quirks_mode: true, // IMPORTANT: Remove that for cds7 !!
|
|
37
43
|
},
|
|
38
44
|
|
|
39
45
|
log: {
|
|
@@ -162,3 +168,5 @@ module.exports = {
|
|
|
162
168
|
},
|
|
163
169
|
|
|
164
170
|
}
|
|
171
|
+
|
|
172
|
+
require('./plugins')(defaults)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// REVISIT: we should have a real modular plugin technique for cds.env
|
|
2
|
+
module.exports = function add_mtx_env (env) {
|
|
3
|
+
|
|
4
|
+
const mtx_env = old_mtx_env() || require('@sap/cds-mtxs/env') // eslint-disable-line cds/no-missing-dependencies
|
|
5
|
+
if (mtx_env) {
|
|
6
|
+
const {requires} = env, {kinds} = requires
|
|
7
|
+
Object.assign (env, mtx_env, {requires})
|
|
8
|
+
Object.assign (requires, mtx_env.requires, {kinds})
|
|
9
|
+
Object.assign (kinds, mtx_env.requires?.kinds)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function require (id) {
|
|
13
|
+
try { return module.require(id) }
|
|
14
|
+
catch(e) { if (e.code !== 'MODULE_NOT_FOUND') throw e }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function old_mtx_env() {
|
|
18
|
+
const {_oldMtx} = require('../utils/cds-utils')
|
|
19
|
+
if (_oldMtx()) return {
|
|
20
|
+
requires: { kinds: {
|
|
21
|
+
"multitenancy": { model: "@sap/cds/srv/mtx", kind: "saas-registry" },
|
|
22
|
+
"extensibility": { model: "@sap/cds/srv/mtx" },
|
|
23
|
+
// "toggles": { model: "@sap/cds/srv/mtx" },
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}}
|
|
27
|
+
|
|
28
|
+
return env
|
|
29
|
+
}
|
|
@@ -5,6 +5,20 @@
|
|
|
5
5
|
"type": "object",
|
|
6
6
|
"additionalProperties": true,
|
|
7
7
|
"properties": {
|
|
8
|
+
"[development]": { "type": "object" },
|
|
9
|
+
"[production]": { "type": "object" },
|
|
10
|
+
"[hybrid]": { "type": "object" },
|
|
11
|
+
"profile": {
|
|
12
|
+
"description": "A single static profile",
|
|
13
|
+
"anyOf": [
|
|
14
|
+
{ "enum": [ "mtx-sidecar", "with-mtx-sidecar" ] },
|
|
15
|
+
{ "type": "string" }
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
"profiles": {
|
|
19
|
+
"description": "An array of profiles",
|
|
20
|
+
"type": "array", "items": { "$ref": "#/properties/profile" }
|
|
21
|
+
},
|
|
8
22
|
"folders": {
|
|
9
23
|
"type": "object",
|
|
10
24
|
"description": "Only set folders if you don't want to use the defaults 'app/', 'db/', 'srv/'.",
|
package/lib/index.js
CHANGED
|
@@ -77,6 +77,9 @@ const cds = module.exports = extend (new facade) .with ({
|
|
|
77
77
|
User: require ('./req/user'),
|
|
78
78
|
ql: lazy => require ('./ql/cds-ql'),
|
|
79
79
|
tx: (..._) => (cds.db || cds.Service.prototype) .tx (..._),
|
|
80
|
+
inferred: lazy => require('@cap-js/sqlite/lib/ql/cds.infer'), // eslint-disable-line cds/no-missing-dependencies
|
|
81
|
+
cqn2sql: lazy => require('@cap-js/sqlite/lib/db/sql/cqn2sql'), // eslint-disable-line cds/no-missing-dependencies
|
|
82
|
+
cqn4sql: lazy => require('@cap-js/sqlite/lib/db/sql/cqn4sql'), // eslint-disable-line cds/no-missing-dependencies
|
|
80
83
|
/** @type Service */ db: undefined,
|
|
81
84
|
|
|
82
85
|
// Protocols and Periphery
|
package/lib/log/cds-log.js
CHANGED
|
@@ -65,9 +65,12 @@ function cds_log (module, options) { // NOSONAR
|
|
|
65
65
|
* Shortcut to `cds.log(...).debug`, returning undefined if `cds.log(...)._debug` is false.
|
|
66
66
|
* @param {string} [module] the module for which a logger is requested
|
|
67
67
|
*/
|
|
68
|
-
exports.debug = function cds_debug (
|
|
69
|
-
const L =
|
|
70
|
-
if (L._debug) return L.debug
|
|
68
|
+
exports.debug = function cds_debug (id, options) {
|
|
69
|
+
const L = cds_log (id, options)
|
|
70
|
+
if (L._debug) return Object.assign(L.debug, {
|
|
71
|
+
time: label => console.time (`[${id}] - ${label}`),
|
|
72
|
+
timeEnd: label => console.timeEnd (`[${id}] - ${label}`),
|
|
73
|
+
})
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
|
|
@@ -87,7 +90,7 @@ exports.Logger = (label, level) => {
|
|
|
87
90
|
const fmt = (level,args) => logger.format (label,level,...args)
|
|
88
91
|
const logger = {
|
|
89
92
|
format: exports.format, // use logger.format as this could be changed dynamically
|
|
90
|
-
trace: level <
|
|
93
|
+
trace: level < DEBUG ? ()=>{} : (...args) => console.trace (...fmt(TRACE,args)),
|
|
91
94
|
debug: level < DEBUG ? ()=>{} : (...args) => console.debug (...fmt(DEBUG,args)),
|
|
92
95
|
log: level < INFO ? ()=>{} : (...args) => console.log (...fmt(INFO,args)),
|
|
93
96
|
info: level < INFO ? ()=>{} : (...args) => console.info (...fmt(INFO,args)),
|