mirror of
https://github.com/cerc-io/watcher-ts
synced 2025-08-01 04:12:06 +00:00
Handle subgraph schema field with derivedFrom directive (#60)
* Handle subgraph schema field with derivedFrom directive * Handle derivedFrom directive in eden-watcher * Fix 1 to N relation error by removing limit from query * Order by id for derivedFrom relations to match graph-node * Refactor example subgraph schema entities * Fix watcher queries to return correct relation field values * Fix hierarchical query for getting two entities at same block
This commit is contained in:
parent
b04f6f2fba
commit
238ad21189
@ -2,9 +2,8 @@
|
|||||||
// Copyright 2021 Vulcanize, Inc.
|
// Copyright 2021 Vulcanize, Inc.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm';
|
import { Entity, PrimaryColumn, Column } from 'typeorm';
|
||||||
import { Claim } from './Claim';
|
|
||||||
import { Slash } from './Slash';
|
|
||||||
import { bigintTransformer } from '@vulcanize/util';
|
import { bigintTransformer } from '@vulcanize/util';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@ -23,10 +22,4 @@ export class Account {
|
|||||||
|
|
||||||
@Column('bigint', { transformer: bigintTransformer })
|
@Column('bigint', { transformer: bigintTransformer })
|
||||||
totalSlashed!: bigint;
|
totalSlashed!: bigint;
|
||||||
|
|
||||||
@ManyToOne(() => Claim)
|
|
||||||
claims!: Claim;
|
|
||||||
|
|
||||||
@ManyToOne(() => Slash)
|
|
||||||
slashes!: Slash;
|
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,9 @@
|
|||||||
// Copyright 2021 Vulcanize, Inc.
|
// Copyright 2021 Vulcanize, Inc.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm';
|
import { Entity, PrimaryColumn, Column } from 'typeorm';
|
||||||
import Decimal from 'decimal.js';
|
import Decimal from 'decimal.js';
|
||||||
|
|
||||||
import { ProducerEpoch } from './ProducerEpoch';
|
|
||||||
import { bigintTransformer, decimalTransformer } from '@vulcanize/util';
|
import { bigintTransformer, decimalTransformer } from '@vulcanize/util';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@ -39,7 +38,4 @@ export class Epoch {
|
|||||||
|
|
||||||
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
producerBlocksRatio!: Decimal;
|
producerBlocksRatio!: Decimal;
|
||||||
|
|
||||||
@ManyToOne(() => ProducerEpoch)
|
|
||||||
producerRewards!: ProducerEpoch;
|
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,11 @@
|
|||||||
// Copyright 2021 Vulcanize, Inc.
|
// Copyright 2021 Vulcanize, Inc.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { Entity, PrimaryColumn, Column, ManyToOne } from 'typeorm';
|
import { Entity, PrimaryColumn, Column } from 'typeorm';
|
||||||
import Decimal from 'decimal.js';
|
import Decimal from 'decimal.js';
|
||||||
|
|
||||||
import { bigintTransformer, decimalTransformer } from '@vulcanize/util';
|
import { bigintTransformer, decimalTransformer } from '@vulcanize/util';
|
||||||
|
|
||||||
import { SlotClaim } from './SlotClaim';
|
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Slot {
|
export class Slot {
|
||||||
@PrimaryColumn('varchar')
|
@PrimaryColumn('varchar')
|
||||||
@ -40,7 +38,4 @@ export class Slot {
|
|||||||
|
|
||||||
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
taxRatePerDay!: Decimal;
|
taxRatePerDay!: Decimal;
|
||||||
|
|
||||||
@ManyToOne(() => SlotClaim)
|
|
||||||
claims!: SlotClaim;
|
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ import { BaseProvider } from '@ethersproject/providers';
|
|||||||
import * as codec from '@ipld/dag-cbor';
|
import * as codec from '@ipld/dag-cbor';
|
||||||
import { EthClient } from '@vulcanize/ipld-eth-client';
|
import { EthClient } from '@vulcanize/ipld-eth-client';
|
||||||
import { StorageLayout } from '@vulcanize/solidity-mapper';
|
import { StorageLayout } from '@vulcanize/solidity-mapper';
|
||||||
import { EventInterface, Indexer as BaseIndexer, IndexerInterface, UNKNOWN_EVENT_NAME, ServerConfig, JobQueue } from '@vulcanize/util';
|
import { EventInterface, Indexer as BaseIndexer, IndexerInterface, UNKNOWN_EVENT_NAME, ServerConfig, JobQueue, BlockHeight } from '@vulcanize/util';
|
||||||
import { GraphWatcher } from '@vulcanize/graph-node';
|
import { GraphWatcher } from '@vulcanize/graph-node';
|
||||||
|
|
||||||
import { Database } from './database';
|
import { Database } from './database';
|
||||||
@ -549,10 +549,10 @@ export class Indexer implements IndexerInterface {
|
|||||||
return (ipfsAddr !== undefined && ipfsAddr !== null && ipfsAddr !== '');
|
return (ipfsAddr !== undefined && ipfsAddr !== null && ipfsAddr !== '');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSubgraphEntity<Entity> (entity: new () => Entity, id: string, blockHash?: string): Promise<any> {
|
async getSubgraphEntity<Entity> (entity: new () => Entity, id: string, block?: BlockHeight): Promise<any> {
|
||||||
const relations = this._relationsMap.get(entity) || {};
|
const relations = this._relationsMap.get(entity) || {};
|
||||||
|
|
||||||
const data = await this._graphWatcher.getEntity(entity, id, relations, blockHash);
|
const data = await this._graphWatcher.getEntity(entity, id, relations, block);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@ -1144,78 +1144,120 @@ export class Indexer implements IndexerInterface {
|
|||||||
this._relationsMap.set(ProducerSet, {
|
this._relationsMap.set(ProducerSet, {
|
||||||
producers: {
|
producers: {
|
||||||
entity: Producer,
|
entity: Producer,
|
||||||
isArray: true
|
isArray: true,
|
||||||
|
isDerived: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this._relationsMap.set(RewardSchedule, {
|
this._relationsMap.set(RewardSchedule, {
|
||||||
rewardScheduleEntries: {
|
rewardScheduleEntries: {
|
||||||
entity: RewardScheduleEntry,
|
entity: RewardScheduleEntry,
|
||||||
isArray: true
|
isArray: true,
|
||||||
|
isDerived: false
|
||||||
},
|
},
|
||||||
activeRewardScheduleEntry: {
|
activeRewardScheduleEntry: {
|
||||||
entity: RewardScheduleEntry,
|
entity: RewardScheduleEntry,
|
||||||
isArray: false
|
isArray: false,
|
||||||
|
isDerived: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this._relationsMap.set(ProducerEpoch, {
|
this._relationsMap.set(ProducerEpoch, {
|
||||||
epoch: {
|
epoch: {
|
||||||
entity: Epoch,
|
entity: Epoch,
|
||||||
isArray: false
|
isArray: false,
|
||||||
|
isDerived: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this._relationsMap.set(Epoch, {
|
this._relationsMap.set(Epoch, {
|
||||||
startBlock: {
|
startBlock: {
|
||||||
entity: Block,
|
entity: Block,
|
||||||
isArray: false
|
isArray: false,
|
||||||
|
isDerived: false
|
||||||
},
|
},
|
||||||
endBlock: {
|
endBlock: {
|
||||||
entity: Block,
|
entity: Block,
|
||||||
isArray: false
|
isArray: false,
|
||||||
|
isDerived: false
|
||||||
|
},
|
||||||
|
producerRewards: {
|
||||||
|
entity: ProducerEpoch,
|
||||||
|
isArray: true,
|
||||||
|
isDerived: true,
|
||||||
|
field: 'epoch'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this._relationsMap.set(SlotClaim, {
|
this._relationsMap.set(SlotClaim, {
|
||||||
slot: {
|
slot: {
|
||||||
entity: Slot,
|
entity: Slot,
|
||||||
isArray: false
|
isArray: false,
|
||||||
|
isDerived: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this._relationsMap.set(Network, {
|
this._relationsMap.set(Network, {
|
||||||
stakers: {
|
stakers: {
|
||||||
entity: Staker,
|
entity: Staker,
|
||||||
isArray: true
|
isArray: true,
|
||||||
|
isDerived: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this._relationsMap.set(Distributor, {
|
this._relationsMap.set(Distributor, {
|
||||||
currentDistribution: {
|
currentDistribution: {
|
||||||
entity: Distribution,
|
entity: Distribution,
|
||||||
isArray: false
|
isArray: false,
|
||||||
|
isDerived: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this._relationsMap.set(Distribution, {
|
this._relationsMap.set(Distribution, {
|
||||||
distributor: {
|
distributor: {
|
||||||
entity: Distributor,
|
entity: Distributor,
|
||||||
isArray: false
|
isArray: false,
|
||||||
|
isDerived: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this._relationsMap.set(Claim, {
|
this._relationsMap.set(Claim, {
|
||||||
account: {
|
account: {
|
||||||
entity: Account,
|
entity: Account,
|
||||||
isArray: false
|
isArray: false,
|
||||||
|
isDerived: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this._relationsMap.set(Slash, {
|
this._relationsMap.set(Slash, {
|
||||||
account: {
|
account: {
|
||||||
entity: Account,
|
entity: Account,
|
||||||
isArray: false
|
isArray: false,
|
||||||
|
isDerived: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this._relationsMap.set(Slot, {
|
||||||
|
claims: {
|
||||||
|
entity: SlotClaim,
|
||||||
|
isArray: true,
|
||||||
|
isDerived: true,
|
||||||
|
field: 'slot'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this._relationsMap.set(Account, {
|
||||||
|
claims: {
|
||||||
|
entity: Claim,
|
||||||
|
isArray: true,
|
||||||
|
isDerived: true,
|
||||||
|
field: 'account'
|
||||||
|
},
|
||||||
|
slashes: {
|
||||||
|
entity: Slash,
|
||||||
|
isArray: true,
|
||||||
|
isDerived: true,
|
||||||
|
field: 'account'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -65,109 +65,109 @@ export const createResolvers = async (indexer: Indexer, eventWatcher: EventWatch
|
|||||||
producer: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
producer: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||||
log('producer', id, block);
|
log('producer', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(Producer, id, block.hash);
|
return indexer.getSubgraphEntity(Producer, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
producerSet: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
producerSet: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||||
log('producerSet', id, block);
|
log('producerSet', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(ProducerSet, id, block.hash);
|
return indexer.getSubgraphEntity(ProducerSet, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
producerSetChange: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
producerSetChange: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||||
log('producerSetChange', id, block);
|
log('producerSetChange', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(ProducerSetChange, id, block.hash);
|
return indexer.getSubgraphEntity(ProducerSetChange, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
producerRewardCollectorChange: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
producerRewardCollectorChange: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||||
log('producerRewardCollectorChange', id, block);
|
log('producerRewardCollectorChange', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(ProducerRewardCollectorChange, id, block.hash);
|
return indexer.getSubgraphEntity(ProducerRewardCollectorChange, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
rewardScheduleEntry: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
rewardScheduleEntry: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||||
log('rewardScheduleEntry', id, block);
|
log('rewardScheduleEntry', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(RewardScheduleEntry, id, block.hash);
|
return indexer.getSubgraphEntity(RewardScheduleEntry, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
rewardSchedule: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
rewardSchedule: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||||
log('rewardSchedule', id, block);
|
log('rewardSchedule', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(RewardSchedule, id, block.hash);
|
return indexer.getSubgraphEntity(RewardSchedule, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
producerEpoch: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
producerEpoch: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||||
log('producerEpoch', id, block);
|
log('producerEpoch', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(ProducerEpoch, id, block.hash);
|
return indexer.getSubgraphEntity(ProducerEpoch, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
block: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
block: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||||
log('block', id, block);
|
log('block', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(Block, id, block.hash);
|
return indexer.getSubgraphEntity(Block, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
epoch: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
epoch: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||||
log('epoch', id, block);
|
log('epoch', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(Epoch, id, block.hash);
|
return indexer.getSubgraphEntity(Epoch, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
slotClaim: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
slotClaim: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||||
log('slotClaim', id, block);
|
log('slotClaim', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(SlotClaim, id, block.hash);
|
return indexer.getSubgraphEntity(SlotClaim, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
slot: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
slot: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||||
log('slot', id, block);
|
log('slot', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(Slot, id, block.hash);
|
return indexer.getSubgraphEntity(Slot, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
staker: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
staker: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||||
log('staker', id, block);
|
log('staker', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(Staker, id, block.hash);
|
return indexer.getSubgraphEntity(Staker, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
network: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
network: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||||
log('network', id, block);
|
log('network', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(Network, id, block.hash);
|
return indexer.getSubgraphEntity(Network, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
distributor: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
distributor: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||||
log('distributor', id, block);
|
log('distributor', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(Distributor, id, block.hash);
|
return indexer.getSubgraphEntity(Distributor, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
distribution: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
distribution: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||||
log('distribution', id, block);
|
log('distribution', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(Distribution, id, block.hash);
|
return indexer.getSubgraphEntity(Distribution, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
claim: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
claim: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||||
log('claim', id, block);
|
log('claim', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(Claim, id, block.hash);
|
return indexer.getSubgraphEntity(Claim, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
slash: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
slash: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||||
log('slash', id, block);
|
log('slash', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(Slash, id, block.hash);
|
return indexer.getSubgraphEntity(Slash, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
account: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
account: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }) => {
|
||||||
log('account', id, block);
|
log('account', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(Account, id, block.hash);
|
return indexer.getSubgraphEntity(Account, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
events: async (_: any, { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }) => {
|
events: async (_: any, { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }) => {
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
## Run
|
## Run
|
||||||
|
|
||||||
* Compare query results from two different GQL endpoints:
|
* Compare query results from two different GQL endpoints:
|
||||||
|
|
||||||
* In a config file (sample: `environments/compare-cli-config.toml`):
|
* In a config file (sample: `environments/compare-cli-config.toml`):
|
||||||
|
|
||||||
* Specify the two GQL endpoints in the endpoints config.
|
* Specify the two GQL endpoints in the endpoints config.
|
||||||
@ -53,7 +53,7 @@
|
|||||||
[endpoints]
|
[endpoints]
|
||||||
gqlEndpoint1 = "http://localhost:8000/subgraphs/name/example1"
|
gqlEndpoint1 = "http://localhost:8000/subgraphs/name/example1"
|
||||||
gqlEndpoint2 = "http://localhost:3008/graphql"
|
gqlEndpoint2 = "http://localhost:3008/graphql"
|
||||||
|
|
||||||
[queries]
|
[queries]
|
||||||
queryDir = "../graph-test-watcher/src/gql/queries"
|
queryDir = "../graph-test-watcher/src/gql/queries"
|
||||||
```
|
```
|
||||||
@ -70,11 +70,11 @@
|
|||||||
* `block-hash`(alias: `b`): Block hash (required).
|
* `block-hash`(alias: `b`): Block hash (required).
|
||||||
* `entity-id`(alias: `i`): Entity Id (required).
|
* `entity-id`(alias: `i`): Entity Id (required).
|
||||||
* `raw-json`(alias: `j`): Whether to print out a raw diff object (default: `false`).
|
* `raw-json`(alias: `j`): Whether to print out a raw diff object (default: `false`).
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
yarn compare-entity --config-file environments/compare-cli-config.toml --query-name exampleEntity --block-hash 0xceed7ee9d3de97c99db12e42433cae9115bb311c516558539fb7114fa17d545b --entity-id 0x2886bae64814bd959aec4282f86f3a97bf1e16e4111b39fd7bdd592b516c66c6
|
yarn compare-entity --config-file environments/compare-cli-config.toml --query-name author --block-hash 0xceed7ee9d3de97c99db12e42433cae9115bb311c516558539fb7114fa17d545b --entity-id 0xdc7d7a8920c8eecc098da5b7522a5f31509b5bfc
|
||||||
```
|
```
|
||||||
|
|
||||||
* The program will exit with code `1` if the query results are not equal.
|
* The program will exit with code `1` if the query results are not equal.
|
||||||
|
@ -6,10 +6,12 @@ import assert from 'assert';
|
|||||||
import {
|
import {
|
||||||
Connection,
|
Connection,
|
||||||
ConnectionOptions,
|
ConnectionOptions,
|
||||||
FindOneOptions
|
FindOneOptions,
|
||||||
|
LessThanOrEqual
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
BlockHeight,
|
||||||
Database as BaseDatabase
|
Database as BaseDatabase
|
||||||
} from '@vulcanize/util';
|
} from '@vulcanize/util';
|
||||||
|
|
||||||
@ -41,7 +43,6 @@ export class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getEntity<Entity> (entity: (new () => Entity) | string, id: string, blockHash?: string): Promise<Entity | undefined> {
|
async getEntity<Entity> (entity: (new () => Entity) | string, id: string, blockHash?: string): Promise<Entity | undefined> {
|
||||||
// TODO: Take block number as an optional argument
|
|
||||||
const queryRunner = this._conn.createQueryRunner();
|
const queryRunner = this._conn.createQueryRunner();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -74,63 +75,108 @@ export class Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEntityWithRelations<Entity> (entity: (new () => Entity) | string, id: string, relations: { [key: string]: any }, blockHash?: string): Promise<Entity | undefined> {
|
async getEntityWithRelations<Entity> (entity: (new () => Entity) | string, id: string, relations: { [key: string]: any }, block: BlockHeight = {}): Promise<Entity | undefined> {
|
||||||
const queryRunner = this._conn.createQueryRunner();
|
const queryRunner = this._conn.createQueryRunner();
|
||||||
|
let { hash: blockHash, number: blockNumber } = block;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const repo = queryRunner.manager.getRepository(entity);
|
const repo = queryRunner.manager.getRepository(entity);
|
||||||
|
|
||||||
let selectQueryBuilder = repo.createQueryBuilder('entity');
|
const whereOptions: any = { id };
|
||||||
|
|
||||||
selectQueryBuilder = selectQueryBuilder.where('entity.id = :id', { id })
|
if (blockNumber) {
|
||||||
.orderBy('entity.block_number', 'DESC')
|
whereOptions.blockNumber = LessThanOrEqual(blockNumber);
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
// Use blockHash if provided.
|
|
||||||
if (blockHash) {
|
|
||||||
// Fetching blockHash for previous entity in frothy region.
|
|
||||||
const { blockHash: entityblockHash, blockNumber, id: frothyId } = await this._baseDatabase.getFrothyEntity(queryRunner, repo, { blockHash, id });
|
|
||||||
|
|
||||||
if (frothyId) {
|
|
||||||
// If entity found in frothy region.
|
|
||||||
selectQueryBuilder = selectQueryBuilder.andWhere('entity.block_hash = :entityblockHash', { entityblockHash });
|
|
||||||
} else {
|
|
||||||
// If entity not in frothy region.
|
|
||||||
const canonicalBlockNumber = blockNumber + 1;
|
|
||||||
|
|
||||||
selectQueryBuilder = selectQueryBuilder.innerJoinAndSelect('block_progress', 'block', 'block.block_hash = entity.block_hash')
|
|
||||||
.andWhere('block.is_pruned = false')
|
|
||||||
.andWhere('entity.block_number <= :canonicalBlockNumber', { canonicalBlockNumber });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement query for nested relations.
|
if (blockHash) {
|
||||||
Object.entries(relations).forEach(([field, data], index) => {
|
whereOptions.blockHash = blockHash;
|
||||||
const { entity: relatedEntity, isArray } = data;
|
const block = await this._baseDatabase.getBlockProgress(queryRunner.manager.getRepository('block_progress'), blockHash);
|
||||||
const alias = `relatedEntity${index}`;
|
blockNumber = block?.blockNumber;
|
||||||
|
}
|
||||||
|
|
||||||
if (isArray) {
|
const findOptions = {
|
||||||
// For one to many relational field.
|
where: whereOptions,
|
||||||
selectQueryBuilder = selectQueryBuilder.leftJoinAndMapMany(
|
order: {
|
||||||
`entity.${field}`,
|
blockNumber: 'DESC'
|
||||||
relatedEntity,
|
|
||||||
alias,
|
|
||||||
`${alias}.id IN (SELECT unnest(entity.${field})) AND ${alias}.block_number <= entity.block_number`
|
|
||||||
)
|
|
||||||
.addOrderBy(`${alias}.block_number`, 'DESC');
|
|
||||||
} else {
|
|
||||||
// For one to one relational field.
|
|
||||||
selectQueryBuilder = selectQueryBuilder.leftJoinAndMapOne(
|
|
||||||
`entity.${field}`,
|
|
||||||
relatedEntity,
|
|
||||||
alias,
|
|
||||||
`entity.${field} = ${alias}.id AND ${alias}.block_number <= entity.block_number`
|
|
||||||
)
|
|
||||||
.addOrderBy(`${alias}.block_number`, 'DESC');
|
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
return selectQueryBuilder.getOne();
|
let entityData: any = await repo.findOne(findOptions as FindOneOptions<Entity>);
|
||||||
|
|
||||||
|
if (!entityData && findOptions.where.blockHash) {
|
||||||
|
entityData = await this._baseDatabase.getPrevEntityVersion(queryRunner, repo, findOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entityData) {
|
||||||
|
// Populate relational fields.
|
||||||
|
// TODO: Implement query for nested relations.
|
||||||
|
const relationQueryPromises = Object.entries(relations).map(async ([field, data]) => {
|
||||||
|
assert(entityData);
|
||||||
|
const { entity: relatedEntity, isArray, isDerived, field: derivedField } = data;
|
||||||
|
|
||||||
|
const repo = queryRunner.manager.getRepository(relatedEntity);
|
||||||
|
let selectQueryBuilder = repo.createQueryBuilder('entity');
|
||||||
|
|
||||||
|
if (isDerived) {
|
||||||
|
// For derived relational field.
|
||||||
|
selectQueryBuilder = selectQueryBuilder.where(`entity.${derivedField} = :id`, { id: entityData.id });
|
||||||
|
|
||||||
|
if (isArray) {
|
||||||
|
selectQueryBuilder = selectQueryBuilder.distinctOn(['entity.id'])
|
||||||
|
.orderBy('entity.id');
|
||||||
|
} else {
|
||||||
|
selectQueryBuilder = selectQueryBuilder.limit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isArray) {
|
||||||
|
// For one to many relational field.
|
||||||
|
selectQueryBuilder = selectQueryBuilder.where('entity.id IN (:...ids)', { ids: entityData[field] })
|
||||||
|
.distinctOn(['entity.id'])
|
||||||
|
.orderBy('entity.id');
|
||||||
|
|
||||||
|
// Subquery example if distinctOn is not performant.
|
||||||
|
//
|
||||||
|
// SELECT c.*
|
||||||
|
// FROM
|
||||||
|
// categories c,
|
||||||
|
// (
|
||||||
|
// SELECT id, MAX(block_number) as block_number
|
||||||
|
// FROM categories
|
||||||
|
// WHERE
|
||||||
|
// id IN ('nature', 'tech', 'issues')
|
||||||
|
// AND
|
||||||
|
// block_number <= 127
|
||||||
|
// GROUP BY id
|
||||||
|
// ) a
|
||||||
|
// WHERE
|
||||||
|
// c.id = a.id AND c.block_number = a.block_number
|
||||||
|
} else {
|
||||||
|
// For one to one relational field.
|
||||||
|
selectQueryBuilder = selectQueryBuilder.where('entity.id = :id', { id: entityData[field] })
|
||||||
|
.limit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
selectQueryBuilder = selectQueryBuilder.addOrderBy('entity.block_number', 'DESC');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockNumber) {
|
||||||
|
selectQueryBuilder = selectQueryBuilder.andWhere(
|
||||||
|
'entity.block_number <= :blockNumber',
|
||||||
|
{ blockNumber }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isArray) {
|
||||||
|
entityData[field] = await selectQueryBuilder.getMany();
|
||||||
|
} else {
|
||||||
|
entityData[field] = await selectQueryBuilder.getOne();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(relationQueryPromises);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entityData;
|
||||||
} finally {
|
} finally {
|
||||||
await queryRunner.release();
|
await queryRunner.release();
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,13 @@ export interface Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const instantiate = async (database: Database, indexer: IndexerInterface, context: Context, filePath: string, data: GraphData = {}): Promise<loader.ResultObject & { exports: any }> => {
|
export const instantiate = async (
|
||||||
|
database: Database,
|
||||||
|
indexer: IndexerInterface,
|
||||||
|
context: Context,
|
||||||
|
filePath: string,
|
||||||
|
data: GraphData = {}
|
||||||
|
): Promise<loader.ResultObject & { exports: any }> => {
|
||||||
const { abis = {}, dataSource } = data;
|
const { abis = {}, dataSource } = data;
|
||||||
const buffer = await fs.readFile(filePath);
|
const buffer = await fs.readFile(filePath);
|
||||||
const provider = getDefaultProvider(NETWORK_URL);
|
const provider = getDefaultProvider(NETWORK_URL);
|
||||||
|
@ -11,7 +11,7 @@ import { ContractInterface, utils } from 'ethers';
|
|||||||
|
|
||||||
import { ResultObject } from '@vulcanize/assemblyscript/lib/loader';
|
import { ResultObject } from '@vulcanize/assemblyscript/lib/loader';
|
||||||
import { EthClient } from '@vulcanize/ipld-eth-client';
|
import { EthClient } from '@vulcanize/ipld-eth-client';
|
||||||
import { IndexerInterface, getFullBlock } from '@vulcanize/util';
|
import { IndexerInterface, getFullBlock, BlockHeight } from '@vulcanize/util';
|
||||||
|
|
||||||
import { createBlock, createEvent, getSubgraphConfig, resolveEntityFieldConflicts } from './utils';
|
import { createBlock, createEvent, getSubgraphConfig, resolveEntityFieldConflicts } from './utils';
|
||||||
import { Context, instantiate } from './loader';
|
import { Context, instantiate } from './loader';
|
||||||
@ -177,9 +177,9 @@ export class GraphWatcher {
|
|||||||
this._indexer = indexer;
|
this._indexer = indexer;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEntity<Entity> (entity: new () => Entity, id: string, relations: { [key: string]: any }, blockHash?: string): Promise<any> {
|
async getEntity<Entity> (entity: new () => Entity, id: string, relations: { [key: string]: any }, block?: BlockHeight): Promise<any> {
|
||||||
// Get entity from the database.
|
// Get entity from the database.
|
||||||
const result = await this._database.getEntityWithRelations(entity, id, relations, blockHash) as any;
|
const result = await this._database.getEntityWithRelations(entity, id, relations, block) as any;
|
||||||
|
|
||||||
// Resolve any field name conflicts in the entity result.
|
// Resolve any field name conflicts in the entity result.
|
||||||
return resolveEntityFieldConflicts(result);
|
return resolveEntityFieldConflicts(result);
|
||||||
|
@ -12,30 +12,33 @@ import {
|
|||||||
BigDecimal
|
BigDecimal
|
||||||
} from "@graphprotocol/graph-ts";
|
} from "@graphprotocol/graph-ts";
|
||||||
|
|
||||||
export class RelatedEntity extends Entity {
|
export class Blog extends Entity {
|
||||||
constructor(id: string) {
|
constructor(id: string) {
|
||||||
super();
|
super();
|
||||||
this.set("id", Value.fromString(id));
|
this.set("id", Value.fromString(id));
|
||||||
|
|
||||||
this.set("paramBigInt", Value.fromBigInt(BigInt.zero()));
|
this.set("kind", Value.fromString(""));
|
||||||
this.set("bigIntArray", Value.fromBigIntArray(new Array(0)));
|
this.set("isActive", Value.fromBoolean(false));
|
||||||
|
this.set("reviews", Value.fromBigIntArray(new Array(0)));
|
||||||
|
this.set("author", Value.fromString(""));
|
||||||
|
this.set("categories", Value.fromStringArray(new Array(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
save(): void {
|
save(): void {
|
||||||
let id = this.get("id");
|
let id = this.get("id");
|
||||||
assert(id != null, "Cannot save RelatedEntity entity without an ID");
|
assert(id != null, "Cannot save Blog entity without an ID");
|
||||||
if (id) {
|
if (id) {
|
||||||
assert(
|
assert(
|
||||||
id.kind == ValueKind.STRING,
|
id.kind == ValueKind.STRING,
|
||||||
"Cannot save RelatedEntity entity with non-string ID. " +
|
"Cannot save Blog entity with non-string ID. " +
|
||||||
'Considering using .toHex() to convert the "id" to a string.'
|
'Considering using .toHex() to convert the "id" to a string.'
|
||||||
);
|
);
|
||||||
store.set("RelatedEntity", id.toString(), this);
|
store.set("Blog", id.toString(), this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static load(id: string): RelatedEntity | null {
|
static load(id: string): Blog | null {
|
||||||
return changetype<RelatedEntity | null>(store.get("RelatedEntity", id));
|
return changetype<Blog | null>(store.get("Blog", id));
|
||||||
}
|
}
|
||||||
|
|
||||||
get id(): string {
|
get id(): string {
|
||||||
@ -47,56 +50,79 @@ export class RelatedEntity extends Entity {
|
|||||||
this.set("id", Value.fromString(value));
|
this.set("id", Value.fromString(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
get paramBigInt(): BigInt {
|
get kind(): string {
|
||||||
let value = this.get("paramBigInt");
|
let value = this.get("kind");
|
||||||
return value!.toBigInt();
|
return value!.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
set paramBigInt(value: BigInt) {
|
set kind(value: string) {
|
||||||
this.set("paramBigInt", Value.fromBigInt(value));
|
this.set("kind", Value.fromString(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
get bigIntArray(): Array<BigInt> {
|
get isActive(): boolean {
|
||||||
let value = this.get("bigIntArray");
|
let value = this.get("isActive");
|
||||||
|
return value!.toBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
set isActive(value: boolean) {
|
||||||
|
this.set("isActive", Value.fromBoolean(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
get reviews(): Array<BigInt> {
|
||||||
|
let value = this.get("reviews");
|
||||||
return value!.toBigIntArray();
|
return value!.toBigIntArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
set bigIntArray(value: Array<BigInt>) {
|
set reviews(value: Array<BigInt>) {
|
||||||
this.set("bigIntArray", Value.fromBigIntArray(value));
|
this.set("reviews", Value.fromBigIntArray(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
get author(): string {
|
||||||
|
let value = this.get("author");
|
||||||
|
return value!.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
set author(value: string) {
|
||||||
|
this.set("author", Value.fromString(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
get categories(): Array<string> {
|
||||||
|
let value = this.get("categories");
|
||||||
|
return value!.toStringArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
set categories(value: Array<string>) {
|
||||||
|
this.set("categories", Value.fromStringArray(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ExampleEntity extends Entity {
|
export class Author extends Entity {
|
||||||
constructor(id: string) {
|
constructor(id: string) {
|
||||||
super();
|
super();
|
||||||
this.set("id", Value.fromString(id));
|
this.set("id", Value.fromString(id));
|
||||||
|
|
||||||
this.set("count", Value.fromBigInt(BigInt.zero()));
|
this.set("blogCount", Value.fromBigInt(BigInt.zero()));
|
||||||
this.set("paramString", Value.fromString(""));
|
this.set("name", Value.fromString(""));
|
||||||
|
this.set("rating", Value.fromBigDecimal(BigDecimal.zero()));
|
||||||
this.set("paramInt", Value.fromI32(0));
|
this.set("paramInt", Value.fromI32(0));
|
||||||
this.set("paramBoolean", Value.fromBoolean(false));
|
|
||||||
this.set("paramBytes", Value.fromBytes(Bytes.empty()));
|
this.set("paramBytes", Value.fromBytes(Bytes.empty()));
|
||||||
this.set("paramEnum", Value.fromString(""));
|
|
||||||
this.set("paramBigDecimal", Value.fromBigDecimal(BigDecimal.zero()));
|
|
||||||
this.set("related", Value.fromString(""));
|
|
||||||
this.set("manyRelated", Value.fromStringArray(new Array(0)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
save(): void {
|
save(): void {
|
||||||
let id = this.get("id");
|
let id = this.get("id");
|
||||||
assert(id != null, "Cannot save ExampleEntity entity without an ID");
|
assert(id != null, "Cannot save Author entity without an ID");
|
||||||
if (id) {
|
if (id) {
|
||||||
assert(
|
assert(
|
||||||
id.kind == ValueKind.STRING,
|
id.kind == ValueKind.STRING,
|
||||||
"Cannot save ExampleEntity entity with non-string ID. " +
|
"Cannot save Author entity with non-string ID. " +
|
||||||
'Considering using .toHex() to convert the "id" to a string.'
|
'Considering using .toHex() to convert the "id" to a string.'
|
||||||
);
|
);
|
||||||
store.set("ExampleEntity", id.toString(), this);
|
store.set("Author", id.toString(), this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static load(id: string): ExampleEntity | null {
|
static load(id: string): Author | null {
|
||||||
return changetype<ExampleEntity | null>(store.get("ExampleEntity", id));
|
return changetype<Author | null>(store.get("Author", id));
|
||||||
}
|
}
|
||||||
|
|
||||||
get id(): string {
|
get id(): string {
|
||||||
@ -108,22 +134,31 @@ export class ExampleEntity extends Entity {
|
|||||||
this.set("id", Value.fromString(value));
|
this.set("id", Value.fromString(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
get count(): BigInt {
|
get blogCount(): BigInt {
|
||||||
let value = this.get("count");
|
let value = this.get("blogCount");
|
||||||
return value!.toBigInt();
|
return value!.toBigInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
set count(value: BigInt) {
|
set blogCount(value: BigInt) {
|
||||||
this.set("count", Value.fromBigInt(value));
|
this.set("blogCount", Value.fromBigInt(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
get paramString(): string {
|
get name(): string {
|
||||||
let value = this.get("paramString");
|
let value = this.get("name");
|
||||||
return value!.toString();
|
return value!.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
set paramString(value: string) {
|
set name(value: string) {
|
||||||
this.set("paramString", Value.fromString(value));
|
this.set("name", Value.fromString(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
get rating(): BigDecimal {
|
||||||
|
let value = this.get("rating");
|
||||||
|
return value!.toBigDecimal();
|
||||||
|
}
|
||||||
|
|
||||||
|
set rating(value: BigDecimal) {
|
||||||
|
this.set("rating", Value.fromBigDecimal(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
get paramInt(): i32 {
|
get paramInt(): i32 {
|
||||||
@ -135,15 +170,6 @@ export class ExampleEntity extends Entity {
|
|||||||
this.set("paramInt", Value.fromI32(value));
|
this.set("paramInt", Value.fromI32(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
get paramBoolean(): boolean {
|
|
||||||
let value = this.get("paramBoolean");
|
|
||||||
return value!.toBoolean();
|
|
||||||
}
|
|
||||||
|
|
||||||
set paramBoolean(value: boolean) {
|
|
||||||
this.set("paramBoolean", Value.fromBoolean(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
get paramBytes(): Bytes {
|
get paramBytes(): Bytes {
|
||||||
let value = this.get("paramBytes");
|
let value = this.get("paramBytes");
|
||||||
return value!.toBytes();
|
return value!.toBytes();
|
||||||
@ -153,68 +179,40 @@ export class ExampleEntity extends Entity {
|
|||||||
this.set("paramBytes", Value.fromBytes(value));
|
this.set("paramBytes", Value.fromBytes(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
get paramEnum(): string {
|
get blogs(): Array<string> {
|
||||||
let value = this.get("paramEnum");
|
let value = this.get("blogs");
|
||||||
return value!.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
set paramEnum(value: string) {
|
|
||||||
this.set("paramEnum", Value.fromString(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
get paramBigDecimal(): BigDecimal {
|
|
||||||
let value = this.get("paramBigDecimal");
|
|
||||||
return value!.toBigDecimal();
|
|
||||||
}
|
|
||||||
|
|
||||||
set paramBigDecimal(value: BigDecimal) {
|
|
||||||
this.set("paramBigDecimal", Value.fromBigDecimal(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
get related(): string {
|
|
||||||
let value = this.get("related");
|
|
||||||
return value!.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
set related(value: string) {
|
|
||||||
this.set("related", Value.fromString(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
get manyRelated(): Array<string> {
|
|
||||||
let value = this.get("manyRelated");
|
|
||||||
return value!.toStringArray();
|
return value!.toStringArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
set manyRelated(value: Array<string>) {
|
set blogs(value: Array<string>) {
|
||||||
this.set("manyRelated", Value.fromStringArray(value));
|
this.set("blogs", Value.fromStringArray(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ManyRelatedEntity extends Entity {
|
export class Category extends Entity {
|
||||||
constructor(id: string) {
|
constructor(id: string) {
|
||||||
super();
|
super();
|
||||||
this.set("id", Value.fromString(id));
|
this.set("id", Value.fromString(id));
|
||||||
|
|
||||||
|
this.set("name", Value.fromString(""));
|
||||||
this.set("count", Value.fromBigInt(BigInt.zero()));
|
this.set("count", Value.fromBigInt(BigInt.zero()));
|
||||||
}
|
}
|
||||||
|
|
||||||
save(): void {
|
save(): void {
|
||||||
let id = this.get("id");
|
let id = this.get("id");
|
||||||
assert(id != null, "Cannot save ManyRelatedEntity entity without an ID");
|
assert(id != null, "Cannot save Category entity without an ID");
|
||||||
if (id) {
|
if (id) {
|
||||||
assert(
|
assert(
|
||||||
id.kind == ValueKind.STRING,
|
id.kind == ValueKind.STRING,
|
||||||
"Cannot save ManyRelatedEntity entity with non-string ID. " +
|
"Cannot save Category entity with non-string ID. " +
|
||||||
'Considering using .toHex() to convert the "id" to a string.'
|
'Considering using .toHex() to convert the "id" to a string.'
|
||||||
);
|
);
|
||||||
store.set("ManyRelatedEntity", id.toString(), this);
|
store.set("Category", id.toString(), this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static load(id: string): ManyRelatedEntity | null {
|
static load(id: string): Category | null {
|
||||||
return changetype<ManyRelatedEntity | null>(
|
return changetype<Category | null>(store.get("Category", id));
|
||||||
store.get("ManyRelatedEntity", id)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get id(): string {
|
get id(): string {
|
||||||
@ -226,6 +224,15 @@ export class ManyRelatedEntity extends Entity {
|
|||||||
this.set("id", Value.fromString(value));
|
this.set("id", Value.fromString(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get name(): string {
|
||||||
|
let value = this.get("name");
|
||||||
|
return value!.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
set name(value: string) {
|
||||||
|
this.set("name", Value.fromString(value));
|
||||||
|
}
|
||||||
|
|
||||||
get count(): BigInt {
|
get count(): BigInt {
|
||||||
let value = this.get("count");
|
let value = this.get("count");
|
||||||
return value!.toBigInt();
|
return value!.toBigInt();
|
||||||
|
@ -1,28 +1,29 @@
|
|||||||
enum EnumType {
|
enum BlogKind {
|
||||||
choice1
|
short
|
||||||
choice2
|
long
|
||||||
}
|
}
|
||||||
|
|
||||||
type RelatedEntity @entity {
|
type Blog @entity {
|
||||||
id: ID!
|
id: ID!
|
||||||
paramBigInt: BigInt!
|
kind: BlogKind!
|
||||||
bigIntArray: [BigInt!]!
|
isActive: Boolean!
|
||||||
|
reviews: [BigInt!]!
|
||||||
|
author: Author!
|
||||||
|
categories: [Category!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExampleEntity @entity {
|
type Author @entity {
|
||||||
id: ID!
|
id: ID!
|
||||||
count: BigInt!
|
blogCount: BigInt!
|
||||||
paramString: String! # string
|
name: String! # string
|
||||||
|
rating: BigDecimal!
|
||||||
paramInt: Int! # uint8
|
paramInt: Int! # uint8
|
||||||
paramBoolean: Boolean!
|
|
||||||
paramBytes: Bytes!
|
paramBytes: Bytes!
|
||||||
paramEnum: EnumType!
|
blogs: [Blog!]! @derivedFrom(field: "author")
|
||||||
paramBigDecimal: BigDecimal!
|
|
||||||
related: RelatedEntity!
|
|
||||||
manyRelated: [ManyRelatedEntity!]!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ManyRelatedEntity @entity {
|
type Category @entity {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
name: String!
|
||||||
count: BigInt!
|
count: BigInt!
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
Example1,
|
Example1,
|
||||||
Test
|
Test
|
||||||
} from '../generated/Example1/Example1';
|
} from '../generated/Example1/Example1';
|
||||||
import { ExampleEntity, ManyRelatedEntity, RelatedEntity } from '../generated/schema';
|
import { Author, Blog, Category } from '../generated/schema';
|
||||||
|
|
||||||
export function handleTest (event: Test): void {
|
export function handleTest (event: Test): void {
|
||||||
log.debug('event.address: {}', [event.address.toHexString()]);
|
log.debug('event.address: {}', [event.address.toHexString()]);
|
||||||
@ -15,52 +15,54 @@ export function handleTest (event: Test): void {
|
|||||||
|
|
||||||
// Entities can be loaded from the store using a string ID; this ID
|
// Entities can be loaded from the store using a string ID; this ID
|
||||||
// needs to be unique across all entities of the same type
|
// needs to be unique across all entities of the same type
|
||||||
let entity = ExampleEntity.load(event.transaction.from.toHex());
|
let author = Author.load(event.transaction.from.toHex());
|
||||||
|
|
||||||
// Entities only exist after they have been saved to the store;
|
// Entities only exist after they have been saved to the store;
|
||||||
// `null` checks allow to create entities on demand
|
// `null` checks allow to create entities on demand
|
||||||
if (!entity) {
|
if (!author) {
|
||||||
entity = new ExampleEntity(event.transaction.from.toHex());
|
author = new Author(event.transaction.from.toHex());
|
||||||
|
|
||||||
// Entity fields can be set using simple assignments
|
// Entity fields can be set using simple assignments
|
||||||
entity.count = BigInt.fromString('0');
|
author.blogCount = BigInt.fromString('0');
|
||||||
}
|
}
|
||||||
|
|
||||||
// BigInt and BigDecimal math are supported
|
// BigInt and BigDecimal math are supported
|
||||||
entity.count = entity.count + BigInt.fromString('1');
|
author.blogCount = author.blogCount + BigInt.fromString('1');
|
||||||
|
|
||||||
// Entity fields can be set based on event parameters
|
// Entity fields can be set based on event parameters
|
||||||
entity.paramString = event.params.param1;
|
author.name = event.params.param1;
|
||||||
entity.paramInt = event.params.param2;
|
author.paramInt = event.params.param2;
|
||||||
entity.paramBoolean = true;
|
author.paramBytes = event.address;
|
||||||
entity.paramBytes = event.address;
|
author.rating = BigDecimal.fromString('4');
|
||||||
entity.paramEnum = 'choice1';
|
|
||||||
entity.paramBigDecimal = BigDecimal.fromString('123');
|
|
||||||
|
|
||||||
let relatedEntity = RelatedEntity.load(event.params.param1);
|
|
||||||
|
|
||||||
if (!relatedEntity) {
|
|
||||||
relatedEntity = new RelatedEntity(event.params.param1);
|
|
||||||
relatedEntity.paramBigInt = BigInt.fromString('123');
|
|
||||||
}
|
|
||||||
|
|
||||||
const bigIntArray = relatedEntity.bigIntArray;
|
|
||||||
bigIntArray.push(entity.count);
|
|
||||||
relatedEntity.bigIntArray = bigIntArray;
|
|
||||||
|
|
||||||
relatedEntity.save();
|
|
||||||
entity.related = relatedEntity.id;
|
|
||||||
|
|
||||||
const manyRelatedEntity = new ManyRelatedEntity(event.transaction.hash.toHexString());
|
|
||||||
manyRelatedEntity.count = entity.count;
|
|
||||||
manyRelatedEntity.save();
|
|
||||||
|
|
||||||
const manyRelated = entity.manyRelated;
|
|
||||||
manyRelated.push(manyRelatedEntity.id);
|
|
||||||
entity.manyRelated = manyRelated;
|
|
||||||
|
|
||||||
// Entities can be written to the store with `.save()`
|
// Entities can be written to the store with `.save()`
|
||||||
entity.save();
|
author.save();
|
||||||
|
|
||||||
|
let category = Category.load(author.blogCount.toString());
|
||||||
|
|
||||||
|
if (!category) {
|
||||||
|
category = new Category(author.blogCount.toString());
|
||||||
|
category.name = event.params.param1;
|
||||||
|
}
|
||||||
|
|
||||||
|
category.count = category.count + BigInt.fromString('1');
|
||||||
|
category.save();
|
||||||
|
|
||||||
|
const blog = new Blog(event.transaction.hash.toHexString());
|
||||||
|
blog.kind = 'long';
|
||||||
|
blog.isActive = true;
|
||||||
|
|
||||||
|
const blogReviews = blog.reviews;
|
||||||
|
blogReviews.push(BigInt.fromString('4'));
|
||||||
|
blog.reviews = blogReviews;
|
||||||
|
|
||||||
|
blog.author = author.id;
|
||||||
|
|
||||||
|
const categories = blog.categories;
|
||||||
|
categories.push(category.id);
|
||||||
|
blog.categories = categories;
|
||||||
|
|
||||||
|
blog.save();
|
||||||
|
|
||||||
const contractAddress = dataSource.address();
|
const contractAddress = dataSource.address();
|
||||||
const contract = Example1.bind(contractAddress);
|
const contract = Example1.bind(contractAddress);
|
||||||
|
@ -16,9 +16,9 @@ import { BlockProgress } from '../../entity/BlockProgress';
|
|||||||
|
|
||||||
import { GetMethod } from '../../entity/GetMethod';
|
import { GetMethod } from '../../entity/GetMethod';
|
||||||
import { _Test } from '../../entity/_Test';
|
import { _Test } from '../../entity/_Test';
|
||||||
import { ExampleEntity } from '../../entity/ExampleEntity';
|
import { Author } from '../../entity/Author';
|
||||||
import { RelatedEntity } from '../../entity/RelatedEntity';
|
import { Blog } from '../../entity/Blog';
|
||||||
import { ManyRelatedEntity } from '../../entity/ManyRelatedEntity';
|
import { Category } from '../../entity/Category';
|
||||||
|
|
||||||
const log = debug('vulcanize:reset-state');
|
const log = debug('vulcanize:reset-state');
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ export const handler = async (argv: any): Promise<void> => {
|
|||||||
const dbTx = await db.createTransactionRunner();
|
const dbTx = await db.createTransactionRunner();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const entities = [BlockProgress, GetMethod, _Test, ExampleEntity, ManyRelatedEntity, RelatedEntity];
|
const entities = [BlockProgress, GetMethod, _Test, Author, Category, Blog];
|
||||||
|
|
||||||
const removeEntitiesPromise = entities.map(async entityClass => {
|
const removeEntitiesPromise = entities.map(async entityClass => {
|
||||||
return db.removeEntities<any>(dbTx, entityClass, { blockNumber: MoreThan(argv.blockNumber) });
|
return db.removeEntities<any>(dbTx, entityClass, { blockNumber: MoreThan(argv.blockNumber) });
|
||||||
|
@ -7,13 +7,8 @@ import Decimal from 'decimal.js';
|
|||||||
|
|
||||||
import { bigintTransformer, decimalTransformer } from '@vulcanize/util';
|
import { bigintTransformer, decimalTransformer } from '@vulcanize/util';
|
||||||
|
|
||||||
enum EnumType {
|
|
||||||
choice1 = 'choice1',
|
|
||||||
choice2 = 'choice2'
|
|
||||||
}
|
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class ExampleEntity {
|
export class Author {
|
||||||
@PrimaryColumn('varchar')
|
@PrimaryColumn('varchar')
|
||||||
id!: string;
|
id!: string;
|
||||||
|
|
||||||
@ -24,33 +19,17 @@ export class ExampleEntity {
|
|||||||
blockNumber!: number;
|
blockNumber!: number;
|
||||||
|
|
||||||
@Column('bigint', { transformer: bigintTransformer })
|
@Column('bigint', { transformer: bigintTransformer })
|
||||||
count!: bigint;
|
blogCount!: bigint;
|
||||||
|
|
||||||
@Column('varchar')
|
@Column('varchar')
|
||||||
paramString!: string
|
name!: string
|
||||||
|
|
||||||
@Column('integer')
|
@Column('integer')
|
||||||
paramInt!: number
|
paramInt!: number
|
||||||
|
|
||||||
@Column('boolean')
|
|
||||||
paramBoolean!: boolean
|
|
||||||
|
|
||||||
@Column('varchar')
|
@Column('varchar')
|
||||||
paramBytes!: string
|
paramBytes!: string
|
||||||
|
|
||||||
@Column({
|
|
||||||
type: 'enum',
|
|
||||||
enum: EnumType,
|
|
||||||
default: EnumType.choice1
|
|
||||||
})
|
|
||||||
paramEnum!: EnumType
|
|
||||||
|
|
||||||
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
@Column('numeric', { default: 0, transformer: decimalTransformer })
|
||||||
paramBigDecimal!: Decimal
|
rating!: Decimal
|
||||||
|
|
||||||
@Column('varchar')
|
|
||||||
related!: string;
|
|
||||||
|
|
||||||
@Column('varchar', { array: true })
|
|
||||||
manyRelated!: string[]
|
|
||||||
}
|
}
|
43
packages/graph-test-watcher/src/entity/Blog.ts
Normal file
43
packages/graph-test-watcher/src/entity/Blog.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 Vulcanize, Inc.
|
||||||
|
//
|
||||||
|
|
||||||
|
import { Entity, PrimaryColumn, Column } from 'typeorm';
|
||||||
|
|
||||||
|
import { bigintArrayTransformer } from '@vulcanize/util';
|
||||||
|
|
||||||
|
enum BlogType {
|
||||||
|
short = 'short',
|
||||||
|
long = 'long'
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class Blog {
|
||||||
|
@PrimaryColumn('varchar')
|
||||||
|
id!: string;
|
||||||
|
|
||||||
|
@PrimaryColumn('varchar', { length: 66 })
|
||||||
|
blockHash!: string;
|
||||||
|
|
||||||
|
@Column('integer')
|
||||||
|
blockNumber!: number;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'enum',
|
||||||
|
enum: BlogType,
|
||||||
|
default: BlogType.short
|
||||||
|
})
|
||||||
|
kind!: BlogType
|
||||||
|
|
||||||
|
@Column('boolean')
|
||||||
|
isActive!: boolean
|
||||||
|
|
||||||
|
@Column('bigint', { transformer: bigintArrayTransformer, array: true })
|
||||||
|
reviews!: bigint[];
|
||||||
|
|
||||||
|
@Column('varchar')
|
||||||
|
author!: string;
|
||||||
|
|
||||||
|
@Column('varchar', { array: true })
|
||||||
|
categories!: string[]
|
||||||
|
}
|
@ -7,7 +7,7 @@ import { Entity, PrimaryColumn, Column } from 'typeorm';
|
|||||||
import { bigintTransformer } from '@vulcanize/util';
|
import { bigintTransformer } from '@vulcanize/util';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class ManyRelatedEntity {
|
export class Category {
|
||||||
@PrimaryColumn('varchar')
|
@PrimaryColumn('varchar')
|
||||||
id!: string;
|
id!: string;
|
||||||
|
|
||||||
@ -19,4 +19,7 @@ export class ManyRelatedEntity {
|
|||||||
|
|
||||||
@Column('bigint', { transformer: bigintTransformer })
|
@Column('bigint', { transformer: bigintTransformer })
|
||||||
count!: bigint;
|
count!: bigint;
|
||||||
|
|
||||||
|
@Column('varchar')
|
||||||
|
name!: string;
|
||||||
}
|
}
|
@ -1,25 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2021 Vulcanize, Inc.
|
|
||||||
//
|
|
||||||
|
|
||||||
import { Entity, PrimaryColumn, Column } from 'typeorm';
|
|
||||||
|
|
||||||
import { bigintTransformer, bigintArrayTransformer } from '@vulcanize/util';
|
|
||||||
|
|
||||||
@Entity()
|
|
||||||
export class RelatedEntity {
|
|
||||||
@PrimaryColumn('varchar')
|
|
||||||
id!: string;
|
|
||||||
|
|
||||||
@PrimaryColumn('varchar', { length: 66 })
|
|
||||||
blockHash!: string;
|
|
||||||
|
|
||||||
@Column('integer')
|
|
||||||
blockNumber!: number;
|
|
||||||
|
|
||||||
@Column('bigint', { transformer: bigintTransformer })
|
|
||||||
paramBigInt!: bigint;
|
|
||||||
|
|
||||||
@Column('bigint', { transformer: bigintArrayTransformer, array: true })
|
|
||||||
bigIntArray!: bigint[];
|
|
||||||
}
|
|
16
packages/graph-test-watcher/src/gql/queries/author.gql
Normal file
16
packages/graph-test-watcher/src/gql/queries/author.gql
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
query author($id: String!, $blockHash: Bytes!){
|
||||||
|
author(id: $id, block: { hash: $blockHash }){
|
||||||
|
id
|
||||||
|
blogCount
|
||||||
|
name
|
||||||
|
rating
|
||||||
|
paramInt
|
||||||
|
paramBytes
|
||||||
|
blogs {
|
||||||
|
id
|
||||||
|
kind
|
||||||
|
reviews
|
||||||
|
isActive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
packages/graph-test-watcher/src/gql/queries/blog.gql
Normal file
17
packages/graph-test-watcher/src/gql/queries/blog.gql
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
query blog($id: String!, $blockHash: Bytes!){
|
||||||
|
blog(id: $id, block: { hash: $blockHash }){
|
||||||
|
id
|
||||||
|
kind
|
||||||
|
reviews
|
||||||
|
isActive
|
||||||
|
author {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
categories {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
packages/graph-test-watcher/src/gql/queries/category.gql
Normal file
7
packages/graph-test-watcher/src/gql/queries/category.gql
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
query category($id: String!, $blockHash: Bytes!){
|
||||||
|
category(id: $id, block: { hash: $blockHash }){
|
||||||
|
id
|
||||||
|
count
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +0,0 @@
|
|||||||
query exampleEntity($id: String!, $blockHash: Bytes!){
|
|
||||||
exampleEntity(id: $id, block: { hash: $blockHash }){
|
|
||||||
id
|
|
||||||
count
|
|
||||||
paramString
|
|
||||||
paramInt
|
|
||||||
paramBoolean
|
|
||||||
paramBytes
|
|
||||||
paramEnum
|
|
||||||
paramBigDecimal
|
|
||||||
related {
|
|
||||||
id
|
|
||||||
paramBigInt
|
|
||||||
bigIntArray
|
|
||||||
}
|
|
||||||
manyRelated {
|
|
||||||
id
|
|
||||||
count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
query manyRelatedEntity($id: String!, $blockHash: Bytes!){
|
|
||||||
manyRelatedEntity(id: $id, block: { hash: $blockHash }){
|
|
||||||
id
|
|
||||||
count
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
query relatedEntity($id: String!, $blockHash: Bytes!){
|
|
||||||
relatedEntity(id: $id, block: { hash: $blockHash }){
|
|
||||||
id
|
|
||||||
paramBigInt
|
|
||||||
bigIntArray
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,7 +16,7 @@ import { BaseProvider } from '@ethersproject/providers';
|
|||||||
import * as codec from '@ipld/dag-cbor';
|
import * as codec from '@ipld/dag-cbor';
|
||||||
import { EthClient } from '@vulcanize/ipld-eth-client';
|
import { EthClient } from '@vulcanize/ipld-eth-client';
|
||||||
import { StorageLayout } from '@vulcanize/solidity-mapper';
|
import { StorageLayout } from '@vulcanize/solidity-mapper';
|
||||||
import { EventInterface, Indexer as BaseIndexer, IndexerInterface, ValueResult, UNKNOWN_EVENT_NAME, ServerConfig, updateStateForElementaryType, JobQueue } from '@vulcanize/util';
|
import { EventInterface, Indexer as BaseIndexer, IndexerInterface, ValueResult, UNKNOWN_EVENT_NAME, ServerConfig, updateStateForElementaryType, JobQueue, BlockHeight } from '@vulcanize/util';
|
||||||
import { GraphWatcher } from '@vulcanize/graph-node';
|
import { GraphWatcher } from '@vulcanize/graph-node';
|
||||||
|
|
||||||
import { Database } from './database';
|
import { Database } from './database';
|
||||||
@ -29,9 +29,9 @@ import { IPLDBlock } from './entity/IPLDBlock';
|
|||||||
import artifacts from './artifacts/Example.json';
|
import artifacts from './artifacts/Example.json';
|
||||||
import { createInitialCheckpoint, handleEvent, createStateDiff, createStateCheckpoint } from './hooks';
|
import { createInitialCheckpoint, handleEvent, createStateDiff, createStateCheckpoint } from './hooks';
|
||||||
import { IPFSClient } from './ipfs';
|
import { IPFSClient } from './ipfs';
|
||||||
import { ExampleEntity } from './entity/ExampleEntity';
|
import { Author } from './entity/Author';
|
||||||
import { RelatedEntity } from './entity/RelatedEntity';
|
import { Blog } from './entity/Blog';
|
||||||
import { ManyRelatedEntity } from './entity/ManyRelatedEntity';
|
import { Category } from './entity/Category';
|
||||||
|
|
||||||
const log = debug('vulcanize:indexer');
|
const log = debug('vulcanize:indexer');
|
||||||
|
|
||||||
@ -546,10 +546,10 @@ export class Indexer implements IndexerInterface {
|
|||||||
return (ipfsAddr !== undefined && ipfsAddr !== null && ipfsAddr !== '');
|
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, block: BlockHeight): Promise<Entity | undefined> {
|
||||||
const relations = this._relationsMap.get(entity) || {};
|
const relations = this._relationsMap.get(entity) || {};
|
||||||
|
|
||||||
const data = await this._graphWatcher.getEntity(entity, id, relations, blockHash);
|
const data = await this._graphWatcher.getEntity(entity, id, relations, block);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@ -739,13 +739,24 @@ export class Indexer implements IndexerInterface {
|
|||||||
|
|
||||||
_populateRelationsMap (): void {
|
_populateRelationsMap (): void {
|
||||||
// Needs to be generated by codegen.
|
// Needs to be generated by codegen.
|
||||||
this._relationsMap.set(ExampleEntity, {
|
this._relationsMap.set(Author, {
|
||||||
related: {
|
blogs: {
|
||||||
entity: RelatedEntity,
|
entity: Blog,
|
||||||
|
isDerived: true,
|
||||||
|
isArray: true,
|
||||||
|
field: 'author'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this._relationsMap.set(Blog, {
|
||||||
|
author: {
|
||||||
|
entity: Author,
|
||||||
|
isDerived: false,
|
||||||
isArray: false
|
isArray: false
|
||||||
},
|
},
|
||||||
manyRelated: {
|
categories: {
|
||||||
entity: ManyRelatedEntity,
|
entity: Category,
|
||||||
|
isDerived: false,
|
||||||
isArray: true
|
isArray: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -11,9 +11,9 @@ import { ValueResult, BlockHeight } from '@vulcanize/util';
|
|||||||
import { Indexer } from './indexer';
|
import { Indexer } from './indexer';
|
||||||
import { EventWatcher } from './events';
|
import { EventWatcher } from './events';
|
||||||
|
|
||||||
import { ExampleEntity } from './entity/ExampleEntity';
|
import { Author } from './entity/Author';
|
||||||
import { RelatedEntity } from './entity/RelatedEntity';
|
import { Blog } from './entity/Blog';
|
||||||
import { ManyRelatedEntity } from './entity/ManyRelatedEntity';
|
import { Category } from './entity/Category';
|
||||||
|
|
||||||
const log = debug('vulcanize:resolver');
|
const log = debug('vulcanize:resolver');
|
||||||
|
|
||||||
@ -57,22 +57,22 @@ export const createResolvers = async (indexer: Indexer, eventWatcher: EventWatch
|
|||||||
return indexer._test(blockHash, contractAddress);
|
return indexer._test(blockHash, contractAddress);
|
||||||
},
|
},
|
||||||
|
|
||||||
relatedEntity: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }): Promise<RelatedEntity | undefined> => {
|
blog: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }): Promise<Blog | undefined> => {
|
||||||
log('relatedEntity', id, block);
|
log('blog', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(RelatedEntity, id, block.hash);
|
return indexer.getSubgraphEntity(Blog, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
manyRelatedEntity: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }): Promise<ManyRelatedEntity | undefined> => {
|
category: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }): Promise<Category | undefined> => {
|
||||||
log('relatedEntity', id, block);
|
log('category', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(ManyRelatedEntity, id, block.hash);
|
return indexer.getSubgraphEntity(Category, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
exampleEntity: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }): Promise<ExampleEntity | undefined> => {
|
author: async (_: any, { id, block = {} }: { id: string, block: BlockHeight }): Promise<Author | undefined> => {
|
||||||
log('exampleEntity', id, block);
|
log('author', id, block);
|
||||||
|
|
||||||
return indexer.getSubgraphEntity(ExampleEntity, id, block.hash);
|
return indexer.getSubgraphEntity(Author, id, block);
|
||||||
},
|
},
|
||||||
|
|
||||||
events: async (_: any, { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }) => {
|
events: async (_: any, { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }) => {
|
||||||
|
@ -77,40 +77,41 @@ type Query {
|
|||||||
eventsInRange(fromBlockNumber: Int!, toBlockNumber: Int!): [ResultEvent!]
|
eventsInRange(fromBlockNumber: Int!, toBlockNumber: Int!): [ResultEvent!]
|
||||||
getMethod(blockHash: String!, contractAddress: String!): ResultString!
|
getMethod(blockHash: String!, contractAddress: String!): ResultString!
|
||||||
_test(blockHash: String!, contractAddress: String!): ResultBigInt!
|
_test(blockHash: String!, contractAddress: String!): ResultBigInt!
|
||||||
relatedEntity(id: String!, block: Block_height): RelatedEntity!
|
blog(id: String!, block: Block_height): Blog!
|
||||||
exampleEntity(id: String!, block: Block_height): ExampleEntity!
|
author(id: String!, block: Block_height): Author!
|
||||||
manyRelatedEntity(id: String!, block: Block_height): ManyRelatedEntity!
|
category(id: String!, block: Block_height): Category!
|
||||||
getStateByCID(cid: String!): ResultIPLDBlock
|
getStateByCID(cid: String!): ResultIPLDBlock
|
||||||
getState(blockHash: String!, contractAddress: String!, kind: String): ResultIPLDBlock
|
getState(blockHash: String!, contractAddress: String!, kind: String): ResultIPLDBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
enum EnumType {
|
enum BlogKind {
|
||||||
choice1
|
short
|
||||||
choice2
|
long
|
||||||
}
|
}
|
||||||
|
|
||||||
type RelatedEntity {
|
type Blog {
|
||||||
id: ID!
|
id: ID!
|
||||||
paramBigInt: BigInt!
|
kind: BlogKind!
|
||||||
bigIntArray: [BigInt!]!
|
isActive: Boolean!
|
||||||
|
reviews: [BigInt!]!
|
||||||
|
author: Author!
|
||||||
|
categories: [Category!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type ManyRelatedEntity {
|
type Category {
|
||||||
id: ID!
|
id: ID!
|
||||||
count: BigInt!
|
count: BigInt!
|
||||||
|
name: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExampleEntity {
|
type Author {
|
||||||
id: ID!
|
id: ID!
|
||||||
count: BigInt!
|
blogCount: BigInt!
|
||||||
paramString: String!
|
name: String!
|
||||||
|
rating: BigDecimal!
|
||||||
paramInt: Int!
|
paramInt: Int!
|
||||||
paramBoolean: Boolean!
|
|
||||||
paramBytes: Bytes!
|
paramBytes: Bytes!
|
||||||
paramEnum: EnumType!
|
blogs: [Blog!]!
|
||||||
paramBigDecimal: BigDecimal!
|
|
||||||
related: RelatedEntity!
|
|
||||||
manyRelated: [ManyRelatedEntity!]!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
|
Loading…
Reference in New Issue
Block a user