@sap/cds 6.8.3 → 7.0.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 +61 -2
- package/README.md +0 -1
- package/bin/cds-serve.js +50 -3
- package/bin/deploy/to-hana.js +1 -0
- package/bin/serve.js +16 -20
- package/lib/auth/basic-auth.js +6 -4
- package/lib/auth/index.js +4 -3
- package/lib/auth/jwt-auth.js +2 -5
- package/lib/compile/cds-compile.js +34 -89
- package/lib/compile/cdsc.js +11 -0
- package/lib/compile/etc/properties.js +2 -2
- package/lib/compile/for/lean_drafts.js +36 -69
- package/lib/compile/for/nodejs.js +2 -1
- package/lib/compile/load.js +1 -1
- package/lib/compile/minify.js +2 -0
- package/lib/compile/to/csn.js +74 -0
- package/{bin/build/provider/hana/2tabledata.js → lib/compile/to/hdbtabledata.js} +4 -6
- package/lib/compile/to/json.js +1 -1
- package/lib/compile/to/sql.js +8 -6
- package/lib/dbs/cds-deploy.js +174 -114
- package/lib/env/cds-env.js +64 -79
- package/lib/env/cds-requires.js +11 -28
- package/lib/env/defaults.js +13 -3
- package/lib/env/plugins.js +1 -12
- package/lib/env/presets.js +25 -21
- package/lib/index.js +121 -147
- package/lib/{core/reflect.js → linked/models.js} +2 -2
- package/lib/{core/infer.js → linked/queries.js} +2 -0
- package/lib/{core/index.js → linked/types.js} +2 -1
- package/lib/log/cds-error.js +13 -7
- package/lib/log/format/cf.js +1 -1
- package/lib/plugins.js +49 -0
- package/lib/ql/Query.js +0 -9
- package/lib/ql/STREAM.js +0 -1
- package/lib/req/context.js +2 -7
- package/lib/req/request.js +6 -2
- package/lib/req/response.js +23 -10
- package/lib/srv/middlewares/ctx-model.js +1 -1
- package/lib/srv/middlewares/errors.js +1 -1
- package/lib/srv/protocols/_legacy.js +1 -0
- package/lib/srv/protocols/graphql.js +7 -16
- package/lib/srv/protocols/index.js +59 -45
- package/lib/srv/protocols/odata-v2-proxy.js +2 -70
- package/lib/srv/srv-api.js +9 -3
- package/lib/srv/srv-dispatch.js +12 -9
- package/lib/srv/srv-models.js +4 -21
- package/lib/srv/srv-tx.js +15 -12
- package/lib/utils/cds-test.js +14 -9
- package/lib/utils/cds-utils.js +2 -12
- package/lib/utils/check-version.js +17 -0
- package/{bin/build → lib/utils}/csv-reader.js +23 -24
- package/libx/_runtime/auth/index.js +27 -23
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +15 -72
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +0 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +33 -63
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +14 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +15 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +5 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +37 -40
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +7 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +101 -38
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/errors/AbstractError.js +5 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/ValueConverter.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +9 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +15 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +4 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +5 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +1 -123
- package/libx/_runtime/cds-services/services/Service.js +79 -107
- package/libx/_runtime/cds-services/services/utils/columns.js +23 -19
- package/libx/_runtime/cds-services/services/utils/compareJson.js +11 -1
- package/libx/_runtime/cds-services/services/utils/differ.js +7 -2
- package/libx/_runtime/cds-services/util/assert.js +65 -2
- package/libx/_runtime/common/composition/data.js +1 -0
- package/libx/_runtime/common/generic/auth/expand.js +1 -1
- package/libx/_runtime/common/generic/auth/restrict.js +5 -10
- package/libx/_runtime/common/generic/auth/restrictions.js +40 -0
- package/libx/_runtime/common/generic/auth/utils.js +1 -2
- package/libx/_runtime/common/generic/crud.js +32 -16
- package/libx/_runtime/common/generic/etag.js +133 -104
- package/libx/_runtime/common/generic/input.js +6 -21
- package/libx/_runtime/common/generic/put.js +1 -1
- package/libx/_runtime/common/generic/stream.js +52 -0
- package/libx/_runtime/common/generic/temporal.js +25 -8
- package/libx/_runtime/common/i18n/messages.properties +0 -2
- package/libx/_runtime/common/utils/cqn.js +1 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +5 -2
- package/libx/_runtime/common/utils/csn.js +0 -51
- package/libx/_runtime/common/utils/etag.js +30 -0
- package/libx/_runtime/common/utils/keys.js +1 -1
- package/libx/_runtime/common/utils/normalizeTimestamp.js +25 -0
- package/libx/_runtime/common/utils/path.js +1 -1
- package/libx/_runtime/common/utils/resolveView.js +2 -1
- package/libx/_runtime/common/utils/rewriteAsterisks.js +6 -4
- package/libx/_runtime/common/utils/search2cqn4sql.js +12 -16
- package/libx/_runtime/common/utils/stream.js +140 -0
- package/libx/_runtime/common/utils/streamProp.js +29 -12
- package/libx/_runtime/common/utils/templateProcessorPathSerializer.js +0 -2
- package/libx/_runtime/db/generic/index.js +0 -2
- package/libx/_runtime/db/query/delete.js +2 -2
- package/libx/_runtime/db/query/insert.js +2 -2
- package/libx/_runtime/db/query/read.js +2 -2
- package/libx/_runtime/db/query/run.js +2 -2
- package/libx/_runtime/db/query/update.js +2 -2
- package/libx/_runtime/db/sql-builder/BaseBuilder.js +0 -6
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +23 -12
- package/libx/_runtime/db/sql-builder/FunctionBuilder.js +18 -6
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -0
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -7
- package/libx/_runtime/db/sql-builder/UpsertBuilder.js +1 -0
- package/libx/_runtime/db/utils/normalizeTimeData.js +7 -3
- package/libx/_runtime/fiori/draft.js +2 -0
- package/libx/_runtime/fiori/generic/activate.js +8 -9
- package/libx/_runtime/fiori/generic/before.js +30 -20
- package/libx/_runtime/fiori/generic/cancel.js +5 -3
- package/libx/_runtime/fiori/generic/delete.js +5 -3
- package/libx/_runtime/fiori/generic/edit.js +7 -7
- package/libx/_runtime/fiori/generic/index.js +10 -16
- package/libx/_runtime/fiori/generic/new.js +5 -3
- package/libx/_runtime/fiori/generic/patch.js +11 -8
- package/libx/_runtime/fiori/generic/prepare.js +13 -6
- package/libx/_runtime/fiori/generic/read.js +12 -6
- package/libx/_runtime/fiori/lean-draft.js +207 -152
- package/libx/_runtime/fiori/utils/delete.js +10 -5
- package/libx/_runtime/fiori/utils/req.js +17 -5
- package/libx/_runtime/fiori/utils/stream.js +36 -0
- package/libx/_runtime/hana/Service.js +12 -9
- package/libx/_runtime/hana/conversion.js +10 -15
- package/libx/_runtime/hana/driver.js +2 -0
- package/libx/_runtime/hana/execute.js +28 -6
- package/libx/_runtime/hana/pool.js +36 -122
- package/libx/_runtime/hana/search2cqn4sql.js +34 -36
- package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +2 -6
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +3 -1
- package/libx/_runtime/messaging/enterprise-messaging.js +10 -58
- package/libx/_runtime/messaging/outbox/utils.js +1 -1
- package/libx/_runtime/remote/Service.js +20 -1
- package/libx/_runtime/remote/utils/client.js +3 -5
- package/libx/_runtime/sqlite/Service.js +4 -6
- package/libx/_runtime/sqlite/conversion.js +3 -13
- package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +9 -6
- package/libx/_runtime/sqlite/customBuilder/CustomUpsertBuilder.js +6 -1
- package/libx/_runtime/sqlite/execute.js +5 -16
- package/libx/odata/afterburner.js +22 -6
- package/libx/odata/grammar.pegjs +6 -1
- package/libx/odata/parser.js +1 -1
- package/libx/rest/RestAdapter.js +16 -9
- package/libx/rest/RestRequest.js +1 -1
- package/libx/rest/middleware/input.js +2 -1
- package/libx/rest/middleware/operation.js +1 -0
- package/libx/rest/middleware/parse.js +3 -2
- package/libx/rest/middleware/payload.js +9 -8
- package/libx/rest/middleware/read.js +1 -0
- package/package.json +9 -16
- package/app/fiori/preview.js +0 -270
- package/app/fiori/routes.js +0 -59
- package/bin/build/buildTaskEngine.js +0 -360
- package/bin/build/buildTaskFactory.js +0 -283
- package/bin/build/buildTaskHandler.js +0 -241
- package/bin/build/buildTaskProvider.js +0 -22
- package/bin/build/buildTaskProviderFactory.js +0 -175
- package/bin/build/cds.js +0 -5
- package/bin/build/constants.js +0 -66
- package/bin/build/index.js +0 -58
- package/bin/build/provider/buildTaskHandlerEdmx.js +0 -82
- package/bin/build/provider/buildTaskHandlerFeatureToggles.js +0 -131
- package/bin/build/provider/buildTaskHandlerInternal.js +0 -254
- package/bin/build/provider/buildTaskProviderInternal.js +0 -383
- package/bin/build/provider/fiori/index.js +0 -171
- package/bin/build/provider/hana/2migration.js +0 -179
- package/bin/build/provider/hana/index.js +0 -505
- package/bin/build/provider/hana/migrationtable.js +0 -472
- package/bin/build/provider/hana/template/.hdiconfig-haas +0 -163
- package/bin/build/provider/hana/template/.hdiconfig-hanacloud +0 -137
- package/bin/build/provider/hana/template/.hdinamespace +0 -4
- package/bin/build/provider/hana/template/package.json +0 -12
- package/bin/build/provider/hana/template/undeploy.json +0 -5
- package/bin/build/provider/java/index.js +0 -111
- package/bin/build/provider/java-cf/index.js +0 -1
- package/bin/build/provider/mtx/index.js +0 -268
- package/bin/build/provider/mtx/resourcesTarBuilder.js +0 -95
- package/bin/build/provider/mtx-extension/index.js +0 -131
- package/bin/build/provider/mtx-sidecar/index.js +0 -137
- package/bin/build/provider/node-cf/index.js +0 -1
- package/bin/build/provider/nodejs/index.js +0 -192
- package/bin/build/util.js +0 -299
- package/bin/cds.js +0 -125
- package/bin/deploy/to-hana/cfUtil.js +0 -355
- package/bin/deploy/to-hana/gitUtil.js +0 -57
- package/bin/deploy/to-hana/hana.js +0 -306
- package/bin/deploy/to-hana/hdiDeployUtil.js +0 -153
- package/bin/deploy/to-hana/index.js +0 -16
- package/bin/deploy/to-hana/mtaUtil.js +0 -170
- package/bin/mtx/in-cds.js +0 -17
- package/bin/plugins.js +0 -32
- package/bin/run.js +0 -24
- package/bin/utils/log.js +0 -24
- package/bin/version.js +0 -178
- package/libx/_runtime/audit/Service.js +0 -222
- package/libx/_runtime/audit/generic/personal/access.js +0 -61
- package/libx/_runtime/audit/generic/personal/index.js +0 -56
- package/libx/_runtime/audit/generic/personal/modification.js +0 -132
- package/libx/_runtime/audit/generic/personal/utils.js +0 -186
- package/libx/_runtime/audit/utils/log.js +0 -23
- package/libx/_runtime/audit/utils/v2.js +0 -176
- package/libx/_runtime/db/data-conversion/timestamp.js +0 -9
- package/libx/_runtime/db/generic/integrity.js +0 -455
- package/srv/audit-log.cds +0 -87
- package/srv/mtx.cds +0 -2
- package/srv/mtx.js +0 -8
- /package/lib/{core → linked}/classes.js +0 -0
- /package/lib/{core → linked}/entities.js +0 -0
package/lib/compile/load.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const cds = require('..')
|
|
2
2
|
const TRACE = cds.debug('trace')
|
|
3
3
|
|
|
4
|
-
module.exports = exports = function
|
|
4
|
+
module.exports = exports = function load (files, options) {
|
|
5
5
|
const all = cds.resolve(files,options)
|
|
6
6
|
if (!all) return Promise.reject (new cds.error ({
|
|
7
7
|
message: `Couldn't find a CDS model for '${files}' in ${cds.root}`,
|
package/lib/compile/minify.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const cds = require('../index')
|
|
2
|
+
const DEBUG = cds.debug('minify')
|
|
2
3
|
|
|
3
4
|
module.exports = function cds_minify (csn, _roots) { // IMPORTANT: don't add cds.env.features.skip_unused as default for _roots here, as that will break VSCode's IntelliSense
|
|
4
5
|
const roots = _roots !== undefined ? _roots : cds.env.features.skip_unused
|
|
@@ -58,6 +59,7 @@ module.exports = function cds_minify (csn, _roots) { // IMPORTANT: don't add cds
|
|
|
58
59
|
const minified = Object.create (csn.__proto__, Object.getOwnPropertyDescriptors(csn))
|
|
59
60
|
const less = minified.definitions = {}
|
|
60
61
|
for (let n in all) if (reached.has(all[n])) less[n] = all[n]
|
|
62
|
+
else DEBUG?.('skipping', all[n].kind, n)
|
|
61
63
|
Object.defineProperty (minified,_minified,{value:true})
|
|
62
64
|
return minified
|
|
63
65
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const cds = require ('../../index')
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This is the central function to compile sources to CSN.
|
|
5
|
+
* @param {string|string[]|{}} model one of:
|
|
6
|
+
* - a single filename starting with 'file:'
|
|
7
|
+
* - a single CDL source string
|
|
8
|
+
* - an object with multiple CDL or CSN sources
|
|
9
|
+
* - an array of one or more filenames
|
|
10
|
+
* @param { _flavor | {flavor:_flavor, ...}} options
|
|
11
|
+
* - an options object or a string specifying the flavor of CSN to generate
|
|
12
|
+
* @param { 'inferred'|'xtended'|'parsed' } _flavor - for internal use only(!)
|
|
13
|
+
* @returns {{ namespace?:string, definitions:{}, extensions?:[], meta:{ flavor:_flavor }}} CSN
|
|
14
|
+
*/
|
|
15
|
+
function cds_compile_to_csn (model, options, _flavor) {
|
|
16
|
+
|
|
17
|
+
if (!model) throw cds.error (`Argument 'model' must be specified`)
|
|
18
|
+
if (_is_csn(model) && _assert_flavor(model,_flavor,options)) return model //> already parsed csn
|
|
19
|
+
|
|
20
|
+
const o = _options4 (options,_flavor)
|
|
21
|
+
const cwd = o.cwd || cds.root
|
|
22
|
+
const files = _is_files (model,cwd)
|
|
23
|
+
const cdsc = require ('../cdsc')
|
|
24
|
+
if (files && o.sync) return _finalize (cdsc.compileSync(files,cwd,o)) //> compile files synchroneously
|
|
25
|
+
if (files) return cdsc.compile(files,cwd,o) .then (_finalize) //> compile files asynchroneously
|
|
26
|
+
else return _finalize (cdsc.compileSources(model,o)) //> compile CDL sources
|
|
27
|
+
|
|
28
|
+
function _finalize (csn) {
|
|
29
|
+
if (o.min) csn = cds.minify(csn)
|
|
30
|
+
// REVISIT: experimental implementation to detect external APIs
|
|
31
|
+
for (let each in csn.definitions) {
|
|
32
|
+
const d = csn.definitions[each]
|
|
33
|
+
if (d.kind === 'service' && cds.requires[each]?.external && (!o.mocked || cds.requires[each].credentials)) {
|
|
34
|
+
Object.defineProperty (d,'@cds.external', { value: cds.requires[each].kind || true })
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (!csn.meta) csn.meta = {}
|
|
38
|
+
csn.meta.flavor = o.flavor
|
|
39
|
+
return csn
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
const _is_csn = (x) => (x.definitions || x.extensions) && !x.$builtins
|
|
45
|
+
const _is_files = (m,root) => {
|
|
46
|
+
if (m === '*' || Array.isArray(m) || /^file:/.test(m) && (m = m.slice(5)))
|
|
47
|
+
return cds.resolve(m,{root}) || cds.error ( `Couldn't find a CDS model for '${m}' in ${root||cds.root}`,{ code:'MODEL_NOT_FOUND', model: m })
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const _assert_flavor = (m,_flavor,options) => {
|
|
51
|
+
if (!m.meta) return true; const f = _flavor || _flavor4 (options)
|
|
52
|
+
return !f || f === m.meta.flavor || cds.error (`cds.compile(...,{flavor:'${f}'}) called on csn with different meta.flavor='${m.meta.flavor}'`)
|
|
53
|
+
}
|
|
54
|
+
const _flavors = {
|
|
55
|
+
'parsed': { level:1, cdsc_options: { parseCdl:true } },
|
|
56
|
+
'xtended': { level:2, cdsc_options: { csnFlavor:'gensrc' } },
|
|
57
|
+
'inferred': { level:3 },
|
|
58
|
+
}
|
|
59
|
+
const _flavor4 = (o) => {
|
|
60
|
+
const f = typeof o === 'string' ? o : o && o.flavor
|
|
61
|
+
return !f || f in _flavors ? f : cds.error (`Option 'flavor' must be one of ${Object.keys(_flavors)}; got: '${f}'`)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const _options4 = (_o, _flavor) => {
|
|
65
|
+
const flavor = _flavor ? _flavor4(_flavor) : _flavor4(_o) || 'inferred'
|
|
66
|
+
const spec = _flavors[flavor]
|
|
67
|
+
const o = { ..._o, flavor, ...spec.cdsc_options, ...cds.env.cdsc, cdsHome: cds.home } // cdsHome is for the compiler resolving @sap/cds/... files
|
|
68
|
+
if (o.docs) o.docComment = true
|
|
69
|
+
if (o.locations) o.withLocations = true
|
|
70
|
+
if (!o.messages) o.messages = []
|
|
71
|
+
return o
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = cds_compile_to_csn
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
const cds = require('
|
|
2
|
-
const CSV = require('../../csv-reader')
|
|
1
|
+
const cds = require('../../../lib')
|
|
3
2
|
const { getElementCdsPersistenceName, getArtifactCdsPersistenceName } = require('@sap/cds-compiler')
|
|
4
|
-
const {
|
|
5
|
-
const { fs, path, isdir } = cds.utils
|
|
3
|
+
const { fs, path, isdir, csv } = cds.utils
|
|
6
4
|
const { readdir } = fs.promises
|
|
7
|
-
let logger = cds.log(
|
|
5
|
+
let logger = cds.log('cds')
|
|
8
6
|
|
|
9
7
|
module.exports = async (model, options = {}) => {
|
|
10
8
|
if (options.logger) {
|
|
@@ -52,7 +50,7 @@ async function _tabledata4(dir, csvFile, model, baseDir, naming) {
|
|
|
52
50
|
|
|
53
51
|
const file = path.join(dir, csvFile)
|
|
54
52
|
const reader = fs.createReadStream(file)
|
|
55
|
-
const { cols, delimiter } = await
|
|
53
|
+
const { cols, delimiter } = await csv.readHeader(reader)
|
|
56
54
|
if (cols.length === 0) return // no columns at all -> skip import
|
|
57
55
|
|
|
58
56
|
cols.forEach(csvCol => {
|
package/lib/compile/to/json.js
CHANGED
package/lib/compile/to/sql.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
const cds = require ('../..')
|
|
2
2
|
const cdsc = require ('../cdsc')
|
|
3
3
|
const keywords = require("@sap/cds-compiler").to.sql.sqlite.keywords
|
|
4
|
+
const { unfold_ddl } = require ('../etc/_localized')
|
|
4
5
|
|
|
5
6
|
function cds_compile_to_sql (csn,_o) {
|
|
6
7
|
csn = _extended(cds.minify(csn))
|
|
7
8
|
const o = cdsc._options.for.sql(_o) //> used twice below...
|
|
8
9
|
const all = cdsc.to.sql(csn,o) .map (each => each.replace(/^-- .+\n/,'')) //> strip comments
|
|
9
|
-
const sql =
|
|
10
|
+
const sql = unfold_ddl(all, csn, o)
|
|
10
11
|
if (o.as === 'str') return `\n${sql.join('\n\n')}\n`
|
|
11
12
|
return sql
|
|
12
13
|
}
|
|
@@ -18,12 +19,13 @@ function cds_compile_to_hdbtable (csn,o) {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
function cds_compile_to_deltaSql (csn, o, beforeCsn) {
|
|
21
|
-
const options = cdsc._options.for.sql(o)
|
|
22
|
+
const options = cdsc._options.for.sql(o)
|
|
23
|
+
if (typeof beforeCsn === 'string') beforeCsn = JSON.parse(beforeCsn)
|
|
22
24
|
const { afterImage, drops, createsAndAlters } = cdsc.to.deltaSql (csn, options, beforeCsn || {definitions: {}, $version: '2.0'} ); // FIXME: As default value in compiler API?
|
|
23
|
-
return {
|
|
24
|
-
afterImage,
|
|
25
|
-
drops:
|
|
26
|
-
createsAndAlters:
|
|
25
|
+
return {
|
|
26
|
+
afterImage,
|
|
27
|
+
drops: unfold_ddl(drops.map (each => each.replace(/^-- .+\n/,'')), csn, options),
|
|
28
|
+
createsAndAlters: unfold_ddl(createsAndAlters.map (each => each.replace(/^-- .+\n/,'')), csn, options)
|
|
27
29
|
};
|
|
28
30
|
}
|
|
29
31
|
|
package/lib/dbs/cds-deploy.js
CHANGED
|
@@ -1,21 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
const cds = require('../index'), { local } = cds.utils
|
|
2
3
|
const COLORS = !!process.stdout.isTTY && !!process.stderr.isTTY
|
|
3
4
|
const GREY = COLORS ? '\x1b[2m' : ''
|
|
4
5
|
const RESET = COLORS ? '\x1b[0m' : ''
|
|
5
|
-
|
|
6
|
-
const TRACE = cds.debug('trace')
|
|
7
|
-
const nativeSchevoSqls = {
|
|
8
|
-
'postgres': {
|
|
9
|
-
create: `CREATE table cds_model (csn text)`,
|
|
10
|
-
insert: `INSERT into cds_model values ('null')`,
|
|
11
|
-
drop: `DROP table if exists cds_model;`
|
|
12
|
-
},
|
|
13
|
-
'sqlite': {
|
|
14
|
-
create: `CREATE table cds_Model (csn CLOB)`,
|
|
15
|
-
insert: `INSERT into cds_Model values ('null')`,
|
|
16
|
-
drop: `DROP table if exists cds_Model;`
|
|
17
|
-
}
|
|
18
|
-
}
|
|
6
|
+
let DEBUG // IMPORTANT: initialized later after await cds.plugins
|
|
19
7
|
|
|
20
8
|
/**
|
|
21
9
|
* Implementation of `cds.deploy` common to all databases.
|
|
@@ -23,43 +11,148 @@ const nativeSchevoSqls = {
|
|
|
23
11
|
* deploy create tables and views in case of a SQL database, then fills
|
|
24
12
|
* in initial data, if present.
|
|
25
13
|
*/
|
|
26
|
-
exports =
|
|
14
|
+
module.exports = exports = function cds_deploy (model,options,csvs) {
|
|
15
|
+
DEBUG = cds.debug('deploy')
|
|
16
|
+
return {
|
|
17
|
+
/** @param {import('@sap/cds/lib/srv/srv-api')} db */
|
|
18
|
+
async to(db, o = options || cds.options || {}) {
|
|
19
|
+
|
|
20
|
+
const TRACE = cds.debug('trace')
|
|
21
|
+
TRACE?.time('cds.deploy db ')
|
|
22
|
+
|
|
23
|
+
if (!model) throw new Error('Must provide a model or a path to model, received: ' + model)
|
|
24
|
+
if (model && !model.definitions) model = await cds.load(model).then(cds.minify)
|
|
25
|
+
|
|
26
|
+
if (o.mocked) exports.include_external_entities_in(model)
|
|
27
|
+
else exports.exclude_external_entities_in(model)
|
|
28
|
+
|
|
29
|
+
if (!db.run) db = await cds.connect.to(db)
|
|
30
|
+
if (!cds.db) cds.db = cds.services.db = db
|
|
31
|
+
if (!db.model) db.model = model
|
|
32
|
+
|
|
33
|
+
// eslint-disable-next-line no-console
|
|
34
|
+
const LOG = o.silent || o.dry || !cds.log('deploy')._info ? () => {} : console.log
|
|
35
|
+
const _deploy = async tx => {
|
|
36
|
+
// create / update schema
|
|
37
|
+
let any = await exports.create(tx, model, o)
|
|
38
|
+
if (!any && !csvs) return db
|
|
39
|
+
// fill in initial data
|
|
40
|
+
await exports.init(tx, model, o, csvs, file => LOG(GREY, ` > init from ${local(file)}`, RESET))
|
|
41
|
+
}
|
|
27
42
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
43
|
+
let url = db.url4(cds.context?.tenant)
|
|
44
|
+
if (url === ':memory:') url = 'in-memory database.'
|
|
45
|
+
else if (!url.startsWith('http:')) url = local(url)
|
|
46
|
+
try {
|
|
47
|
+
await (o.dry ? _deploy(db) : db.run(_deploy))
|
|
48
|
+
LOG('/> successfully deployed to', url, '\n')
|
|
49
|
+
} catch (e) {
|
|
50
|
+
LOG('/> deployment to', url, 'failed\n')
|
|
51
|
+
throw e
|
|
52
|
+
}
|
|
31
53
|
|
|
32
|
-
|
|
33
|
-
|
|
54
|
+
TRACE?.timeEnd('cds.deploy db ')
|
|
55
|
+
return db
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
// continue to support cds.deploy() as well...
|
|
59
|
+
then(n, e) {
|
|
60
|
+
return this.to(cds.db || 'db').then(n, e)
|
|
61
|
+
},
|
|
62
|
+
catch(e) {
|
|
63
|
+
return this.to(cds.db || 'db').catch(e)
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
}
|
|
34
67
|
|
|
35
|
-
|
|
36
|
-
else exports.exclude_external_entities_in (model)
|
|
68
|
+
exports.create = async function cds_deploy_create (db, csn=db.model, o) {
|
|
37
69
|
|
|
38
|
-
|
|
39
|
-
if (!cds.db) cds.db = cds.services.db = db
|
|
40
|
-
if (!db.model) db.model = model
|
|
70
|
+
/* eslint-disable no-console */
|
|
41
71
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
72
|
+
if (!o.to || o.to === db.options.kind) o = { ...db.options, ...o }
|
|
73
|
+
if (o.impl === '@cap-js/sqlite') {
|
|
74
|
+
// REVISIT: Move that to configuration or to cds.compile -> currently cds compile creates wrong output
|
|
75
|
+
o.betterSqliteSessionVariables = true
|
|
76
|
+
o.sqlDialect = 'sqlite'
|
|
77
|
+
}
|
|
46
78
|
|
|
47
|
-
|
|
48
|
-
|
|
79
|
+
let drops, creas
|
|
80
|
+
let schevo = o.schema_evolution === 'auto' || o['with-auto-schema-evolution'] || o['model-only'] || o['delta-from'] || (o.kind === 'postgres' && o.schema_evolution !== false);
|
|
81
|
+
if (schevo) {
|
|
82
|
+
const { prior, table_exists } = await get_prior_model()
|
|
83
|
+
const { afterImage, drops: d, createsAndAlters } = cds.compile.to.sql.delta(csn, o, prior && JSON.parse(prior))
|
|
84
|
+
const after = JSON.stringify(afterImage)
|
|
85
|
+
if (!o.dry && after != prior) {
|
|
86
|
+
if (!table_exists) {
|
|
87
|
+
const CLOB = o.dialect === 'postgres' || o.kind === 'postgres' ? 'text' : 'CLOB'
|
|
88
|
+
await db.run(`CREATE table cds_model (csn ${CLOB})`)
|
|
89
|
+
await db.run(`INSERT into cds_model values (?)`, after)
|
|
90
|
+
} else {
|
|
91
|
+
await db.run(`UPDATE cds_model SET csn = ?`, after)
|
|
92
|
+
}
|
|
93
|
+
db.options.schema_evolution = 'auto' // for updating package.json
|
|
94
|
+
}
|
|
95
|
+
// cds deploy --model-only > fills in table cds_model above
|
|
96
|
+
if (o['model-only']) return o.dry && console.log(after)
|
|
97
|
+
// cds deploy -- with auto schema evolution > upgrade by applying delta to former model
|
|
98
|
+
creas = createsAndAlters
|
|
99
|
+
drops = d
|
|
100
|
+
} else {
|
|
101
|
+
// cds deploy -- w/o auto schema evoution > drop-create db
|
|
102
|
+
creas = cds.compile.to.sql(csn, o)
|
|
103
|
+
}
|
|
49
104
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
105
|
+
if (!drops)
|
|
106
|
+
drops = creas
|
|
107
|
+
.map(each => {
|
|
108
|
+
let [, kind, entity] = each.match(/^CREATE (TABLE|VIEW) ("[^"]+"|[^\s(]+)/im) || []
|
|
109
|
+
return `DROP ${kind} IF EXISTS ${entity};`
|
|
110
|
+
})
|
|
111
|
+
.reverse()
|
|
53
112
|
|
|
54
|
-
|
|
55
|
-
return db
|
|
56
|
-
},
|
|
113
|
+
if (!drops.length && !creas.length) return !o.dry
|
|
57
114
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
115
|
+
if (o.dry) {
|
|
116
|
+
console.log()
|
|
117
|
+
for (let each of drops) console.log(each)
|
|
118
|
+
console.log()
|
|
119
|
+
for (let each of creas) console.log(each, '\n')
|
|
120
|
+
return
|
|
121
|
+
}
|
|
62
122
|
|
|
123
|
+
// Set the context model while deploying for cqn42sql in new db layers
|
|
124
|
+
db.model = cds.compile.for.nodejs(csn)
|
|
125
|
+
await db.run(drops)
|
|
126
|
+
await db.run(creas)
|
|
127
|
+
return true
|
|
128
|
+
|
|
129
|
+
async function get_prior_model() {
|
|
130
|
+
let file = o['delta-from']
|
|
131
|
+
if (file) {
|
|
132
|
+
let prior = await cds.utils.read(file)
|
|
133
|
+
return { prior }
|
|
134
|
+
}
|
|
135
|
+
if (o.dry) return {}
|
|
136
|
+
|
|
137
|
+
let [table_exists] = await db.run(
|
|
138
|
+
// REVISIT: prettier forced this horrible, unreadable formatting:
|
|
139
|
+
db.kind === 'postgres'
|
|
140
|
+
? `SELECT 1 from pg_tables WHERE tablename = 'cds_model' and schemaname = current_schema()`
|
|
141
|
+
: db.kind === 'sqlite'
|
|
142
|
+
? `SELECT 1 from sqlite_schema WHERE name = 'cds_model'`
|
|
143
|
+
: cds.error`Schema evolution is not supported for ${db.kind} databases`,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
if (o['model-only'])
|
|
147
|
+
return { table_exists };
|
|
148
|
+
|
|
149
|
+
if (table_exists) {
|
|
150
|
+
let [{ csn }] = await db.run('SELECT csn from cds_model')
|
|
151
|
+
return { prior: csn, table_exists }
|
|
152
|
+
}
|
|
153
|
+
return { table_exists } // no prior csn
|
|
154
|
+
}
|
|
155
|
+
}
|
|
63
156
|
|
|
64
157
|
|
|
65
158
|
const { fs, path, read } = cds.utils
|
|
@@ -73,7 +166,7 @@ exports.include_external_entities_in = function (model) {
|
|
|
73
166
|
const def = model.definitions[each]
|
|
74
167
|
if (def['@cds.persistence.mock'] === false) continue
|
|
75
168
|
if (def['@cds.persistence.skip'] === true) {
|
|
76
|
-
DEBUG
|
|
169
|
+
DEBUG?.('including mocked', each)
|
|
77
170
|
delete def['@cds.persistence.skip']
|
|
78
171
|
}
|
|
79
172
|
}
|
|
@@ -86,7 +179,7 @@ exports.exclude_external_entities_in = function (csn) { // NOSONAR
|
|
|
86
179
|
for (let [each,{service=each,model,credentials}] of Object.entries (cds.env.requires)) {
|
|
87
180
|
if (!model) continue //> not for internal services like cds.requires.odata
|
|
88
181
|
if (!credentials && csn._mocked) continue //> not for mocked unbound services
|
|
89
|
-
DEBUG
|
|
182
|
+
DEBUG?.('excluding external entities for', service, '...')
|
|
90
183
|
const prefix = service+'.'
|
|
91
184
|
for (let each in csn.definitions) if (each.startsWith(prefix)) _exclude (each)
|
|
92
185
|
}
|
|
@@ -95,7 +188,7 @@ exports.exclude_external_entities_in = function (csn) { // NOSONAR
|
|
|
95
188
|
function _exclude (each) {
|
|
96
189
|
const def = csn.definitions[each]; if (def.kind !== 'entity') return
|
|
97
190
|
if (def['@cds.persistence.table'] === true) return // do not exclude replica table
|
|
98
|
-
DEBUG
|
|
191
|
+
DEBUG?.('excluding external entity', each)
|
|
99
192
|
def['@cds.persistence.skip'] = true
|
|
100
193
|
// propagate to all views on top...
|
|
101
194
|
for (let other in csn.definitions) {
|
|
@@ -107,66 +200,6 @@ exports.exclude_external_entities_in = function (csn) { // NOSONAR
|
|
|
107
200
|
}
|
|
108
201
|
|
|
109
202
|
|
|
110
|
-
exports.create = async function cds_deploy_create (db, csn=db.model, o) {
|
|
111
|
-
// REVISIT: How to find out if we use better-sqlite?
|
|
112
|
-
if (db._source === '@cap-js/sqlite') {
|
|
113
|
-
// it's required to set both properties
|
|
114
|
-
o.betterSqliteSessionVariables = true
|
|
115
|
-
o.sqlDialect = 'sqlite'
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const sqls = nativeSchevoSqls[db.options?.dialect || db.options?.kind || 'sqlite'];
|
|
119
|
-
let drops, creas, schevo = db.options?.schema_evolution === 'auto' || o.schema_evolution === 'auto'
|
|
120
|
-
if (schevo) {
|
|
121
|
-
// REVISIT: replace by db-specific ways to read/updated recent deployed model
|
|
122
|
-
let before;
|
|
123
|
-
try {
|
|
124
|
-
const [{csn}] = await db.read('cds.Model');
|
|
125
|
-
before = JSON.parse(csn);
|
|
126
|
-
} catch(e) {
|
|
127
|
-
if (!e.message.includes('no such table') && !e.message.includes('relation "cds_model" does not exist')) throw e
|
|
128
|
-
// TODO: Put both into one tx - read can not be part of it, as the transaction gets killed in PG once an error appears
|
|
129
|
-
await db.run(sqls.create)
|
|
130
|
-
await db.run(sqls.insert)
|
|
131
|
-
before = null;
|
|
132
|
-
}
|
|
133
|
-
const { afterImage, drops:_drops, createsAndAlters:_creas } = cds.compile.to.sql.delta (csn, {...o, ...db.options}, before)
|
|
134
|
-
creas = _creas.concat (o.dry ? [] : UPDATE('cds.Model').with({ csn: JSON.stringify(afterImage) }))
|
|
135
|
-
drops = before ? _drops : _drops4(_creas)
|
|
136
|
-
} else {
|
|
137
|
-
creas = cds.compile.to.sql (csn, {...o, ...db.options})
|
|
138
|
-
drops = _drops4(creas) .concat (sqls.drop)
|
|
139
|
-
}
|
|
140
|
-
if (!creas || creas.length === 0) return
|
|
141
|
-
|
|
142
|
-
function _drops4 (creas) {
|
|
143
|
-
return creas.map (each => {
|
|
144
|
-
let [, kind, entity] = each.match(/^CREATE (TABLE|VIEW) ("[^"]+"|[^\s(]+)/im) || []
|
|
145
|
-
return `DROP ${kind} IF EXISTS ${entity};`
|
|
146
|
-
}).reverse()
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (o.dry) {
|
|
150
|
-
console.log(); for (let each of drops) console.log(each)
|
|
151
|
-
console.log(); for (let each of creas) console.log(each,'\n')
|
|
152
|
-
}
|
|
153
|
-
else return db.run (async tx => {
|
|
154
|
-
// This runs in a new transaction if called from CLI, while joining
|
|
155
|
-
// existing root tx, e.g. when called from DeploymentService.
|
|
156
|
-
if (!schevo && db.kind == 'sqlite' && await tx.exists('sqlite.schema').where({name:'cds_xt_Extensions'})) {
|
|
157
|
-
// Poor man's schema evolution for extensions in drop-create deployments
|
|
158
|
-
drops = drops.filter(d => !d.includes('cds_xt_Extensions'))
|
|
159
|
-
creas = creas.filter(c => !c.includes('cds_xt_Extensions'))
|
|
160
|
-
}
|
|
161
|
-
// Set the context model while deploying for cqn42sql in new db layers
|
|
162
|
-
tx.model = cds.compile.for.nodejs(csn)
|
|
163
|
-
await tx.run(drops)
|
|
164
|
-
await tx.run(creas)
|
|
165
|
-
return true
|
|
166
|
-
})
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
170
203
|
exports.init = async function cds_deploy_init (db, csn=db.model, o, srces, log=()=>{}) {
|
|
171
204
|
const t = cds.context?.tenant; if (t && t === cds.requires.multitenancy?.t0) return
|
|
172
205
|
return db.run (async tx => {
|
|
@@ -184,7 +217,7 @@ exports.init = async function cds_deploy_init (db, csn=db.model, o, srces, log=(
|
|
|
184
217
|
throw Object.assign (e, { message: 'in cds.deploy(): ' + e.message +'\n'+ cds.utils.inspect(q) })
|
|
185
218
|
}
|
|
186
219
|
} else { //> init.js/ts case
|
|
187
|
-
if (typeof src === 'function') src(tx,csn)
|
|
220
|
+
if (typeof src === 'function') await src(tx,csn)
|
|
188
221
|
}
|
|
189
222
|
}
|
|
190
223
|
})
|
|
@@ -199,16 +232,19 @@ exports.data = async function cds_deploy_prepare_data (csn, srces) {
|
|
|
199
232
|
return [ file, e, src ]
|
|
200
233
|
})
|
|
201
234
|
// If not, we load them from cds.deploy.resources(csn)
|
|
235
|
+
const data = []
|
|
202
236
|
const resources = await exports.resources(csn, { testdata: cds.env.features.test_data })
|
|
203
|
-
|
|
237
|
+
const resEntries = Object.entries(resources).reverse() // reversed $sources, relevant as UPSERT order
|
|
238
|
+
for (const [file,e] of resEntries) {
|
|
204
239
|
if (e === '*') {
|
|
205
240
|
let init_js = await cds.utils._import (file)
|
|
206
|
-
|
|
241
|
+
data.push([ file, null, init_js.default || init_js ])
|
|
207
242
|
} else {
|
|
208
243
|
let src = await read (file, 'utf8')
|
|
209
|
-
|
|
244
|
+
data.push([ file, e, src ])
|
|
210
245
|
}
|
|
211
|
-
}
|
|
246
|
+
}
|
|
247
|
+
return data
|
|
212
248
|
}
|
|
213
249
|
|
|
214
250
|
|
|
@@ -230,14 +266,14 @@ exports.resources = async function cds_deploy_resources (csn, opts) {
|
|
|
230
266
|
const f = fx.slice(0,-ext.length)
|
|
231
267
|
if (/[._]texts$/.test(f) && files.some(g => g.startsWith(f+'_'))) {
|
|
232
268
|
// ignores 'Books_texts.csv/json' if there is any 'Books_texts_LANG.csv/json'
|
|
233
|
-
DEBUG
|
|
269
|
+
DEBUG?.(`ignoring '${fx}' in favor of translated ones`)
|
|
234
270
|
continue
|
|
235
271
|
}
|
|
236
272
|
const e = _entity4(f,csn); if (_skip(e)) continue
|
|
237
273
|
if (cds.env.features.deploy_data_onconflict === 'replace' && !/[._]texts_/.test(f)) {
|
|
238
274
|
const seenBefore = Object.entries(found).find(([_, entity]) => entity === e.name )
|
|
239
275
|
if (seenBefore) {
|
|
240
|
-
DEBUG
|
|
276
|
+
DEBUG?.(`Conflict for '${e.name}': replacing '${local(seenBefore[0])}' with '${local(path.join(subdir,fx))}'`)
|
|
241
277
|
continue
|
|
242
278
|
}
|
|
243
279
|
}
|
|
@@ -267,7 +303,7 @@ const _entity4 = (file,csn) => {
|
|
|
267
303
|
const base = csn.definitions [RegExp.$1]
|
|
268
304
|
return base?.elements?.texts && _entity4 (base.elements.texts.target, csn)
|
|
269
305
|
}
|
|
270
|
-
else return DEBUG
|
|
306
|
+
else return DEBUG?.(`warning: ${name} not in model`)
|
|
271
307
|
}
|
|
272
308
|
// We also support insert into simple views if they have no projection
|
|
273
309
|
const p = entity.query && entity.query.SELECT || entity.projection
|
|
@@ -305,7 +341,7 @@ const _queries4 = (db,csn) => !db.cqn2sql ? q => q : q => {
|
|
|
305
341
|
|
|
306
342
|
|
|
307
343
|
const INSERT_from4 = (db,o) => {
|
|
308
|
-
const schevo = db.options?.schema_evolution === 'auto' || o?.schema_evolution === 'auto'
|
|
344
|
+
const schevo = db.options?.schema_evolution === 'auto' || cds.env.requires.db?.schema_evolution === 'auto' || o?.schema_evolution === 'auto'
|
|
309
345
|
const INSERT_into = (schevo ? UPSERT : INSERT).into
|
|
310
346
|
return (file) => ({
|
|
311
347
|
'.json': { into (entity, json) {
|
|
@@ -320,4 +356,28 @@ const INSERT_from4 = (db,o) => {
|
|
|
320
356
|
}
|
|
321
357
|
|
|
322
358
|
const _skip = e => !e || e['@cds.persistence.skip'] === true
|
|
323
|
-
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
if (!module.parent) (async () => {
|
|
363
|
+
await cds.plugins // IMPORTANT: that has to go before any call to cds.env, like through cds.deploy or cds.requires below
|
|
364
|
+
let db = cds.requires.db
|
|
365
|
+
try {
|
|
366
|
+
let o={}, recent
|
|
367
|
+
for (let each of process.argv.slice(2)) {
|
|
368
|
+
if (each.startsWith('--')) o[(recent = each.slice(2))] = true
|
|
369
|
+
else o[recent] = each
|
|
370
|
+
}
|
|
371
|
+
if (o.to) {
|
|
372
|
+
db = { kind: o.to, dialect: o.to }
|
|
373
|
+
if (o.url) (db.credentials ??= {}).url = o.url
|
|
374
|
+
if (o.host) (db.credentials ??= {}).host = o.host
|
|
375
|
+
if (o.port) (db.credentials ??= {}).port = o.port
|
|
376
|
+
if (o.username) (db.credentials ??= {}).username = o.username
|
|
377
|
+
if (o.password) (db.credentials ??= {}).password = o.password
|
|
378
|
+
}
|
|
379
|
+
db = await cds.deploy('*',o).to(db)
|
|
380
|
+
} finally {
|
|
381
|
+
await db?.disconnect?.()
|
|
382
|
+
}
|
|
383
|
+
})().catch(console.error)
|