Skip to content

Commit

Permalink
Merge pull request #115 from poap-xyz/field-expand
Browse files Browse the repository at this point in the history
Field expand
  • Loading branch information
jm42 authored Apr 22, 2024
2 parents 2648cdd + bcfcec5 commit 249adbb
Show file tree
Hide file tree
Showing 16 changed files with 133 additions and 63 deletions.
31 changes: 28 additions & 3 deletions docs/pages/packages/utils/Queries.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ And an enum that let you choose what fields you want to order by:

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

Expand All @@ -39,7 +39,7 @@ You can then build the variable `$orderBy` with `createOrderBy`:
import { Order, OrderByVariables, createOrderBy } from '@poap-xyz/utils';

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

Expand Down Expand Up @@ -79,3 +79,28 @@ The possible filters to create are:
- `createInFilter`: if the field is contained in any of the given values.
- `createNinFilter`: not contained in the given values.
- `createBetweenFilter`: from and to values, mostly used for dates.

### Use of DOT notation

All keys can be given as dot separated of nested entries when the field to filter is located inside
another entity. For example, when you have the query:

```graphql
query Collectors($where: collectors_bool_exp) {
collectors(where: $where) {
address
}
}
```

And you would like to retrieve the list of collector's addresses of a certain drop, then you can
build the variables as:

