Skip to content

Commit

Permalink
Add queries to entity generation.
Browse files Browse the repository at this point in the history
  • Loading branch information
prathamesh0 committed Sep 22, 2021
1 parent e042078 commit d83d6e5
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 68 deletions.
10 changes: 5 additions & 5 deletions packages/codegen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* Run the following command to generate a watcher from a contract file:

```bash
yarn codegen -i <input-file-path> -c <contract-name> -o [output-folder] -m [eth_call | storage] -f [true | false]
yarn codegen --input-file <input-file-path> --contract-name <contract-name> --output-folder [output-folder] --mode [eth_call | storage] --flatten [true | false]
```

* `input-file`(alias: `i`): Input contract file path or an URL (required).
Expand All @@ -27,11 +27,11 @@
Examples:

```bash
yarn codegen -i ./test/examples/contracts/ERC20.sol -c ERC20 -o ../ERC20-watcher -m eth_call
yarn codegen --input-file ./test/examples/contracts/ERC20.sol --contract-name ERC20 --output-folder ../my-erc20-watcher --mode eth_call
```

```bash
yarn codegen -i https://git.io/Jupci -c ERC721 -o ../ERC721-watcher -m storage
yarn codegen --input-file https://git.io/Jupci --contract-name ERC721 --output-folder ../my-erc721-watcher --mode storage
```

## Demo
Expand All @@ -45,13 +45,13 @@
* Generate a watcher from a contract file:

```bash
yarn codegen -i ../../node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol -c ERC20 -o ../new-watcher -m eth_call
yarn codegen --input-file ../../node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol --contract-name ERC20 --output-folder ../demo-erc20-watcher --mode eth_call
```

* Generate a watcher from a flattened contract file from an URL:

```bash
yarn codegen -i https://git.io/Jupci -c ERC721 -o ../new-watcher -m storage
yarn codegen --input-file https://git.io/Jupci --contract-name ERC721 --output-folder ../demo-erc721-watcher --mode storage
```

## References
Expand Down
124 changes: 66 additions & 58 deletions packages/codegen/src/entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,75 +4,83 @@

import fs from 'fs';
import path from 'path';
import assert from 'assert';
import Handlebars from 'handlebars';
import { Writable } from 'stream';

import { getTsForSol, getPgForTs } from './utils/typemappings';
import { Param } from './utils/types';

const TEMPLATE_FILE = './templates/entityTemplate.handlebars';

