UNPKG

@sap/cds-dk

Version:

Command line client and development toolkit for the SAP Cloud Application Programming Model

128 lines (111 loc) 4.65 kB
const { utils: { local }} = require ('../cds'); const { join } = require('path'); const { readFileSync, existsSync } = require('fs'); const { login } = require('./auth_manager'); const buildSystem = require('../build') const { buildResponseError, buildNetworkError, mtxFetch } = require('./util/request'); const { getMessage } = require('./util/logging'); const { schemaRegex } = require('./util/urls'); 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 pushResult = await this.pushTgz(params, content, extensionName); if (params.get('async')) { await this.pollUntilFinished(pushResult.headers.get('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' }); } const response = await mtxFetch('GET', src, {}, params); content = Buffer.from(await response.arrayBuffer()).toString('base64'); } 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 reqAuth = params.get('reqAuth') || {}; const headers = { 'content-type': 'application/json', ...reqAuth.headers }; const async = params.get('async'); if (async) { headers['prefer'] = 'respond-async'; } const response = await mtxFetch('POST', pushUrl, { headers, body: JSON.stringify({ extension: tgz, tag: extensionName }) }, params); return response; } static async pollUntilFinished(jobUrl, params) { const reqAuth = params.get('reqAuth') || {}; const headers = { ...reqAuth.headers }; return new Promise((resolve, reject) => { const timeout = setTimeout(() => { clearInterval(interval); reject(new Error('cds push timed out after 5 minutes')); }, 5 * 60 * 1000); const interval = setInterval(async () => { try { const response = await fetch(jobUrl, { headers }); if (!response.ok) { clearInterval(interval); clearTimeout(timeout); reject(await buildResponseError('GET', jobUrl, response)); return; } const jobResult = await response.json(); const { status, tasks } = jobResult; 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)); } } } catch (e) { clearInterval(interval); clearTimeout(timeout); if (e.status) { reject(e); } else { reject(buildNetworkError('GET', jobUrl, e)); } } }, 1000); }); } }