@sap/cds-dk
Version:
Command line client and development toolkit for the SAP Cloud Application Programming Model
114 lines (97 loc) • 4.03 kB
JavaScript
const { utils: { local }} = require ('../cds');
const { join } = require('path');
const { readFileSync, existsSync } = require('fs');
const axios = require('axios');
const { login } = require('./auth_manager');
const buildSystem = require('../build')
require('../util/axios').pruneErrors();
const { getMessage } = require('./util/logging');
const { schemaRegex } = require('./util/urls');
const { handleHttpError } = require('./util/errors');
const JOB_STATUS = {
QUEUED: 'QUEUED',
RUNNING: 'RUNNING',
FINISHED: 'FINISHED',
FAILED: 'FAILED'
};
module.exports = class Push {
static async run(paramValues, pushOptions = {}) {
const params = await login(paramValues);
const projectFolder = params.get('projectFolder');
const extensionName = this.getExtensionName(projectFolder);
if (!('extArchive' in pushOptions)) {
await buildSystem.build();
}
const { src, content } = await this.readSource(params, pushOptions);
const url = params.get('appUrl');
const subdomain = params.get('subdomain');
const target = { url, ...subdomain && { subdomain } };
console.log(`\nPushing extension '${extensionName}' from`, { src }, 'to', target);
const push = await this.pushTgz(params, content, extensionName);
if (params.get('async')) {
await this.pollUntilFinished(push.headers.location, params)
}
console.log ('Activation succeeded.\n')
}
static async readSource(params, pushOptions) {
let src = pushOptions?.extArchive ?? join(params.get('projectFolder'), 'gen/extension.tgz');
let content;
if (existsSync(src)) {
content = readFileSync(src, { encoding: 'base64' });
src = local(src);
} else {
if (!schemaRegex.test(src)) {
throw getMessage(`Non-existent path: ${src}`, { command: 'push' });
}
try {
content = (await axios.get(src, { responseEncoding: 'base64' })).data;
} catch (error) {
handleHttpError(error, params, { url: src });
}
}
return { src, content };
}
static getExtensionName(projectFolder) {
try {
return require(join(projectFolder, 'package.json')).name;
} catch (error) {
throw getMessage(`Extension project at ${projectFolder} is missing package.json file`, { error, command: 'push' });
}
}
static async pushTgz(params, tgz, extensionName) {
const pushUrl = `${params.get('appUrl')}/-/cds/extensibility/push`;
const options = { ...params.get('reqAuth') };
const async = params.get('async');
if (async) {
options.headers = { prefer: 'respond-async', ...options.headers }
}
return axios.post(pushUrl, {
extension: tgz,
tag: extensionName
}, options)
.catch(error => handleHttpError(error, params, { url: pushUrl }));
}
static async pollUntilFinished(jobUrl, params) {
return new Promise((resolve, reject) => {
const options = { ...params.get('reqAuth') };
const timeout = setTimeout(() => {
clearInterval(interval);
reject(new Error('cds push timed out after 5 minutes'));
}, 5 * 60 * 1000);
const interval = setInterval(async () => {
const jobResult = await axios.get(jobUrl, options);
const { status, tasks } = jobResult.data;
const error = tasks[0]?.error ?? 'cds push failed'
if (status === JOB_STATUS.FINISHED || status === JOB_STATUS.FAILED) {
clearInterval(interval);
clearTimeout(timeout);
if (status === JOB_STATUS.FINISHED) {
resolve();
} else if (status === JOB_STATUS.FAILED) {
reject(new Error(error));
}
}
}, 1000);
});
}
}