datum-focus
Version:
Data shape, model, metadata, JSON, JSON Schema, GraphQL, MongoDB query and aggregations, iterator generators
143 lines (123 loc) • 4.24 kB
text/typescript
import * as retry from './retry';
import path from 'path';
const log4js = require('log4js');
const logger = log4js.getLogger(path.basename(__filename));
logger.level = 'debug';
export const RETRYABLE_DEFAULTS = {
retries: 5,
factor: 1,
minTimeout: 5 * 1000,
maxTimeout: 5 * 1000
};
/**
*
* RETRYABLE
*
* @param: promiseFunc - The funtion to retry. The function returns a promise.
* @param: retryOptions - See node module "retry" defaults is
* {
* retries: 5,
* factor: 1,
* minTimeout: 5 * 1000,
* maxTimeout: 5 * 1000
* }
* @param: isRetryableErrorCb - if supplied should return true if the error can be retried.
* If cannot retry on this error return false; Default retries on all errors;
*
* */
export let RETRYABLE_PROMISE_WRAPPER = {
promiseFuncWrapper: (promiseFunc: any, count: number) => {
return promiseFunc(count);
}
};
export function RETRYABLE(promiseFunc: any, retryOptions: any = RETRYABLE_DEFAULTS, isRetryableErrorCb: any = () => { return true; }) {
return new Promise((resolve, reject) => {
var operation = retry.operation(retryOptions);
operation.attempt(function (count: any) {
RETRYABLE_PROMISE_WRAPPER.promiseFuncWrapper(promiseFunc, count)
.then((result: any) => {
resolve(result);
})
.catch((error: any) => {
logger.error(`Error occurred in operation => ${error.stack}`);
var isRetryable = isRetryableErrorCb(error);
let stringError = error + "";
try {
stringError = JSON.stringify(error);
}
catch (e) {
}
if (isRetryable) {
retryOptions['forever']
? logger.error(`Error: ${stringError}; forever: ${retryOptions['forever']}; retrying-- attempt #${count} `)
: logger.error(`Error: ${stringError}; retrying-- attempt #${count}/${retryOptions['retries']}`);
if (operation.retry(error)) {
return;
}
}
else {
logger.error(`Error: Not retyable - ${stringError}`);
}
reject(error);
});
});
});
}
class MyError extends Error {
constructor(captureStart: any) {
super();
Error.prepareStackTrace = (error, structuredStackTrace) => {
return structuredStackTrace;
};
Error.captureStackTrace(this, captureStart);
}
}
export class RetryableUtil {
static getRetryableCallsite(captureStart: any = RETRYABLE): any {
let origPrepareStackTrace = Error.prepareStackTrace;
const myObject = new MyError(captureStart);
const siteList = myObject.stack;
Error.prepareStackTrace = origPrepareStackTrace;
return siteList;
}
// eslint-disable-next-line sonarjs/cognitive-complexity
static breakOn(options: any = {
functionNameRE: undefined,
fileNameRE: undefined,
lineNumber: undefined,
captureStart: undefined,
showCallStack: undefined
}) {
let captureStart = options.captureStart || RETRYABLE;
const siteList: any = RetryableUtil.getRetryableCallsite(captureStart);
if (siteList.length === 0) {
return null;
}
let functionName = null;
let fileName = null;
let lineNumber = -1;
let matched = false;
const callStack: any[] = [];
for (let site of siteList) {
functionName = site.getFunctionName() || 'anonymouse';
fileName = site.getFileName();
lineNumber = site.getLineNumber();
if (options['showCallStack'] === true) {
callStack.push(`functionName: ${functionName} fileName: ${fileName} lineNumber: ${lineNumber}`);
}
let functionNameREMatched = (options.functionNameRE) ? options.functionNameRE.test(functionName) : true;
let fileNameREMatched = (options.fileNameRE) ? options.fileNameRE.test(fileName) : true;
let lineNumberMatched = (options.lineNumber) ? options.lineNumber === lineNumber : true;
matched = (functionNameREMatched && fileNameREMatched && lineNumberMatched);
if (matched) {
if (options['showCallStack'] === true) {
for (const stack_line of callStack) {
logger.info(stack_line);
}
}
return site;
}
}
return null;
}
}