UNPKG

ring-websites-toolbelt

Version:

Ring Publishing Platform tool to work with Ring Websites

286 lines (236 loc) 10.3 kB
const request = require('request'); const cryptoJS = require('crypto-js'); const fs = require('fs'); const path = require('path'); const DLSigner = require('ring-auth-node').DLSigner; const aws4 = require('aws4'); class ExternalApiProvider { constructor(themeJson, ucsConfig) { this.ucsConfig = ucsConfig; this.namespaceId = ucsConfig.namespaceId; this.variant = ucsConfig.ucsVariant; this.theme = themeJson.theme; this.themeVersion = themeJson.version; this.signer = new DLSigner({ service: 'pulsapi', accessKey: ucsConfig.publicKey, secretKey: ucsConfig.secretKey }); } async getLogs() { try { return await this._sendRequest('GET', `/namespaces/${this.namespaceId}/variants/${this.variant}/logs`); } catch (err) { return []; } } async getThemes() { return this._sendRequest('GET', `/namespaces/${this.namespaceId}/themes`); } async getVariants() { return this._sendRequest('GET', `/namespaces/${this.namespaceId}/variants`); } async getNamespace() { return this._sendRequest('GET', `/namespaces/${this.namespaceId}`); } async createVariant(params) { return this._sendRequest('POST', `/namespaces/${this.namespaceId}/variants`, { ...params, theme: this.theme }); } async updateVariant(params) { return this._sendRequest('PATCH', `/namespaces/${this.namespaceId}/variants/${params.ucsVariant}`, params); } async deployEmptyTheme() { await this._sendRequest('POST', `/namespaces/${this.namespaceId}/themes/${this.theme}/versions/0.0.0`, {}); } async insertThemeConfig(config) { await this._sendRequest('POST', `/namespaces/${this.namespaceId}/variants/${this.variant}/themes/${this.theme}/theme-configs`, config); } async purgeTheme() { await this._sendRequest('DELETE', `/namespaces/${this.namespaceId}/variants/${this.variant}/themes/${this.theme}`); } async deployTheme(ocdnUrl, version) { let deploymentId = null; let data = { ocdnUrl: ocdnUrl, themeName: this.theme, themeVersion: this.themeVersion, ringWebsitesDeployer: true }; let result = await this._sendRequest('POST', `/namespaces/${this.namespaceId}/deploys`, data); if (result && result.deployId) { deploymentId = result.deployId; } else { throw new Error('Error sending theme to depployment api', result); } return deploymentId; } async deployRouter(routerName, routerVersion, ocdnUrl) { const result = await this._sendRequest('POST', `/namespaces/${this.namespaceId}/routers/${routerName}/deployments`, { routerVersion, ocdnUrl, ringWebsitesDeployer:true }); if (!result || !result.deploymentId) { throw new Error('Error in router deployment', result); } return result.deploymentId; } async checkIfRouterDeployed(routerName, deploymentId) { let result = null; try { result = await this._sendRequest('GET', `/namespaces/${this.namespaceId}/routers/${routerName}/deployments/${deploymentId}`); let ret = {}; if (result && result.finished) { ret.finished = true; ret.error = result.error; } else { ret.finished = false; //even in case of error. Api may return an error even though the deployment will be successful. } return ret; } catch (err) { console.log('Error while checking if theme deployed', err); throw err; } } async checkIfThemeDeployed(deploymentId) { let result = null; try { result = await this._sendRequest('GET', `/namespaces/${this.namespaceId}/deploys/${deploymentId}`); let ret = {}; if (result && result.finished) { ret.finished = true; ret.error = result.error; } else { ret.finished = false; //even in case of error. Api may return an error even though the deployment will be successful. } return ret; } catch (err) { console.log('Error while checking if theme deployed', err); throw err; } } async insertFiles(entries, key) { let filesPack = {}; filesPack[key] = []; let filesSize = 0; let i = 0; for (const entry of entries) { let fileData = null; try { let buff = fs.readFileSync(path.join(entry.basePath, entry.relativePath)); //DO NOT MODIFY - size calculated for file saved as hex filesSize = filesSize + buff.length * 2; fileData = buff.toString('hex'); } catch (e) { return new Error('Can\'t read file %s', path.join(entry.basePath, entry.relativePath)); } if (key === ExternalApiProvider.CODES.TE_TEMPLATES) { let kindRole = entry.relativePath.split('/'); let kind = kindRole[0]; delete kindRole[0]; let role = kindRole.join('/').substring(1).slice(0, -6); filesPack[key].push({kind: kind, role: role, value: fileData}); } else if (key === ExternalApiProvider.CODES.TRANSLATIONS) { filesPack[key].push({name: entry.relativePath, value: fileData}); } else { filesPack[key].push({name: entry.relativePath.split('.')[0], value: fileData}); } if (!entries[i + 1] || (filesSize + entries[i + 1].size * 2) > ExternalApiProvider.OPAL_LIMIT) { console.info('Send files pack:', key, filesSize / 1024, "kB"); await this._sendRequest('POST', '/namespaces/' + this.namespaceId + '/variants/' + this.variant + '/themes/' + this.theme + '/theme-files', filesPack); filesPack[key] = []; filesSize = 0; } i++; } } async deleteFiles(entries, key) { let filesPack = {}; filesPack[key] = []; let i = 0; for (const entry of entries) { if (key === ExternalApiProvider.CODES.TE_TEMPLATES) { let kindRole = entry.relativePath.split('/'); let kind = kindRole[0]; delete kindRole[0]; let role = kindRole.join('/').substring(1).slice(0, -6); filesPack[key].push({kind: kind, role: role}); } else if (key === ExternalApiProvider.CODES.TRANSLATIONS) { filesPack[key].push({name: entry.relativePath}); } else { filesPack[key].push({name: entry.relativePath.split('.')[0]}); } i++; } await this._sendRequest('DELETE', '/namespaces/' + this.namespaceId + '/variants/' + this.variant + '/themes/' + this.theme + '/theme-files', filesPack); } async _retry(requestFunction, retries) { try { return await requestFunction(); } catch (error) { if (retries <= 0) { throw error; } return await this._retry(requestFunction, retries - 1); } } _sendRequest(method, path, data) { return new Promise((resolve, reject) => { const body = method !== 'GET' && data ? JSON.stringify(data) : ''; let options = { url: `https://api.ringpublishing.com/websites/v1/development${path}`, path: `/websites/v1/development${path}`, host: "api.ringpublishing.com", method, headers: { 'Content-Type': 'application/json' }, }; if (body) { options.body = body; } options.service = "execute-api"; options.region = "eu-central-1"; aws4.sign(options, { accessKeyId: this.ucsConfig.publicKey, secretAccessKey: this.ucsConfig.secretKey }); // options.headers.cookie = 'acc_variant={"current":"ringwebsitesapi.pulse2.eu::dev_sjanecki"}' // options.headers.cookie = 'acc_variant={"current":"auth-coordinator.ucs2.cloud.onet::dev_mkmiecik"}' request(options, (error, response, body) => { if (error) { return reject(error); } if (response.statusCode < 200 || response.statusCode >= 300) { const { message } = response.body; if (message) { if (message.errorType === 'JSON_RPC' && message.error) { return reject(message.error); } return reject(message); } else if (response.statusCode >= 500 && typeof response.body === 'string') { try { return reject(JSON.parse(response.body)); } catch (err) { return reject(`Unexpected API error occured: statusCode=${response.statusCode}`); } } return reject(response.body); } try { const result = typeof response.body === 'string' ? JSON.parse(response.body) : response.body; if (result.status === 'ok') { return resolve(result.data); } else { return reject(result); } } catch (error) { return reject(error); } }); }); } } ExternalApiProvider.OPAL_LIMIT = 600 * 1024; // in bytes ExternalApiProvider.CODES = {}; ExternalApiProvider.CODES.CDF_TEMPLATES = 'cdf_templates'; ExternalApiProvider.CODES.TE_TEMPLATES = 'te_templates'; ExternalApiProvider.CODES.VIEW_HELPERS = 'te_view_helpers'; ExternalApiProvider.CODES.TRANSLATIONS = 'translations'; module.exports = ExternalApiProvider;