attemptify
Version:
TypeScript retry library with no dependencies.
163 lines (146 loc) • 4.06 kB
text/typescript
import {Duration, seconds} from '@/duration';
import {RetryContext} from '@/retry-context';
import {RetryPolicy} from '@/policy/retry-policy';
import {ErrorConstructor} from '@/policy/retry-policy';
/**
* Basic retry policy: wait constant duration milliseconds in this class.
*/
export class SimpleRetryPolicy implements RetryPolicy {
private errorsNotRetryOn: ErrorConstructor[] = [];
/**
* @param {number} _durationMsec
* @param {number} _maxAttempts
*/
constructor(
private _durationMsec: Duration,
private _maxAttempts: number,
) {}
/**
* Creates a new Simple Retry policy with default settings
* * interval duration ... 1 second
* * max attempts ... 5 times
* @return {RetryPolicy}
*/
static ofDefaults(): RetryPolicy {
return new SimpleRetryPolicy(seconds(1), 4);
}
/**
* Add an error to the list not retrying on it.
* @param {Error} e
*/
notRetryOn(e: ErrorConstructor): void {
this.errorsNotRetryOn.push(e);
}
/**
* Return true if a passed error should not be retryed.
* @param {Error} e
* @return {boolean}
*/
shouldNotRetry(e: Error): boolean {
return this.errorsNotRetryOn.some((v) => e instanceof v);
}
/**
* @param {SimpleRetryPolicy} retryPolicy
* @return {boolean}
*/
equals(retryPolicy: SimpleRetryPolicy): boolean {
return this._maxAttempts === retryPolicy._maxAttempts &&
this._durationMsec.equals(retryPolicy.durationMsec);
}
/**
* @return {string}
*/
toString(): string {
return `SimpleRetryPolicy(
durationMsec=${this._durationMsec}), maxAttemps=${this._maxAttempts}
)`;
}
/**
*
* @param {RetryContext} retryContext
* @return {boolean} if a passed context can continue retrying
*/
canRetry(retryContext: RetryContext): boolean {
return !retryContext.lastError ||
retryContext.attemptsCount < this._maxAttempts;
}
/**
*
* @param {RetryContext} retryContext
* @return {number} retrun next delay milliseconds
*/
getNextDelay(): Duration {
return this._durationMsec;
}
/**
*/
get maxAttempts(): number {
return this._maxAttempts;
}
/**
*/
get durationMsec(): Duration {
return this._durationMsec;
}
}
export namespace SimpleRetryPolicy {
export const newBuilder = (): SimpleRetryPolicy.Builder => {
return new SimpleRetryPolicy.Builder();
};
/**
* Builder for {@link SimpleRetryPolicy}
*/
export class Builder {
private _duration?: Duration;
private _maxAttempts?: number;
private errorsNotRetryOn: ErrorConstructor[] = [];
/**
* Set duration of interval to builder
* @param {Duration} duration
* @return {Builder}
*/
duration(duration: Duration): Builder {
this._duration = duration;
return this;
}
/**
* Set max attempts to builder
* @param {number} attempts
* @return {Builder}
*/
maxAttempts(attempts: number): Builder {
this._maxAttempts = attempts;
return this;
}
/**
* Set an error not retry on to builder
* @param {ErrorConstructor} e
* @return {Builder}
*/
notRetryOn(e: ErrorConstructor): Builder {
this.notRetrysOn([e]);
return this;
}
/**
* Set an error not retry on to builder
* @param {ErrorConstructor[]} constructors
* @return {Builder}
*/
notRetrysOn(constructors: ErrorConstructor[]): Builder {
constructors.forEach((c) => this.errorsNotRetryOn.push(c));
return this;
}
/**
* Create new {@link SimpleRetryPolicy} with properties.
* Properties that is not set will be default settings.
* @return {SimpleRetryPolicy}
*/
build(): SimpleRetryPolicy {
const duration = this._duration ? this._duration : seconds(1);
const maxAttempts = this._maxAttempts ? this._maxAttempts : 4;
const policy = new SimpleRetryPolicy(duration, maxAttempts);
this.errorsNotRetryOn.forEach((e) => policy.notRetryOn(e));
return policy;
}
}
}