Skip to content

Commit

Permalink
Merge pull request #110 from poap-xyz/compass-variables-type
Browse files Browse the repository at this point in the history
Compass variables type
  • Loading branch information
jm42 authored Apr 12, 2024
2 parents 428d1ed + dc12f50 commit bd904e5
Show file tree
Hide file tree
Showing 45 changed files with 540 additions and 276 deletions.
3 changes: 2 additions & 1 deletion docs/pages/_meta.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"index": "Introduction",
"packages": "Packages"
"packages": "Packages",
"changelog": "Changelog"
}
11 changes: 11 additions & 0 deletions docs/pages/changelog.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Changelog

## 0.2.0

- Change types where before there was `any` to be strict.
- Removes `HttpProvider`.
- Rename query utils to create filters and order by.
- Compass response type must not include `data` anymore.
- Compass variables can be typed.
- Compass throws errors on malformed requests/queries.
- Fetch moments order must be value from enum `Order` instead of string.
88 changes: 88 additions & 0 deletions docs/pages/packages/providers/Compass.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Compass Provider

To query Compass, a simple request wrapper is provided. To initialize it you'll need an API key.

```typescript
import { PoapCompass } from '@poap-xyz/providers';

const compass = new PoapCompass({
apiKey: 'my-secret-api-key',
});
```

## Requests

Query requests can be made by giving the query in string and optionally the variables to pass. All
success requests are returned as an object with a `data` property which the structure differs
depending on the query done. When the request fail, errors are thrown instead.

### Quering

To do a query request, a response data type is needed and, in the case the query accepts variables,
a type for its structure is also needed.

For example, if we want to retrieve the last POAP token ids we could have the query:

```typescript
const query = `
query LastTokenIds($limit: Int!) {
poaps(limit: $limit, order_by: { id: desc }) {
id
}
}
`;
```

Which will return the type:

```typescript
type LastTokenIdsResponse = {
poaps: Array<{
id: number;
}>;
};
```

And have the variables type:

```typescript
type LastTokenIdsVariables = {
limit: number;
};
```

Then the query can be executed:

```typescript
const lastTokenIds: number[] = [];
try {
const { data } = await compass.request<
LastTokenIdsResponse,
LastTokenIdsVariables
>(query, { limit: 3 });

lastTokenIds.push(
...data.poaps.map((poap) => poap.id)
);
} catch (error: unknown) {
console.error(error)
}
```

### Errors

There are two types of errors, HTTP errors and requests errors. The former are thrown when there is
an issue with the HTTP requests itself and the later when the query has some malformed or
unavailable structure.

#### HTTP errors

When the HTTP request is malformed a `CompassBadRequestError` is thrown, which should not happen
unless there is a migration to be made. When the API key given is incorrect or expired, then a
`CompassUnauthorizedError` error will be thrown.

#### Query errors

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.
3 changes: 3 additions & 0 deletions docs/pages/packages/providers/_meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"Compass": "Compass"
}
77 changes: 77 additions & 0 deletions docs/pages/packages/utils/Queries.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Queries Utils

When building Compass queries, it might be useful to have some little utilities to not repeat the
same code many times. There two types of utils for queries, building orders and building filters.

## Order By

When you have a query that accepts an `$orderBy` variable that let you input the field name and the
sort order, for example:

```graphql
query MyDrops(
$orderBy: [drops_order_by!]
$where: {
drop_id: { _in: [14] }
}
) {
drops(order_by: $orderBy) {
id
name
}
}
```

And an enum that let you choose what fields you want to order by:

```typescript
enum MyDropSortFields {
Id = 'id',
Name = 'name',
}
```

You can then build the variable `$orderBy` with `createOrderBy`:

```typescript
import { Order, OrderByVariables, createOrderBy } from '@poap-xyz/utils';

const variables: OrderByVariables = {
orderBy: createOrderBy<MyDropSortFields>(MyDropSortFields.Name, Order.ASC),
};
```

## Filters

There are many filters with different types and for different results, normally you will have a
query that accepts a `$where` variable and then you can construct the variables with the use of the
filters utils.

For example:

```typescript
import {
FilterVariables,
createBoolFilter,
createLikeFilter,
createBetweenFilter,
} from '@poap-xyz/utils';

const variables: FilterVariables = {
where: {
...createBoolFilter('private', true),
...createLikeFilter('name', 'My Awesome POAP'),
...createBetweenFilter('created_date', '2023-12-23', '2024-02-28'),
},
};
```

The possible filters to create are:

- `createLikeFilter`: searchs for the value to be included case-unsensitive in the field.
- `createEqFilter`: match exact field value.
- `createNeqFilter`: match not equal field value.
- `createBoolFilter`: when the value is true or false.
- `createAddressFilter`: given the address match it case-unsensitive and/or excludes the zero address.
- `createInFilter`: if the field is contained in any of the given values.
- `createBetweenFilter`: from and to values, mostly used for dates.
3 changes: 3 additions & 0 deletions docs/pages/packages/utils/_meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"Queries": "Queries"
}
4 changes: 3 additions & 1 deletion examples/moments/backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ async function main(): Promise<void> {
});

// We use Compass for read operations
const compass = new PoapCompass('your_api_key');
const compass = new PoapCompass({
apiKey: 'your_api_key',
});

// Use your library here
const client = new MomentsClient(momentsApi, compass);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FetchMomentsInput, Moment, MomentsClient } from '@poap-xyz/moments';
import { PaginatedResult } from '@poap-xyz/utils';
import { Order, PaginatedResult } from '@poap-xyz/utils';

