@axway/axway-central-cli
Version:
Manage APIs, services and publish to the Amplify Marketplace
312 lines (304 loc) • 11.6 kB
JavaScript
;
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;