mirror of
https://github.com/cerc-io/watcher-ts
synced 2024-11-19 20:36:19 +00:00
Generate relations and entity types maps in indexer (#88)
This commit is contained in:
parent
d96cc095bb
commit
18f73e57c0
@ -11,6 +11,7 @@ import { Writable } from 'stream';
|
||||
|
||||
import { getTsForSol, getPgForTs, getTsForGql } from './utils/type-mappings';
|
||||
import { Param } from './utils/types';
|
||||
import { getFieldType } from './utils/subgraph';
|
||||
|
||||
const TEMPLATE_FILE = './templates/entity-template.handlebars';
|
||||
const TABLES_DIR = './data/entities';
|
||||
@ -334,7 +335,7 @@ export class Entity {
|
||||
columnType: 'Column'
|
||||
};
|
||||
|
||||
const { typeName, array, nullable } = this._getFieldType(field.type);
|
||||
const { typeName, array, nullable } = getFieldType(field.type);
|
||||
let tsType = getTsForGql(typeName);
|
||||
|
||||
if (!tsType) {
|
||||
@ -396,19 +397,4 @@ export class Entity {
|
||||
|
||||
return entityObject;
|
||||
}
|
||||
|
||||
_getFieldType (typeNode: any): { typeName: string, array: boolean, nullable: boolean } {
|
||||
if (typeNode.kind === 'ListType') {
|
||||
return { typeName: this._getFieldType(typeNode.type).typeName, array: true, nullable: true };
|
||||
}
|
||||
|
||||
if (typeNode.kind === 'NonNullType') {
|
||||
const fieldType = this._getFieldType(typeNode.type);
|
||||
|
||||
return { typeName: fieldType.typeName, array: fieldType.array, nullable: false };
|
||||
}
|
||||
|
||||
// If 'NamedType'.
|
||||
return { typeName: typeNode.name.value, array: false, nullable: true };
|
||||
}
|
||||
}
|
||||
|
@ -12,17 +12,20 @@ import _ from 'lodash';
|
||||
import { getTsForSol } from './utils/type-mappings';
|
||||
import { Param } from './utils/types';
|
||||
import { MODE_ETH_CALL, MODE_STORAGE } from './utils/constants';
|
||||
import { getFieldType } from './utils/subgraph';
|
||||
|
||||
const TEMPLATE_FILE = './templates/indexer-template.handlebars';
|
||||
|
||||
export class Indexer {
|
||||
_queries: Array<any>;
|
||||
_events: Array<any>;
|
||||
_subgraphEntities: Array<any>;
|
||||
_templateString: string;
|
||||
|
||||
constructor () {
|
||||
this._queries = [];
|
||||
this._events = [];
|
||||
this._subgraphEntities = [];
|
||||
this._templateString = fs.readFileSync(path.resolve(__dirname, TEMPLATE_FILE)).toString();
|
||||
}
|
||||
|
||||
@ -99,6 +102,70 @@ export class Indexer {
|
||||
this._events.push(eventObject);
|
||||
}
|
||||
|
||||
addSubgraphEntities (subgraphSchemaDocument: any): void {
|
||||
// Add subgraph entities for creating the relations and entity types maps in the indexer.
|
||||
const subgraphTypeDefs = subgraphSchemaDocument.definitions;
|
||||
|
||||
subgraphTypeDefs.forEach((def: any) => {
|
||||
if (def.kind !== 'ObjectTypeDefinition') {
|
||||
return;
|
||||
}
|
||||
|
||||
let entityObject: any = {
|
||||
className: def.name.value,
|
||||
columns: [],
|
||||
relations: []
|
||||
};
|
||||
|
||||
entityObject = this._addSubgraphColumns(subgraphTypeDefs, entityObject, def);
|
||||
|
||||
this._subgraphEntities.push(entityObject);
|
||||
});
|
||||
}
|
||||
|
||||
_addSubgraphColumns (subgraphTypeDefs: any, entityObject: any, def: any): any {
|
||||
// Process each field of the entity type def.
|
||||
def.fields.forEach((field: any) => {
|
||||
const columnObject: any = {
|
||||
name: field.name.value,
|
||||
type: '',
|
||||
isRelation: false,
|
||||
isArray: false
|
||||
};
|
||||
|
||||
// Process field properties.
|
||||
const { typeName, array } = getFieldType(field.type);
|
||||
|
||||
columnObject.type = typeName;
|
||||
columnObject.isArray = array;
|
||||
|
||||
// Add a relation if the type is a object type available in subgraph type defs.
|
||||
const columnType = subgraphTypeDefs.find((def: any) => {
|
||||
return def.name.value === typeName && def.kind === 'ObjectTypeDefinition';
|
||||
});
|
||||
|
||||
if (columnType) {
|
||||
columnObject.isRelation = true;
|
||||
|
||||
// Process the derivedFrom directive for the relation field.
|
||||
const { isDerived, derivedFromField } = this._getDerivedFrom(field.directives);
|
||||
|
||||
if (isDerived) {
|
||||
columnObject.isDerived = true;
|
||||
columnObject.derivedFromField = derivedFromField;
|
||||
} else {
|
||||
columnObject.isDerived = false;
|
||||
}
|
||||
|
||||
entityObject.relations.push(columnObject);
|
||||
}
|
||||
|
||||
entityObject.columns.push(columnObject);
|
||||
});
|
||||
|
||||
return entityObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the indexer file generated from a template to a stream.
|
||||
* @param outStream A writable output stream to write the indexer file to.
|
||||
@ -110,6 +177,7 @@ export class Indexer {
|
||||
const obj = {
|
||||
inputFileNames,
|
||||
queries: this._queries,
|
||||
subgraphEntities: this._subgraphEntities,
|
||||
constants: {
|
||||
MODE_ETH_CALL,
|
||||
MODE_STORAGE
|
||||
@ -120,4 +188,21 @@ export class Indexer {
|
||||
const indexer = template(obj);
|
||||
outStream.write(indexer);
|
||||
}
|
||||
|
||||
_getDerivedFrom (directives: any): { isDerived: boolean, derivedFromField: string } {
|
||||
const derivedFromDirective = directives.find((directive: any) => {
|
||||
return directive.name.value === 'derivedFrom';
|
||||
});
|
||||
|
||||
let isDerived = false;
|
||||
let derivedFromField = '';
|
||||
|
||||
// Get the derivedFrom field name if derivedFrom directive is present.
|
||||
if (derivedFromDirective) {
|
||||
isDerived = true;
|
||||
derivedFromField = derivedFromDirective.arguments[0].value.value;
|
||||
}
|
||||
|
||||
return { isDerived, derivedFromField };
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,10 @@ import { HookStatus } from './entity/HookStatus';
|
||||
import { BlockProgress } from './entity/BlockProgress';
|
||||
import { IPLDBlock } from './entity/IPLDBlock';
|
||||
|
||||
{{#each subgraphEntities as | subgraphEntity |}}
|
||||
import { {{subgraphEntity.className}} } from './entity/{{subgraphEntity.className}}';
|
||||
{{/each}}
|
||||
|
||||
const log = debug('vulcanize:indexer');
|
||||
|
||||
{{#each events as | event |}}
|
||||
@ -129,7 +133,10 @@ export class Indexer implements IndexerInterface {
|
||||
this._contract = new ethers.utils.Interface(this._abi);
|
||||
|
||||
this._entityTypesMap = new Map();
|
||||
this._populateEntityTypesMap();
|
||||
|
||||
this._relationsMap = new Map();
|
||||
this._populateRelationsMap();
|
||||
}
|
||||
|
||||
async init (): Promise<void> {
|
||||
@ -563,6 +570,48 @@ export class Indexer implements IndexerInterface {
|
||||
return this._baseIndexer.getAncestorAtDepth(blockHash, depth);
|
||||
}
|
||||
|
||||
getEntityTypesMap (): Map<string, { [key: string]: string }> {
|
||||
return this._entityTypesMap;
|
||||
}
|
||||
|
||||
_populateEntityTypesMap (): void {
|
||||
{{#each subgraphEntities as | subgraphEntity |}}
|
||||
this._entityTypesMap.set('{{subgraphEntity.className}}', {
|
||||
{{#each subgraphEntity.columns as | column |}}
|
||||
{{#unless column.isDerived}}
|
||||
{{~#unless @first}},
|
||||
{{/unless}}
|
||||
{{column.name}}: '{{column.type}}'
|
||||
{{~/unless}}
|
||||
{{/each}}
|
||||
|
||||
});
|
||||
{{/each}}
|
||||
}
|
||||
|
||||
_populateRelationsMap (): void {
|
||||
{{#each subgraphEntities as | subgraphEntity |}}
|
||||
{{#if subgraphEntity.relations}}
|
||||
this._relationsMap.set({{subgraphEntity.className}}, {
|
||||
{{#each subgraphEntity.relations as | relation |}}
|
||||
{{~#unless @first}},
|
||||
{{/unless}}
|
||||
{{relation.name}}: {
|
||||
entity: {{relation.type}},
|
||||
isArray: {{relation.isArray}},
|
||||
isDerived: {{relation.isDerived}}
|
||||
{{~#if relation.isDerived}},
|
||||
field: '{{relation.derivedFromField}}'
|
||||
{{~/if}}
|
||||
|
||||
}
|
||||
{{~/each}}
|
||||
|
||||
});
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
}
|
||||
|
||||
async _fetchAndSaveEvents ({ cid: blockCid, blockHash }: DeepPartial<BlockProgress>): Promise<BlockProgress> {
|
||||
assert(blockHash);
|
||||
|
||||
|
@ -20,9 +20,6 @@ export function parseSubgraphSchema (subgraphPath: string): any {
|
||||
const subgraphTypeDefs = subgraphSchemaDocument.definitions;
|
||||
|
||||
subgraphTypeDefs.forEach((def: any) => {
|
||||
// Remove type directives.
|
||||
def.directives = [];
|
||||
|
||||
if (def.kind === 'ObjectTypeDefinition') {
|
||||
def.fields.forEach((field: any) => {
|
||||
// Parse the field type.
|
||||
@ -37,6 +34,21 @@ export function parseSubgraphSchema (subgraphPath: string): any {
|
||||
return subgraphSchemaDocument;
|
||||
}
|
||||
|
||||
export function getFieldType (typeNode: any): { typeName: string, array: boolean, nullable: boolean } {
|
||||
if (typeNode.kind === 'ListType') {
|
||||
return { typeName: getFieldType(typeNode.type).typeName, array: true, nullable: true };
|
||||
}
|
||||
|
||||
if (typeNode.kind === 'NonNullType') {
|
||||
const fieldType = getFieldType(typeNode.type);
|
||||
|
||||
return { typeName: fieldType.typeName, array: fieldType.array, nullable: false };
|
||||
}
|
||||
|
||||
// If 'NamedType'.
|
||||
return { typeName: typeNode.name.value, array: false, nullable: true };
|
||||
}
|
||||
|
||||
function parseType (typeNode: any): any {
|
||||
// Check if 'NamedType' is reached.
|
||||
if (typeNode.kind === 'NamedType') {
|
||||
|
@ -15,6 +15,7 @@ _solToTs.set('uint16', 'number');
|
||||
_solToTs.set('uint64', 'bigint');
|
||||
_solToTs.set('uint128', 'bigint');
|
||||
_solToTs.set('uint256', 'bigint');
|
||||
_solToTs.set('uint', 'bigint');
|
||||
_solToTs.set('address', 'string');
|
||||
_solToTs.set('bool', 'boolean');
|
||||
_solToTs.set('bytes', 'string');
|
||||
|
@ -76,6 +76,7 @@ export class Visitor {
|
||||
stateVariableDeclarationVisitor (node: any): void {
|
||||
// TODO Handle multiples variables in a single line.
|
||||
// TODO Handle array types.
|
||||
// TODO Handle user defined type .
|
||||
const variable = node.variables[0];
|
||||
const name: string = variable.name;
|
||||
const stateVariableType: string = variable.typeName.type;
|
||||
@ -83,13 +84,6 @@ export class Visitor {
|
||||
const params: Param[] = [];
|
||||
|
||||
let typeName = variable.typeName;
|
||||
|
||||
// TODO Handle user defined type.
|
||||
if (typeName.type === 'UserDefinedTypeName') {
|
||||
// Skip in case of UserDefinedTypeName.
|
||||
return;
|
||||
}
|
||||
|
||||
let numParams = 0;
|
||||
|
||||
// If the variable type is mapping, extract key as a param:
|
||||
@ -100,6 +94,11 @@ export class Visitor {
|
||||
numParams++;
|
||||
}
|
||||
|
||||
if (['UserDefinedTypeName', 'ArrayTypeName'].includes(typeName.type)) {
|
||||
// Skip in case of UserDefinedTypeName | ArrayTypeName.
|
||||
return;
|
||||
}
|
||||
|
||||
const returnType = typeName.name;
|
||||
|
||||
this._schema.addQuery(name, params, returnType);
|
||||
@ -138,6 +137,7 @@ export class Visitor {
|
||||
this._entity.addSubgraphEntities(subgraphSchemaDocument);
|
||||
this._resolvers.addSubgraphResolvers(subgraphSchemaDocument);
|
||||
this._reset.addSubgraphEntities(subgraphSchemaDocument);
|
||||
this._indexer.addSubgraphEntities(subgraphSchemaDocument);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user