From 1836c5eea9719862c6c70d5efe7baa84829cf288 Mon Sep 17 00:00:00 2001 From: Marc Velmer Date: Thu, 4 Jul 2024 19:03:15 +0200 Subject: [PATCH] Modified `getStrategyHolders` in Census3 and using new queue endpoint --- src/api/census3/strategy.ts | 73 ++++++++++++++++------- src/census3.ts | 39 ++++++++---- test/census3/integration/strategy.test.ts | 9 +-- 3 files changed, 83 insertions(+), 38 deletions(-) diff --git a/src/api/census3/strategy.ts b/src/api/census3/strategy.ts index e990c2e4..865ed469 100644 --- a/src/api/census3/strategy.ts +++ b/src/api/census3/strategy.ts @@ -13,6 +13,7 @@ enum Census3StrategyAPIMethods { VALIDATE_PREDICATE = '/strategies/predicate/validate', OPERATORS = '/strategies/predicate/operators', HOLDERS = '/strategies/{id}/holders', + HOLDERS_QUEUE = '/strategies/{id}/holders/queue/{queueId}', } export interface ICensus3StrategiesListResponse { @@ -29,20 +30,6 @@ export interface ICensus3StrategiesListResponsePaginated extends ICensus3Strateg pagination: Census3Pagination; } -export interface ICensus3StrategyHoldersResponse { - /** - * The list of the strategy holders - */ - holders: { [key: string]: string }; -} - -export interface ICensus3StrategyHoldersResponsePaginated extends ICensus3StrategyHoldersResponse { - /** - * The pagination information - */ - pagination: Census3Pagination; -} - export type Census3Strategy = { /** * The strategy identifier @@ -183,6 +170,38 @@ export interface ICensus3StrategyImportQueueResponse { progress: number; } +export interface ICensus3StrategyHoldersQueueResponse { + /** + * If the queue has been done + */ + done: boolean; + + /** + * The error of the queue + */ + error: { + /** + * The code of the error + */ + code: number; + + /** + * The string of the error + */ + error: string; + }; + + /** + * The list of the strategy holders + */ + data: { [key: string]: string }; + + /** + * The importing progress + */ + progress: number; +} + export interface ICensus3StrategyToken { /** * The id (address) of the token. @@ -290,16 +309,26 @@ export abstract class Census3StrategyAPI extends Census3API { * * @param url - API endpoint URL * @param id - The identifier of the strategy - * @param pagination - Pagination options + * @returns The queue identifier */ - public static holders( - url: string, - id: number, - pagination?: Census3Pagination - ): Promise { + public static holders(url: string, id: number): Promise { + return axios + .get(url + Census3StrategyAPIMethods.HOLDERS.replace('{id}', String(id))) + .then((response) => response.data) + .catch(this.isApiError); + } + + /** + * Returns the information of the strategy holders queue + * + * @param url - API endpoint URL + * @param id - The identifier of the strategy + * @param queueId - The identifier of the strategy holders queue + */ + public static holdersQueue(url: string, id: number, queueId: string): Promise { return axios - .get( - url + Census3StrategyAPIMethods.HOLDERS.replace('{id}', String(id)) + this.serializePagination(pagination) + .get( + url + Census3StrategyAPIMethods.HOLDERS_QUEUE.replace('{id}', String(id)).replace('{queueId}', queueId) ) .then((response) => response.data) .catch(this.isApiError); diff --git a/src/census3.ts b/src/census3.ts index e06cafca..abf61395 100644 --- a/src/census3.ts +++ b/src/census3.ts @@ -17,16 +17,14 @@ import { } from './api'; import invariant from 'tiny-invariant'; import { isAddress } from '@ethersproject/address'; -import { TokenCensus } from './types'; +import { TokenCensus, StrategyCensus } from './types'; import { delay } from './util/common'; -import { Census3Pagination } from './api/census3/api'; -import { StrategyCensus } from './types/census/census3/strategy'; export type Token = Omit & { tags: string[] }; export type TokenSummary = Omit & { tags: string[] }; export type Strategy = Census3Strategy; export type StrategyHolder = { holder: string; weight: bigint }; -export type StrategyHolders = { holders: StrategyHolder[]; pagination: Census3Pagination }; +export type StrategyHolders = StrategyHolder[]; export type StrategyToken = Census3CreateStrategyToken; export type Census3Census = ICensus3CensusResponse; export type SupportedChain = ICensus3SupportedChain; @@ -187,14 +185,35 @@ export class VocdoniCensus3Client { * Returns the strategy holders * * @param id - The id of the strategy - * @param pagination - Pagination options * @returns The list strategy holders */ - getStrategyHolders(id: number, pagination: Census3Pagination = { pageSize: -1 }): Promise { - return Census3StrategyAPI.holders(this.url, id, pagination).then((response) => ({ - holders: Object.entries(response.holders).map(([key, value]) => ({ holder: key, weight: BigInt(value) })) ?? [], - pagination: response.pagination, - })); + getStrategyHolders(id: number): Promise { + invariant(id, 'No id set'); + const waitForQueue = (queueId: string, wait?: number, attempts?: number): Promise => { + const waitTime = wait ?? this.queueWait?.retryTime; + const attemptsNum = attempts ?? this.queueWait?.attempts; + invariant(waitTime, 'No queue wait time set'); + invariant(attemptsNum >= 0, 'No queue attempts set'); + + return attemptsNum === 0 + ? Promise.reject('Time out waiting for queue with id: ' + queueId) + : Census3StrategyAPI.holdersQueue(this.url, id, queueId).then((queue) => { + switch (true) { + case queue.done && queue.error?.code?.toString().length > 0: + return Promise.reject(new Error('Could not get the strategy holders')); + case queue.done: + return Promise.resolve( + Object.entries(queue.data).map(([key, value]) => ({ holder: key, weight: BigInt(value) })) ?? [] + ); + default: + return delay(waitTime).then(() => waitForQueue(queueId, waitTime, attemptsNum - 1)); + } + }); + }; + + return Census3StrategyAPI.holders(this.url, id) + .then((queueResponse) => queueResponse.queueID) + .then((queueId) => waitForQueue(queueId)); } /** diff --git a/test/census3/integration/strategy.test.ts b/test/census3/integration/strategy.test.ts index 9fc66c1f..41307254 100644 --- a/test/census3/integration/strategy.test.ts +++ b/test/census3/integration/strategy.test.ts @@ -37,16 +37,13 @@ describe('Census3 strategies integration tests', () => { const client = new VocdoniCensus3Client({ env: EnvOptions.DEV }); const strategies = await client.getStrategies(); if (strategies.length > 1) { - const holders = await client.getStrategyHolders(strategies[1].ID, { pageSize: 10 }); - holders.holders.forEach((holder) => { + const holders = await client.getStrategyHolders(strategies[1].ID); + holders.forEach((holder) => { expect(isAddress(holder.holder)).toBe(true); expect(typeof holder.weight).toBe('bigint'); }); - expect(holders.pagination.pageSize).toBe(10); - expect(isAddress(holders.pagination.nextCursor)).toBe(true); - expect(isAddress(holders.pagination.prevCursor)).toBe(true); } - }, 5000); + }, 85000); it('should return the given strategy', async () => { const client = new VocdoniCensus3Client({ env: EnvOptions.DEV }); const strategies = await client.getStrategies();