const main = async (): Promise<void> => {
const templateString = fs.readFileSync(path.resolve(__dirname, TEMPLATE_FILE)).toString();
export class Entity {
_entities: Array<any>;
_templateString: string;

// TODO Replace registerHelper implementation with inbuilt helpers.
Handlebars.registerHelper('indexHelper', function (context) {
let toReturn = '';
constructor () {
this._entities = [];
this._templateString = fs.readFileSync(path.resolve(__dirname, TEMPLATE_FILE)).toString();
}

for (let i = 0; i < context.length; i++) {
toReturn = `${toReturn}@Index(['`;
toReturn = `${toReturn}${context[i].columns.join('\', \'')}`;
toReturn = `${toReturn}']`;
toReturn = `${toReturn} { unique: ${context[i].unique} })`;
addQuery (name: string, params: Array<Param>, returnType: string): void {
if (this._entities.filter(entity => entity.name === name).length !== 0) {
return;
}

return new Handlebars.SafeString(toReturn);
});
const entityObject: any = {
// Capitalize the first letter of name.
className: `${name.charAt(0).toUpperCase()}${name.slice(1)}`,
indexOn: {},
columns: [{}],
returnType: returnType
};

Handlebars.registerHelper('columnHelper', function (context) {
let toReturn = '';
entityObject.indexOn.columns = params.map((param) => {
return param.name;
});
entityObject.indexOn.unique = true;

for (let i = 0; i < context.length; i++) {
toReturn = `${toReturn}@Column('`;
entityObject.columns = params.map((param) => {
const length = param.type === 'address' ? 42 : null;
const name = param.name;

// TODO Prepare a GraphQL -> postgres typemapping.
toReturn = `${toReturn}${context[i].columnType}', `;
const tsType = getTsForSol(param.type);
assert(tsType);

// TODO Use #if for misc properties.
// TODO Specify length for strings according to contract variable type.
toReturn = context[i].length ? `${toReturn}{ length: ${context[i].length} })\n` : ')\n';
const pgType = getPgForTs(tsType);
assert(pgType);

// TODO Prepare a GraphQL -> ts typemapping.
toReturn = `${toReturn}\t${context[i].columnName}!: ${context[i].columnType};\n\n\t`;
}
return {
name,
pgType,
tsType,
length
};
});

const tsReturnType = getTsForSol(returnType);
assert(tsReturnType);

const pgReturnType = getPgForTs(tsReturnType);
assert(pgReturnType);

entityObject.columns.push({
name: 'value',
pgType: pgReturnType,
tsType: tsReturnType
});

this._entities.push(entityObject);
}

return new Handlebars.SafeString(toReturn);
});

const template = Handlebars.compile(templateString);
const obj = {
indexOn: [
{
columns: [
'blockHash',
'contractAddress'
],
unique: true
}
],
className: 'Allowance',
columns: [
{
columnName: 'blockHash',
columnType: 'string',
length: 66
},
{
columnName: 'contractAddress',
columnType: 'string',
length: 42
}
]
};
console.log(template(obj));
};

main().catch(err => {
console.error(err);
});
exportEntities (entityDir: string): void {
const template = Handlebars.compile(this._templateString);
this._entities.forEach(entityObj => {
const entity = template(entityObj);
const outStream: Writable = entityDir
? fs.createWriteStream(path.join(entityDir, `${entityObj.className}.ts`))
: process.stdout;
outStream.write(entity);
});
}
}
8 changes: 8 additions & 0 deletions packages/codegen/src/generate-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ const main = async (): Promise<void> => {

const artifactsFolder = path.join(outputDir, 'src/artifacts');
if (!fs.existsSync(artifactsFolder)) fs.mkdirSync(artifactsFolder, { recursive: true });

const entitiesFolder = path.join(outputDir, 'src/entities');
if (!fs.existsSync(entitiesFolder)) fs.mkdirSync(entitiesFolder, { recursive: true });
}

let outStream = outputDir
Expand Down Expand Up @@ -143,6 +146,11 @@ const main = async (): Promise<void> => {
? fs.createWriteStream(path.join(outputDir, 'tsconfig.json'))
: process.stdout;
exportTSConfig(outStream);

const entityDir = outputDir
? path.join(outputDir, 'src/entities')
: '';
visitor.exportEntities(entityDir);
};

main().catch(err => {
Expand Down
2 changes: 2 additions & 0 deletions packages/codegen/src/indexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ export class Indexer {
}

addQuery (name: string, params: Array<Param>, returnType: string): void {
// TODO Use array.some()
if (this._queries.filter(query => query.name === name).length !== 0) {
return;
}

// TODO No unnecessary cloning.
const queryObject = {
name: _.cloneDeep(name),
params: _.cloneDeep(params),
Expand Down
25 changes: 22 additions & 3 deletions packages/codegen/src/templates/entityTemplate.handlebars
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
//
// Copyright 2021 Vulcanize, Inc.
//

import { Entity, PrimaryGeneratedColumn, Column, Index } from 'typeorm';

@Entity()
{{indexHelper indexOn}}

{{#if indexOn.columns}}
@Index(['blockHash', 'contractAddress'
{{~#each indexOn.columns}}, '{{this}}'
{{~/each}}], { unique: {{indexOn.unique}} })
{{/if}}
export class {{className}} {
@PrimaryGeneratedColumn()
id!: number;

{{columnHelper columns}}
@Column('varchar', { length: 66 })
blockHash!: string;

@Column('varchar', { length: 42 })
contractAddress!: string;

{{#each columns as | column |}}
@Column('{{column.pgType}}' {{~#if column.length}}, { length: {{column.length}} } {{~/if}})
{{column.name}}!: {{column.tsType}};

{{/each}}
@Column('text', { nullable: true })
proof!: string;
}
2 changes: 1 addition & 1 deletion packages/codegen/src/templates/tsconfigTemplate.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */

/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */

/* Advanced Options */
Expand Down
12 changes: 11 additions & 1 deletion packages/codegen/src/utils/typemappings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

const _solToTs: Map<string, string> = new Map();
const _tsToGql: Map<string, string> = new Map();
const _tsToPg: Map<string, string> = new Map();

// TODO Get typemapping from ethersjs.
_solToTs.set('string', 'string');
Expand All @@ -18,6 +19,11 @@ _tsToGql.set('number', 'Int');
_tsToGql.set('bigint', 'BigInt');
_tsToGql.set('boolean', 'Boolean');

_tsToPg.set('string', 'varchar');
_tsToPg.set('number', 'numeric');
_tsToPg.set('bigint', 'numeric');
_tsToPg.set('boolean', 'boolean');

function getTsForSol (solType: string): string | undefined {
return _solToTs.get(solType);
}
Expand All @@ -26,4 +32,8 @@ function getGqlForTs (tsType: string): string | undefined {
return _tsToGql.get(tsType);
}

export { getTsForSol, getGqlForTs };
function getPgForTs (tsType: string): string | undefined {
return _tsToPg.get(tsType);
}

export { getTsForSol, getGqlForTs, getPgForTs };
9 changes: 9 additions & 0 deletions packages/codegen/src/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import { Writable } from 'stream';

import { Entity } from './entity';
import { Indexer } from './indexer';
import { Resolvers } from './resolvers';
import { Schema } from './schema';
Expand All @@ -13,11 +14,13 @@ export class Visitor {
_schema: Schema;
_resolvers: Resolvers;
_indexer: Indexer;
_entity: Entity

constructor () {
this._schema = new Schema();
this._resolvers = new Resolvers();
this._indexer = new Indexer();
this._entity = new Entity();
}

/**
Expand All @@ -37,6 +40,7 @@ export class Visitor {
this._schema.addQuery(name, params, returnType);
this._resolvers.addQuery(name, params, returnType);
this._indexer.addQuery(name, params, returnType);
this._entity.addQuery(name, params, returnType);
}
}

Expand Down Expand Up @@ -68,6 +72,7 @@ export class Visitor {
this._schema.addQuery(name, params, returnType);
this._resolvers.addQuery(name, params, returnType);
this._indexer.addQuery(name, params, returnType);
this._entity.addQuery(name, params, returnType);
}

/**
Expand Down Expand Up @@ -98,4 +103,8 @@ export class Visitor {
exportIndexer (outStream: Writable): void {
this._indexer.exportIndexer(outStream);
}

exportEntities (entityDir: string): void {
this._entity.exportEntities(entityDir);
}
}

0 comments on commit d83d6e5

Please sign in to comment.