diff --git a/docs/pages/packages/utils/Queries.mdx b/docs/pages/packages/utils/Queries.mdx index e76bc132..8e02a993 100644 --- a/docs/pages/packages/utils/Queries.mdx +++ b/docs/pages/packages/utils/Queries.mdx @@ -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', } ``` @@ -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.Name, Order.ASC), + orderBy: createOrderBy(MyDropSortFields.NAME, Order.ASC), }; ``` @@ -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'), + }, +}; +``` diff --git a/packages/drops/package.json b/packages/drops/package.json index c63c87b3..6d45b7fc 100644 --- a/packages/drops/package.json +++ b/packages/drops/package.json @@ -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", @@ -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" } } diff --git a/packages/moments/package.json b/packages/moments/package.json index 0a4afee8..c20513cb 100644 --- a/packages/moments/package.json +++ b/packages/moments/package.json @@ -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", @@ -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": { diff --git a/packages/poaps/package.json b/packages/poaps/package.json index ec19716c..ee36d197 100644 --- a/packages/poaps/package.json +++ b/packages/poaps/package.json @@ -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", @@ -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" diff --git a/packages/providers/package.json b/packages/providers/package.json index 54599224..40e2c510 100644 --- a/packages/providers/package.json +++ b/packages/providers/package.json @@ -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", @@ -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": { diff --git a/packages/utils/package.json b/packages/utils/package.json index a756cf4d..fc9aa7d2 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -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", diff --git a/packages/utils/src/constants/address.ts b/packages/utils/src/constants/address.ts new file mode 100644 index 00000000..beb1658d --- /dev/null +++ b/packages/utils/src/constants/address.ts @@ -0,0 +1,2 @@ +export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; +export const DEAD_ADDRESS = '0x000000000000000000000000000000000000dead'; diff --git a/packages/utils/src/constants/index.ts b/packages/utils/src/constants/index.ts new file mode 100644 index 00000000..cef16803 --- /dev/null +++ b/packages/utils/src/constants/index.ts @@ -0,0 +1 @@ +export * from './address'; diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index c864bc3d..3198a64b 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,4 +1,5 @@ export * from './types'; +export * from './constants'; export * from './functions'; export * from './queries'; export * from './format'; diff --git a/packages/utils/src/queries/field.ts b/packages/utils/src/queries/field.ts new file mode 100644 index 00000000..64cd6600 --- /dev/null +++ b/packages/utils/src/queries/field.ts @@ -0,0 +1,17 @@ +/** + * Deep recursive field with `V` final value. + */ +type Field = { [key: string]: Field | V }; + +/** + * Creates a deep recursive object with dot separated keys and a final value. + * For example, `createField('a.b.c', 42)` will return `{ a: { b: { c: 42 } } }`. + */ +export function createField(keys: string, value: V): Field { + return keys.split('.').reduceRight( + (prev: Field | V, key: string): Field => ({ + [key]: prev, + }), + value, + ) as Field; // casted becuse keys is assumed to not be empty +} diff --git a/packages/utils/src/queries/filter.ts b/packages/utils/src/queries/filter.ts index ccb32bcc..8bee051d 100644 --- a/packages/utils/src/queries/filter.ts +++ b/packages/utils/src/queries/filter.ts @@ -1,3 +1,4 @@ +import { createField } from './field'; import { EqFilter, FieldFilter, @@ -11,26 +12,29 @@ import { NinFilter, Value, } from '../types/filter'; +import { ZERO_ADDRESS, DEAD_ADDRESS } from '../constants'; export function createLikeFilter( key: string, value?: string, ): FieldFilter> { - return value ? { [key]: { _ilike: `%${value}%` } } : {}; + return value + ? createField>(key, { _ilike: `%${value}%` }) + : {}; } export function createEqFilter( key: string, value?: V, ): FieldFilter> { - return value ? { [key]: { _eq: value } } : {}; + return value ? createField>(key, { _eq: value }) : {}; } export function createNeqFilter( key: string, value?: V, ): FieldFilter> { - return value ? { [key]: { _neq: value } } : {}; + return value ? createField>(key, { _neq: value }) : {}; } export function createBoolFilter( @@ -38,7 +42,9 @@ export function createBoolFilter( value?: boolean, ): FieldFilter> { return typeof value === 'boolean' - ? { [key]: { _eq: value ? 'true' : 'false' } } + ? createField>(key, { + _eq: value ? 'true' : 'false', + }) : {}; } @@ -47,34 +53,29 @@ export function createAddressFilter( value?: string, ): FieldFilter> { return value - ? { - [key]: { - _eq: value.toLowerCase(), - }, - } + ? createField>(key, { _eq: value.toLowerCase() }) : {}; } export function createNotNullAddressFilter( key: string, - filterZeroAddress?: boolean, - filterDeadAddress?: boolean, + filterZeroAddress = true, + filterDeadAddress = true, ): FieldFilter> | FieldFilter> { if (filterZeroAddress && filterDeadAddress) { - return { - [key]: { - _nin: [ - '0x0000000000000000000000000000000000000000', - '0x000000000000000000000000000000000000dead', - ], - }, - }; + return createField>(key, { + _nin: [ZERO_ADDRESS, DEAD_ADDRESS], + }); } if (filterZeroAddress) { - return { [key]: { _neq: '0x0000000000000000000000000000000000000000' } }; + return createField>(key, { + _neq: ZERO_ADDRESS, + }); } if (filterDeadAddress) { - return { [key]: { _neq: '0x000000000000000000000000000000000000dead' } }; + return createField>(key, { + _neq: DEAD_ADDRESS, + }); } return {}; } @@ -83,42 +84,46 @@ export function createInFilter( key: string, values?: Array, ): FieldFilter> { - return values && values.length > 0 ? { [key]: { _in: values } } : {}; + return values && values.length > 0 + ? createField>(key, { _in: values }) + : {}; } export function createNinFilter( key: string, values?: Array, ): FieldFilter> { - return values && values.length > 0 ? { [key]: { _nin: values } } : {}; + return values && values.length > 0 + ? createField>(key, { _nin: values }) + : {}; } export function createLtFilter( key: string, value?: V, ): FieldFilter> { - return value ? { [key]: { _lt: value } } : {}; + return value ? createField>(key, { _lt: value }) : {}; } export function createLteFilter( key: string, value?: V, ): FieldFilter> { - return value ? { [key]: { _lte: value } } : {}; + return value ? createField>(key, { _lte: value }) : {}; } export function createGtFilter( key: string, value?: V, ): FieldFilter> { - return value ? { [key]: { _gt: value } } : {}; + return value ? createField>(key, { _gt: value }) : {}; } export function createGteFilter( key: string, value?: V, ): FieldFilter> { - return value ? { [key]: { _gte: value } } : {}; + return value ? createField>(key, { _gte: value }) : {}; } export function createBetweenFilter( @@ -133,5 +138,10 @@ export function createBetweenFilter( if (to) { betweenFilter._lte = to; } - return from || to ? { [key]: betweenFilter } : {}; + return from || to + ? createField> & Partial>>( + key, + betweenFilter, + ) + : {}; } diff --git a/packages/utils/src/queries/order.ts b/packages/utils/src/queries/order.ts index 956aed9f..5085bc27 100644 --- a/packages/utils/src/queries/order.ts +++ b/packages/utils/src/queries/order.ts @@ -1,8 +1,9 @@ +import { createField } from './field'; import { Order, FieldOrderBy } from '../types/order'; export function createOrderBy( key: E | undefined, - value?: Order | undefined, + value?: Order, ): FieldOrderBy { - return key && value ? { [key]: value } : {}; + return key && value ? createField(key, value) : {}; } diff --git a/packages/utils/src/types/filter.ts b/packages/utils/src/types/filter.ts index 82151b7c..4023a3bb 100644 --- a/packages/utils/src/types/filter.ts +++ b/packages/utils/src/types/filter.ts @@ -47,7 +47,7 @@ export type AnyFilter = Partial & Partial; export type FieldFilter = { - [key: string]: F; + [key: string]: FieldFilter | F; }; export interface ConditionFilter { @@ -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. diff --git a/packages/utils/src/types/order.ts b/packages/utils/src/types/order.ts index bfd33a71..be9b6244 100644 --- a/packages/utils/src/types/order.ts +++ b/packages/utils/src/types/order.ts @@ -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; +export type OrderBy = FieldOrderBy | Array; /** * Order by query variables. diff --git a/packages/utils/test/queries/field.spec.ts b/packages/utils/test/queries/field.spec.ts new file mode 100644 index 00000000..45813de4 --- /dev/null +++ b/packages/utils/test/queries/field.spec.ts @@ -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 }, + }); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index bb872e5d..58aceae7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -884,8 +884,8 @@ __metadata: version: 0.0.0-use.local resolution: "@poap-xyz/drops@workspace:packages/drops" 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 languageName: unknown linkType: soft @@ -901,8 +901,8 @@ __metadata: version: 0.0.0-use.local resolution: "@poap-xyz/moments@workspace:packages/moments" 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 "@types/uuid": ^9.0.2 uuid: ^9.0.0 languageName: unknown @@ -912,22 +912,22 @@ __metadata: version: 0.0.0-use.local resolution: "@poap-xyz/poaps@workspace:packages/poaps" 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 languageName: unknown linkType: soft -"@poap-xyz/providers@*, @poap-xyz/providers@0.2.3, @poap-xyz/providers@workspace:packages/providers": +"@poap-xyz/providers@*, @poap-xyz/providers@0.2.4, @poap-xyz/providers@workspace:packages/providers": version: 0.0.0-use.local resolution: "@poap-xyz/providers@workspace:packages/providers" dependencies: - "@poap-xyz/utils": 0.2.3 + "@poap-xyz/utils": 0.2.4 axios: ^1.3.5 axios-mock-adapter: ^1.21.4 languageName: unknown linkType: soft -"@poap-xyz/utils@*, @poap-xyz/utils@0.2.3, @poap-xyz/utils@workspace:packages/utils": +"@poap-xyz/utils@*, @poap-xyz/utils@0.2.4, @poap-xyz/utils@workspace:packages/utils": version: 0.0.0-use.local resolution: "@poap-xyz/utils@workspace:packages/utils" languageName: unknown