From 9c6aa343182a0c5fdc18fa8b83c1d0d96d14ebfa Mon Sep 17 00:00:00 2001 From: rlajous Date: Thu, 14 Sep 2023 13:46:16 +0200 Subject: [PATCH] PLT-1397 Refactor code --- packages/poaps/src/utils/ClaimChecker.ts | 49 +++++------------ packages/poaps/src/utils/PoapIndexed.ts | 52 +++++------------- packages/poaps/src/utils/RetryableTask.ts | 54 +++++++++++++++++++ .../src/core/PoapTokenApi/PoapTokenApi.ts | 13 +++-- 4 files changed, 87 insertions(+), 81 deletions(-) create mode 100644 packages/poaps/src/utils/RetryableTask.ts diff --git a/packages/poaps/src/utils/ClaimChecker.ts b/packages/poaps/src/utils/ClaimChecker.ts index 9c7a6f37..feab0c19 100644 --- a/packages/poaps/src/utils/ClaimChecker.ts +++ b/packages/poaps/src/utils/ClaimChecker.ts @@ -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} callback - The callback function to retry. - */ - private backoffAndRetry(callback: () => Promise): 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 ( @@ -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 ( @@ -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} 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 { try { @@ -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()); } } } diff --git a/packages/poaps/src/utils/PoapIndexed.ts b/packages/poaps/src/utils/PoapIndexed.ts index cf695760..67bbf9f6 100644 --- a/packages/poaps/src/utils/PoapIndexed.ts +++ b/packages/poaps/src/utils/PoapIndexed.ts @@ -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} callback - The callback function to retry. - * @returns {Promise} The response of the callback function. - */ - private async backoffAndRetry( - callback: () => Promise, - ): Promise { - 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} A promise that resolves with the indexed token's claim code response. + * @returns {Promise} 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 { let response = await this.tokensApiProvider.getClaimCode(this.qr_hash); diff --git a/packages/poaps/src/utils/RetryableTask.ts b/packages/poaps/src/utils/RetryableTask.ts new file mode 100644 index 00000000..cfa937cc --- /dev/null +++ b/packages/poaps/src/utils/RetryableTask.ts @@ -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} callback - The asynchronous function representing the task to be retried. + * @returns {Promise} 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(callback: () => Promise): Promise { + if (this.retries >= MAX_RETRIES) { + throw new Error('Max retries reached'); + } + this.retries++; + this.delay *= BACKOFF_FACTOR; + + return new Promise((resolve, reject) => { + setTimeout(() => { + (async (): Promise => { + try { + const response = await callback(); + resolve(response); + } catch (error) { + reject(error); + } + })().catch(reject); // Add a catch to handle potential rejections + }, this.delay); + }); + } +} diff --git a/packages/providers/src/core/PoapTokenApi/PoapTokenApi.ts b/packages/providers/src/core/PoapTokenApi/PoapTokenApi.ts index 0bafa321..adfd1848 100644 --- a/packages/providers/src/core/PoapTokenApi/PoapTokenApi.ts +++ b/packages/providers/src/core/PoapTokenApi/PoapTokenApi.ts @@ -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'; /** @@ -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. * @@ -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 + }); } /** @@ -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,