@elastic.io/maester-client
Version:
The official object-storage client
147 lines (146 loc) • 6.89 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.StorageClient = exports.getStreamWithContentType = void 0;
/* eslint-disable no-continue,default-param-last */
const axios_1 = __importDefault(require("axios"));
const util_1 = require("util");
const http_1 = __importDefault(require("http"));
const https_1 = __importDefault(require("https"));
const jsonwebtoken_1 = require("jsonwebtoken");
const stream_mime_type_1 = require("stream-mime-type");
const logger_1 = __importDefault(require("./logger"));
const package_json_1 = __importDefault(require("../package.json"));
const errors_1 = require("./errors");
const utils_1 = require("./utils");
const interfaces_1 = require("./interfaces");
const getStreamWithContentType = async (getStream) => {
const { mime = 'application/json', stream } = await (0, stream_mime_type_1.getMimeType)(await getStream(), { strict: true });
return { mime, stream };
};
exports.getStreamWithContentType = getStreamWithContentType;
class StorageClient {
constructor(config) {
this.api = axios_1.default.create({
baseURL: config.uri,
httpAgent: StorageClient.httpAgent,
httpsAgent: StorageClient.httpsAgent,
maxContentLength: Infinity,
maxRedirects: 0
});
this.userAgent = `${config.userAgent || ''} axios/${package_json_1.default.dependencies.axios}`;
this.msgId = config.msgId || '';
this.jwtSecret = config.jwtSecret;
}
async requestRetry(requestConfig, retryOptions) {
var _a;
const { retriesCount, requestTimeout } = (0, utils_1.validateAndGetRetryOptions)(retryOptions);
let currentRetries = 0;
let res;
let err;
while (currentRetries <= retriesCount) {
err = null;
res = null;
try {
const { axiosReqConfig, getFreshStream } = requestConfig;
let bodyAsStream;
if (getFreshStream) {
const { mime, stream } = await (0, exports.getStreamWithContentType)(getFreshStream);
if (!axiosReqConfig.headers[interfaces_1.CONTENT_TYPE_HEADER])
axiosReqConfig.headers[interfaces_1.CONTENT_TYPE_HEADER] = mime;
bodyAsStream = stream;
}
res = await this.api.request({ ...axiosReqConfig, data: bodyAsStream, timeout: requestTimeout });
}
catch (e) {
if (e instanceof errors_1.ObjectStorageClientError) {
throw e;
}
err = e;
if (((_a = err === null || err === void 0 ? void 0 : err.response) === null || _a === void 0 ? void 0 : _a.status) < 500)
throw new errors_1.ClientTransportError(`Client error during request: ${err.message}`, err.response.status);
}
if ((err || res.status >= 500) && currentRetries < retriesCount) {
logger_1.default.warn({ err, status: res === null || res === void 0 ? void 0 : res.status, statusText: res === null || res === void 0 ? void 0 : res.statusText }, `Error during object request, retrying (${currentRetries + 1})`);
await (0, utils_1.exponentialSleep)(currentRetries);
currentRetries++;
continue;
}
break;
}
if (err || (res === null || res === void 0 ? void 0 : res.status) >= 500) {
throw new errors_1.ServerTransportError(`Server error during request: "${(err === null || err === void 0 ? void 0 : err.message) || 'unknown error'}"`, {
code: res === null || res === void 0 ? void 0 : res.status,
cause: err
});
}
return res;
}
async formHeaders(jwtPayloadOrToken, headers) {
if (typeof jwtPayloadOrToken !== 'string' && !this.jwtSecret) {
throw new errors_1.JwtNotProvidedError('Neither JWT token passed, nor JWT secret provided during initialization');
}
const token = typeof jwtPayloadOrToken === 'string'
? jwtPayloadOrToken
: await (0, util_1.promisify)(jsonwebtoken_1.sign)(jwtPayloadOrToken, this.jwtSecret);
return {
Authorization: `Bearer ${token}`,
'User-Agent': this.userAgent,
'x-request-id': `f:${process.env.ELASTICIO_FLOW_ID};s:${process.env.ELASTICIO_STEP_ID};m:${this.msgId}`,
...headers
};
}
// wrap for 'post' and 'put' methods
async reqWithBody(getFreshStream, { headers = {}, jwtPayloadOrToken = this.jwtSecret, retryOptions = {} } = {}, objectId) {
return this.requestRetry({
getFreshStream,
axiosReqConfig: {
method: objectId ? 'put' : 'post',
url: objectId ? `/objects/${objectId}` : '/objects',
headers: await this.formHeaders(jwtPayloadOrToken, headers)
},
}, retryOptions);
}
async post(getFreshStream, reqWithBodyOptions) {
return this.reqWithBody(getFreshStream, reqWithBodyOptions);
}
async put(objectId, getFreshStream, reqWithBodyOptions) {
return this.reqWithBody(getFreshStream, reqWithBodyOptions, objectId);
}
/**
* fetches object(s) from maester by id/params
* @param searchCriteria objectId/request-params
*/
async get(searchCriteria, { jwtPayloadOrToken = this.jwtSecret, retryOptions = {} }) {
const byId = typeof searchCriteria === 'string';
return this.requestRetry({
axiosReqConfig: {
method: 'get',
url: byId ? `/objects/${searchCriteria}` : '/objects',
responseType: 'stream',
params: byId ? {} : searchCriteria,
headers: await this.formHeaders(jwtPayloadOrToken)
}
}, retryOptions);
}
/**
* delete object(s) from maester by id/params
* @param searchCriteria objectId/request-params
*/
async delete(searchCriteria, { jwtPayloadOrToken = this.jwtSecret, retryOptions = {} }) {
const byId = typeof searchCriteria === 'string';
return this.requestRetry({
axiosReqConfig: {
method: 'delete',
url: byId ? `/objects/${searchCriteria}` : '/objects',
params: byId ? {} : searchCriteria,
headers: await this.formHeaders(jwtPayloadOrToken)
}
}, retryOptions);
}
}
exports.StorageClient = StorageClient;
StorageClient.httpAgent = new http_1.default.Agent({ keepAlive: true });
StorageClient.httpsAgent = new https_1.default.Agent({ keepAlive: true });