Skip to content

Commit

Permalink
Implement remaining graph-node host APIs for templates (#475)
Browse files Browse the repository at this point in the history
* Implement createWithContext graph-node dataSource host API

* Parse individual context entries

* Implement graph-node dataSource context host API

* Handle array type fields and refactor code

* Resolve all values when parsing an array field

* Fix wasm entity creation with array type fields

* Required codegen changes
  • Loading branch information
prathamesh0 authored Nov 20, 2023
1 parent c2070a8 commit 2faf905
Show file tree
Hide file tree
Showing 11 changed files with 155 additions and 57 deletions.
7 changes: 7 additions & 0 deletions packages/codegen/src/data/entities/Contract.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ columns:
pgType: integer
tsType: number
columnType: Column
- name: context
pgType: jsonb
tsType: 'Record<string, { data: any, type: number }>'
columnType: Column
columnOptions:
- option: nullable
value: true
imports:
- toImport:
- Entity
Expand Down
4 changes: 2 additions & 2 deletions packages/codegen/src/templates/database-template.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,10 @@ export class Database implements DatabaseInterface {
return this._baseDatabase.saveBlockProgress(repo, block);
}

async saveContract (queryRunner: QueryRunner, address: string, kind: string, checkpoint: boolean, startingBlock: number): Promise<Contract> {
async saveContract (queryRunner: QueryRunner, address: string, kind: string, checkpoint: boolean, startingBlock: number, context?: any): Promise<Contract> {
const repo = queryRunner.manager.getRepository(Contract);

return this._baseDatabase.saveContract(repo, address, kind, checkpoint, startingBlock);
return this._baseDatabase.saveContract(repo, address, kind, checkpoint, startingBlock, context);
}

async updateSyncStatusIndexedBlock (queryRunner: QueryRunner, blockHash: string, blockNumber: number, force = false): Promise<SyncStatus> {
Expand Down
2 changes: 1 addition & 1 deletion packages/codegen/src/templates/entity-template.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class {{className}} {{~#if implements}} implements {{implements}} {{~/if}
{{~#unless @last}},{{/unless}}
{{~/each}} }
{{~/if}})
{{column.name}}!: {{column.tsType}};
{{column.name}}!: {{{column.tsType}}};
{{~#unless @last}}

{{/unless}}
Expand Down
4 changes: 2 additions & 2 deletions packages/codegen/src/templates/indexer-template.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -595,8 +595,8 @@ export class Indexer implements IndexerInterface {
}

{{/if}}
async watchContract (address: string, kind: string, checkpoint: boolean, startingBlock: number): Promise<void> {
return this._baseIndexer.watchContract(address, kind, checkpoint, startingBlock);
async watchContract (address: string, kind: string, checkpoint: boolean, startingBlock: number, context?: any): Promise<void> {
return this._baseIndexer.watchContract(address, kind, checkpoint, startingBlock, context);
}

updateStateStatusMap (address: string, stateStatus: StateStatus): void {
Expand Down
24 changes: 20 additions & 4 deletions packages/graph-node/src/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export const instantiate = async (
const dbEntity = await database.saveEntity(entityName, dbData);
database.cacheUpdatedEntityByName(entityName, dbEntity);

// Update the in-memory subgraph state enabled
// Update the in-memory subgraph state if enabled
if (indexer.serverConfig.enableState) {
// Prepare diff data for the entity update
assert(indexer.getRelationsMap);
Expand Down Expand Up @@ -698,10 +698,14 @@ export const instantiate = async (
return Address.fromString(addressStringPtr);
},
'dataSource.context': async () => {
// TODO: Implement use in data source templates.
// https://thegraph.com/docs/en/developer/create-subgraph-hosted/#data-source-context
assert(context.contractAddress);
const contract = indexer.isWatchedContract(context.contractAddress);

if (!contract) {
return null;
}

return Entity.__new();
return database.toGraphContext(instanceExports, contract.context);
},
'dataSource.network': async () => {
assert(dataSource);
Expand All @@ -715,6 +719,18 @@ export const instantiate = async (
assert(indexer.watchContract);
assert(context.block);
await indexer.watchContract(utils.getAddress(addressString), contractKind, true, Number(context.block.blockNumber));
},
'dataSource.createWithContext': async (name: number, params: number, dataSourceContext: number) => {
const [addressStringPtr] = __getArray(params);
const addressString = __getString(addressStringPtr);
const contractKind = __getString(name);

const contextInstance = await Entity.wrap(dataSourceContext);
const dbData = await database.fromGraphContext(instanceExports, contextInstance);

assert(indexer.watchContract);
assert(context.block);
await indexer.watchContract(utils.getAddress(addressString), contractKind, true, Number(context.block.blockNumber), dbData);
}
},
json: {
Expand Down
4 changes: 2 additions & 2 deletions packages/util/src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -601,13 +601,13 @@ export class Database {
.getMany();
}

async saveContract (repo: Repository<ContractInterface>, address: string, kind: string, checkpoint: boolean, startingBlock: number): Promise<ContractInterface> {
async saveContract (repo: Repository<ContractInterface>, address: string, kind: string, checkpoint: boolean, startingBlock: number, context?: any): Promise<ContractInterface> {
const contract = await repo
.createQueryBuilder()
.where('address = :address', { address })
.getOne();

const entity = repo.create({ address, kind, checkpoint, startingBlock });
const entity = repo.create({ address, kind, checkpoint, startingBlock, context });

// If contract already present, overwrite fields.
if (contract) {
Expand Down
63 changes: 59 additions & 4 deletions packages/util/src/graph/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,20 @@ import { RawSqlResultsToEntityTransformer } from 'typeorm/query-builder/transfor
import { SelectionNode } from 'graphql';
import _ from 'lodash';
import debug from 'debug';
import JSONbig from 'json-bigint';

import { Database as BaseDatabase, QueryOptions, Where, CanonicalBlockHeight } from '../database';
import { BlockProgressInterface } from '../types';
import { cachePrunedEntitiesCount, eventProcessingLoadEntityCacheHitCount, eventProcessingLoadEntityCount, eventProcessingLoadEntityDBQueryDuration } from '../metrics';
import { ServerConfig } from '../config';
import { Block, fromEntityValue, getLatestEntityFromEntity, resolveEntityFieldConflicts, toEntityValue } from './utils';
import { Block, formatValue, fromEntityValue, getLatestEntityFromEntity, parseEntityValue, resolveEntityFieldConflicts, toEntityValue } from './utils';
import { fromStateEntityValues } from './state-utils';
import { ValueKind } from './types';

const log = debug('vulcanize:graph-database');

const JSONbigNative = JSONbig({ useNativeBigInt: true });

export const FILTER_CHANGE_BLOCK = '_change_block';

export const DEFAULT_LIMIT = 100;
Expand Down Expand Up @@ -953,7 +957,51 @@ export class GraphDatabase {
return entityInstance;
}

async fromGraphEntity (instanceExports: any, block: Block, entityName: string, entityInstance: any): Promise<{ [key: string]: any } > {
async toGraphContext (instanceExports: any, contextData: any): Promise<any> {
const { Entity } = instanceExports;
const contextInstance = await Entity.__new();

const { __newString } = instanceExports;
const contextValuePromises = Object.entries(contextData as Record<string, { type: ValueKind, data: any }>).map(async ([key, { type, data }]) => {
const contextKey = await __newString(key);

const value = JSONbigNative.parse(data);
const contextValue = await formatValue(instanceExports, type, value);

return contextInstance.set(contextKey, contextValue);
});

await Promise.all(contextValuePromises);

return contextInstance;
}

async fromGraphContext (instanceExports: any, contextInstance: any): Promise<{ [key: string]: any }> {
const { __getString, __getArray, ValueTypedMapEntry } = instanceExports;

const contextInstanceEntries = __getArray(await contextInstance.entries);
const contextValuePromises = contextInstanceEntries.map(async (entryPtr: any) => {
const entry = await ValueTypedMapEntry.wrap(entryPtr);
const contextKeyPtr = await entry.key;
const contextValuePtr = await entry.value;

const key = await __getString(contextKeyPtr);
const parsedValue = await parseEntityValue(instanceExports, contextValuePtr);

return { key, ...parsedValue };
});

const contextValues = await Promise.all(contextValuePromises);

return contextValues.reduce((acc: { [key: string]: any }, contextValue: any) => {
const { key, type, data } = contextValue;
acc[key] = { type, data: JSONbigNative.stringify(data) };

return acc;
}, {});
}

async fromGraphEntity (instanceExports: any, block: Block, entityName: string, entityInstance: any): Promise<{ [key: string]: any }> {
// TODO: Cache schema/columns.
const repo = this._conn.getRepository(entityName);
const entityFields = repo.metadata.columns;
Expand Down Expand Up @@ -981,10 +1029,17 @@ export class GraphDatabase {

// Get blockNumber as _blockNumber and blockHash as _blockHash from the entityInstance (wasm).
if (['_blockNumber', '_blockHash'].includes(propertyName)) {
return fromEntityValue(instanceExports, entityInstance, propertyName.slice(1));
const entityValue = await fromEntityValue(instanceExports, entityInstance, propertyName.slice(1));
return entityValue.data;
}

const entityValue = await fromEntityValue(instanceExports, entityInstance, propertyName);

if (entityValue.type === ValueKind.ARRAY) {
return entityValue.data.map((el: any) => el.data);
}

return fromEntityValue(instanceExports, entityInstance, propertyName);
return entityValue.data;
}, {});

const entityValues = await Promise.all(entityValuePromises);
Expand Down
11 changes: 11 additions & 0 deletions packages/util/src/graph/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ export enum ValueKind {
BIGINT = 7,
}

export const TypeNameToValueKind: Record<string, ValueKind> = {
String: ValueKind.STRING,
Int: ValueKind.INT,
BigDecimal: ValueKind.BIGDECIMAL,
Boolean: ValueKind.BOOL,
Array: ValueKind.ARRAY,
Null: ValueKind.NULL,
Bytes: ValueKind.BYTES,
BigInt: ValueKind.BIGINT
};

export enum Level {
CRITICAL = 0,
ERROR = 1,
Expand Down
Loading

0 comments on commit 2faf905

Please sign in to comment.