export const fetch_moments_by_drop_ids = async (
client: MomentsClient,
Expand All @@ -9,7 +9,7 @@ export const fetch_moments_by_drop_ids = async (
offset: 0,
limit: 10,
drop_ids: [1],
idOrder: 'desc',
idOrder: Order.DESC,
};
console.log(input);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { FetchMomentsInput, Moment, MomentsClient } from '@poap-xyz/moments';
import { PaginatedResult } from '@poap-xyz/utils';
import { Order, PaginatedResult } from '@poap-xyz/utils';

export const fetch_multiple_moments = async (
client: MomentsClient,
): Promise<void> => {
const input: FetchMomentsInput = {
offset: 0,
limit: 10,
idOrder: 'desc',
idOrder: Order.DESC,
};

try {
Expand Down
4 changes: 2 additions & 2 deletions examples/moments/backend/src/methods/fetch_single_moment.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FetchMomentsInput, Moment, MomentsClient } from '@poap-xyz/moments';
import { PaginatedResult } from '@poap-xyz/utils';
import { Order, PaginatedResult } from '@poap-xyz/utils';

export const fetch_single_moment = async (
client: MomentsClient,
Expand All @@ -8,7 +8,7 @@ export const fetch_single_moment = async (
offset: 0,
limit: 10,
id: '7284219b-1bc7-43b8-ab27-44749bdd91e1',
idOrder: 'desc',
idOrder: Order.DESC,
};
try {
const data: PaginatedResult<Moment> = await client.fetch(input);
Expand Down
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.1.8",
"version": "0.2.0",
"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.1.8",
"@poap-xyz/utils": "0.1.8"
"@poap-xyz/providers": "0.2.0",
"@poap-xyz/utils": "0.2.0"
}
}
45 changes: 27 additions & 18 deletions packages/drops/src/DropsClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,38 @@ import {
DropResponse as ProviderDropResponse,
} from '@poap-xyz/providers';
import { Drop } from './domain/Drop';
import { PaginatedDropsResponse, PAGINATED_DROPS_QUERY } from './queries';
import {
PAGINATED_DROPS_QUERY,
PaginatedDropsResponse,
PaginatedDropsVariables,
} from './queries/PaginatedDrop';
import {
CreateDropsInput,
DropImageResponse,
DropResponse,
DropsSortFields,
FetchDropsInput,
SearchDropsInput,
UpdateDropsInput,
} from './types';
import {
PaginatedResult,
nextCursor,
creatPrivateFilter,
createUndefinedOrder,
createBetweenFilter,
createFilter,
createInFilter,
Order,
isNumeric,
removeSpecialCharacters,
createOrderBy,
createBoolFilter,
createLikeFilter,
} from '@poap-xyz/utils';
import { DropImage } from './types/dropImage';
import { SEARCH_DROPS_QUERY, SearchDropsResponse } from './queries/SearchDrops';
import {
SEARCH_DROPS_QUERY,
SearchDropsResponse,
SearchDropsVariables,
} from './queries/SearchDrops';

/**
* Represents a client for working with POAP drops.
Expand Down Expand Up @@ -67,22 +76,22 @@ export class DropsClient {
isPrivate,
} = input;

const variables = {
const variables: PaginatedDropsVariables = {
limit,
offset,
orderBy: createUndefinedOrder(sortField, sortDir),
orderBy: createOrderBy<DropsSortFields>(sortField, sortDir),
where: {
...creatPrivateFilter('private', isPrivate),
...createFilter('name', name),
...createBoolFilter('private', isPrivate),
...createLikeFilter('name', name),
...createBetweenFilter('created_date', from, to),
...createInFilter('id', ids),
},
};

const { data } = await this.compassProvider.request<PaginatedDropsResponse>(
PAGINATED_DROPS_QUERY,
variables,
);
const { data } = await this.compassProvider.request<
PaginatedDropsResponse,
PaginatedDropsVariables
>(PAGINATED_DROPS_QUERY, variables);

const drops = data.drops.map(
(drop: DropResponse): Drop => this.mapDrop(drop),
Expand All @@ -109,7 +118,7 @@ export class DropsClient {
return new PaginatedResult<Drop>([], null);
}

const variables = {
const variables: SearchDropsVariables = {
limit,
offset,
...(isNumeric(search) && { orderBy: { id: Order.ASC } }),
Expand All @@ -118,10 +127,10 @@ export class DropsClient {
},
};

const { data } = await this.compassProvider.request<SearchDropsResponse>(
SEARCH_DROPS_QUERY,
variables,
);
const { data } = await this.compassProvider.request<
SearchDropsResponse,
SearchDropsVariables
>(SEARCH_DROPS_QUERY, variables);

const drops = data.search_drops.map(
(drop: DropResponse): Drop => this.mapDrop(drop),
Expand Down
13 changes: 10 additions & 3 deletions packages/drops/src/queries/PaginatedDrop.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import {
FilterVariables,
OrderByVariables,
PaginatedVariables,
} from '@poap-xyz/utils';
import { DropResponse } from '../types/DropResponse';

export const PAGINATED_DROPS_QUERY = `
Expand Down Expand Up @@ -49,7 +54,9 @@ export const PAGINATED_DROPS_QUERY = `
`;

export interface PaginatedDropsResponse {
data: {
drops: DropResponse[];
};
drops: DropResponse[];
}

export type PaginatedDropsVariables = FilterVariables &
OrderByVariables &
PaginatedVariables;
Loading

0 comments on commit bd904e5

Please sign in to comment.