@sap/cds-mtxs
Advanced tools
Comparing version 2.0.6 to 2.1.0
#!/usr/bin/env node | ||
/* eslint-disable no-console */ | ||
async function run(param, options) { | ||
@@ -6,0 +4,0 @@ const cds = require('@sap/cds') |
#!/usr/bin/env node | ||
/* eslint-disable no-console */ | ||
@@ -4,0 +3,0 @@ const cds = require('@sap/cds') |
@@ -9,6 +9,15 @@ # Change Log | ||
## Version 2.0.6 - 2024-08-15 | ||
## Version 2.1.0 - 2024-08-29 | ||
### Fixed | ||
- On-the-fly CSN calculations are only done with `extensibility: true` in the main app. | ||
- The request correlation ID is appended to generic HDI deployment error messages. | ||
- Asynchronous extension activation now reverts faulty extensions correctly. | ||
- Parallel extension activation calls do no longer create inconsistent extension states. | ||
## Version 2.0.6 - 2024-08-16 | ||
### Fixed | ||
- Subscription Manager service subscriptions are now working again. | ||
@@ -15,0 +24,0 @@ - The `cds.features.assertIntegrity` is correctly added to the compiler options for HANA builds. |
@@ -34,3 +34,3 @@ const cds = require('@sap/cds') | ||
try { | ||
return await fn() // eslint-disable-line no-await-in-loop | ||
return await fn() | ||
} catch (error) { | ||
@@ -42,3 +42,3 @@ if (error.code === 400) throw error | ||
LOG.error('attempt', errorCount, 'errored with', error, '- retrying attempt', errorCount + 1, 'of', retryCount) | ||
await promisify(setTimeout)(retryGap) // eslint-disable-line no-await-in-loop | ||
await promisify(setTimeout)(retryGap) | ||
retryGap *= 1.5 | ||
@@ -45,0 +45,0 @@ } |
{ | ||
"name": "@sap/cds-mtxs", | ||
"version": "2.0.6", | ||
"version": "2.1.0", | ||
"description": "SAP Cloud Application Programming Model - Multitenancy library", | ||
@@ -5,0 +5,0 @@ "homepage": "https://cap.cloud.sap/", |
const cds = require('@sap/cds/lib') | ||
const LOG = cds.log('mtx'), DEBUG = cds.debug('mtx') | ||
const main = require('../config') | ||
const migration = require('../../lib/migration/migration') | ||
@@ -13,3 +14,2 @@ const DeploymentService = 'cds.xt.DeploymentService' | ||
const { t0 = 't0' } = cds.env.requires.multitenancy | ||
const isExtensible = !!cds.requires.extensibility || !!cds.requires['cds.xt.ExtensibilityService'] | ||
@@ -97,3 +97,3 @@ module.exports = class ProvisioningService extends cds.ApplicationService { | ||
const tx = js.tx({ tenant: context.tenant, user: new cds.User.Privileged() }) | ||
return tx.enqueue('subscribe', [new Set([tenant])], { data, options }, error => { | ||
return tx.enqueue('cds.xt.DeploymentService', 'subscribe', [new Set([tenant])], { data, options }, error => { | ||
if (error) this._sendCallback('FAILED', 'Tenant creation failed') | ||
@@ -140,3 +140,3 @@ else this._sendCallback('SUCCEEDED', 'Tenant creation succeeded', appUrl) | ||
const all = tenantIds.includes('*') | ||
const sharedGenDir = !isExtensible | ||
const sharedGenDir = !main.requires.extensibility | ||
if (sharedGenDir) (options ??= {}).skipResources ??= sharedGenDir | ||
@@ -175,3 +175,3 @@ const tenants = all ? await this._getTenants() : tenantIds | ||
// REVISIT: use jobs service for sync and async operations (might also be interesting for concurrency control) | ||
return tx.enqueue('upgrade', dbToTenants, { options }, error => { | ||
return tx.enqueue('cds.xt.DeploymentService', 'upgrade', dbToTenants, { options }, error => { | ||
if (error) this._sendCallback('FAILED', 'Tenant upgrade failed') | ||
@@ -218,3 +218,3 @@ else this._sendCallback('SUCCEEDED', 'Tenant upgrade succeeded') | ||
const tx = lcs.tx({ tenant: context.tenant, user: new cds.User.Privileged() }) | ||
return tx.enqueue('unsubscribe', [new Set([tenant])], { metadata }, error => { | ||
return tx.enqueue('cds.xt.DeploymentService', 'unsubscribe', [new Set([tenant])], { metadata }, error => { | ||
if (error) this._sendCallback('FAILED', 'Tenant deletion failed') | ||
@@ -264,3 +264,3 @@ else this._sendCallback('SUCCEEDED', 'Tenant deletion succeeded') | ||
if (pending.length >= limit) { | ||
await Promise.race(pending) // eslint-disable-line no-await-in-loop | ||
await Promise.race(pending) | ||
} | ||
@@ -267,0 +267,0 @@ } |
@@ -20,3 +20,3 @@ const cds = require ('@sap/cds/lib') | ||
plugins.forEach (p => DEBUG ('\x1b[2m ', local(p), '\x1b[0m')) | ||
console.debug() // eslint-disable-line no-console | ||
console.debug() | ||
loaded.forEach (({ file, module:m }) => { if (m.activated) { | ||
@@ -23,0 +23,0 @@ DEBUG ('activated deployer plugin:', { for: m.activated, impl: local(file) }) |
@@ -11,6 +11,8 @@ const cds = require('@sap/cds/lib') | ||
const { getMigratedProjects } = require('../lib/migration/migration') | ||
const { runLinter } = require('./extensibility/utils') | ||
const LOG = cds.log('mtx') | ||
module.exports = class ExtensibilityService extends cds.ApplicationService { | ||
async init() { | ||
async init() { | ||
this.on('push', push) | ||
@@ -49,6 +51,40 @@ this.on('pull', pull) | ||
} | ||
}) | ||
}) | ||
return super.init() | ||
} | ||
} | ||
// from push.js | ||
async activateExtension(tenant, tag, extCsn, bundles, csvs, sources, activate) { | ||
try { | ||
// remove current extension with tag | ||
if (tag) await DELETE.from('cds.xt.Extensions').where({ tag }) | ||
// insert and activate extension | ||
const ID = cds.utils.uuid() | ||
await INSERT.into('cds.xt.Extensions').entries({ | ||
ID, | ||
csn: JSON.stringify(extCsn), | ||
i18n: bundles ? JSON.stringify(bundles) : null, | ||
sources, | ||
activated: activate, | ||
tag | ||
}) | ||
// do validation after extension table update - trust transaction handling for rollback | ||
// extension linters | ||
await runLinter(tenant, extCsn, tag) | ||
if (activate === 'database') { | ||
LOG.info(`activating extension '${tag}' ...`) | ||
const { 'cds.xt.DeploymentService': ds } = cds.services | ||
await ds.extend(tenant, csvs) | ||
} | ||
} catch (error) { | ||
// needs to be serialized because it is stored in the db by the job service - TODO check for HDI error somehow? | ||
if (error.code || error.status) throw new Error(JSON.stringify(error)) | ||
throw new Error(JSON.stringify({ status: 422, message: error.message })) // for HDI errors | ||
} | ||
} | ||
} |
const cds = require('@sap/cds/lib') | ||
const { getCompilerError } = require('../../lib/utils') | ||
const activate = async function (tenant, csvs, async) { | ||
if (tenant) cds.context = { tenant } | ||
const activate = async function (tenant, tag, extCsn, bundles, csvs, sources, activate, req) { | ||
const async = cds.context.http?.req?.headers?.prefer === 'respond-async' | ||
try { | ||
const js = await cds.connect.to('cds.xt.JobsService') | ||
return await new Promise((resolve, reject) => { | ||
if (async) { | ||
const { 'cds.xt.JobsService': js } = cds.services | ||
const tx = js.tx({ tenant: tenant ?? cds.context.tenant, user: new cds.User.Privileged() }) | ||
return tx.enqueue('extend', [new Set([tenant])], [csvs]) | ||
} else { | ||
const { 'cds.xt.DeploymentService': ds } = cds.services | ||
await ds.extend(tenant, csvs) | ||
const cb = !async ? error => { | ||
if (error) { | ||
try { | ||
const errorObject = JSON.parse(error) | ||
return reject(errorObject) | ||
} catch { | ||
return reject(error) | ||
} | ||
} | ||
cds.context.http?.res.status(204) | ||
return resolve() | ||
} : () => {} | ||
const tx = js.tx({ tenant: req.tenant, user: new cds.User.Privileged() }) | ||
const jobs = tx.enqueue('cds.xt.ExtensibilityService', 'activateExtension', [new Set([tenant])], { tag, extCsn, bundles, csvs, sources, activate }, cb) | ||
if (async) { | ||
cds.context.http?.res.status(202) | ||
resolve(jobs) | ||
} | ||
}) | ||
} catch (err) { | ||
if (err.code === 'ERR_CDS_COMPILATION_FAILURE') req.reject(422, getCompilerError(err.messages)) | ||
else req.reject(err) | ||
} | ||
} | ||
module.exports = activate | ||
module.exports = { activate } |
@@ -6,2 +6,4 @@ const cds = require('@sap/cds/lib') | ||
const isAsync = function () { return cds.context.http?.req?.headers?.prefer === 'respond-async' } | ||
const readExtension = async function (req) { | ||
@@ -20,4 +22,9 @@ const tenant = _tenant(req) | ||
// set handles the tenant switch in that case | ||
await set_(req, { extension: [...req.data.csn], resources: req.data.i18n, tag: req.data.ID, tenant: tenant ?? req.tenant }) | ||
const job = await set_(req, { extension: [...req.data.csn], resources: req.data.i18n, tag: req.data.ID, tenant: tenant ?? req.tenant }) | ||
if (isAsync()) { | ||
cds.context.http?.res.status(202) | ||
return job | ||
} | ||
const res = await SELECT.one.from('cds.xt.Extensions').where({ tag: req.data.ID }) | ||
cds.context.http?.res.status(200) | ||
return { ID: res.tag, csn: res.csn, i18n: res.i18n !== '{}' ? res.i18n : undefined, timestamp: res.timestamp } | ||
@@ -33,3 +40,7 @@ } | ||
// leave tombstone for deployment - ID cannot be used in case of all extensions (no ID passed) | ||
await set_(req, { extension: ['{}'], tag: TOMBSTONE_ID, tenant: tenant ?? req.tenant }) | ||
const job = await set_(req, { extension: ['{}'], tag: TOMBSTONE_ID, tenant: tenant ?? req.tenant }) | ||
if (isAsync()) { | ||
cds.context.http?.res.status(202) | ||
return job | ||
} | ||
@@ -36,0 +47,0 @@ return result |
@@ -18,4 +18,4 @@ const cds = require('@sap/cds/lib') | ||
let x | ||
for (let p of LINTER_OPTIONS) if ((x = conf[p] || compat[p])) linter_options[p] = x // eslint-disable-line no-cond-assign | ||
for (let p of LEGACY_OPTIONS) if ((x = compat[p])) linter_options[p] = x // eslint-disable-line no-cond-assign | ||
for (let p of LINTER_OPTIONS) if ((x = conf[p] || compat[p])) linter_options[p] = x | ||
for (let p of LEGACY_OPTIONS) if ((x = compat[p])) linter_options[p] = x | ||
const hasConfig = Object.keys(linter_options).length | ||
@@ -22,0 +22,0 @@ |
@@ -6,5 +6,5 @@ const cds = require('@sap/cds/lib'), { fs, path, tar, rimraf } = cds.utils | ||
const activate = require('./activate') | ||
const { activate } = require('./activate') | ||
const { addCodeAnnotations } = require('./code-extensibility/addCodeAnnotProd') | ||
const { readData, runLinter } = require('./utils') | ||
const { readData } = require('./utils') | ||
@@ -63,25 +63,5 @@ const TEMP_DIR = fs.realpathSync(require('os').tmpdir()) | ||
// remove current extension with tag | ||
if (tag) await DELETE.from('cds.xt.Extensions').where({ tag }) | ||
// insert and activate extension | ||
const ID = cds.utils.uuid() | ||
await INSERT.into('cds.xt.Extensions').entries({ | ||
ID, | ||
csn: JSON.stringify(extCsn), | ||
i18n: bundles ? JSON.stringify(bundles) : null, | ||
sources, | ||
activated: 'database', | ||
tag | ||
}) | ||
// do validation after extension table update - trust transaction handling for rollback | ||
// extension linters | ||
await runLinter(tenant, extCsn, tag, req) | ||
LOG.info(`activating extension '${tag}' ...`) | ||
const async = cds.context.http?.req?.headers?.prefer === 'respond-async' | ||
await activate(tenant, csvs, async) | ||
await activate(tenant, tag, extCsn, bundles, csvs, sources, 'database', req) | ||
} | ||
module.exports = { push, pull } |
const cds = require('@sap/cds/lib') | ||
const LOG = cds.log('mtx') | ||
const { activate: activateExt} = require('./activate') | ||
const { getCompilerError } = require('../../lib/utils') | ||
const activateExt = require('./activate') | ||
const { runLinter } = require('./utils') | ||
const _isCSN = str => str.substring(0, 1) === '{' | ||
const _validateInput = function (req, extension) { | ||
if (!req.user.is('internal-user') && req.data.tenant && req.data.tenant !== req.tenant) | ||
req.reject(403, `No permission to add extensions to tenants other than ${req.tenant}`) | ||
// if (!req.user.is('internal-user') && req.data.tenant && req.data.tenant !== req.tenant) | ||
// req.reject(403, `No permission to add extensions to tenants other than ${req.tenant}`) | ||
if (!extension) req.reject(400, 'Property "extension" is missing') | ||
if (!extension) throw new cds.error ({ message: 'Property "extension" is missing', code: 400 }) | ||
if (Array.isArray(extension)) { | ||
if (!extension.length) req.reject(400, 'Property "extension" is empty') | ||
if (!extension.length) throw new cds.error ({ message: 'Property "extension" is empty', code: 400 }) | ||
} else { | ||
const length = typeof extension === 'string' ? extension.length : Object.keys(extension).length | ||
if (!length) req.reject(400, 'Property "extension" is malformed') | ||
if (!length) throw new cds.error ({ message: 'Property "extension" is malformed', code: 400 }) | ||
} | ||
@@ -45,3 +41,3 @@ } | ||
const _getFiles = function (resources, req) { | ||
const _getFiles = function (resources) { | ||
let bundles = {} | ||
@@ -54,3 +50,3 @@ let fromJson = false | ||
if (key) { | ||
if (fromJson) req.reject(422, `Mixed i18n file types not supported: i18n.json and ${file.name}`) | ||
if (fromJson) throw new cds.error ({ message: `Mixed i18n file types not supported: i18n.json and ${file.name}`, code: 422 }) | ||
bundles[key[1]] = _toJson(file.content) | ||
@@ -61,3 +57,3 @@ return | ||
if (key) { | ||
if (Object.entries(bundles).length) req.reject(422, `Mixed i18n file types not supported: i18n.json and .properties`) | ||
if (Object.entries(bundles).length) throw new cds.error ({ message:`Mixed i18n file types not supported: i18n.json and .properties`, code: 422 }) | ||
try { | ||
@@ -67,3 +63,3 @@ bundles = JSON.parse(file.content) | ||
} catch (e) { | ||
req.reject(422, `Invalid json content in i18n.json: ${e.message}`) | ||
throw new cds.error ({ message: `Invalid json content in i18n.json: ${e.message}`, code: 422 }) | ||
} | ||
@@ -82,27 +78,3 @@ return | ||
const _addExtension = async function (extCsn, tag, bundles, csvs, tenant, activate, req) { | ||
if (tenant) cds.context = { tenant } | ||
const ID = cds.utils.uuid() | ||
await cds.db.run( | ||
INSERT.into('cds.xt.Extensions').entries([{ | ||
ID, | ||
tag, | ||
i18n: bundles ? JSON.stringify(bundles) : null, | ||
csn: JSON.stringify(extCsn), | ||
activated: activate }]) | ||
) | ||
LOG.info(`validating extension with tag '${tag}' ...`) | ||
try { | ||
if (activate === 'database') { | ||
LOG.info(`activating extension to '${activate}' ...`) | ||
await activateExt(tenant, csvs) | ||
} | ||
} catch (err) { | ||
if (err.code === 'ERR_CDS_COMPILATION_FAILURE') req.reject(422, getCompilerError(err.messages)) | ||
else req.reject(400, err.message) | ||
} | ||
} | ||
const setExtension = async function (req) { | ||
const setExtension = async function(req) { | ||
let { extension, tag, resources, activate } = req.data | ||
@@ -114,3 +86,3 @@ const tenant = (req.user.is('internal-user') && req.data.tenant) || req.tenant || '' // revisit magic | ||
const set_ = async function (req, { extension, tag, resources, activate, tenant }) { | ||
_validateInput(req, extension) | ||
_validateInput(undefined, extension) | ||
@@ -125,10 +97,10 @@ if (tenant) cds.context = { tenant } | ||
if (typeof ext === 'string') { | ||
if (!ext.length) req.reject(400, 'Missing extension') | ||
if (!ext.length) throw new cds.error ({ message: 'Missing extension', code: 400 }) | ||
if (_isCSN(ext)) extCsn = _mergeCSN(JSON.parse(ext), extCsn) | ||
else try { extCsn = _mergeCSN(cds.parse.cdl(ext), extCsn) } catch (e) { | ||
if (e.code === 'ERR_CDS_COMPILATION_FAILURE') req.reject(422, e.message) | ||
if (e.code === 'ERR_CDS_COMPILATION_FAILURE') throw new cds.error ({ message: e.message, code: 422 }) | ||
else throw e | ||
} | ||
} else { | ||
if (!Object.keys(ext).length) req.reject(400, 'Missing extension') | ||
if (!Object.keys(ext).length) throw new cds.error ({ message: 'Missing extension', code: 400 }) | ||
extCsn = _mergeCSN(ext, extCsn) | ||
@@ -139,10 +111,7 @@ } | ||
// call linter | ||
await runLinter(tenant, extCsn, tag, req) | ||
const { bundles, csvs } = _getFiles(resources) | ||
const { bundles, csvs } = _getFiles(resources, req) | ||
if (tag) await DELETE.from('cds.xt.Extensions').where({ tag }) | ||
await _addExtension(extCsn, tag, bundles, csvs, tenant, activate, req) | ||
await activateExt(tenant, tag, extCsn, bundles, csvs, null, activate, req) | ||
} | ||
module.exports = { set_, setExtension } |
const cds = require('@sap/cds/lib'); | ||
const { getAuthProvider } = require('./authProvider/AuthProviderFactory'); | ||
const AuthProvider = require('./authProvider/AuthProvider'); | ||
@@ -5,0 +4,0 @@ const LOG = cds.log('mtx'); |
@@ -30,3 +30,3 @@ const { path, tar, read, readdir } = cds.utils | ||
const runLinter = async function (tenant, extCsn, tag, req) { | ||
const runLinter = async function (tenant, extCsn, tag) { | ||
@@ -41,3 +41,3 @@ LOG.info(`validating extension '${tag}' ...`) | ||
} catch (err) { | ||
return req.reject(400, getCompilerError(err.messages)) | ||
throw new cds.error ({ message: getCompilerError(err.messages), code: 400 }) | ||
} | ||
@@ -49,3 +49,3 @@ | ||
message += findings.map(f => ' - ' + f.message).join('\n') + '\n' | ||
return req.reject(422, message) | ||
throw new cds.error ({ message, status: 422 }) | ||
} | ||
@@ -52,0 +52,0 @@ |
@@ -11,3 +11,3 @@ const { inspect } = require('util') | ||
queueSize = 100, clusterSize = 1, workerSize = 1, poolSize = 1 | ||
} = cds.env.requires.multitenancy.jobs | ||
} = cds.env.requires.multitenancy?.jobs | ||
?? cds.env.requires['cds.xt.SaasProvisioningService']?.jobs | ||
@@ -55,6 +55,6 @@ ?? cds.env.requires['cds.xt.SmsProvisioningService']?.jobs | ||
}) | ||
super.init() | ||
return super.init() | ||
} | ||
async enqueue(op, clusters, args, onJobDone) { | ||
async enqueue(service, op, clusters, args, onJobDone) { | ||
const _inspect = obj => obj && Object.values(obj).filter(Boolean).length > 0 ? inspect(obj, { depth: 5, colors: true }) : [] | ||
@@ -72,3 +72,3 @@ const inspected = Object.entries(args).reduce((acc, [k, v]) => { | ||
LOG.info(`enqueuing`, { op }, 'for', _format(clusters), ..._args) | ||
LOG.info(`enqueuing`, { service, op }, 'for', _format(clusters), ..._args) | ||
@@ -84,4 +84,4 @@ const job_ID = uuid() | ||
jobQueue.enqueue({ job_ID, clusters: jobs, fn: task => { | ||
const { 'cds.xt.DeploymentService': ds } = cds.services | ||
return ds.tx({ tenant: cds.context.tenant }, tx => tx[op](task.tenant, ...Object.values(args))) | ||
const serviceInstance = cds.services[service] | ||
return serviceInstance.tx({ tenant: cds.context.tenant }, tx => tx[op](task.tenant, ...Object.values(args))) | ||
}, onJobDone }) | ||
@@ -142,3 +142,3 @@ pickJob() | ||
if (pending.length >= limit) { | ||
await Promise.race(pending) // eslint-disable-line no-await-in-loop | ||
await Promise.race(pending) | ||
} | ||
@@ -217,3 +217,3 @@ } | ||
) | ||
}, cds.env.requires.multitenancy.jobCleanupInterval ?? 24*hours) | ||
}, cds.env.requires.multitenancy?.jobCleanupInterval ?? 24*hours) | ||
jobCleanup.unref() | ||
@@ -225,3 +225,3 @@ | ||
await t0_(DELETE.from(Jobs, { createdAt: { '<': cutoff.toISOString() }})) | ||
}, cds.env.requires.multitenancy.jobCleanupIntervalStale ?? 48*hours) | ||
}, cds.env.requires.multitenancy?.jobCleanupIntervalStale ?? 48*hours) | ||
jobCleanupStale.unref() | ||
@@ -228,0 +228,0 @@ |
@@ -16,5 +16,2 @@ const cds = require('@sap/cds/lib'), {db} = cds.env.requires | ||
// FIXME: Do that check in a better way, as ExtensibilityService is always on with mtx-sidecar preset | ||
const isExtensible = cds.requires.extensibility || cds.requires['cds.xt.ExtensibilityService'] | ||
if (db?.kind === 'hana') { | ||
@@ -137,6 +134,5 @@ if (!db.credentials?.sm_url) cds.error('No Service Manager credentials found. Make sure the application is bound to a BTP Service Manager instance.') | ||
async function resources4 (out) { | ||
const { 'cds.xt.ModelProviderService': mp } = cds.services | ||
try { | ||
const rscs = await mp.getResources(true) | ||
const rscs = await mp.getResources() | ||
await tar.xz(rscs).to(out) | ||
@@ -155,3 +151,3 @@ return out | ||
} | ||
module.exports.resources4 = resources4 // required in SPS to prepare shared deployment directory | ||
module.exports.resources4 = resources4 // required in abstract provisioning service to prepare shared deployment directory | ||
@@ -169,6 +165,6 @@ async function csvs4(tenant, outRoot) { | ||
} | ||
module.exports.csvs4 = csvs4 // required in SPS to prepare shared deployment directory | ||
module.exports.csvs4 = csvs4 // required in abstract provisioning service to prepare shared deployment directory | ||
async function _readExtCsvs(tenant) { | ||
if (!isExtensible) return | ||
if (!main.requires.extensibility) return | ||
const { 'cds.xt.ModelProviderService': mp } = cds.services | ||
@@ -254,3 +250,4 @@ const extensions = await mp.getExtResources(tenant) | ||
// Can already start getting the csn if later required | ||
const _csn = isExtensible && !csnFromParameter && !skipExt ? csn4(tenant) : csnFromParameter | ||
const requiresCsn = main.requires.extensibility && !csnFromParameter && !skipExt | ||
const _csn = requiresCsn ? csn4(tenant) : csnFromParameter | ||
@@ -257,0 +254,0 @@ // 1. Unpack what comes from getResources() |
@@ -35,3 +35,3 @@ const { join } = require('path') | ||
if (response?.exitCode) { | ||
let message = `HDI deployment failed with exit code ${response.exitCode}` | ||
let message = `HDI deployment failed with exit code ${response.exitCode}. Correlation ID: ${cds.context.id}` | ||
if (response.signal) message += `. ${response.signal}` | ||
@@ -38,0 +38,0 @@ return reject(new Error(message)) |
@@ -166,3 +166,2 @@ const https = require('https') | ||
do { | ||
// eslint-disable-next-line no-await-in-loop | ||
const { data } = await fetchApi('service_bindings', { | ||
@@ -169,0 +168,0 @@ params: { token, labelQuery, fieldQuery } |
@@ -34,3 +34,3 @@ const cds = require('@sap/cds/lib'), {db} = cds.requires, {fs, rimraf, path} = cds.utils | ||
LOG.info (`(re-)deploying SQLite database for tenant: ${t}`) | ||
const deployOptions = cds.requires.extensibility ? { schema_evolution: 'auto' } : {} | ||
const deployOptions = main.requires.extensibility ? { schema_evolution: 'auto' } : {} | ||
const t0 = cds.requires.multitenancy?.t0 ?? 't0' | ||
@@ -37,0 +37,0 @@ if (t !== t0) Object.assign(deployOptions, main.env.cdsc) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
256167
4712