Skip to content

Commit

Permalink
Generate relations and entity types maps in indexer (#88)
Browse files Browse the repository at this point in the history
  • Loading branch information
prathamesh0 committed Dec 23, 2021
1 parent 75e9025 commit 184fd2e
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 26 deletions.
18 changes: 2 additions & 16 deletions packages/codegen/src/entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 };
}
}
85 changes: 85 additions & 0 deletions packages/codegen/src/indexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down Expand Up @@ -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.
Expand All @@ -110,6 +177,7 @@ export class Indexer {
const obj = {
inputFileNames,
queries: this._queries,
subgraphEntities: this._subgraphEntities,
constants: {
MODE_ETH_CALL,
MODE_STORAGE
Expand All @@ -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 };
}
}
49 changes: 49 additions & 0 deletions packages/codegen/src/templates/indexer-template.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -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 |}}
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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);

Expand Down
18 changes: 15 additions & 3 deletions packages/codegen/src/utils/subgraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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') {
Expand Down
1 change: 1 addition & 0 deletions packages/codegen/src/utils/type-mappings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
14 changes: 7 additions & 7 deletions packages/codegen/src/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,20 +76,14 @@ 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;

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:
Expand All @@ -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);
Expand Down Expand Up @@ -138,6 +137,7 @@ export class Visitor {
this._entity.addSubgraphEntities(subgraphSchemaDocument);
this._resolvers.addSubgraphResolvers(subgraphSchemaDocument);
this._reset.addSubgraphEntities(subgraphSchemaDocument);
this._indexer.addSubgraphEntities(subgraphSchemaDocument);
}

/**
Expand Down

0 comments on commit 184fd2e

Please sign in to comment.