@sap/cds-dk 10.0.5 → 10.0.7
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 +28 -0
- package/bin/export.js +1 -1
- package/bin/version.js +7 -3
- package/lib/build/plugins/hana/index.js +19 -2
- package/lib/import/asyncapi/parserContext.js +136 -54
- package/lib/init/projectReader.js +4 -2
- package/lib/init/template/ams/files/package.json.hbs +1 -1
- package/lib/init/template/nodejs/index.js +4 -2
- package/lib/init/template/test/files/test/test.xs.hbs +3 -2
- package/lib/init/template/xsuaa/index.js +11 -7
- package/node_modules/@cap-js/openapi/lib/compile/csdl.js +3 -3
- package/node_modules/@cap-js/openapi/lib/compile/csdl2openapi.js +111 -103
- package/node_modules/@cap-js/openapi/lib/compile/diagram.js +4 -4
- package/node_modules/@cap-js/openapi/lib/compile/types.d.ts +3 -1
- package/node_modules/@cap-js/openapi/package.json +3 -3
- package/node_modules/@sap/cds/CHANGELOG.md +16 -0
- package/node_modules/@sap/cds/lib/compile/for/nodejs.js +1 -0
- package/node_modules/@sap/cds/lib/env/defaults.js +1 -0
- package/node_modules/@sap/cds/lib/plugins.js +4 -2
- package/node_modules/@sap/cds/lib/srv/srv-handlers.js +1 -1
- package/node_modules/@sap/cds/libx/_runtime/common/Service.js +1 -1
- package/node_modules/@sap/cds/libx/_runtime/messaging/common-utils/authorizedRequest.js +3 -2
- package/node_modules/@sap/cds/libx/_runtime/remote/Service.js +1 -1
- package/node_modules/@sap/cds/package.json +1 -1
- package/node_modules/@sap/cds-compiler/CHANGELOG.md +12 -0
- package/node_modules/@sap/cds-compiler/lib/compiler/populate.js +1 -0
- package/node_modules/@sap/cds-compiler/lib/compiler/resolve.js +1 -1
- package/node_modules/@sap/cds-compiler/lib/model/csnUtils.js +13 -8
- package/node_modules/@sap/cds-compiler/package.json +1 -1
- package/node_modules/@sap/cds-mtxs/CHANGELOG.md +36 -0
- package/node_modules/@sap/cds-mtxs/bin/cds-mtx-migrate.js +32 -48
- package/node_modules/@sap/cds-mtxs/bin/cds-mtx.js +1 -0
- package/node_modules/@sap/cds-mtxs/cds-plugin.js +18 -0
- package/node_modules/@sap/cds-mtxs/lib/migration/migration.js +3 -408
- package/node_modules/@sap/cds-mtxs/package.json +1 -1
- package/node_modules/@sap/cds-mtxs/srv/cf/abstract-provisioning-service.js +5 -6
- package/node_modules/@sap/cds-mtxs/srv/extensibility/linter/annotations.js +5 -2
- package/node_modules/@sap/cds-mtxs/srv/extensibility/linter/index.js +6 -5
- package/node_modules/@sap/cds-mtxs/srv/extensibility-service.cds +0 -11
- package/node_modules/@sap/cds-mtxs/srv/extensibility-service.js +12 -22
- package/node_modules/@sap/cds-mtxs/srv/jobs-service.js +30 -24
- package/node_modules/@sap/cds-mtxs/srv/model-provider.js +2 -2
- package/node_modules/@sap/cds-mtxs/srv/plugins/hana/ctnr-mgr-base.js +5 -2
- package/node_modules/@sap/cds-mtxs/srv/plugins/hana/hdi.js +5 -3
- package/node_modules/@sap/cds-mtxs/srv/plugins/hana/srv-mgr.js +3 -2
- package/node_modules/@sap/cds-mtxs/srv/plugins/hana/tms.js +17 -0
- package/node_modules/@sap/cds-mtxs/srv/plugins/hana.js +14 -13
- package/node_modules/media-typer/README.md +15 -14
- package/node_modules/media-typer/index.js +0 -8
- package/node_modules/media-typer/package.json +16 -12
- package/node_modules/sax/lib/sax.js +27 -9
- package/node_modules/sax/package.json +1 -1
- package/node_modules/ws/package.json +1 -1
- package/package.json +4 -4
- package/lib/init/template/nodejs/files/.gitignore.hbs +0 -34
- package/node_modules/@sap/cds-mtxs/lib/migration/extension-project-builder.js +0 -129
- package/node_modules/@sap/cds-mtxs/lib/migration/extension-project.js +0 -89
- package/node_modules/@sap/cds-mtxs/lib/migration/mtx-adapter.js +0 -230
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
const path = require('path')
|
|
2
|
-
const fs = require('fs')
|
|
3
|
-
const cds = require('@sap/cds')
|
|
4
|
-
const DEBUG = cds.debug('mtx') //Note: cds.debug returns false for @sap/cds@(< 6.2)
|
|
5
|
-
|
|
6
|
-
module.exports.build = async function (options) {
|
|
7
|
-
const { project } = options
|
|
8
|
-
const env = cds.env
|
|
9
|
-
const root = cds.root
|
|
10
|
-
try {
|
|
11
|
-
cds.root = project
|
|
12
|
-
cds.env = cds.env.for('cds', project)
|
|
13
|
-
options.dest = path.join(project, cds.env.build.target, 'ext')
|
|
14
|
-
await new ExtensionBuilder(options).run()
|
|
15
|
-
} finally {
|
|
16
|
-
cds.root = root
|
|
17
|
-
cds.env = env
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
class ExtensionBuilder {
|
|
22
|
-
constructor(options) {
|
|
23
|
-
this._files = []
|
|
24
|
-
this._options = options
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async run() {
|
|
28
|
-
const { project, dest } = this._options
|
|
29
|
-
|
|
30
|
-
// clean any existing content
|
|
31
|
-
await fs.promises.rm(dest, { force: true, recursive: true })
|
|
32
|
-
|
|
33
|
-
// check existence of appPackage folder only
|
|
34
|
-
const appPackageFolder = this._getAppPackageFolder()
|
|
35
|
-
if (!fs.existsSync(path.join(project, appPackageFolder))) {
|
|
36
|
-
throw new Error(`The SaaS application base model '${appPackageFolder}' is missing.`)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// full compile of the extension using base model - ensuring consistency
|
|
40
|
-
const modelFolders = cds.resolve('*', false).filter(p => !p.startsWith('@sap/cds-mtx'))
|
|
41
|
-
DEBUG && DEBUG(`Extension model folders: ${modelFolders}`)
|
|
42
|
-
|
|
43
|
-
// throws an error if no model is found -> e.code === MODEL_NOT_FOUND
|
|
44
|
-
const model = await cds.load(modelFolders)
|
|
45
|
-
|
|
46
|
-
// extension CSN using parsed format
|
|
47
|
-
const extModel = await cds.load(this._resolveExtensionFiles(model, project), { flavor: 'parsed' })
|
|
48
|
-
if (extModel.requires) {
|
|
49
|
-
extModel.requires.length = 0
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const csn = cds.compile.to.json(extModel)
|
|
53
|
-
await this._write(csn, path.join(dest, 'extension.csn'))
|
|
54
|
-
await this._collectLanguageBundles(extModel, path.join(dest, 'i18n'))
|
|
55
|
-
|
|
56
|
-
const files = Object.keys(await cds.deploy.resources(model))
|
|
57
|
-
if (files.length > 0) {
|
|
58
|
-
const dataDest = path.join(dest, 'data')
|
|
59
|
-
await Promise.all(
|
|
60
|
-
files
|
|
61
|
-
.filter(file => /\.csv$/.test(file))
|
|
62
|
-
.map(csv => this._copy(csv, path.join(dataDest, path.basename(csv))))
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
await this._copy(path.join(project, 'package.json'), path.join(dest, 'package.json'))
|
|
66
|
-
// add all resources contained in 'gen/ext' folder
|
|
67
|
-
await this._writeTarFile(path.join(project, cds.env.build.target, 'extension.tgz'), dest)
|
|
68
|
-
|
|
69
|
-
DEBUG && DEBUG('build output:\n' + this._files.map(file => ` ${path.relative(project, file)}`).join('\n'))
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
_getAppPackageFolder() {
|
|
73
|
-
return path.join('node_modules', cds.env.extends || '_base');
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
_resolveExtensionFiles(model, project) {
|
|
77
|
-
const node_modules = path.join(project, 'node_modules')
|
|
78
|
-
const paths = model['$sources'].reduce((acc, file) => {
|
|
79
|
-
if (file.startsWith(project) && !file.startsWith(node_modules)) {
|
|
80
|
-
acc.push(file)
|
|
81
|
-
}
|
|
82
|
-
return acc
|
|
83
|
-
}, [])
|
|
84
|
-
|
|
85
|
-
return paths
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
async _write(data, dest) {
|
|
89
|
-
this._files.push(dest)
|
|
90
|
-
await fs.promises.mkdir(path.dirname(dest), { recursive: true })
|
|
91
|
-
await fs.promises.writeFile(dest, typeof data === "object" && !Buffer.isBuffer(data) ? JSON.stringify(data, null, 2) : data)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async _copy(src, dest) {
|
|
95
|
-
this._files.push(dest)
|
|
96
|
-
await fs.promises.mkdir(path.dirname(dest), { recursive: true })
|
|
97
|
-
await fs.promises.copyFile(src, dest)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async _writeTarFile(tarFile, root, resources) {
|
|
101
|
-
const { tar } = cds.utils
|
|
102
|
-
await tar.czfd(tarFile, root, resources)
|
|
103
|
-
this._files.push(tarFile)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async _collectLanguageBundles(model, dest) {
|
|
107
|
-
// collect effective i18n properties...
|
|
108
|
-
let bundles = {}
|
|
109
|
-
const bundleGenerator = cds.localize.bundles4(model)
|
|
110
|
-
if (bundleGenerator && bundleGenerator[Symbol.iterator]) {
|
|
111
|
-
for (let [locale, bundle] of bundleGenerator) {
|
|
112
|
-
// fallback bundle has the name ""
|
|
113
|
-
if (typeof locale === 'string') {
|
|
114
|
-
bundles[locale] = bundle
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
// omit bundles in case the fallback bundle is the only existing entry
|
|
119
|
-
const keys = Object.keys(bundles)
|
|
120
|
-
if (keys.length === 1 && keys[0] === "" && Object.keys(bundles[keys[0]]).length === 0) {
|
|
121
|
-
bundles = {}
|
|
122
|
-
}
|
|
123
|
-
// copied from ../compile/i18n.js
|
|
124
|
-
const { file: base = 'i18n' } = cds.env.i18n
|
|
125
|
-
if (Object.keys(bundles).length > 0) {
|
|
126
|
-
await this._write(bundles, path.join(dest, base + '.json'))
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
const cds = require('@sap/cds')
|
|
2
|
-
const path = require('path')
|
|
3
|
-
const fs = require('fs').promises
|
|
4
|
-
const mtxAdapter = require('./mtx-adapter')
|
|
5
|
-
const { build } = require('./extension-project-builder')
|
|
6
|
-
module.exports.DEFAULT_TAG = 'migrated'
|
|
7
|
-
|
|
8
|
-
module.exports.getExtensionTag = function (filename, tagRegexString, defaultTag = module.exports.DEFAULT_TAG) {
|
|
9
|
-
if (!tagRegexString) return defaultTag
|
|
10
|
-
const regex = new RegExp(tagRegexString)
|
|
11
|
-
const match = filename.match(regex)
|
|
12
|
-
return match?.[1] ? match[1] : defaultTag
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
module.exports.createProjects = async function (mtxExtension, dir, tagRegex, defaultTag = module.exports.DEFAULT_TAG) {
|
|
16
|
-
|
|
17
|
-
const validTags = []
|
|
18
|
-
|
|
19
|
-
const extensions = _splitExtensionByTag(mtxExtension, tagRegex, defaultTag)
|
|
20
|
-
|
|
21
|
-
for (const [tag, extension] of Object.entries(extensions)) {
|
|
22
|
-
|
|
23
|
-
const projectFolder = path.join(dir, tag)
|
|
24
|
-
try {
|
|
25
|
-
await fs.rm(projectFolder, { recursive: true, force: true })
|
|
26
|
-
} catch {
|
|
27
|
-
// ignore
|
|
28
|
-
}
|
|
29
|
-
await fs.mkdir(projectFolder, { recursive: true })
|
|
30
|
-
// extension is a map of files
|
|
31
|
-
await mtxAdapter.writeFilesFromMap(Array.from(extension), projectFolder)
|
|
32
|
-
// overwrite package.json
|
|
33
|
-
await _writePackageJson(projectFolder, tag, '_base')
|
|
34
|
-
|
|
35
|
-
// keep in mind to avoid parallel execution !! - use runner from old mtx?
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
await build({ project: projectFolder })
|
|
39
|
-
} catch (error) {
|
|
40
|
-
if (error.code !== 'MODEL_NOT_FOUND') throw error
|
|
41
|
-
// ignore extensions with no model
|
|
42
|
-
continue
|
|
43
|
-
}
|
|
44
|
-
validTags.push(tag)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return validTags
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function _splitExtensionByTag(mtxExtension, tagRegex, defaultTag) {
|
|
51
|
-
|
|
52
|
-
const result = {}
|
|
53
|
-
|
|
54
|
-
// create separate tags
|
|
55
|
-
for (const [file, content] of mtxExtension) {
|
|
56
|
-
if (!file.startsWith('node_modules')) {
|
|
57
|
-
const tag = module.exports.getExtensionTag(file, tagRegex, defaultTag)
|
|
58
|
-
if (!result[tag]) result[tag] = new Map()
|
|
59
|
-
result[tag].set(file, content)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// add node modules to each project to allow build
|
|
64
|
-
for (const [file, content] of mtxExtension) {
|
|
65
|
-
if (file.startsWith('node_modules')) {
|
|
66
|
-
for (const tag in result) {
|
|
67
|
-
result[tag].set(file, content)
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return result
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async function _writePackageJson(dir, name, main) {
|
|
76
|
-
const packageJson = {
|
|
77
|
-
"name": name,
|
|
78
|
-
"version": "1.0.0",
|
|
79
|
-
"description": "Generated extension project",
|
|
80
|
-
"cds": {
|
|
81
|
-
"extends": main
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// add compiler settings from main
|
|
86
|
-
packageJson.cds.cdsc = cds.env.cdsc
|
|
87
|
-
|
|
88
|
-
await fs.writeFile(path.join(dir, 'package.json'), JSON.stringify(packageJson, 2))
|
|
89
|
-
}
|
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
const cds = require('@sap/cds')
|
|
2
|
-
|
|
3
|
-
const { fs, path } = cds.utils
|
|
4
|
-
const tempDir = fs.realpathSync(require('os').tmpdir());
|
|
5
|
-
const LOG = cds.log('mtx')
|
|
6
|
-
|
|
7
|
-
module.exports.getDbId = async () => {
|
|
8
|
-
LOG.info('Retrieving database ID from existing containers...')
|
|
9
|
-
const hana = require('@sap/cds-mtxs/srv/plugins/hana/hdi-ctnr-mgr').defaultInstance()
|
|
10
|
-
const all = await hana.getAll()
|
|
11
|
-
const foundDbId = all.length ? all[0].credentials?.database_id : null
|
|
12
|
-
LOG.info('Found database ID:', foundDbId || 'none')
|
|
13
|
-
return foundDbId
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
module.exports.getAllTenantIds = async () => {
|
|
17
|
-
|
|
18
|
-
const hana = require('@sap/cds-mtxs/srv/plugins/hana/hdi-ctnr-mgr').defaultInstance()
|
|
19
|
-
|
|
20
|
-
// copied from mtx
|
|
21
|
-
const META_TENANT_REGEX = /TENANT-(.*)-META/i
|
|
22
|
-
const MT_LIB_TENANT_REGEX = /MT_LIB_TENANT-(.*)/i
|
|
23
|
-
const GLOBAL_DATA_META_TENANT = '__META__'
|
|
24
|
-
function _tenantFilter(tenantId, index, allTenants) {
|
|
25
|
-
const t0 = cds.env.requires.multitenancy?.t0 ?? 't0'
|
|
26
|
-
return tenantId
|
|
27
|
-
&& !META_TENANT_REGEX.test(tenantId)
|
|
28
|
-
&& !MT_LIB_TENANT_REGEX.test(tenantId)
|
|
29
|
-
&& tenantId.toLowerCase() !== GLOBAL_DATA_META_TENANT.toLowerCase()
|
|
30
|
-
&& tenantId !== t0
|
|
31
|
-
&& allTenants.includes(_prefixTenant(tenantId))
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// TODO remove duplicates resulting from multiple bindings
|
|
35
|
-
// replaces Tenant.getAllTenantIds()
|
|
36
|
-
return (await hana.getAll()).reduce((result, t) => [ ...result, t.labels.tenant_id[0]] , []).filter(_tenantFilter)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function _prefixTenant(tenantId) {
|
|
40
|
-
return `TENANT-${tenantId}-META`
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
module.exports.getMetaTenantName = (tenant) => {
|
|
44
|
-
return _prefixTenant(tenant)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
module.exports.getMetadata = async (tenant) => {
|
|
48
|
-
return getMetadata(tenant)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async function getMetadata(tenant, type = 'onboarding') {
|
|
52
|
-
|
|
53
|
-
// replaces Tenant.getMetadata(tenant)
|
|
54
|
-
const CONTENT_METADATA_QUERY = 'SELECT CONTENT FROM TENANT_METADATA WHERE ID = ? AND DOMAIN = ? LIMIT 1'
|
|
55
|
-
const domain = cds.env.mtx && cds.env.mtx.domain || '__default__'
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
const result = (await cds.tx({ tenant: _prefixTenant(tenant) }, tx => tx.run(CONTENT_METADATA_QUERY, [type, domain])))
|
|
59
|
-
const CONTENT = result?.[0]?.CONTENT || result?.[0]?.content // small tribute to sqlite for testing
|
|
60
|
-
if (type === 'onboarding') {
|
|
61
|
-
const metadata = CONTENT ? JSON.parse(CONTENT) : {}
|
|
62
|
-
// more robust behaviour
|
|
63
|
-
if (!metadata.subscribedTenantId) metadata.subscribedTenantId = tenant
|
|
64
|
-
return metadata
|
|
65
|
-
} else {
|
|
66
|
-
return CONTENT ? JSON.parse(CONTENT) : null
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
} catch (error) {
|
|
70
|
-
LOG.error('Failed to query metadata for tenant', tenant, '(domain:', domain, '):', error)
|
|
71
|
-
throw error;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
module.exports.saveMetadata = async (tenant, type, data) => {
|
|
76
|
-
const domain = cds.env.mtx && cds.env.mtx.domain || '__default__'
|
|
77
|
-
const statement = 'UPSERT TENANT_METADATA (ID, CONTENT, DOMAIN) VALUES(?, ?, ?) WHERE ID = ? AND DOMAIN = ?';
|
|
78
|
-
await cds.tx({ tenant: _prefixTenant(tenant) }, tx => tx.run(statement, [type, JSON.stringify(data), domain, type, domain]))
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const COMPILED_FILES = {
|
|
82
|
-
CSN_NODE: 'csn_node.json', // deprecated
|
|
83
|
-
CSN: 'csn.json',
|
|
84
|
-
ODATA_CSN: 'odata_csn.json',
|
|
85
|
-
I18N: '_i18n/i18n.json'
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const MODEL_TYPE = {
|
|
89
|
-
base: 'base',
|
|
90
|
-
extension: 'extension',
|
|
91
|
-
compiled: 'compiled',
|
|
92
|
-
migration: 'migration'
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
module.exports.getCsn = async (tenant) => {
|
|
96
|
-
|
|
97
|
-
const CONTENT_FILE_QUERY = 'SELECT CONTENT FROM TENANT_FILES WHERE TYPE = ? AND FILENAME = ? AND DOMAIN = ? LIMIT 1';
|
|
98
|
-
|
|
99
|
-
const domain = cds.env.mtx && cds.env.mtx.domain || '__default__'
|
|
100
|
-
|
|
101
|
-
try {
|
|
102
|
-
const result = (await cds.tx({ tenant: _prefixTenant(tenant) }, tx => tx.run(CONTENT_FILE_QUERY, [MODEL_TYPE.compiled, COMPILED_FILES.CSN_NODE, domain])))
|
|
103
|
-
const { CONTENT } = result && result[0] || {}
|
|
104
|
-
return CONTENT ? JSON.parse(CONTENT) : null
|
|
105
|
-
|
|
106
|
-
} catch (error) {
|
|
107
|
-
this.logger.error(`Failed to query collectedSources for tenant '${tenant}' (domain: ${domain}): ${error}`)
|
|
108
|
-
throw error;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// const loadedModel = await Tenant.getModel(tenant, 'collectedSources');
|
|
112
|
-
// return JSON.parse(loadedModel);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// replaces Content.getExtension(tenant)
|
|
116
|
-
module.exports.getExtension = async (tenant) => {
|
|
117
|
-
|
|
118
|
-
function _isModelFile(filename) {
|
|
119
|
-
const BASE_MODEL_EXTENSION_PROJECT_FILTER = /(.*\.cds$|.*\.csn$|.*\/csn.json$|^csn.json$|.*\/i18n(|_.*)(\.json|\.properties)$)/
|
|
120
|
-
return BASE_MODEL_EXTENSION_PROJECT_FILTER.test(filename);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function _convertToExtensionProject(files) {
|
|
124
|
-
const BASE_MODEL_NAME = '_base'
|
|
125
|
-
const basemodelFiles = new Map();
|
|
126
|
-
|
|
127
|
-
for (const [file, content] of files) {
|
|
128
|
-
if (_isModelFile(file)) {
|
|
129
|
-
const prefixedPath = path.join('node_modules', BASE_MODEL_NAME, file);
|
|
130
|
-
basemodelFiles.set(prefixedPath, content);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return basemodelFiles;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
async function readFiles(modelType) {
|
|
138
|
-
const MODEL_FILE_QUERY = 'SELECT FILENAME, CONTENT, TYPE FROM TENANT_FILES WHERE TYPE = ? AND DOMAIN = ?';
|
|
139
|
-
const domain = cds.env.mtx && cds.env.mtx.domain || '__default__'
|
|
140
|
-
try {
|
|
141
|
-
const files = (await cds.tx({ tenant: _prefixTenant(tenant) }, tx => tx.run(MODEL_FILE_QUERY, [modelType, domain])));
|
|
142
|
-
return files.map(({ FILENAME, CONTENT }) => [FILENAME, CONTENT.toString('utf-8')]);
|
|
143
|
-
|
|
144
|
-
} catch (error) {
|
|
145
|
-
LOG.error('Failed to query extensions for tenant', tenant, '(domain:', domain, '):', error);
|
|
146
|
-
throw error;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const extensions = await readFiles(MODEL_TYPE.extension);
|
|
151
|
-
if (!extensions?.length) return // return nothing if no extensions exist
|
|
152
|
-
|
|
153
|
-
const base = await readFiles(MODEL_TYPE.base);
|
|
154
|
-
|
|
155
|
-
return [...extensions, ..._convertToExtensionProject(base)]
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
module.exports.hasExtensions = async (tenant) => {
|
|
159
|
-
if (!(await module.exports.wasOldMtx())) return false
|
|
160
|
-
const MODEL_FILE_QUERY = 'SELECT FILENAME FROM TENANT_FILES WHERE TYPE = ? AND DOMAIN = ?';
|
|
161
|
-
const domain = cds.env.mtx && cds.env.mtx.domain || '__default__'
|
|
162
|
-
try {
|
|
163
|
-
const files = (await cds.tx({ tenant: _prefixTenant(tenant) }, tx => tx.run(MODEL_FILE_QUERY, ['extension', domain])));
|
|
164
|
-
return files.length
|
|
165
|
-
} catch (error) {
|
|
166
|
-
LOG.debug('Upgrade check: No @sap/mtx (old mtx) extensions found for tenant', tenant, '(domain:', domain, '):', error);
|
|
167
|
-
return false;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const wasOldMtx = []
|
|
172
|
-
|
|
173
|
-
// use this before automatic tenant list update in upgrade
|
|
174
|
-
module.exports.wasOldMtx = async () => {
|
|
175
|
-
if (!wasOldMtx.length) {
|
|
176
|
-
if (cds.env.requires.db?.credentials?.sm_url) { // old mtx was service-manager based
|
|
177
|
-
const hana = require('@sap/cds-mtxs/srv/plugins/hana/hdi-ctnr-mgr').defaultInstance()
|
|
178
|
-
try {
|
|
179
|
-
wasOldMtx.push(!!(await hana.get('__META__')))
|
|
180
|
-
} catch (error) {
|
|
181
|
-
if (error.status === 404) wasOldMtx.push(false)
|
|
182
|
-
else throw error
|
|
183
|
-
}
|
|
184
|
-
} else {
|
|
185
|
-
wasOldMtx.push(false)
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
return wasOldMtx[0]
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
module.exports.isMigrated = async (tenant) => {
|
|
192
|
-
return getMetadata(tenant, 'migrated')
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
module.exports.getMigrationParameters = async (tenant) => {
|
|
196
|
-
return getMetadata(tenant, 'migrationParameters')
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
module.exports.setMigrated = async (tenant, tagRule, defaultTag, force) => {
|
|
200
|
-
const timestamp = new Date();
|
|
201
|
-
const timestampIso = timestamp.toISOString();
|
|
202
|
-
const data = {
|
|
203
|
-
timestamp: timestampIso
|
|
204
|
-
}
|
|
205
|
-
await module.exports.saveMetadata(tenant, 'migrated', data)
|
|
206
|
-
await module.exports.saveMetadata(tenant, 'migrationParameters', { tagRule, defaultTag, force })
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function unixPath(filename) {
|
|
210
|
-
return filename.replace(/\\/g, '/');
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
module.exports.writeFilesFromMap = async (files, baseDir) => {
|
|
214
|
-
for (const [filename, content] of files) {
|
|
215
|
-
const unixFilename = unixPath(filename);
|
|
216
|
-
const absoluteFilename = path.resolve(baseDir, unixFilename);
|
|
217
|
-
|
|
218
|
-
if (absoluteFilename.includes(baseDir)) {
|
|
219
|
-
//this.logger.debug(`Writing file: ${absoluteFilename}`);
|
|
220
|
-
await fs.promises.mkdir(path.dirname(absoluteFilename), { recursive: true });
|
|
221
|
-
await fs.promises.writeFile(absoluteFilename, content);
|
|
222
|
-
} else {
|
|
223
|
-
//this.logger.debug(`Do not write file outside of target: ${absoluteFilename}`);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
module.exports.mkdirTemp = async () => {
|
|
229
|
-
return fs.promises.mkdtemp(`${tempDir}${path.sep}`);
|
|
230
|
-
}
|