@elastic.io/component-commons-library
Version:
Library for most common component development cases
100 lines (86 loc) • 3.41 kB
text/typescript
import http from 'http';
import https from 'https';
import axios, { AxiosInstance } from 'axios';
import removeTrailingSlash from 'remove-trailing-slash';
import removeLeadingSlash from 'remove-leading-slash';
import { axiosReqWithRetryOnServerError, getErrMsg, getFacelessRetriesCount } from '../externalApi';
import { PlatformApiLogicClient } from '../platformApi/PlatformApiLogicClient';
export class FacelessRestClient {
emitter;
cfg;
request;
private readonly axiosInst: AxiosInstance;
private accessToken: null;
logger: any;
platformClient: PlatformApiLogicClient;
constructor(emitter, cfg, userAgent?: string, msgId?: string) {
this.emitter = emitter;
this.cfg = cfg;
this.axiosInst = axios.create({
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
});
this.cfg.resourceServerUrl = cfg.resourceServerUrl;
this.accessToken = null;
this.logger = emitter.logger;
this.platformClient = new PlatformApiLogicClient(emitter, cfg, userAgent, msgId);
}
protected async addAuthenticationToRequestOptions(requestOptions) {
if (!this.accessToken) {
const { secretId } = this.cfg;
if (secretId) {
this.logger.debug('Fetching credentials by secretId');
const secret = await this.platformClient.fetchSecretById({ secretId });
this.accessToken = secret.attributes.credentials.access_token;
} else {
this.logger.debug('Fetching credentials from this.cfg');
this.accessToken = this.cfg.oauth.access_token;
}
}
// eslint-disable-next-line no-param-reassign
requestOptions.headers.Authorization = `Bearer ${this.accessToken}`;
}
// options expects the following sub-variables:
// url: Url to call
// method: HTTP verb to use
// data: Body of the request, if applicable. Defaults to undefined.
// headers: Any HTTP headers to add to the request. Defaults to {}
// urlIsSegment: Whether to append to the base server url or
// if the provided URL is an absolute path. Defaults to true
async makeRequest(options) {
const {
url, method, data, headers = {}, urlIsSegment = true,
} = options;
const urlToCall = urlIsSegment
? `${removeTrailingSlash(this.cfg.resourceServerUrl.trim())}/${removeLeadingSlash(url.trim())}` // Trim trailing or leading '/'
: url.trim();
this.logger.debug(`Making ${method} request...`);
const requestOptions = {
method,
url: urlToCall,
data,
headers,
};
let error;
let currentRetry = 0;
const facelessRetriesCount = getFacelessRetriesCount();
while (currentRetry < facelessRetriesCount) {
await this.addAuthenticationToRequestOptions(requestOptions);
try {
const response = await axiosReqWithRetryOnServerError.call(this, requestOptions, this.axiosInst);
return response;
} catch (err) {
this.logger.error(getErrMsg(err.response));
error = err;
if (err.response?.status < 500 && err.response?.status !== 401) {
throw err;
}
this.logger.info(`Request failed, faceless retrying(${1 + currentRetry})`);
this.accessToken = null;
currentRetry++;
}
}
this.logger.error('The number of attempts to receive the proper token has been exhausted.');
throw error;
}
}