@sap/cds-mtxs
Advanced tools
Comparing version 1.15.0 to 1.16.0
@@ -15,5 +15,7 @@ #!/usr/bin/env node | ||
if (!tenant) return _handleError(`Please provide a tenant: cds-mtx ${cmd} <tenant>`) | ||
const { 'cds.xt.DeploymentService':ds } = await cds.serve ([ | ||
if (/.*,.*/.test(tenant)) return _handleError(`List of tenants not supported: ${tenant}`) | ||
const { 'cds.xt.DeploymentService':ds, 'cds.xt.SaasProvisioningService':sps } = await cds.serve ([ | ||
'@sap/cds-mtxs/srv/deployment-service', | ||
'@sap/cds-mtxs/srv/model-provider' | ||
'@sap/cds-mtxs/srv/model-provider', | ||
'@sap/cds-mtxs/srv/cf/saas-provisioning-service' | ||
]) | ||
@@ -26,8 +28,16 @@ await _local_server_js() | ||
let connectedTenants = [tenant] | ||
try { | ||
if (tenant === '*') { | ||
const tenants = await sps.read('tenant') | ||
connectedTenants = tenants.map(t => t.subscribedTenantId) | ||
await sps.upgrade([tenant]) | ||
return | ||
} | ||
await ds[cmd](tenant) | ||
} finally { | ||
if (cds.db) { | ||
cds.db.disconnect(cds.requires.multitenancy.t0) | ||
cds.db.disconnect(tenant) | ||
cds.db.disconnect(cds.env.requires.multitenancy.t0 ?? 't0') | ||
for (const t of connectedTenants) cds.db.disconnect(t) | ||
} | ||
@@ -66,3 +76,3 @@ } | ||
async function _handleError(message) { | ||
console.log(message) | ||
console.error(message) | ||
if (isCli) { | ||
@@ -69,0 +79,0 @@ process.exit(1) |
@@ -9,2 +9,19 @@ # Change Log | ||
## Version 1.16.0 - 2024-02-26 | ||
### Added | ||
- `cds-mtx upgrade` now allows to pass `*` to upgrade all tenants. | ||
### Changed | ||
- The Service Manager polling timeout is increased from 60 to 180 seconds. | ||
- On failing UAA token request, MTXS now responds to client with JSON to enable parsing the passcode URL. | ||
### Fixed | ||
- `upgrade` action is now also provided by `cds.xt.SmsProvisioningService`. | ||
- Cleanup option of MTXS Migration deletes old `__META__` tenant only if cleanup is triggered for `*` (all tenants). | ||
- Improved formatting of errors when fetching auth tokens. | ||
## Version 1.15.0 - 2024-01-30 | ||
@@ -19,3 +36,3 @@ | ||
- Additional services needed when using `SERVICE_REPLACEMENTS` for HDI deployment can now also be consumed in Kyma after adding them to the `cds` configuration like | ||
``` | ||
```json | ||
"requires": { | ||
@@ -22,0 +39,0 @@ "myuserprovided": { |
@@ -112,3 +112,3 @@ const path = require('path') | ||
// only cleanup __META__ if all other meta tenants have been cleaned up successfully | ||
if (!dry && !migrationResult.hasError) { | ||
if (!dry && tenants.includes('*') && !migrationResult.hasError) { | ||
const ds = await cds.connect.to('cds.xt.DeploymentService') | ||
@@ -154,3 +154,3 @@ const commonMetaTenant = '__META__' | ||
const { directory, dry, force, tagRule, tag: defaultTag, "skip-verification": skipVerification, "ignore-migrations": ignoreMigrations } = options | ||
const { directory, dry, force, tagRule, tag: defaultTag, "skip-verification": skipVerification} = options | ||
@@ -303,3 +303,10 @@ const migrationResult = new MigrationResult() | ||
module.exports.addMetadata = async function addMetadata(tenant, metadata) { | ||
await t0_(UPSERT.into('cds.xt.Tenants', { ID: tenant, metadata: JSON.stringify(metadata) })) | ||
try { | ||
// can't use UPSERT here so @cds.on.insert still works for createdAt | ||
await t0_(INSERT.into('cds.xt.Tenants', { ID: tenant, metadata: JSON.stringify(metadata) })) | ||
} catch (e) { | ||
if (e.message === 'ENTITY_ALREADY_EXISTS') { | ||
await t0_(UPSERT.into('cds.xt.Tenants', { ID: tenant, metadata: JSON.stringify(metadata) })) | ||
} else throw e | ||
} | ||
} | ||
@@ -401,3 +408,3 @@ | ||
// remove gen folder | ||
if (new RegExp(`${directory}\/.*\/gen$`).test(file)) { | ||
if (new RegExp(`${directory}/.*/gen$`).test(file)) { | ||
await rimraf(file) | ||
@@ -409,3 +416,3 @@ } | ||
const content = await read(file, 'utf-8') | ||
const fixedContent = content.replace(/\'_base\/.*\'/g, '\'_base\'') | ||
const fixedContent = content.replace(/'_base\/.*'/g, '\'_base\'') | ||
await write(file, fixedContent, 'utf-8') | ||
@@ -412,0 +419,0 @@ } |
@@ -10,3 +10,7 @@ module.exports = axError => { | ||
} | ||
const reason = data?.error /* RFC 6749 */ ? inspect(data.error) : axError.message; | ||
const reason = data?.error /* RFC 6749 */ | ||
? typeof data.error === 'string' | ||
? data.error | ||
: inspect(data.error) | ||
: axError.message; | ||
const prefix = (url && method | ||
@@ -29,5 +33,5 @@ ? `${method.toUpperCase()} ${url} ` | ||
cds.log('req').error(`Details on error '${prefix}': ` + | ||
(data.error_description /* RFC 6749 */ || inspect(data)) + `'`); | ||
(data.error_description /* RFC 6749 */ || inspect(data))); | ||
} | ||
return Promise.reject(error); | ||
} |
{ | ||
"name": "@sap/cds-mtxs", | ||
"version": "1.15.0", | ||
"version": "1.16.0", | ||
"description": "SAP Cloud Application Programming Model - Multitenancy library", | ||
@@ -5,0 +5,0 @@ "homepage": "https://cap.cloud.sap/", |
@@ -139,3 +139,5 @@ const cds = require('@sap/cds/lib') | ||
clusterSize = 1, workerSize = 1, poolSize = 1 | ||
} = cds.env.requires.multitenancy.jobs ?? cds.env.requires['cds.xt.SaasProvisioningService']?.jobs ?? {} | ||
} = cds.env.requires.multitenancy.jobs | ||
?? cds.env.requires[this.name]?.jobs | ||
?? {} | ||
const dbToTenants = clusterSize > 1 ? await this._tenantsByDb(tenants) : [new Set(tenants)] | ||
@@ -142,0 +144,0 @@ LOG.info('upgrading', { tenants }) |
const cds = require('@sap/cds/lib') | ||
const https = require('https'); | ||
const LOG = cds.log('mtx'), DEBUG = cds.debug('mtx') | ||
@@ -3,0 +4,0 @@ const axiosInstance = require('axios').create() |
const cds = require('@sap/cds/lib') | ||
const https = require('https'); | ||
const AbstractProvisioningService = require('./abstract-provisioning-service') | ||
@@ -6,3 +7,3 @@ const LOG = cds.log('mtx'), DEBUG = cds.debug('mtx') | ||
const axiosInstance = require('axios').create() | ||
//axiosInstance.interceptors.response.use(response => response, require('../../lib/pruneAxiosErrors')) | ||
axiosInstance.interceptors.response.use(response => response, require('../../lib/pruneAxiosErrors')) | ||
@@ -16,2 +17,3 @@ module.exports = class SmsProvisioningService extends AbstractProvisioningService { | ||
this.on('READ', 'dependencies', this._dependencies) | ||
this.on('upgrade', super._upgrade) | ||
this.on('getAppUrl', super._getAppUrl) | ||
@@ -18,0 +20,0 @@ await super.init() |
@@ -14,4 +14,5 @@ const { EXT_BACK_PACK, hasExtendedEntity, getTargetRead } = require('../../../lib/utils') | ||
const _processorFn = ({ row, key }) => { | ||
if (row[EXT_BACK_PACK]) { | ||
const extensions = JSON.parse(row[EXT_BACK_PACK]) | ||
let extensions__ = row[EXT_BACK_PACK] | ||
if (extensions__) { | ||
const extensions = typeof extensions__ === 'object' ? extensions__ : JSON.parse(extensions__) | ||
if (extensions[key]) { | ||
@@ -18,0 +19,0 @@ row[key] = extensions[key] |
@@ -42,3 +42,3 @@ const { EXT_BACK_PACK, getTargetWrite, isExtendedEntity } = require('../../../lib/utils') | ||
async function transformExtendedFieldsUPDATE(req) { | ||
if (!req.target || !req.query.UPDATE.where) return | ||
if (!req.target || !(req.query.UPDATE.entity?.ref?.[0]?.where ||req.query.UPDATE.where)) return | ||
@@ -45,0 +45,0 @@ const target = getTargetWrite(req.target, this.model) |
@@ -95,3 +95,3 @@ const cds = require('@sap/cds/lib'), { fs, path, tar, rimraf } = cds.utils | ||
message += findings.map(f => ' - ' + f.message).join('\n') + '\n' | ||
return req.reject(400, message) | ||
return req.reject(422, message) | ||
} | ||
@@ -98,0 +98,0 @@ |
@@ -15,3 +15,3 @@ const { URL } = require('url'); | ||
*/ | ||
static #validate(credentials, query) { | ||
static #validate(credentials) { | ||
assertDefined('credentials', credentials); | ||
@@ -57,3 +57,3 @@ assertDefined('credentials.clientid', credentials.clientid); | ||
constructor(credentials, query) { | ||
AuthProvider.#validate(credentials, query); | ||
AuthProvider.#validate(credentials); | ||
this.credentials = credentials; | ||
@@ -60,0 +60,0 @@ this.query = query; |
@@ -63,4 +63,7 @@ const cds = require('@sap/cds/lib'); | ||
const toLog = `Authentication failed: ${axError.message} ${details}Passcode URL: ${passcodeUrl}`; | ||
const toSend = `Authentication failed: ${axError.message} Passcode URL: ${passcodeUrl}`; | ||
const toSend = { | ||
error: 'Authentication failed', | ||
error_description: axError.message, | ||
passcode_url: passcodeUrl.toString() | ||
}; | ||
const status = axError.status ?? 500; | ||
@@ -67,0 +70,0 @@ |
@@ -11,3 +11,6 @@ const { inspect } = require('util') | ||
queueSize = 100, clusterSize = 1, workerSize = 1, poolSize = 1 | ||
} = cds.env.requires.multitenancy.jobs ?? cds.env.requires['cds.xt.SaasProvisioningService']?.jobs ?? {} | ||
} = cds.env.requires.multitenancy.jobs | ||
?? cds.env.requires['cds.xt.SaasProvisioningService']?.jobs | ||
?? cds.env.requires['cds.xt.SmsProvisioningService']?.jobs | ||
?? {} | ||
@@ -14,0 +17,0 @@ const RUNNING = 'RUNNING', FINISHED = 'FINISHED', FAILED = 'FAILED' |
@@ -8,3 +8,3 @@ const cds = require('@sap/cds/lib'), {db} = cds.env.requires | ||
const { cacheBindings = true, t0 = 't0' } = cds.requires.multitenancy ?? {} | ||
const { accessSync, constants: fsConstants, existsSync } = require('fs') | ||
const { existsSync } = require('fs') | ||
const { mkdirp } = cds.utils | ||
@@ -258,4 +258,9 @@ | ||
const csn = await _csn | ||
if (csn) await build (csn,tenant,updateCsvs) | ||
DEBUG?.('finished HANA build') | ||
if (csn) try { | ||
await build (csn,tenant,updateCsvs) | ||
DEBUG?.('finished HANA build') | ||
} catch (e) { | ||
if (e.code !== 'ERR_CDS_COMPILATION_FAILURE') throw e | ||
req.reject(422, e.message) | ||
} | ||
} | ||
@@ -262,0 +267,0 @@ if (csnFromParameter) { |
@@ -166,3 +166,3 @@ const https = require('https') | ||
function _poll(location) { | ||
let attempts = 0 | ||
let attempts = 0, maxAttempts = 60, pollingTimeout = 3000, maxTime = pollingTimeout * maxAttempts/1000 | ||
const _next = async (resolve, reject) => { | ||
@@ -172,3 +172,3 @@ const { data, data: { state, errors } } = await api.get(location.slice('/v1/'.length)) | ||
if (state === 'failed') return reject(errors[0] ?? errors) | ||
if (attempts > 20) return reject(new Error(`Polling ${location} timed out`)) | ||
if (attempts > maxAttempts) return reject(new Error(`Polling ${location} timed out after ${maxTime} seconds`)) | ||
setTimeout(++attempts && _next, 3000, resolve, reject) | ||
@@ -175,0 +175,0 @@ } |
@@ -53,3 +53,9 @@ const cds = require('@sap/cds/lib'), {db} = cds.requires, {fs, rimraf, path} = cds.utils | ||
cds.db?.disconnect(t) // REVISIT: ideally not necessary | ||
if (dbUrl) await fs.rimraf(dbUrl) | ||
if (dbUrl) { | ||
await Promise.all([ | ||
fs.rimraf(dbUrl), | ||
fs.rimraf(dbUrl+'-shm'), // REVISIT: ideally not necessary | ||
fs.rimraf(dbUrl+'-wal') // REVISIT: ideally not necessary | ||
]) | ||
} | ||
} | ||
@@ -56,0 +62,0 @@ }) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
253996
4833
4