mirror of
https://github.com/cerc-io/watcher-ts
synced 2025-01-22 19:19:05 +00:00
Implement filters for uni-info-watcher resolvers. (#180)
Co-authored-by: nabarun <nabarun@deepstacksoft.com>
This commit is contained in:
parent
d9366017da
commit
eea3a5864b
@ -28,6 +28,23 @@ import { SyncStatus } from './entity/SyncStatus';
|
||||
const DEFAULT_LIMIT = 100;
|
||||
const DEFAULT_SKIP = 0;
|
||||
|
||||
const OPERATOR_MAP = {
|
||||
equals: '=',
|
||||
gt: '>',
|
||||
lt: '<',
|
||||
gte: '>=',
|
||||
lte: '<=',
|
||||
in: 'IN',
|
||||
contains: 'LIKE',
|
||||
starts: 'LIKE',
|
||||
ends: 'LIKE'
|
||||
};
|
||||
|
||||
export interface BlockHeight {
|
||||
number?: number;
|
||||
hash?: string;
|
||||
}
|
||||
|
||||
export enum OrderDirection {
|
||||
asc = 'asc',
|
||||
desc = 'desc'
|
||||
@ -40,6 +57,14 @@ export interface QueryOptions {
|
||||
orderDirection?: OrderDirection;
|
||||
}
|
||||
|
||||
interface Where {
|
||||
[key: string]: {
|
||||
value: any;
|
||||
not: boolean;
|
||||
operator: keyof typeof OPERATOR_MAP;
|
||||
}
|
||||
}
|
||||
|
||||
export class Database {
|
||||
_config: ConnectionOptions
|
||||
_conn!: Connection
|
||||
@ -230,7 +255,8 @@ export class Database {
|
||||
where: whereOptions,
|
||||
order: {
|
||||
blockNumber: 'DESC'
|
||||
}
|
||||
},
|
||||
relations: ['pool']
|
||||
};
|
||||
|
||||
let entity = await repo.findOne(findOptions as FindOneOptions<PoolDayData>);
|
||||
@ -362,17 +388,16 @@ export class Database {
|
||||
return entity;
|
||||
}
|
||||
|
||||
async getEntities<Entity> (entity: new () => Entity, where: { [key: string]: any } = {}, queryOptions: QueryOptions = {}, relations: string[] = []): Promise<Entity[]> {
|
||||
const { blockHash, blockNumber, ...filter } = where;
|
||||
async getEntities<Entity> (entity: new () => Entity, block: BlockHeight, where: Where = {}, queryOptions: QueryOptions = {}, relations: string[] = []): Promise<Entity[]> {
|
||||
const repo = this._conn.getRepository(entity);
|
||||
const { tableName } = repo.metadata;
|
||||
|
||||
let subQuery = await repo.createQueryBuilder('subTable')
|
||||
let subQuery = repo.createQueryBuilder('subTable')
|
||||
.select('MAX(subTable.block_number)')
|
||||
.where(`subTable.id = ${tableName}.id`);
|
||||
|
||||
if (blockHash) {
|
||||
const { canonicalBlockNumber, blockHashes } = await this._getBranchInfo(blockHash);
|
||||
if (block.hash) {
|
||||
const { canonicalBlockNumber, blockHashes } = await this._getBranchInfo(block.hash);
|
||||
|
||||
subQuery = subQuery
|
||||
.andWhere(new Brackets(qb => {
|
||||
@ -381,8 +406,8 @@ export class Database {
|
||||
}));
|
||||
}
|
||||
|
||||
if (blockNumber) {
|
||||
subQuery = subQuery.andWhere('subTable.block_number <= :blockNumber', { blockNumber });
|
||||
if (block.number) {
|
||||
subQuery = subQuery.andWhere('subTable.block_number <= :blockNumber', { blockNumber: block.number });
|
||||
}
|
||||
|
||||
let selectQueryBuilder = repo.createQueryBuilder(tableName)
|
||||
@ -393,8 +418,40 @@ export class Database {
|
||||
selectQueryBuilder = selectQueryBuilder.leftJoinAndSelect(`${repo.metadata.tableName}.${relation}`, relation);
|
||||
});
|
||||
|
||||
Object.entries(filter).forEach(([field, value]) => {
|
||||
selectQueryBuilder = selectQueryBuilder.andWhere(`${tableName}.${field} = :value`, { value });
|
||||
Object.entries(where).forEach(([field, filter]) => {
|
||||
// Form the where clause.
|
||||
const { not, operator, value } = filter;
|
||||
const columnMetadata = repo.metadata.findColumnWithPropertyName(field);
|
||||
assert(columnMetadata);
|
||||
let whereClause = `${tableName}.${columnMetadata.propertyAliasName} `;
|
||||
|
||||
if (not) {
|
||||
if (operator === 'equals') {
|
||||
whereClause += '!';
|
||||
} else {
|
||||
whereClause += 'NOT ';
|
||||
}
|
||||
}
|
||||
|
||||
whereClause += `${OPERATOR_MAP[operator]} `;
|
||||
|
||||
if (['contains', 'starts'].some(el => el === operator)) {
|
||||
whereClause += '%:';
|
||||
} else if (operator === 'in') {
|
||||
whereClause += '(:...';
|
||||
} else {
|
||||
whereClause += ':';
|
||||
}
|
||||
|
||||
whereClause += 'value';
|
||||
|
||||
if (['contains', 'ends'].some(el => el === operator)) {
|
||||
whereClause += '%';
|
||||
} else if (operator === 'in') {
|
||||
whereClause += ')';
|
||||
}
|
||||
|
||||
selectQueryBuilder = selectQueryBuilder.andWhere(whereClause, { value });
|
||||
});
|
||||
|
||||
const { limit = DEFAULT_LIMIT, orderBy, orderDirection, skip = DEFAULT_SKIP } = queryOptions;
|
||||
@ -404,7 +461,9 @@ export class Database {
|
||||
.limit(limit);
|
||||
|
||||
if (orderBy) {
|
||||
selectQueryBuilder = selectQueryBuilder.orderBy(`${tableName}.${orderBy}`, orderDirection === 'desc' ? 'DESC' : 'ASC');
|
||||
const columnMetadata = repo.metadata.findColumnWithPropertyName(orderBy);
|
||||
assert(columnMetadata);
|
||||
selectQueryBuilder = selectQueryBuilder.orderBy(`${tableName}.${columnMetadata.propertyAliasName}`, orderDirection === 'desc' ? 'DESC' : 'ASC');
|
||||
}
|
||||
|
||||
return selectQueryBuilder.getMany();
|
||||
|
@ -14,7 +14,7 @@ import { convertTokenToDecimal, loadTransaction, safeDiv } from './utils';
|
||||
import { createTick } from './utils/tick';
|
||||
import Decimal from 'decimal.js';
|
||||
import { Position } from './entity/Position';
|
||||
import { Database, QueryOptions, OrderDirection } from './database';
|
||||
import { Database, QueryOptions, OrderDirection, BlockHeight } from './database';
|
||||
import { Event } from './entity/Event';
|
||||
import { ResultEvent, Block, Transaction, PoolCreatedEvent, InitializeEvent, MintEvent, BurnEvent, SwapEvent, IncreaseLiquidityEvent, DecreaseLiquidityEvent, CollectEvent, TransferEvent } from './events';
|
||||
import { Factory } from './entity/Factory';
|
||||
@ -26,7 +26,6 @@ import { Swap } from './entity/Swap';
|
||||
import { PositionSnapshot } from './entity/PositionSnapshot';
|
||||
import { SyncStatus } from './entity/SyncStatus';
|
||||
import { BlockProgress } from './entity/BlockProgress';
|
||||
import { BlockHeight } from './resolvers';
|
||||
|
||||
const log = debug('vulcanize:indexer');
|
||||
|
||||
@ -37,7 +36,7 @@ export interface ValueResult {
|
||||
}
|
||||
}
|
||||
|
||||
export { OrderDirection };
|
||||
export { OrderDirection, BlockHeight };
|
||||
|
||||
export class Indexer {
|
||||
_db: Database
|
||||
@ -203,8 +202,31 @@ export class Indexer {
|
||||
return this._db.getToken({ id, blockHash: block.hash, blockNumber: block.number });
|
||||
}
|
||||
|
||||
async getEntities<Entity> (entity: new () => Entity, where: Partial<Entity>, queryOptions: QueryOptions, relations?: string[]): Promise<Entity[]> {
|
||||
const res = await this._db.getEntities(entity, where, queryOptions, relations);
|
||||
async getEntities<Entity> (entity: new () => Entity, block: BlockHeight, where: { [key: string]: any } = {}, queryOptions: QueryOptions, relations?: string[]): Promise<Entity[]> {
|
||||
where = Object.entries(where).reduce((acc: { [key: string]: any }, [fieldWithSuffix, value]) => {
|
||||
const [field, ...suffix] = fieldWithSuffix.split('_');
|
||||
|
||||
acc[field] = {
|
||||
value,
|
||||
not: false,
|
||||
operator: 'equals'
|
||||
};
|
||||
|
||||
let operator = suffix.shift();
|
||||
|
||||
if (operator === 'not') {
|
||||
acc[field].not = true;
|
||||
operator = suffix.shift();
|
||||
}
|
||||
|
||||
if (operator) {
|
||||
acc[field].operator = operator;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const res = await this._db.getEntities(entity, block, where, queryOptions, relations);
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -368,7 +390,7 @@ export class Indexer {
|
||||
|
||||
// TODO: In subgraph factory is fetched by hardcoded factory address.
|
||||
// Currently fetching first factory in database as only one exists.
|
||||
const [factory] = await this._db.getEntities(Factory, { blockHash: block.hash }, { limit: 1 });
|
||||
const [factory] = await this._db.getEntities(Factory, { hash: block.hash }, {}, { limit: 1 });
|
||||
|
||||
const token0 = pool.token0;
|
||||
const token1 = pool.token1;
|
||||
@ -504,7 +526,7 @@ export class Indexer {
|
||||
|
||||
// TODO: In subgraph factory is fetched by hardcoded factory address.
|
||||
// Currently fetching first factory in database as only one exists.
|
||||
const [factory] = await this._db.getEntities(Factory, { blockHash: block.hash }, { limit: 1 });
|
||||
const [factory] = await this._db.getEntities(Factory, { hash: block.hash }, {}, { limit: 1 });
|
||||
|
||||
const token0 = pool.token0;
|
||||
const token1 = pool.token1;
|
||||
@ -622,7 +644,7 @@ export class Indexer {
|
||||
|
||||
// TODO: In subgraph factory is fetched by hardcoded factory address.
|
||||
// Currently fetching first factory in database as only one exists.
|
||||
const [factory] = await this._db.getEntities(Factory, { blockHash: block.hash }, { limit: 1 });
|
||||
const [factory] = await this._db.getEntities(Factory, { hash: block.hash }, {}, { limit: 1 });
|
||||
|
||||
const pool = await this._db.getPool({ id: contractAddress, blockHash: block.hash });
|
||||
assert(pool);
|
||||
|
@ -2,7 +2,7 @@ import assert from 'assert';
|
||||
import BigInt from 'apollo-type-bigint';
|
||||
import debug from 'debug';
|
||||
|
||||
import { Indexer, OrderDirection } from './indexer';
|
||||
import { Indexer, OrderDirection, BlockHeight } from './indexer';
|
||||
import { Burn } from './entity/Burn';
|
||||
import { Bundle } from './entity/Bundle';
|
||||
import { Factory } from './entity/Factory';
|
||||
@ -12,13 +12,14 @@ import { Pool } from './entity/Pool';
|
||||
import { Swap } from './entity/Swap';
|
||||
import { Tick } from './entity/Tick';
|
||||
import { Token } from './entity/Token';
|
||||
import { TokenDayData } from './entity/TokenDayData';
|
||||
import { TokenHourData } from './entity/TokenHourData';
|
||||
import { Transaction } from './entity/Transaction';
|
||||
import { UniswapDayData } from './entity/UniswapDayData';
|
||||
|
||||
const log = debug('vulcanize:resolver');
|
||||
|
||||
export interface BlockHeight {
|
||||
number?: number;
|
||||
hash?: string;
|
||||
}
|
||||
export { BlockHeight };
|
||||
|
||||
export const createResolvers = async (indexer: Indexer): Promise<any> => {
|
||||
assert(indexer);
|
||||
@ -36,25 +37,25 @@ export const createResolvers = async (indexer: Indexer): Promise<any> => {
|
||||
bundles: async (_: any, { block = {}, first }: { first: number, block: BlockHeight }) => {
|
||||
log('bundles', block, first);
|
||||
|
||||
return indexer.getEntities(Bundle, { blockHash: block.hash, blockNumber: block.number }, { limit: first });
|
||||
return indexer.getEntities(Bundle, block, {}, { limit: first });
|
||||
},
|
||||
|
||||
burns: async (_: any, { first, orderBy, orderDirection, where }: { first: number, orderBy: string, orderDirection: OrderDirection, where: Partial<Burn> }) => {
|
||||
burns: async (_: any, { first, orderBy, orderDirection, where }: { first: number, orderBy: string, orderDirection: OrderDirection, where: { [key: string]: any } }) => {
|
||||
log('burns', first, orderBy, orderDirection, where);
|
||||
|
||||
return indexer.getEntities(Burn, where, { limit: first, orderBy, orderDirection }, ['pool', 'transaction']);
|
||||
return indexer.getEntities(Burn, {}, where, { limit: first, orderBy, orderDirection }, ['pool', 'transaction']);
|
||||
},
|
||||
|
||||
factories: async (_: any, { block = {}, first }: { first: number, block: BlockHeight }) => {
|
||||
log('factories', block, first);
|
||||
|
||||
return indexer.getEntities(Factory, { blockHash: block.hash, blockNumber: block.number }, { limit: first });
|
||||
return indexer.getEntities(Factory, block, {}, { limit: first });
|
||||
},
|
||||
|
||||
mints: async (_: any, { first, orderBy, orderDirection, where }: { first: number, orderBy: string, orderDirection: OrderDirection, where: Partial<Burn> }) => {
|
||||
mints: async (_: any, { first, orderBy, orderDirection, where }: { first: number, orderBy: string, orderDirection: OrderDirection, where: { [key: string]: any } }) => {
|
||||
log('burns', first, orderBy, orderDirection, where);
|
||||
|
||||
return indexer.getEntities(Mint, where, { limit: first, orderBy, orderDirection }, ['pool', 'transaction']);
|
||||
return indexer.getEntities(Mint, {}, where, { limit: first, orderBy, orderDirection }, ['pool', 'transaction']);
|
||||
},
|
||||
|
||||
pool: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||
@ -63,32 +64,28 @@ export const createResolvers = async (indexer: Indexer): Promise<any> => {
|
||||
return indexer.getPool(id, block);
|
||||
},
|
||||
|
||||
poolDayDatas: async (_: any, { first, skip, orderBy, orderDirection, where }: { first: number, skip: number, orderBy: string, orderDirection: OrderDirection, where: Partial<Burn> }) => {
|
||||
poolDayDatas: async (_: any, { first, skip, orderBy, orderDirection, where }: { first: number, skip: number, orderBy: string, orderDirection: OrderDirection, where: { [key: string]: any } }) => {
|
||||
log('poolDayDatas', first, skip, orderBy, orderDirection, where);
|
||||
|
||||
return indexer.getEntities(PoolDayData, where, { limit: first, skip, orderBy, orderDirection });
|
||||
return indexer.getEntities(PoolDayData, {}, where, { limit: first, skip, orderBy, orderDirection });
|
||||
},
|
||||
|
||||
pools: async (_: any, { block = {}, first, orderBy, orderDirection, where = {} }: { block: BlockHeight, first: number, orderBy: string, orderDirection: OrderDirection, where: Partial<Burn> }) => {
|
||||
pools: async (_: any, { block = {}, first, orderBy, orderDirection, where = {} }: { block: BlockHeight, first: number, orderBy: string, orderDirection: OrderDirection, where: { [key: string]: any } }) => {
|
||||
log('burns', block, first, orderBy, orderDirection, where);
|
||||
where.blockHash = block.hash;
|
||||
where.blockNumber = block.number;
|
||||
|
||||
return indexer.getEntities(Pool, where, { limit: first, orderBy, orderDirection }, ['token0', 'token1']);
|
||||
return indexer.getEntities(Pool, block, where, { limit: first, orderBy, orderDirection }, ['token0', 'token1']);
|
||||
},
|
||||
|
||||
swaps: async (_: any, { first, orderBy, orderDirection, where }: { first: number, orderBy: string, orderDirection: OrderDirection, where: Partial<Burn> }) => {
|
||||
swaps: async (_: any, { first, orderBy, orderDirection, where }: { first: number, orderBy: string, orderDirection: OrderDirection, where: { [key: string]: any } }) => {
|
||||
log('swaps', first, orderBy, orderDirection, where);
|
||||
|
||||
return indexer.getEntities(Swap, where, { limit: first, orderBy, orderDirection }, ['pool', 'transaction']);
|
||||
return indexer.getEntities(Swap, {}, where, { limit: first, orderBy, orderDirection }, ['pool', 'transaction']);
|
||||
},
|
||||
|
||||
ticks: async (_: any, { block = {}, first, skip, where = {} }: { block: BlockHeight, first: number, skip: number, where: Partial<Tick> }) => {
|
||||
ticks: async (_: any, { block = {}, first, skip, where = {} }: { block: BlockHeight, first: number, skip: number, where: { [key: string]: any } }) => {
|
||||
log('ticks', block, first, skip, where);
|
||||
where.blockHash = block.hash;
|
||||
where.blockNumber = block.number;
|
||||
|
||||
return indexer.getEntities(Tick, where, { limit: first, skip });
|
||||
return indexer.getEntities(Tick, block, where, { limit: first, skip });
|
||||
},
|
||||
|
||||
token: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||
@ -97,10 +94,34 @@ export const createResolvers = async (indexer: Indexer): Promise<any> => {
|
||||
return indexer.getToken(id, block);
|
||||
},
|
||||
|
||||
tokens: async (_: any, { orderBy, orderDirection, where }: { orderBy: string, orderDirection: OrderDirection, where: Partial<Token> }) => {
|
||||
tokens: async (_: any, { orderBy, orderDirection, where }: { orderBy: string, orderDirection: OrderDirection, where: { [key: string]: any } }) => {
|
||||
log('tokens', orderBy, orderDirection, where);
|
||||
|
||||
return indexer.getEntities(Token, where, { orderBy, orderDirection });
|
||||
return indexer.getEntities(Token, {}, where, { orderBy, orderDirection });
|
||||
},
|
||||
|
||||
tokenDayDatas: async (_: any, { first, skip, orderBy, orderDirection, where }: { first: number, skip: number, orderBy: string, orderDirection: OrderDirection, where: { [key: string]: any } }) => {
|
||||
log('tokenDayDatas', first, skip, orderBy, orderDirection, where);
|
||||
|
||||
return indexer.getEntities(TokenDayData, {}, where, { limit: first, skip, orderBy, orderDirection });
|
||||
},
|
||||
|
||||
tokenHourDatas: async (_: any, { first, skip, orderBy, orderDirection, where }: { first: number, skip: number, orderBy: string, orderDirection: OrderDirection, where: { [key: string]: any } }) => {
|
||||
log('tokenDayDatas', first, skip, orderBy, orderDirection, where);
|
||||
|
||||
return indexer.getEntities(TokenHourData, {}, where, { limit: first, skip, orderBy, orderDirection });
|
||||
},
|
||||
|
||||
transactions: async (_: any, { first, orderBy, orderDirection }: { first: number, orderBy: string, orderDirection: OrderDirection}) => {
|
||||
log('transactions', first, orderBy, orderDirection);
|
||||
|
||||
return indexer.getEntities(Transaction, {}, {}, { limit: first, orderBy, orderDirection }, ['burns', 'mints', 'swaps']);
|
||||
},
|
||||
|
||||
uniswapDayDatas: async (_: any, { first, skip, orderBy, orderDirection, where }: { first: number, skip: number, orderBy: string, orderDirection: OrderDirection, where: { [key: string]: any } }) => {
|
||||
log('uniswapDayDatas', first, skip, orderBy, orderDirection, where);
|
||||
|
||||
return indexer.getEntities(UniswapDayData, {}, where, { limit: first, skip, orderBy, orderDirection });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -21,7 +21,7 @@ export const updateUniswapDayData = async (db: Database, event: { contractAddres
|
||||
|
||||
// TODO: In subgraph factory is fetched by hardcoded factory address.
|
||||
// Currently fetching first factory in database as only one exists.
|
||||
const [factory] = await db.getEntities(Factory, { blockHash: block.hash }, { limit: 1 });
|
||||
const [factory] = await db.getEntities(Factory, { hash: block.hash }, {}, { limit: 1 });
|
||||
|
||||
const dayID = Math.floor(block.timestamp / 86400); // Rounded.
|
||||
const dayStartTimestamp = dayID * 86400;
|
||||
|
Loading…
Reference in New Issue
Block a user