Skip to content

Commit

Permalink
Merge pull request #39 from kodadot/some-better-events
Browse files Browse the repository at this point in the history
⚡ Event Lists supercharged
  • Loading branch information
vikiival authored Oct 26, 2023
2 parents 1b31d00 + 0f139fa commit 550cab3
Show file tree
Hide file tree
Showing 13 changed files with 405 additions and 112 deletions.
295 changes: 245 additions & 50 deletions src/clients/SquidClient.ts

Large diffs are not rendered by default.

52 changes: 35 additions & 17 deletions src/clients/abstractClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { BaseEvent, GraphLike, GraphQuery, ObjProp, QueryProps } from '../types'
interface AbstractClient<C, T, E = BaseEvent> {
collectionById(id: string, fields?: ObjProp<C>): GraphQuery
// collectionListBy(id: string, field: KeyOf<C>, fields?: ObjProp<C>): GraphQuery
collectionCountByIssuer(issuer: string): GraphQuery ;
collectionCountByName(name: string): GraphQuery ;
collectionCountByOwner(owner: string): GraphQuery ;
collectionCountCreatedAfter(date: Date): GraphQuery ;
collectionCountByIssuer(issuer: string): GraphQuery
collectionCountByName(name: string): GraphQuery
collectionCountByOwner(owner: string): GraphQuery
collectionCountCreatedAfter(date: Date): GraphQuery
collectionListByIssuer(issuer: string, options?: QueryProps<C>): GraphQuery
collectionListByName(name: string, options?: QueryProps<C>): GraphQuery
collectionListByOwner(owner: string, options?: QueryProps<C>): GraphQuery
Expand All @@ -15,23 +15,41 @@ interface AbstractClient<C, T, E = BaseEvent> {
eventList(options?: QueryProps<E>): GraphQuery
eventListByAddress(address: string, options?: QueryProps<E>): GraphQuery
eventListByCollectionId(id: string, options?: QueryProps<E>): GraphQuery
// eventListByCollectionIdAndInteraction(id: string, interaction: string, options?: QueryProps<E>): GraphQuery
eventListByInteraction(interaction: string, options?: QueryProps<E>): GraphQuery
eventListByCollectionIdAndInteraction(id: string, interaction: string, options?: QueryProps<E>): GraphQuery
eventListByCollectionIdAndInteractionList(id: string, interactions: string[], options?: QueryProps<E>): GraphQuery
eventListByInteraction(
interaction: string,
options?: QueryProps<E>,
): GraphQuery
eventListByItemId(id: string, options?: QueryProps<E>): GraphQuery
eventListByItemIdAndInteraction(
id: string,
interaction: string,
options?: QueryProps<E>,
): GraphQuery
eventListByItemIdAndInteractionList(
id: string,
interactions: string[],
options?: QueryProps<E>,
): GraphQuery
itemById(id: string, fields?: ObjProp<T>): GraphQuery
itemCountByOwner(owner: string): GraphQuery ;
itemCountByIssuer(issuer: string): GraphQuery ;
itemCountByName(name: string): GraphQuery ;
itemCountCollectedBy(address: string): GraphQuery ;
itemCountSoldBy(address: string): GraphQuery ;
itemCountByCollectionId(id: string): GraphQuery ;
itemCountForSale(): GraphQuery ;
itemCountForSaleByCollectionId(id: string): GraphQuery ;
itemCountByCollectionIdAndOwner(id: string, owner: string): GraphQuery ;
itemCountCreatedAfter(date: Date): GraphQuery ;
itemCountByOwner(owner: string): GraphQuery
itemCountByIssuer(issuer: string): GraphQuery
itemCountByName(name: string): GraphQuery
itemCountCollectedBy(address: string): GraphQuery
itemCountSoldBy(address: string): GraphQuery
itemCountByCollectionId(id: string): GraphQuery
itemCountForSale(): GraphQuery
itemCountForSaleByCollectionId(id: string): GraphQuery
itemCountByCollectionIdAndOwner(id: string, owner: string): GraphQuery
itemCountCreatedAfter(date: Date): GraphQuery
// itemListBy(id: string, field: KeyOf<T>, fields?: ObjProp<T>): GraphQuery
itemListByCollectionId(id: string, options?: QueryProps<T>): GraphQuery
itemListByCollectionIdAndOwner(id: string, owner: string, options?: QueryProps<T>): GraphQuery
itemListByCollectionIdAndOwner(
id: string,
owner: string,
options?: QueryProps<T>,
): GraphQuery
itemListByCollectionIdList(ids: string[], options?: QueryProps<T>): GraphQuery
itemListByIssuer(issuer: string, options?: QueryProps<T>): GraphQuery
itemListByName(name: string, options?: QueryProps<T>): GraphQuery
Expand Down
69 changes: 52 additions & 17 deletions src/clients/defaults.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,63 @@
import { ObjProp, Fields, QueryOptions, BaseEvent, AbstractBase, QueryProps, QueryEntity } from '../types'

export const defaultField: ObjProp<AbstractBase> = ['id', 'createdAt', 'name', 'metadata', 'currentOwner', 'issuer']
export const defaultEventField: ObjProp<BaseEvent> = ['id', 'interaction', 'timestamp', 'caller', 'meta']
export const DEFAULT_LIMIT = 100
import {
AbstractBase,
BaseEvent,
Fields,
ObjProp,
QueryEntity,
QueryOptions,
QueryProps,
} from '../types'

export const defaultField: ObjProp<AbstractBase> = [
'id',
'createdAt',
'name',
'metadata',
'currentOwner',
'issuer',
]
export const defaultEventField: ObjProp<BaseEvent> = [
'id',
'interaction',
'timestamp',
'caller',
'meta',
]
export const DEFAULT_LIMIT = 200
// todo: add default orderBy
export const defaultQueryOptions: QueryOptions = {
limit: DEFAULT_LIMIT
limit: DEFAULT_LIMIT,
}

function hasMetaField(field: any): boolean {
return typeof field === 'string' && field === 'meta'
}

export function extendFields<T extends AbstractBase>(fields: ObjProp<T>): ObjProp<T> {
export function extendFields<T extends AbstractBase>(
fields: ObjProp<T>,
): ObjProp<T> {
const set = new Set([...defaultField, ...fields])
return [...set]
}

export function getFields<T>(fields?: ObjProp<T>, defaultList: ObjProp<T> | string[] = defaultField, replaceMetaField = true): Fields<T> {
export function getFields<T>(
fields?: ObjProp<T>,
defaultList: ObjProp<T> | string[] = defaultField,
replaceMetaField = true,
): Fields<T> {
const list = fields ?? defaultList

if (replaceMetaField) {
const metaIndex = list.findIndex(hasMetaField)

if (metaIndex !== -1) {
list.splice(metaIndex, 1, { meta: ['id', 'name', 'description', 'image', 'animationUrl', 'type'] } as any)
list.splice(
metaIndex,
1,
{
meta: ['id', 'name', 'description', 'image', 'animationUrl', 'type'],
} as any,
)
}
}
return list
Expand All @@ -36,7 +69,7 @@ export function wrapSubqueryList<T>(fields: Fields<T>): [{ nodes: Fields<T> }] {

export function optionToQuery(
options: QueryOptions,
injectDefault = true
injectDefault = true,
): string {
const final = injectDefault ? ensureOptions(options) : options
let query = ''
Expand All @@ -57,7 +90,10 @@ export function ensureOptions(options?: QueryOptions): QueryOptions {
return {
...defaultQueryOptions,
...queryOptions,
limit: Math.min(queryOptions.limit ?? DEFAULT_LIMIT, defaultQueryOptions.limit)
limit: Math.min(
queryOptions.limit ?? DEFAULT_LIMIT,
defaultQueryOptions.limit,
),
}
}

Expand Down Expand Up @@ -91,23 +127,22 @@ type GraphEntity = 'collection' | 'item' | 'event'
export const realEntityName: Record<GraphEntity, string> = {
collection: 'CollectionEntity',
item: 'NFTEntity',
event: 'Event'
event: 'Event',
}

function addWhere(whereType: string, whereValue?: any) {
if (whereValue) {
return {
where: {
type: whereType,
value: whereValue
}
value: whereValue,
},
}
}

return {}
}


type OperationName = `${GraphEntity}Count`
export function genericCountQuery(entity: GraphEntity, whereValue?: any) {
const name = entity + 'Count' as OperationName
Expand All @@ -120,8 +155,8 @@ export function genericCountQuery(entity: GraphEntity, whereValue?: any) {
fields: ['totalCount'],
variables: {
orderBy: { value: 'id_ASC', type: `[${types}OrderByInput!]!` },
...where
...where,
},
whereType: `${types}WhereInput`
whereType: `${types}WhereInput`,
}
}
16 changes: 10 additions & 6 deletions src/clients/filters.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import {
FilterBuilder, FilterMappingFn, FilterOrderDirection, FilterOrderType, FilterType
FilterBuilder,
FilterMappingFn,
FilterOrderDirection,
FilterOrderType,
FilterType,
} from '../types'

export function getFilters(filters: FilterBuilder[]) {
Expand All @@ -9,11 +13,11 @@ export function getFilters(filters: FilterBuilder[]) {

function generateFilters(
filters: FilterBuilder[],
mappingFn: FilterMappingFn
mappingFn: FilterMappingFn,
): string[] {
return filters
.flatMap(([type, directions]) =>
getFilterOrders(directions).map(direction => mappingFn(type, direction))
getFilterOrders(directions).map((direction) => mappingFn(type, direction))
)
}

Expand All @@ -28,20 +32,20 @@ function generateFilters(

function subsquidFilterMapping(
filter: FilterType,
direction: FilterOrderDirection
direction: FilterOrderDirection,
): string {
return appendFilterDirection(filter, direction)
}

function appendFilterDirection(
filter: string,
direction: FilterOrderDirection
direction: FilterOrderDirection,
) {
return filter + '_' + direction
}

function getFilterOrders(
filterOrders: FilterOrderType
filterOrders: FilterOrderType,
): FilterOrderDirection[] {
if (!filterOrders) {
return ['ASC', 'DESC']
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ export { fetchQuery, graphFetch } from './indexers'
export * from './rest'

// eslint-disable-next-line unicorn/prefer-export-from
export { SquidClient, getClient, getUrl }
export { getClient, getUrl, SquidClient }
18 changes: 13 additions & 5 deletions src/indexers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,30 @@ function getUrl(chain: Prefix, provider: Or<Provider, ''> = ''): string {

if (!result || provider === 'subquery') {
const message = chain
? `Indexer for chain ${chain} not found, allowed values are ${Object.keys(
INDEXERS
)}`
? `Indexer for chain ${chain} not found, allowed values are ${
Object.keys(
INDEXERS,
)
}`
: `Cannot use fetch for non-existinng chain, add parameter prefix when calling getClient or fetchQuery`
throw new ReferenceError(message)
}

return result
}

export function graphFetch<D>(baseURL: string, query: GraphQuery): Promise<GraphLike<D>> {
export function graphFetch<D>(
baseURL: string,
query: GraphQuery,
): Promise<GraphLike<D>> {
const opts = getOptions({ query, baseURL, path: '' })
return $fetch<GraphLike<D>>(baseURL, opts)
}

export function fetchQuery<D>(prefix: Prefix, query: GraphQuery): Promise<GraphLike<D>> {
export function fetchQuery<D>(
prefix: Prefix,
query: GraphQuery,
): Promise<GraphLike<D>> {
const baseURL = getUrl(prefix)
return graphFetch<D>(baseURL, query)
}
Expand Down
4 changes: 2 additions & 2 deletions src/indexers/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { GraphLike, GraphRequest } from '../rest/types'

export const getOptions = ({
baseURL,
query
query,
}: GraphRequest): FetchOptions<'json'> => ({
baseURL,
method: 'POST',
body: query
body: query,
})

export const uwrapRequest = <T>(req: GraphLike<T>): T => {
Expand Down
10 changes: 7 additions & 3 deletions src/queryBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { query } from 'gql-query-builder'
import { KeyValue, FieldList, GraphQuery } from './types'
import { FieldList, GraphQuery, KeyValue } from './types'

function build(operation: string, fields: FieldList, variables?: KeyValue): GraphQuery {
function build(
operation: string,
fields: FieldList,
variables?: KeyValue,
): GraphQuery {
return query({
operation,
variables,
fields
fields,
})
}

Expand Down
5 changes: 4 additions & 1 deletion src/rest/ask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import { GraphLike } from './types'

const GRAPHQL_PATH = '/graphql'

function askFor<T>(path: string, options?: QueryProps<T>): Promise<GraphLike<T>> {
function askFor<T>(
path: string,
options?: QueryProps<T>,
): Promise<GraphLike<T>> {
const request = pathToRequest(path, options)
const opts = getOptions(request)
return $fetch<GraphLike<T>>(GRAPHQL_PATH, opts)
Expand Down
3 changes: 2 additions & 1 deletion src/rest/indexers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ import { Prefix } from './types'

export const getUrl = (chain: Prefix | ''): string => INDEXERS[chain]

export const getAvailableChains = (): Prefix[] => Object.keys(INDEXERS) as Prefix[]
export const getAvailableChains = (): Prefix[] =>
Object.keys(INDEXERS) as Prefix[]
13 changes: 10 additions & 3 deletions src/rest/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const pathMap: Record<string, ClientCall> = {
itemByOwner: 'itemListByOwner',
itemCollectedBy: 'itemListCollectedBy',
itemForSaleByCollection: 'itemListForSaleByCollectionId',
itemSoldBy: 'itemListSoldBy'
itemSoldBy: 'itemListSoldBy',
}

export const parsePath = (pathname: string): string[] => {
Expand All @@ -37,7 +37,11 @@ const supportChain = (chain: string | undefined): boolean => {
}

const urlOf = (path: string): $URL => new $URL(path)
const makeQuery = (call: string, id: string, options?: QueryProps<any>): GraphQuery => {
const makeQuery = (
call: string,
id: string,
options?: QueryProps<any>,
): GraphQuery => {
const client = getClient()
const method = getCall(call)
const fn: any | undefined = client[method]
Expand All @@ -50,7 +54,10 @@ const makeQuery = (call: string, id: string, options?: QueryProps<any>): GraphQu

// /bsx/item/:id
// TODO: should return GraphRequest
export function pathToRequest(path: string, options?: QueryProps<any>): GraphRequest {
export function pathToRequest(
path: string,
options?: QueryProps<any>,
): GraphRequest {
const { pathname } = urlOf(path) // query: options
const [chain, call, id] = parsePath(pathname)
if (!hasCall(call) || !supportChain(chain)) {
Expand Down
2 changes: 1 addition & 1 deletion src/rest/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { GraphQuery, Or } from '../types'
export { GraphLike } from '../types'

export type GraphRequest = {
baseURL: string,
baseURL: string
query: GraphQuery
path: string
}
Expand Down
Loading

0 comments on commit 550cab3

Please sign in to comment.