Skip to content

Commit

Permalink
PLT-1397 Refactor code
Browse files Browse the repository at this point in the history
  • Loading branch information
rlajous committed Sep 14, 2023
1 parent bcf1787 commit 9c6aa34
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 81 deletions.
49 changes: 13 additions & 36 deletions packages/poaps/src/utils/ClaimChecker.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,32 @@
import { MintingStatus } from '@poap-xyz/utils';
import { TokensApiProvider, ClaimStatusResponse } from '@poap-xyz/providers';
import { FinishedWithError } from '../errors/FinishedWithError';

const MAX_RETRIES = 20;
const INITIAL_DELAY = 1000;
const BACKOFF_FACTOR = 1.2;
import { RetryableTask } from './RetryableTask';

/**
* A utility class designed to continually check the status of a Poap token claim.
* If a claim is still pending or in process, it implements a backoff retry mechanism.
*/
export class ClaimChecker {
private retries = 0;
private delay: number = INITIAL_DELAY;
export class ClaimChecker extends RetryableTask {
private queue_uid: string;
private tokensApiProvider: TokensApiProvider;

/**
* Initializes a new instance of the `ClaimChecker` class.
* Constructs a new instance of the ClaimChecker class.
*
* @param {string} queue_uid - The unique identifier for the token claim.
* @param {TokensApiProvider} tokensApiProvider - The provider to fetch the claim status.
*/
constructor(queue_uid: string, tokensApiProvider: TokensApiProvider) {
super(tokensApiProvider);
this.queue_uid = queue_uid;
this.tokensApiProvider = tokensApiProvider;
}

/**
* Delays the callback function based on a delay that increases after each retry.
*
* @private
* @param {() => Promise<void>} callback - The callback function to retry.
*/
private backoffAndRetry(callback: () => Promise<void>): void {
if (this.retries >= MAX_RETRIES) {
throw new Error('Max retries reached');
}
this.retries++;
this.delay *= BACKOFF_FACTOR;
setTimeout(() => {
callback().catch(() => {
// Retry on error
});
}, this.delay); // Wrap the callback invocation
}

/**
* Determines if a retry should occur based on the minting status.
* Determines if a retry should be performed based on the provided minting status.
*
* @private
* @param {MintingStatus} status - The current minting status.
* @returns {boolean} `true` if should retry, otherwise `false`.
* @returns {boolean} Returns true if a retry should be performed, otherwise false.
*/
private shouldRetry(status: MintingStatus): boolean {
return (
Expand All @@ -60,11 +35,12 @@ export class ClaimChecker {
}

/**
* Handles errors from the claim status response.
* Throws an error if the minting process finished with an error.
* Handles any error statuses from the claim status response.
* If the minting process finishes with an error, an exception will be thrown.
*
* @private
* @param {ClaimStatusResponse} claimStatusResponse - The response from the claim status check.
* @throws {FinishedWithError} Throws an error if the minting process finished with an error.
*/
private handleErrorStatus(claimStatusResponse: ClaimStatusResponse): void {
if (
Expand All @@ -77,10 +53,11 @@ export class ClaimChecker {

/**
* Checks the current status of a token claim.
* Retries the check with an increased delay if the claim is still pending or in process.
* If the claim is still pending or in process, it will retry the check with an increased delay.
*
* @public
* @returns {Promise<void>} A promise that resolves once the status has been checked.
* @throws {FinishedWithError} Throws an error if the minting process finished with an error.
*/
public async checkClaimStatus(): Promise<void> {
try {
Expand All @@ -89,12 +66,12 @@ export class ClaimChecker {
);

if (this.shouldRetry(claimStatusResponse.status)) {
this.backoffAndRetry(() => this.checkClaimStatus());
await this.backoffAndRetry(() => this.checkClaimStatus());
} else {
this.handleErrorStatus(claimStatusResponse);
}
} catch (e) {
this.backoffAndRetry(() => this.checkClaimStatus());
await this.backoffAndRetry(() => this.checkClaimStatus());
}
}
}
52 changes: 14 additions & 38 deletions packages/poaps/src/utils/PoapIndexed.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,32 @@
import { TokensApiProvider, GetClaimCodeResponse } from '@poap-xyz/providers';

const MAX_RETRIES = 20;
const INITIAL_DELAY = 1000;
const BACKOFF_FACTOR = 1.2;
import { RetryableTask } from './RetryableTask';

/**
* A utility class designed to periodically check if a POAP (Proof of Attendance Protocol) token is indexed.
* It implements a backoff retry mechanism if the token hasn't been indexed yet.
* @class PoapIndexed
* @extends {RetryableTask}
*
* Represents a utility class designed to periodically check if a POAP (Proof of Attendance Protocol) token is indexed.
* This class extends `RetryableTask` to utilize its backoff retry mechanism in case the token hasn't been indexed yet.
*/
export class PoapIndexed {
private retries = 0;
private delay: number = INITIAL_DELAY;
export class PoapIndexed extends RetryableTask {
private qr_hash: string;
private tokensApiProvider: TokensApiProvider;

/**
* Initializes a new instance of the `PoapIndexed` class.
* Creates an instance of the PoapIndexed class.
*
* @param {string} qr_hash - The unique QR hash of the token.
* @param {TokensApiProvider} tokensApiProvider - The provider to check if the token is indexed.
* @param {string} qr_hash - A unique QR hash representing the token.
* @param {TokensApiProvider} tokensApiProvider - An instance of the TokensApiProvider used to check the indexing status of the token.
*/
constructor(qr_hash: string, tokensApiProvider: TokensApiProvider) {
super(tokensApiProvider);
this.qr_hash = qr_hash;
this.tokensApiProvider = tokensApiProvider;
}

/**
* Delays the callback function based on a delay that increases after each retry.
*
* @private
* @param {() => Promise<GetClaimCodeResponse>} callback - The callback function to retry.
* @returns {Promise<GetClaimCodeResponse>} The response of the callback function.
*/
private async backoffAndRetry(
callback: () => Promise<GetClaimCodeResponse>,
): Promise<GetClaimCodeResponse> {
if (this.retries >= MAX_RETRIES) {
throw new Error('Max retries reached');
}
this.retries++;
this.delay *= BACKOFF_FACTOR;

await new Promise((resolve) => setTimeout(resolve, this.delay));
return callback();
}

/**
* Continuously checks if a POAP token is indexed.
* It will keep retrying based on a delay that increases each time until the token is indexed or max retries are reached.
* Periodically checks if the POAP token, represented by its QR hash, is indexed.
* This method will continue retrying with an increasing delay until either the token is indexed or it reaches the maximum allowed retries.
*
* @public
* @returns {Promise<GetClaimCodeResponse>} A promise that resolves with the indexed token's claim code response.
* @returns {Promise<GetClaimCodeResponse>} A promise that either resolves with the indexed token's claim code response or rejects due to reaching the max retry limit.
*/
public async waitPoapIndexed(): Promise<GetClaimCodeResponse> {
let response = await this.tokensApiProvider.getClaimCode(this.qr_hash);
Expand Down
54 changes: 54 additions & 0 deletions packages/poaps/src/utils/RetryableTask.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { TokensApiProvider } from '@poap-xyz/providers';

const MAX_RETRIES = 20;
const INITIAL_DELAY = 1000;
const BACKOFF_FACTOR = 1.2;

/**
* Abstract class representing a task that can be retried with an increasing delay.
*/
export abstract class RetryableTask {
protected retries = 0;
protected delay: number = INITIAL_DELAY;
protected tokensApiProvider: TokensApiProvider;

/**
* Constructs a new RetryableTask instance.
*
* @param {TokensApiProvider} tokensApiProvider - The provider used to perform operations that might be retried.
*/
constructor(tokensApiProvider: TokensApiProvider) {
this.tokensApiProvider = tokensApiProvider;
}

/**
* Attempts to perform a given task. If the task fails, it retries with an increasing delay until
* maximum retries are reached.
*
* @protected
* @template T - The type of value that the callback returns.
* @param {() => Promise<T>} callback - The asynchronous function representing the task to be retried.
* @returns {Promise<T>} A promise that resolves to the result of the task or rejects with an error.
* @throws {Error} Throws an error if maximum retries are reached.
*/
protected backoffAndRetry<T>(callback: () => Promise<T>): Promise<T> {
if (this.retries >= MAX_RETRIES) {
throw new Error('Max retries reached');
}
this.retries++;
this.delay *= BACKOFF_FACTOR;

return new Promise<T>((resolve, reject) => {
setTimeout(() => {
(async (): Promise<void> => {
try {
const response = await callback();
resolve(response);
} catch (error) {
reject(error);
}
})().catch(reject); // Add a catch to handle potential rejections
}, this.delay);
});
}
}
13 changes: 6 additions & 7 deletions packages/providers/src/core/PoapTokenApi/PoapTokenApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@ import {
import { ClaimCodeInput } from './../../ports/TokensApiProvider/Types/input';
import { AuthenticationProvider } from './../../ports/AuthenticationProvider/AuthenticationProvider';
import { TokensApiProvider } from './../../ports/TokensApiProvider/TokensApiProvider';
import axios from 'axios';

const instance = axios.create({
timeout: 10000, // 10 seconds
});
import axios, { AxiosInstance } from 'axios';

const DEFAULT_DROP_BASE_URL = 'https://api.poap.tech';
/**
Expand All @@ -26,7 +22,7 @@ export class PoapTokenApi implements TokensApiProvider {
private apiKey: string;
private baseUrl: string;
private authenticationProvider?: AuthenticationProvider;

private poapApi: AxiosInstance;
/**
* Constructs a new instance of the `PoapTokenApi` class.
*
Expand All @@ -44,6 +40,9 @@ export class PoapTokenApi implements TokensApiProvider {
this.apiKey = apiKey;
this.baseUrl = baseUrl;
this.authenticationProvider = authenticationProvider;
this.poapApi = axios.create({
timeout: 10000, // 10 seconds
});
}

/**
Expand Down Expand Up @@ -112,7 +111,7 @@ export class PoapTokenApi implements TokensApiProvider {
};

return (
await instance(url, {
await this.poapApi(url, {
method: options.method,
data: options.body,
headers: headersWithApiKey,
Expand Down

0 comments on commit 9c6aa34

Please sign in to comment.