Skip to content

Commit

Permalink
Merge pull request #102 from poap-xyz/compass-batch
Browse files Browse the repository at this point in the history
Implement compass batch
  • Loading branch information
jm42 authored Apr 30, 2024
2 parents 249adbb + 7e121a2 commit 684a5f0
Show file tree
Hide file tree
Showing 10 changed files with 313 additions and 78 deletions.
60 changes: 60 additions & 0 deletions docs/pages/packages/providers/Compass.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,63 @@ unless there is a migration to be made. When the API key given is incorrect or e
All errors derived from a malformed structure on the query will throw a `CompassRequestError` which
has a public property called `errors` of the type `CompassError` with more information about what
went wrong.

## Batch Requests

It is possible to do many requests that only differs on the variables given to the same
query. Such requests will be done in batch not to overload the server.

For example, if what we want is to retrieve collectors of a drop:

```typescript
const total = 800; // Number of collectors we want to retrieve
const limit = 100; // Number of collectors we retrieve per page

const MY_DROP_COLLECTORS_QUERY = `
query MyDropCollectors(
$offset: Int
$limit: Int
) {
poaps(
where: {
drop_id: { _eq: 151249 }
}
offset: $offset
limit: $limit
) {
collector_address
}
}
`;

type CollectorsQueryResponse = {
poaps: Array<{
collector_address: string;
}>;
};

const responses = await compass.batch<
CollectorsQueryResponse,
PaginatedVariables
>(
MY_DROP_COLLECTORS_QUERY,
Array.from(
{ length: Math.ceil(total / limit) },
(_, page): PaginatedVariables => ({
offset: page * limit,
limit: limit,
}),
),
);

const collectors = responses.reduce(
(addresses, response) => [
...addresses,
...response.data.poaps.map((poap) => poap.collector_address),
],
[],
);
```

In this case, we are doing eight requests but in two groups requested in parallel, one of
five and one of three requests.
6 changes: 3 additions & 3 deletions packages/drops/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@poap-xyz/drops",
"version": "0.2.4",
"version": "0.2.5",
"description": "Drops module for the poap.js library",
"main": "dist/cjs/index.cjs",
"module": "dist/esm/index.mjs",
Expand Down Expand Up @@ -29,7 +29,7 @@
"node": ">=18"
},
"dependencies": {
"@poap-xyz/providers": "0.2.4",
"@poap-xyz/utils": "0.2.4"
"@poap-xyz/providers": "0.2.5",
"@poap-xyz/utils": "0.2.5"
}
}
6 changes: 3 additions & 3 deletions packages/moments/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@poap-xyz/moments",
"version": "0.2.4",
"version": "0.2.5",
"description": "Moments module for the poap.js library",
"main": "dist/cjs/index.cjs",
"module": "dist/esm/index.mjs",
Expand All @@ -26,8 +26,8 @@
"build": "rollup -c --bundleConfigAsCjs"
},
"dependencies": {
"@poap-xyz/providers": "0.2.4",
"@poap-xyz/utils": "0.2.4",
"@poap-xyz/providers": "0.2.5",
"@poap-xyz/utils": "0.2.5",
"uuid": "^9.0.0"
},
"engines": {
Expand Down
6 changes: 3 additions & 3 deletions packages/poaps/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@poap-xyz/poaps",
"version": "0.2.4",
"version": "0.2.5",
"description": "Poaps module for the poap.js library",
"main": "dist/cjs/index.cjs",
"module": "dist/esm/index.mjs",
Expand All @@ -26,8 +26,8 @@
"build": "rollup -c --bundleConfigAsCjs"
},
"dependencies": {
"@poap-xyz/providers": "0.2.4",
"@poap-xyz/utils": "0.2.4"
"@poap-xyz/providers": "0.2.5",
"@poap-xyz/utils": "0.2.5"
},
"engines": {
"node": ">=18"
Expand Down
10 changes: 6 additions & 4 deletions packages/providers/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@poap-xyz/providers",
"version": "0.2.4",
"version": "0.2.5",
"description": "Providers module for the poap.js library",
"main": "dist/cjs/index.cjs",
"module": "dist/esm/index.mjs",
Expand All @@ -26,11 +26,13 @@
"build": "rollup -c --bundleConfigAsCjs"
},
"dependencies": {
"@poap-xyz/utils": "0.2.4",
"axios": "^1.3.5"
"@poap-xyz/utils": "0.2.5",
"axios": "^1.3.5",
"lodash.chunk": "^4.2.0"
},
"devDependencies": {
"axios-mock-adapter": "^1.21.4"
"axios-mock-adapter": "^1.21.4",
"jest-fetch-mock": "^3.0.3"
},
"engines": {
"node": ">=18"
Expand Down
24 changes: 24 additions & 0 deletions packages/providers/src/core/PoapCompass/PoapCompass.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import chunk from 'lodash.chunk';
import { CompassProvider } from '../../ports/CompassProvider/CompassProvider';
import { CompassErrors } from '../../ports/CompassProvider/types/CompassErrors';
import { CompassError } from '../../ports/CompassProvider/types/CompassError';
Expand All @@ -14,6 +15,11 @@ const DEFAULT_COMPASS_BASE_URL = 'https://public.compass.poap.tech/v1/graphql';
* @implements {CompassProvider}
*/
export class PoapCompass implements CompassProvider {
/**
* Maximum number of request to do at the time in a batch.
*/
protected batchSize = 5;

private apiKey: string;
private baseUrl: string;

Expand Down Expand Up @@ -172,6 +178,24 @@ export class PoapCompass implements CompassProvider {
): Promise<{ data: D }> {
return await this.fetchGraphQL<{ data: D }>(query, variables ?? {}, signal);
}

async batch<D, V = { readonly [variable: string]: unknown }>(
query: string,
variables: V[],
signal?: AbortSignal,
): Promise<{ data: D }[]> {
const results: { data: D }[] = [];
const chunks: V[][] = chunk(variables, this.batchSize);

for (const chunk of chunks) {
const responses = await Promise.all(
chunk.map((variables) => this.request<D, V>(query, variables, signal)),
);
results.push(...responses);
}

return results;
}
}

/**
Expand Down
21 changes: 21 additions & 0 deletions packages/providers/src/ports/CompassProvider/CompassProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,31 @@ export interface CompassProvider {
* @param {AbortSignal} signal - When given, the request can be aborted with its controller.
* @returns {Promise<{ data: D }>} A Promise that resolves with the result of the query.
* @template D - The type of the result's data.
* @template V - The type of the query's variables.
*/
request<D, V = { readonly [variable: string]: unknown }>(
query: string,
variables?: null | undefined | V,
signal?: AbortSignal,
): Promise<{ data: D }>;

/**
* Batch fetch data from compass and return the deserialized responses. This
* is used to make many fetch calls at once, but in batches to avoid
* overloading the server.
*
* @function
* @name CompassProvider#batch
* @param {string} query - The query string to execute.
* @param {{ readonly [variable: string]: unknown }[]} variables - The variables to pass with the each query.
* @param {AbortSignal} [signal] - When given, the requests can be aborted with its controller.
* @returns {Promise<{ data: D }[]>} A Promise that resolves with the results of the queries.
* @template D - The type of the result's data.
* @template V - The type of the query's variables.
*/
batch<D, V = { readonly [variable: string]: unknown }>(
query: string,
variables: V[],
signal?: AbortSignal,
): Promise<{ data: D }[]>;
}
Loading

0 comments on commit 684a5f0

Please sign in to comment.