@elastic.io/component-commons-library
Version:
Library for most common component development cases
116 lines (101 loc) • 4.73 kB
text/typescript
/* eslint-disable class-methods-use-this */
import axios, { AxiosRequestConfig } from 'axios';
import { URL } from 'url';
import { ObjectStorage } from '@elastic.io/maester-client';
import { RetryOptions, ResponseType, CONTENT_TYPE_HEADER, REQUEST_TIMEOUT, RETRIES_COUNT } from '@elastic.io/maester-client/dist/src/interfaces';
import { Readable } from 'stream';
import { getLogger } from '../logger/logger';
import packageJson from '../../package.json';
const logger = getLogger();
const maesterClientVersion = packageJson.dependencies['@elastic.io/maester-client'];
const axiosVersion = packageJson.dependencies.axios;
export const STORAGE_TYPE_PARAMETER = 'storage_type';
export const DEFAULT_STORAGE_TYPE = 'steward';
export const MAESTER_OBJECT_ID_ENDPOINT = '/objects/';
const { ELASTICIO_OBJECT_STORAGE_TOKEN = '', ELASTICIO_OBJECT_STORAGE_URI = '' } = process.env;
const maesterCreds = { jwtSecret: ELASTICIO_OBJECT_STORAGE_TOKEN, uri: ELASTICIO_OBJECT_STORAGE_URI };
const DEFAULT_ATTACHMENT_REQUEST_TIMEOUT = process.env.REQUEST_TIMEOUT ? parseInt(process.env.REQUEST_TIMEOUT, 10) : REQUEST_TIMEOUT.maxValue; // 20s
export class AttachmentProcessor {
private userAgent: string;
private msgId: string;
public constructor(userAgent?: string, msgId?: string) {
this.userAgent = userAgent;
this.msgId = msgId;
}
async getAttachment(url: string, responseType: string, retryOptions: RetryOptions = {}) {
const storageType = this.getStorageTypeByUrl(url);
const axConfig = {
url,
responseType,
method: 'get',
timeout: retryOptions.requestTimeout || DEFAULT_ATTACHMENT_REQUEST_TIMEOUT,
retry: retryOptions.retriesCount || RETRIES_COUNT.defaultValue,
} as AxiosRequestConfig;
switch (storageType) {
case 'steward': return this.getStewardAttachment(axConfig);
case 'maester': return this.getMaesterAttachment(axConfig as any);
default: throw new Error(`Storage type "${storageType}" is not supported`);
}
}
async uploadAttachment(getAttachment: () => Promise<Readable>, retryOptions: RetryOptions = {}, contentType?: string) {
logger.debug('uploading attachment..');
const headers = {};
if (contentType) headers[CONTENT_TYPE_HEADER] = contentType;
const objectStorage = new ObjectStorage({ ...maesterCreds, userAgent: this.userAgent });
return objectStorage.add(getAttachment, {
headers,
retryOptions: {
requestTimeout: retryOptions.requestTimeout || DEFAULT_ATTACHMENT_REQUEST_TIMEOUT,
retriesCount: retryOptions.retriesCount || RETRIES_COUNT.defaultValue,
},
});
}
getMaesterAttachmentUrlById(attachmentId): string {
return `${maesterCreds.uri}${MAESTER_OBJECT_ID_ENDPOINT}${attachmentId}?${STORAGE_TYPE_PARAMETER}=maester`;
}
private async getStewardAttachment(axConfig) {
const ax = axios.create();
this.addRetryCountInterceptorToAxios(ax);
const userAgent = `${this.userAgent} axios/${axiosVersion}`;
return ax({
...axConfig,
headers: {
'User-Agent': userAgent,
'x-request-id': `f:${process.env.ELASTICIO_FLOW_ID};s:${process.env.ELASTICIO_STEP_ID};m:${this.msgId}`,
}
});
}
private async getMaesterAttachment({ url, responseType }: { url: string, responseType: ResponseType }) {
const userAgent = `${this.userAgent} maester-client/${maesterClientVersion}`;
const objectStorage = new ObjectStorage({ ...maesterCreds, userAgent, msgId: this.msgId });
const maesterAttachmentId = this.getMaesterAttachmentIdByUrl(url);
return objectStorage.getOne(maesterAttachmentId, { responseType });
}
private getStorageTypeByUrl(urlString) {
const url = new URL(urlString);
const storageType = url.searchParams.get(STORAGE_TYPE_PARAMETER);
return storageType || DEFAULT_STORAGE_TYPE;
}
private getMaesterAttachmentIdByUrl(urlString): string {
const { pathname } = new URL(urlString);
const maesterAttachmentId = pathname.split(MAESTER_OBJECT_ID_ENDPOINT)[1];
if (!maesterAttachmentId) {
throw new Error('Invalid Maester Endpoint');
}
return maesterAttachmentId;
}
private addRetryCountInterceptorToAxios(ax) {
ax.interceptors.response.use(undefined, (err) => { // Retry count interceptor for axios
const { config } = err;
if (!config || !config.retry || !config.delay) {
return Promise.reject(err);
}
config.currentRetryCount = config.currentRetryCount || 0;
if (config.currentRetryCount >= config.retry) {
return Promise.reject(err);
}
config.currentRetryCount += 1;
return new Promise((resolve) => setTimeout(() => resolve(ax(config)), config.delay));
});
}
}