mirror of
https://github.com/cerc-io/watcher-ts
synced 2024-11-19 20:36:19 +00:00
Handle type and field name conflicts for eden-watcher subgraph entities (#50)
* Rename Block, Transaction types and add block query in eden-watcher schema * Handle field name conflicts in eden subgraph entities * Resolve entity field name conflicts while sending data for auto-diff
This commit is contained in:
parent
fd86b4d5e3
commit
158c3928c9
@ -3,6 +3,7 @@
|
||||
//
|
||||
|
||||
import { Entity, PrimaryColumn, Column } from 'typeorm';
|
||||
import { bigintTransformer } from '@vulcanize/util';
|
||||
|
||||
@Entity()
|
||||
export class ProducerRewardCollectorChange {
|
||||
@ -15,6 +16,9 @@ export class ProducerRewardCollectorChange {
|
||||
@Column('integer')
|
||||
blockNumber!: number;
|
||||
|
||||
@Column('bigint', { transformer: bigintTransformer })
|
||||
_blockNumber!: bigint;
|
||||
|
||||
@Column('varchar')
|
||||
producer!: string;
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
//
|
||||
|
||||
import { Entity, PrimaryColumn, Column } from 'typeorm';
|
||||
import { bigintTransformer } from '@vulcanize/util';
|
||||
|
||||
enum ProducerSetChangeType {
|
||||
Added,
|
||||
@ -20,6 +21,9 @@ export class ProducerSetChange {
|
||||
@Column('integer')
|
||||
blockNumber!: number;
|
||||
|
||||
@Column('bigint', { transformer: bigintTransformer })
|
||||
_blockNumber!: bigint;
|
||||
|
||||
@Column('varchar')
|
||||
producer!: string;
|
||||
|
||||
|
@ -524,7 +524,7 @@ export class Indexer implements IndexerInterface {
|
||||
return (ipfsAddr !== undefined && ipfsAddr !== null && ipfsAddr !== '');
|
||||
}
|
||||
|
||||
async getSubgraphEntity<Entity> (entity: new () => Entity, id: string, blockHash: string): Promise<Entity | undefined> {
|
||||
async getSubgraphEntity<Entity> (entity: new () => Entity, id: string, blockHash: string): Promise<any> {
|
||||
return this._graphWatcher.getEntity(entity, id, blockHash);
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ import { ProducerRewardCollectorChange } from './entity/ProducerRewardCollectorC
|
||||
import { RewardScheduleEntry } from './entity/RewardScheduleEntry';
|
||||
import { RewardSchedule } from './entity/RewardSchedule';
|
||||
import { ProducerEpoch } from './entity/ProducerEpoch';
|
||||
import { Block } from './entity/Block';
|
||||
import { Epoch } from './entity/Epoch';
|
||||
import { SlotClaim } from './entity/SlotClaim';
|
||||
import { Slot } from './entity/Slot';
|
||||
@ -58,109 +59,109 @@ export const createResolvers = async (indexer: Indexer, eventWatcher: EventWatch
|
||||
},
|
||||
|
||||
Query: {
|
||||
producer: async (_: any, { id, blockHash }: { id: string, blockHash: string }): Promise<Producer | undefined> => {
|
||||
producer: async (_: any, { id, blockHash }: { id: string, blockHash: string }) => {
|
||||
log('producer', id, blockHash);
|
||||
|
||||
return indexer.getSubgraphEntity(Producer, id, blockHash);
|
||||
},
|
||||
|
||||
producerSet: async (_: any, { id, blockHash }: { id: string, blockHash: string }): Promise<ProducerSet | undefined> => {
|
||||
producerSet: async (_: any, { id, blockHash }: { id: string, blockHash: string }) => {
|
||||
log('producerSet', id, blockHash);
|
||||
|
||||
return indexer.getSubgraphEntity(ProducerSet, id, blockHash);
|
||||
},
|
||||
|
||||
producerSetChange: async (_: any, { id, blockHash }: { id: string, blockHash: string }): Promise<ProducerSetChange | undefined> => {
|
||||
producerSetChange: async (_: any, { id, blockHash }: { id: string, blockHash: string }) => {
|
||||
log('producerSetChange', id, blockHash);
|
||||
|
||||
return indexer.getSubgraphEntity(ProducerSetChange, id, blockHash);
|
||||
},
|
||||
|
||||
producerRewardCollectorChange: async (_: any, { id, blockHash }: { id: string, blockHash: string }): Promise<ProducerRewardCollectorChange | undefined> => {
|
||||
producerRewardCollectorChange: async (_: any, { id, blockHash }: { id: string, blockHash: string }) => {
|
||||
log('producerRewardCollectorChange', id, blockHash);
|
||||
|
||||
return indexer.getSubgraphEntity(ProducerRewardCollectorChange, id, blockHash);
|
||||
},
|
||||
|
||||
rewardScheduleEntry: async (_: any, { id, blockHash }: { id: string, blockHash: string }): Promise<RewardScheduleEntry | undefined> => {
|
||||
rewardScheduleEntry: async (_: any, { id, blockHash }: { id: string, blockHash: string }) => {
|
||||
log('rewardScheduleEntry', id, blockHash);
|
||||
|
||||
return indexer.getSubgraphEntity(RewardScheduleEntry, id, blockHash);
|
||||
},
|
||||
|
||||
rewardSchedule: async (_: any, { id, blockHash }: { id: string, blockHash: string }): Promise<RewardSchedule | undefined> => {
|
||||
rewardSchedule: async (_: any, { id, blockHash }: { id: string, blockHash: string }) => {
|
||||
log('rewardSchedule', id, blockHash);
|
||||
|
||||
return indexer.getSubgraphEntity(RewardSchedule, id, blockHash);
|
||||
},
|
||||
|
||||
producerEpoch: async (_: any, { id, blockHash }: { id: string, blockHash: string }): Promise<ProducerEpoch | undefined> => {
|
||||
producerEpoch: async (_: any, { id, blockHash }: { id: string, blockHash: string }) => {
|
||||
log('producerEpoch', id, blockHash);
|
||||
|
||||
return indexer.getSubgraphEntity(ProducerEpoch, id, blockHash);
|
||||
},
|
||||
|
||||
// block: async (_: any, { id, blockHash }: { id: string, blockHash: string }): Promise<Block | undefined> => {
|
||||
// log('block', id, blockHash);
|
||||
block: async (_: any, { id, blockHash }: { id: string, blockHash: string }) => {
|
||||
log('block', id, blockHash);
|
||||
|
||||
// return indexer.getSubgraphEntity(Block, id, blockHash);
|
||||
// },
|
||||
return indexer.getSubgraphEntity(Block, id, blockHash);
|
||||
},
|
||||
|
||||
epoch: async (_: any, { id, blockHash }: { id: string, blockHash: string }): Promise<Epoch | undefined> => {
|
||||
epoch: async (_: any, { id, blockHash }: { id: string, blockHash: string }) => {
|
||||
log('epoch', id, blockHash);
|
||||
|
||||
return indexer.getSubgraphEntity(Epoch, id, blockHash);
|
||||
},
|
||||
|
||||
slotClaim: async (_: any, { id, blockHash }: { id: string, blockHash: string }): Promise<SlotClaim | undefined> => {
|
||||
slotClaim: async (_: any, { id, blockHash }: { id: string, blockHash: string }) => {
|
||||
log('slotClaim', id, blockHash);
|
||||
|
||||
return indexer.getSubgraphEntity(SlotClaim, id, blockHash);
|
||||
},
|
||||
|
||||
slot: async (_: any, { id, blockHash }: { id: string, blockHash: string }): Promise<Slot | undefined> => {
|
||||
slot: async (_: any, { id, blockHash }: { id: string, blockHash: string }) => {
|
||||
log('slot', id, blockHash);
|
||||
|
||||
return indexer.getSubgraphEntity(Slot, id, blockHash);
|
||||
},
|
||||
|
||||
staker: async (_: any, { id, blockHash }: { id: string, blockHash: string }): Promise<Staker | undefined> => {
|
||||
staker: async (_: any, { id, blockHash }: { id: string, blockHash: string }) => {
|
||||
log('staker', id, blockHash);
|
||||
|
||||
return indexer.getSubgraphEntity(Staker, id, blockHash);
|
||||
},
|
||||
|
||||
network: async (_: any, { id, blockHash }: { id: string, blockHash: string }): Promise<Network | undefined> => {
|
||||
network: async (_: any, { id, blockHash }: { id: string, blockHash: string }) => {
|
||||
log('network', id, blockHash);
|
||||
|
||||
return indexer.getSubgraphEntity(Network, id, blockHash);
|
||||
},
|
||||
|
||||
distributor: async (_: any, { id, blockHash }: { id: string, blockHash: string }): Promise<Distributor | undefined> => {
|
||||
distributor: async (_: any, { id, blockHash }: { id: string, blockHash: string }) => {
|
||||
log('distributor', id, blockHash);
|
||||
|
||||
return indexer.getSubgraphEntity(Distributor, id, blockHash);
|
||||
},
|
||||
|
||||
distribution: async (_: any, { id, blockHash }: { id: string, blockHash: string }): Promise<Distribution | undefined> => {
|
||||
distribution: async (_: any, { id, blockHash }: { id: string, blockHash: string }) => {
|
||||
log('distribution', id, blockHash);
|
||||
|
||||
return indexer.getSubgraphEntity(Distribution, id, blockHash);
|
||||
},
|
||||
|
||||
claim: async (_: any, { id, blockHash }: { id: string, blockHash: string }): Promise<Claim | undefined> => {
|
||||
claim: async (_: any, { id, blockHash }: { id: string, blockHash: string }) => {
|
||||
log('claim', id, blockHash);
|
||||
|
||||
return indexer.getSubgraphEntity(Claim, id, blockHash);
|
||||
},
|
||||
|
||||
slash: async (_: any, { id, blockHash }: { id: string, blockHash: string }): Promise<Slash | undefined> => {
|
||||
slash: async (_: any, { id, blockHash }: { id: string, blockHash: string }) => {
|
||||
log('slash', id, blockHash);
|
||||
|
||||
return indexer.getSubgraphEntity(Slash, id, blockHash);
|
||||
},
|
||||
|
||||
account: async (_: any, { id, blockHash }: { id: string, blockHash: string }): Promise<Account | undefined> => {
|
||||
account: async (_: any, { id, blockHash }: { id: string, blockHash: string }) => {
|
||||
log('account', id, blockHash);
|
||||
|
||||
return indexer.getSubgraphEntity(Account, id, blockHash);
|
||||
|
@ -24,7 +24,7 @@ type ResultBigInt {
|
||||
proof: Proof
|
||||
}
|
||||
|
||||
type Block {
|
||||
type _Block_ {
|
||||
cid: String!
|
||||
hash: String!
|
||||
number: Int!
|
||||
@ -32,7 +32,7 @@ type Block {
|
||||
parentHash: String!
|
||||
}
|
||||
|
||||
type Transaction {
|
||||
type _Transaction_ {
|
||||
hash: String!
|
||||
index: Int!
|
||||
from: String!
|
||||
@ -40,8 +40,8 @@ type Transaction {
|
||||
}
|
||||
|
||||
type ResultEvent {
|
||||
block: Block!
|
||||
tx: Transaction!
|
||||
block: _Block_!
|
||||
tx: _Transaction_!
|
||||
contract: String!
|
||||
eventIndex: Int!
|
||||
event: Event!
|
||||
@ -204,7 +204,7 @@ type RoleRevokedEvent {
|
||||
}
|
||||
|
||||
type ResultIPLDBlock {
|
||||
block: Block!
|
||||
block: _Block_!
|
||||
contractAddress: String!
|
||||
cid: String!
|
||||
kind: String!
|
||||
@ -221,6 +221,7 @@ type Query {
|
||||
rewardScheduleEntry(id: String!, blockHash: String!): RewardScheduleEntry!
|
||||
rewardSchedule(id: String!, blockHash: String!): RewardSchedule!
|
||||
producerEpoch(id: String!, blockHash: String!): ProducerEpoch!
|
||||
block(id: String!, blockHash: String!): Block!
|
||||
epoch(id: String!, blockHash: String!): Epoch!
|
||||
slotClaim(id: String!, blockHash: String!): SlotClaim!
|
||||
slot(id: String!, blockHash: String!): Slot!
|
||||
@ -283,6 +284,25 @@ type RewardSchedule {
|
||||
activeRewardScheduleEntry: RewardScheduleEntry
|
||||
}
|
||||
|
||||
type Block {
|
||||
id: ID!
|
||||
fromActiveProducer: Boolean!
|
||||
hash: String!
|
||||
parentHash: String!
|
||||
unclesHash: String!
|
||||
author: String!
|
||||
stateRoot: String!
|
||||
transactionsRoot: String!
|
||||
receiptsRoot: String!
|
||||
number: BigInt!
|
||||
gasUsed: BigInt!
|
||||
gasLimit: BigInt!
|
||||
timestamp: BigInt!
|
||||
difficulty: BigInt!
|
||||
totalDifficulty: BigInt!
|
||||
size: BigInt
|
||||
}
|
||||
|
||||
type Epoch {
|
||||
id: ID!
|
||||
finalized: Boolean!
|
||||
|
@ -83,7 +83,7 @@ export class Database {
|
||||
const entityValuePromises = entityFields.filter(field => {
|
||||
const { propertyName } = field;
|
||||
|
||||
// TODO: Will clash if entity has blockHash and blockNumber fields.
|
||||
// Filter out blockHash and blockNumber from entity fields to fill the entityInstance (wasm).
|
||||
if (propertyName === 'blockHash' || propertyName === 'blockNumber') {
|
||||
return false;
|
||||
}
|
||||
@ -92,6 +92,11 @@ export class Database {
|
||||
}).map(async (field) => {
|
||||
const { type, propertyName } = field;
|
||||
|
||||
// Fill _blockNumber as blockNumber and _blockHash as blockHash in the entityInstance (wasm).
|
||||
if (['_blockNumber', '_blockHash'].includes(propertyName)) {
|
||||
return toEntityValue(instanceExports, entityInstance, data, type.toString(), propertyName.slice(1));
|
||||
}
|
||||
|
||||
return toEntityValue(instanceExports, entityInstance, data, type.toString(), propertyName);
|
||||
}, {});
|
||||
|
||||
@ -112,15 +117,21 @@ export class Database {
|
||||
const entityValuePromises = entityFields.map(async (field: any) => {
|
||||
const { type, propertyName } = field;
|
||||
|
||||
// TODO: Will clash if entity has blockHash and blockNumber fields.
|
||||
// Get blockHash property for db entry from block instance.
|
||||
if (propertyName === 'blockHash') {
|
||||
return block.blockHash;
|
||||
}
|
||||
|
||||
// Get blockNumber property for db entry from block instance.
|
||||
if (propertyName === 'blockNumber') {
|
||||
return block.blockNumber;
|
||||
}
|
||||
|
||||
// Get blockNumber as _blockNumber and blockHash as _blockHash from the entityInstance (wasm).
|
||||
if (['_blockNumber', '_blockHash'].includes(propertyName)) {
|
||||
return fromEntityValue(instanceExports, entityInstance, type.toString(), propertyName.slice(1));
|
||||
}
|
||||
|
||||
return fromEntityValue(instanceExports, entityInstance, type.toString(), propertyName);
|
||||
}, {});
|
||||
|
||||
|
@ -18,7 +18,7 @@ import JSONbig from 'json-bigint';
|
||||
import { IndexerInterface } from '@vulcanize/util';
|
||||
|
||||
import { TypeId } from './types';
|
||||
import { Block, fromEthereumValue, toEthereumValue } from './utils';
|
||||
import { Block, fromEthereumValue, toEthereumValue, resolveEntityFieldConflicts } from './utils';
|
||||
import { Database } from './database';
|
||||
|
||||
const NETWORK_URL = 'http://127.0.0.1:8081';
|
||||
@ -66,15 +66,15 @@ export const instantiate = async (database: Database, indexer: IndexerInterface,
|
||||
const entityInstance = await Entity.wrap(data);
|
||||
|
||||
assert(context.event.block);
|
||||
const dbData = await database.fromGraphEntity(exports, context.event.block, entityName, entityInstance);
|
||||
let dbData = await database.fromGraphEntity(exports, context.event.block, entityName, entityInstance);
|
||||
await database.saveEntity(entityName, dbData);
|
||||
|
||||
// Remove blockNumber and blockHash from dbData for auto-diff.
|
||||
delete dbData.blockNumber;
|
||||
delete dbData.blockHash;
|
||||
// Resolve any field name conflicts in the dbData for auto-diff.
|
||||
dbData = resolveEntityFieldConflicts(dbData);
|
||||
|
||||
// Prepare the diff data.
|
||||
const diffData: any = { state: {} };
|
||||
|
||||
// JSON stringify and parse data for handling unknown types when encoding.
|
||||
// For example, decimal.js values are converted to string in the diff data.
|
||||
diffData.state[entityName] = JSONbig.parse(JSONbig.stringify(dbData));
|
||||
|
@ -396,3 +396,25 @@ export const fromEntityValue = async (instanceExports: any, entityInstance: any,
|
||||
throw new Error(`Unsupported type: ${type}`);
|
||||
}
|
||||
};
|
||||
|
||||
export const resolveEntityFieldConflicts = (entity: any): any => {
|
||||
if (entity) {
|
||||
// Remove fields blockHash and blockNumber from the entity.
|
||||
delete entity.blockHash;
|
||||
delete entity.blockNumber;
|
||||
|
||||
// Rename _blockHash -> blockHash.
|
||||
if ('_blockHash' in entity) {
|
||||
entity.blockHash = entity._blockHash;
|
||||
delete entity._blockHash;
|
||||
}
|
||||
|
||||
// Rename _blockNumber -> blockNumber.
|
||||
if ('_blockNumber' in entity) {
|
||||
entity.blockNumber = entity._blockNumber;
|
||||
delete entity._blockNumber;
|
||||
}
|
||||
}
|
||||
|
||||
return entity;
|
||||
};
|
||||
|
@ -13,7 +13,7 @@ import { ResultObject } from '@vulcanize/assemblyscript/lib/loader';
|
||||
import { EthClient } from '@vulcanize/ipld-eth-client';
|
||||
import { IndexerInterface } from '@vulcanize/util';
|
||||
|
||||
import { createBlock, createEvent, getSubgraphConfig } from './utils';
|
||||
import { createBlock, createEvent, getSubgraphConfig, resolveEntityFieldConflicts } from './utils';
|
||||
import { Context, instantiate } from './loader';
|
||||
import { Database } from './database';
|
||||
|
||||
@ -183,7 +183,11 @@ export class GraphWatcher {
|
||||
this._indexer = indexer;
|
||||
}
|
||||
|
||||
async getEntity<Entity> (entity: new () => Entity, id: string, blockHash: string): Promise<Entity | undefined> {
|
||||
return this._database.getEntity(entity, id, blockHash);
|
||||
async getEntity<Entity> (entity: new () => Entity, id: string, blockHash: string): Promise<any> {
|
||||
// Get entity from the database.
|
||||
const result = await this._database.getEntity(entity, id, blockHash) as any;
|
||||
|
||||
// Resolve any field name conflicts in the entity result.
|
||||
return resolveEntityFieldConflicts(result);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user