UNPKG

@axway/axway-central-cli

Version:

Manage APIs, services and publish to the Amplify Marketplace

312 lines (304 loc) 11.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getBaseUrl = exports.dataService = void 0; var _amplifyCliUtils = require("@axway/amplify-cli-utils"); var _got = require("got"); var _flatten = _interopRequireDefault(require("lodash/flatten")); var _promiseLimit = _interopRequireDefault(require("promise-limit")); var _snooplogg = _interopRequireDefault(require("snooplogg")); var _url = require("url"); var _CoreConfigController = require("./CoreConfigController"); var _types = require("./types"); var _utils = require("./utils"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } // NOTE: removing update-notifier due to some security issues reported. // There is suppression in '.csr-profile.json' that needed to be removed if this library is used in production code. // import updateNotifier from 'update-notifier'; // import semver from 'semver'; // dep of @axway/amplify-cli-utils -> @axway/amplify-request const thisPackage = require('../../package.json'); const { log } = (0, _snooplogg.default)('central: dataService'); const getDefaultHeaders = ({ token, orgId }) => { return { accept: 'application/json', authorization: `Bearer ${token}`, 'X-Axway-Tenant-Id': orgId || '', [_types.cliVersionHeader]: thisPackage.version, 'User-Agent': 'axway-cli' }; }; // TODO: need to refactor this part, data service is used as part of many basic command (create / update, etc.) now. // Plus need to revisit this logic since no testing can be done currently (no new prod versions has been released yet) // https://jira.axway.com/browse/APIGOV-14969 // const versionCheckWarning = async () => { // const notifier = updateNotifier({ pkg: thisPackage, updateCheckInterval: 1000 * 300 }); // const latestCliVersion = notifier.update && notifier.update.latest; // const currentCliVersion = thisPackage.version; // if (latestCliVersion) // if (semver.lt(currentCliVersion, latestCliVersion)) { // console.log( // chalk.red.bgWhite(`\n\n WARNING:`), // chalk.white(`Update available ${currentCliVersion} → ${latestCliVersion}`) // ); // console.log(`Run "amplify pm update central" to update.`); // } // }; const getBaseUrl = async (baseUrl, basePath, region, orgRegion) => { const configuredBaseUrl = baseUrl || process.env.AXWAY_CENTRAL_BASE_URL || (await (0, _utils.getConfig)())[_types.ConfigTypes.BASE_URL]; if (configuredBaseUrl) { return basePath ? configuredBaseUrl + basePath : configuredBaseUrl; } else { const configRegion = (await (0, _utils.getConfig)())[_types.ConfigTypes.REGION]; const configuredRegion = String(region || configRegion || orgRegion || _types.Regions.US).toUpperCase(); log(`Using region "${configuredRegion}" from ${region ? '--region' : configRegion ? 'config' : orgRegion ? 'org' : 'default'}`); const prodBaseurl = _types.ProdBaseUrls[configuredRegion]; if (!prodBaseurl) throw Error('Unknown region provided, check your region config, should be one of: ' + Object.keys(_types.ProdBaseUrls).join(', ')); return basePath ? prodBaseurl + basePath : prodBaseurl; } }; exports.getBaseUrl = getBaseUrl; const handleResponse = response => { return /application\/json/.test(response.headers['content-type']) ? JSON.parse(response.body) : response.body; }; const updateRequestError = err => { var _err$response; // Do not change given object if it's a timeout error. if (err instanceof _got.TimeoutError) { return; } // If we have a JSON HTTP body, then turn it into a dictionary. let jsonBody = null; if (err instanceof _got.RequestError && (_err$response = err.response) !== null && _err$response !== void 0 && _err$response.body) { jsonBody = handleResponse(err.response); } if (!jsonBody) { return; } // Turn given Error object into an "ApiServerError" or "ApiServerErrorResponse" object. if (typeof jsonBody.code === 'number' && typeof jsonBody.description === 'string') { // We received a "Platform" server error response. err.status = jsonBody.code; err.detail = jsonBody.description; } else { // Assume we received a "Central" server error response which should already conform to "ApiServerError". Object.assign(err, jsonBody); } }; /** * Creates an object with various functions communicating with the API Server. * @param {String} clientId Client id to use. * @param {String} [team] The preferred team to use. This value overrides the default from the Axway CLI config. * @param {String} [region] The preferred region to use. * @returns Object containing data retrieval functions. */ const dataService = async ({ account, baseUrl, basePath = _types.BasePaths.ApiServer, clientId, region, team, forceGetAuthInfo }) => { // see above // await versionCheckWarning(); const { orgId, orgRegion, teamGuid, token } = await new _CoreConfigController.CoreConfigController().getAuthInfo({ account, clientId, team, forceGetAuthInfo }); baseUrl = await getBaseUrl(baseUrl, basePath, region, orgRegion); const defaultHeaders = getDefaultHeaders({ orgId, token }); const got = (0, _amplifyCliUtils.createRequestClient)(); const fetch = async (method, url, params = {}) => { try { // add the team guid if (teamGuid !== undefined) { const parsed = new _url.URL(url); parsed.searchParams.set('query', teamGuid ? `owner.id==${teamGuid},(owner.id==null;metadata.scope.owner.id==${teamGuid})` : 'owner.id==null'); url = parsed.toString(); } const response = await got[method](url, { followRedirect: false, retry: 0, timeout: _types.ABORT_TIMEOUT, ...params }); if (response.statusCode === 301 || response.statusCode === 302) { log(`${method.toUpperCase()}: ${url} ${response.statusCode} redirecting to ${response.headers.location}`); return await fetch(method, response.headers.location, params); } return response; } catch (err) { updateRequestError(err); throw err; } }; return { postFormData: (route, body, headers = {}) => { log(`POST (from data): ${baseUrl + route}`); return fetch('post', baseUrl + route, { headers: { ...defaultHeaders, ...body.getHeaders(), ...headers }, body }).then(handleResponse); }, post: (route, data, headers = {}) => { log(`POST: ${baseUrl + route}`); log(data); return fetch('post', baseUrl + route, { headers: { ...defaultHeaders, ...headers }, json: data }).then(handleResponse); }, put: (route, data, headers = {}) => { log(`PUT: ${baseUrl + route}`); return fetch('put', baseUrl + route, { headers: { ...defaultHeaders, ...headers }, json: data }).then(handleResponse); }, get: (route, headers = {}, skipDefaultHeaders = false) => { const h = skipDefaultHeaders ? headers : { ...defaultHeaders, ...headers }; log(`GET: ${baseUrl + route}`, h); return fetch('get', baseUrl + route, { headers: h }).then(handleResponse); }, head: (route, queryParams, headers = {}, skipDefaultHeaders = false) => { const h = skipDefaultHeaders ? headers : { ...defaultHeaders, ...headers }; log(`HEAD: ${baseUrl + route}`, h); const fullUrl = new _url.URL(baseUrl + route); if (queryParams) { fullUrl.searchParams.set('query', queryParams); } return fetch('head', fullUrl.toString(), { headers: h }).then(response => { return response.headers['x-axway-total-count']; }); }, /** * Get the entire list using pagination. Method is trying to define total number of items based on response header * and makes additional calls if needed to retrieve additional pages. * Note: currently this only present correct results if response is an array (see the "allPages" var spread logic) * @param route route to hit * @param queryParams specific query params * @param pageSize page size to use, by default = 50 * @param headers headers to add * @param progressListener invoked multiple times where argument is assigned progress value 0-100 */ getWithPagination: async function (route, queryParams = '', pageSize = 50, headers = {}, progressListener) { const fullUrl = new _url.URL(baseUrl + route); if (queryParams) { fullUrl.searchParams.set('query', queryParams); } fullUrl.searchParams.set('pageSize', `${pageSize}`); log(`GET (with auto-pagination): ${fullUrl.href}`); const response = await fetch('get', fullUrl.toString(), { headers: { ...defaultHeaders, ...headers } }); const totalCountHeader = response.headers['x-axway-total-count']; if (totalCountHeader === null || totalCountHeader === undefined) { log(`GET (with auto-pagination), warning: cannot figure out 'total count' header, resolving response as-is`); return handleResponse(response); } log(`GET (with auto-pagination), 'total count' header found, count = ${totalCountHeader}, will fire additional GET calls if needed`); const totalPages = Math.max(Math.ceil(Number(totalCountHeader) / pageSize), 1); const allPages = new Array(totalPages); allPages[0] = handleResponse(response); if (totalPages > 1) { const limit = (0, _promiseLimit.default)(8); // Limits number of concurrrent HTTP requests. const otherPagesCalls = []; let pageDownloadCount = 1; const updateProgress = () => { if (progressListener && totalPages > 4) { progressListener(Math.floor(pageDownloadCount / totalPages * 100)); } }; updateProgress(); for (let pageIndex = 1; pageIndex < totalPages; pageIndex++) { const thisPageIndex = pageIndex; fullUrl.searchParams.set('page', `${thisPageIndex + 1}`); const nextRoute = fullUrl.href.substring(baseUrl.length); otherPagesCalls.push(limit(async () => { allPages[thisPageIndex] = await this.get(nextRoute, headers); pageDownloadCount++; updateProgress(); })); } await Promise.all(otherPagesCalls); } return (0, _flatten.default)(allPages); }, delete: (route, headers = {}) => { log(`DELETE: ${baseUrl + route}`); return fetch('delete', baseUrl + route, { headers: { ...defaultHeaders, ...headers } }).then(handleResponse); }, download: async route => { try { return await new Promise((resolve, reject) => { log(`DOWNLOAD: ${baseUrl + route}`); const stream = got.stream(baseUrl + route, { retry: 0, timeout: _types.ABORT_TIMEOUT }); stream.on('response', response => { if (response.statusCode < 300) { resolve({ response, stream }); } else { reject(new Error()); } }); stream.on('error', reject); }); } catch (err) { updateRequestError(err); throw err; } } }; }; exports.dataService = dataService;