```typescript
const variables: FilterVariables = {
where: {
...createEqFilter('poaps.drop_id', 14),
...createNotNullAddressFilter('address'),
},
};
```
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.3",
"version": "0.2.4",
"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.3",
"@poap-xyz/utils": "0.2.3"
"@poap-xyz/providers": "0.2.4",
"@poap-xyz/utils": "0.2.4"
}
}
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.3",
"version": "0.2.4",
"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.3",
"@poap-xyz/utils": "0.2.3",
"@poap-xyz/providers": "0.2.4",
"@poap-xyz/utils": "0.2.4",
"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.3",
"version": "0.2.4",
"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.3",
"@poap-xyz/utils": "0.2.3"
"@poap-xyz/providers": "0.2.4",
"@poap-xyz/utils": "0.2.4"
},
"engines": {
"node": ">=18"
Expand Down
4 changes: 2 additions & 2 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.3",
"version": "0.2.4",
"description": "Providers module for the poap.js library",
"main": "dist/cjs/index.cjs",
"module": "dist/esm/index.mjs",
Expand All @@ -26,7 +26,7 @@
"build": "rollup -c --bundleConfigAsCjs"
},
"dependencies": {
"@poap-xyz/utils": "0.2.3",
"@poap-xyz/utils": "0.2.4",
"axios": "^1.3.5"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/utils/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@poap-xyz/utils",
"version": "0.2.3",
"version": "0.2.4",
"description": "Utils module for the poap.js library",
"main": "dist/cjs/index.cjs",
"module": "dist/esm/index.mjs",
Expand Down
2 changes: 2 additions & 0 deletions packages/utils/src/constants/address.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
export const DEAD_ADDRESS = '0x000000000000000000000000000000000000dead';
1 change: 1 addition & 0 deletions packages/utils/src/constants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './address';
1 change: 1 addition & 0 deletions packages/utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './types';
export * from './constants';
export * from './functions';
export * from './queries';
export * from './format';
Expand Down
17 changes: 17 additions & 0 deletions packages/utils/src/queries/field.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Deep recursive field with `V` final value.
*/
type Field<V> = { [key: string]: Field<V> | V };

/**
* Creates a deep recursive object with dot separated keys and a final value.
* For example, `createField<number>('a.b.c', 42)` will return `{ a: { b: { c: 42 } } }`.
*/
export function createField<V>(keys: string, value: V): Field<V> {
return keys.split('.').reduceRight(
(prev: Field<V> | V, key: string): Field<V> => ({
[key]: prev,
}),
value,
) as Field<V>; // casted becuse keys is assumed to not be empty
}
66 changes: 38 additions & 28 deletions packages/utils/src/queries/filter.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createField } from './field';
import {
EqFilter,
FieldFilter,
Expand All @@ -11,34 +12,39 @@ import {
NinFilter,
Value,
} from '../types/filter';
import { ZERO_ADDRESS, DEAD_ADDRESS } from '../constants';

export function createLikeFilter(
key: string,
value?: string,
): FieldFilter<LikeFilter<string>> {
return value ? { [key]: { _ilike: `%${value}%` } } : {};
return value
? createField<LikeFilter<string>>(key, { _ilike: `%${value}%` })
: {};
}

export function createEqFilter<V = Value>(
key: string,
value?: V,
): FieldFilter<EqFilter<V>> {
return value ? { [key]: { _eq: value } } : {};
return value ? createField<EqFilter<V>>(key, { _eq: value }) : {};
}

export function createNeqFilter<V = Value>(
key: string,
value?: V,
): FieldFilter<NeqFilter<V>> {
return value ? { [key]: { _neq: value } } : {};
return value ? createField<NeqFilter<V>>(key, { _neq: value }) : {};
}

export function createBoolFilter(
key: string,
value?: boolean,
): FieldFilter<EqFilter<'true' | 'false'>> {
return typeof value === 'boolean'
? { [key]: { _eq: value ? 'true' : 'false' } }
? createField<EqFilter<'true' | 'false'>>(key, {
_eq: value ? 'true' : 'false',
})
: {};
}

Expand All @@ -47,34 +53,29 @@ export function createAddressFilter(
value?: string,
): FieldFilter<EqFilter<string>> {
return value
? {
[key]: {
_eq: value.toLowerCase(),
},
}
? createField<EqFilter<string>>(key, { _eq: value.toLowerCase() })
: {};
}

export function createNotNullAddressFilter(
key: string,
filterZeroAddress?: boolean,
filterDeadAddress?: boolean,
filterZeroAddress = true,
filterDeadAddress = true,
): FieldFilter<NeqFilter<string>> | FieldFilter<NinFilter<string>> {
if (filterZeroAddress && filterDeadAddress) {
return {
[key]: {
_nin: [
'0x0000000000000000000000000000000000000000',
'0x000000000000000000000000000000000000dead',
],
},
};
return createField<NinFilter<string>>(key, {
_nin: [ZERO_ADDRESS, DEAD_ADDRESS],
});
}
if (filterZeroAddress) {
return { [key]: { _neq: '0x0000000000000000000000000000000000000000' } };
return createField<NeqFilter<string>>(key, {
_neq: ZERO_ADDRESS,
});
}
if (filterDeadAddress) {
return { [key]: { _neq: '0x000000000000000000000000000000000000dead' } };
return createField<NeqFilter<string>>(key, {
_neq: DEAD_ADDRESS,
});
}
return {};
}
Expand All @@ -83,42 +84,46 @@ export function createInFilter<V = Value>(
key: string,
values?: Array<V>,
): FieldFilter<InFilter<V>> {
return values && values.length > 0 ? { [key]: { _in: values } } : {};
return values && values.length > 0
? createField<InFilter<V>>(key, { _in: values })
: {};
}

export function createNinFilter<V = Value>(
key: string,
values?: Array<V>,
): FieldFilter<NinFilter<V>> {
return values && values.length > 0 ? { [key]: { _nin: values } } : {};
return values && values.length > 0
? createField<NinFilter<V>>(key, { _nin: values })
: {};
}

export function createLtFilter<V = Value>(
key: string,
value?: V,
): FieldFilter<LtFilter<V>> {
return value ? { [key]: { _lt: value } } : {};
return value ? createField<LtFilter<V>>(key, { _lt: value }) : {};
}

export function createLteFilter<V = Value>(
key: string,
value?: V,
): FieldFilter<LteFilter<V>> {
return value ? { [key]: { _lte: value } } : {};
return value ? createField<LteFilter<V>>(key, { _lte: value }) : {};
}

export function createGtFilter<V = Value>(
key: string,
value?: V,
): FieldFilter<GtFilter<V>> {
return value ? { [key]: { _gt: value } } : {};
return value ? createField<GtFilter<V>>(key, { _gt: value }) : {};
}

export function createGteFilter<V = Value>(
key: string,
value?: V,
): FieldFilter<GteFilter<V>> {
return value ? { [key]: { _gte: value } } : {};
return value ? createField<GteFilter<V>>(key, { _gte: value }) : {};
}

export function createBetweenFilter<V = Value>(
Expand All @@ -133,5 +138,10 @@ export function createBetweenFilter<V = Value>(
if (to) {
betweenFilter._lte = to;
}
return from || to ? { [key]: betweenFilter } : {};
return from || to
? createField<Partial<GteFilter<V>> & Partial<LteFilter<V>>>(
key,
betweenFilter,
)
: {};
}
5 changes: 3 additions & 2 deletions packages/utils/src/queries/order.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { createField } from './field';
import { Order, FieldOrderBy } from '../types/order';

export function createOrderBy<E extends string = string>(
key: E | undefined,
value?: Order | undefined,
value?: Order,
): FieldOrderBy {
return key && value ? { [key]: value } : {};
return key && value ? createField<Order>(key, value) : {};
}
7 changes: 5 additions & 2 deletions packages/utils/src/types/filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export type AnyFilter = Partial<EqFilter> &
Partial<LteFilter>;

export type FieldFilter<F = AnyFilter> = {
[key: string]: F;
[key: string]: FieldFilter<F> | F;
};

export interface ConditionFilter {
Expand All @@ -56,7 +56,10 @@ export interface ConditionFilter {
_not?: Filter;
}

export type Filter = FieldFilter | ConditionFilter | { [key: string]: Filter };
export type Filter =
| ConditionFilter
| FieldFilter
| { [key: string]: ConditionFilter | FieldFilter };

/**
* Filter query variables.
Expand Down
9 changes: 2 additions & 7 deletions packages/utils/src/types/order.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,9 @@ export enum Order {
DESC = 'desc',
}

export interface FieldOrderBy {
[key: string]: Order;
}
export type FieldOrderBy = { [key: string]: FieldOrderBy | Order };

export type OrderBy =
| FieldOrderBy
| { [key: string]: FieldOrderBy }
| Array<OrderBy>;
export type OrderBy = FieldOrderBy | Array<FieldOrderBy>;

/**
* Order by query variables.
Expand Down
15 changes: 15 additions & 0 deletions packages/utils/test/queries/field.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createField } from '../../src/queries/field';

describe('field', () => {
describe('createField', () => {
it('returns a field with given value', () => {
expect(createField('field_name', 42)).toEqual({ field_name: 42 });
});

it('returns a field with another field inside when keys are given separated by dots', () => {
expect(createField('field_name.sub_field', 42)).toEqual({
field_name: { sub_field: 42 },
});
});
});
});
Loading

0 comments on commit 249adbb

Please sign in to comment.