Skip to content

Commit

Permalink
Support basic array return types of contract methods in codegen (#363)
Browse files Browse the repository at this point in the history
* Fix ValueTransformers to check for undefined value

* Create Result types if needed

* Update codegen to include array return type queries in schema

* Update codegen to include array return type queries in resolvers

* Update codegen to include array return type queries in indexers

* Codegen skip db caching methods for queries returning unhandled types

* Codegen handle bigint array in indexer

* Update codegen to handle basic array types

* Remove redundant code and refactor

* Codegen map Solidity types directly to GraphQL types

* Codegen get TS from GQL types

* Codegen handle ethers BigNumber array in indexer template

* Fix GQL type returned in client queries

* Codegen unused method isElementaryType should not be exported

* Codegen update README with solc version in config

* Codegen update README with continue-on-error flag

* Codegen add continue-on-error flag for storage mode

* Codegen generate gitignore and license for watchers

* Codegen refactor gitignore and license generation utils

---------

Co-authored-by: Dhruv Srivastava <dhruvdhs.ds@gmail.com>
  • Loading branch information
nikugogoi and dafaqdhruv authored Apr 20, 2023
1 parent aaa6043 commit 49d6789
Show file tree
Hide file tree
Showing 17 changed files with 998 additions and 231 deletions.
12 changes: 12 additions & 0 deletions packages/codegen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ Steps:
# Watcher server port (default: 3008).
port: 3008
# Solc version to use (optional)
# If not defined, uses solc version listed in dependencies
solc: v0.8.0+commit.c7dfd78e
# Flatten the input contract file(s) [true | false] (default: true).
flatten: true
Expand All @@ -72,6 +76,8 @@ Steps:

* `config-file`(alias: `c`): Watcher generation config file path (yaml) (required).

* `continue-on-error` (alias: `e`): To continue generation if any unhandled data type is encountered (optional).

Example:

* Generate code using a config file `config.yaml`:
Expand All @@ -80,6 +86,12 @@ Steps:
yarn codegen --config-file ./config.yaml
```

* Generate code ignoring any unhandled data types:

```bash
yarn codegen --config-file ./config.yaml --continue-on-error
```

This will create a folder containing the generated code at the path provided in config. Follow the steps in [Run Generated Watcher](#run-generated-watcher) to setup and run the generated watcher.

## Development
Expand Down
6 changes: 6 additions & 0 deletions packages/codegen/src/assets/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules/
dist/
out/

.vscode
.idea
661 changes: 661 additions & 0 deletions packages/codegen/src/assets/LICENSE

Large diffs are not rendered by default.

16 changes: 12 additions & 4 deletions packages/codegen/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import { Writable } from 'stream';
import _ from 'lodash';
import { gqlGenerate } from 'gql-generator';

import { getTsForSol } from './utils/type-mappings';
import { getGqlForSol, getTsForGql } from './utils/type-mappings';
import { Param } from './utils/types';
import { getBaseType } from './utils/helpers';

const TEMPLATE_FILE = './templates/client-template.handlebars';

Expand All @@ -30,12 +31,15 @@ export class Client {
* @param params Parameters to the query.
* @param returnType Return type for the query.
*/
addQuery (name: string, params: Array<Param>, returnType: string): void {
addQuery (name: string, params: Array<Param>, typeName: any): void {
// Check if the query is already added.
if (this._queries.some(query => query.name === name)) {
return;
}

const returnType = getBaseType(typeName);
assert(returnType);

const queryObject = {
name,
getQueryName: '',
Expand All @@ -48,13 +52,17 @@ export class Client {
: `get${name.charAt(0).toUpperCase()}${name.slice(1)}`;

queryObject.params = queryObject.params.map((param) => {
const tsParamType = getTsForSol(param.type);
const gqlParamType = getGqlForSol(param.type);
assert(gqlParamType);
const tsParamType = getTsForGql(gqlParamType);
assert(tsParamType);
param.type = tsParamType;
return param;
});

const tsReturnType = getTsForSol(returnType);
const gqlReturnType = getGqlForSol(returnType);
assert(gqlReturnType);
const tsReturnType = getTsForGql(gqlReturnType);
assert(tsReturnType);
queryObject.returnType = tsReturnType;

Expand Down
18 changes: 13 additions & 5 deletions packages/codegen/src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import Handlebars from 'handlebars';
import { Writable } from 'stream';
import _ from 'lodash';

import { getTsForSol } from './utils/type-mappings';
import { getGqlForSol, getTsForGql } from './utils/type-mappings';
import { Param } from './utils/types';
import { getBaseType } from './utils/helpers';

const TEMPLATE_FILE = './templates/database-template.handlebars';

Expand All @@ -31,12 +32,15 @@ export class Database {
* @param params Parameters to the query.
* @param returnType Return type for the query.
*/
addQuery (name: string, params: Array<Param>, returnType: string): void {
addQuery (name: string, params: Array<Param>, typeName: any): void {
// Check if the query is already added.
if (this._queries.some(query => query.name === name)) {
return;
}

const returnType = getBaseType(typeName);
assert(returnType);

const queryObject = {
name,
entityName: '',
Expand All @@ -61,16 +65,20 @@ export class Database {
}

queryObject.params = queryObject.params.map((param) => {
const tsParamType = getTsForSol(param.type);
const gqlParamType = getGqlForSol(param.type);
assert(gqlParamType);
const tsParamType = getTsForGql(gqlParamType);
assert(tsParamType);
param.type = tsParamType;
return param;
});

const tsReturnType = getTsForSol(returnType);
const gqlReturnType = getGqlForSol(returnType);
assert(gqlReturnType);
const tsReturnType = getTsForGql(gqlReturnType);
assert(tsReturnType);
queryObject.returnType = tsReturnType;

queryObject.returnType = tsReturnType;
this._queries.push(queryObject);
}

Expand Down
18 changes: 12 additions & 6 deletions packages/codegen/src/entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import yaml from 'js-yaml';
import Handlebars from 'handlebars';
import { Writable } from 'stream';

import { getTsForSol, getPgForTs, getTsForGql } from './utils/type-mappings';
import { getPgForTs, getTsForGql, getGqlForSol } from './utils/type-mappings';
import { Param } from './utils/types';
import { getFieldType } from './utils/subgraph';
import { getBaseType } from './utils/helpers';

const TEMPLATE_FILE = './templates/entity-template.handlebars';
const TABLES_DIR = './data/entities';
Expand All @@ -31,7 +32,7 @@ export class Entity {
* @param params Parameters to the query.
* @param returnType Return type for the query.
*/
addQuery (name: string, params: Array<Param>, returnType: string): void {
addQuery (name: string, params: Array<Param>, typeName: any): void {
// Check if the query is already added.
if (this._entities.some(entity => entity.className.toLowerCase() === name.toLowerCase())) {
return;
Expand Down Expand Up @@ -109,9 +110,10 @@ export class Entity {
params.map((param) => {
const name = param.name;

const tsType = getTsForSol(param.type);
const gqlType = getGqlForSol(param.type);
assert(gqlType);
const tsType = getTsForGql(gqlType);
assert(tsType);

const pgType = getPgForTs(tsType);
assert(pgType);

Expand All @@ -136,9 +138,13 @@ export class Entity {
})
);

const tsReturnType = getTsForSol(returnType);
assert(tsReturnType);
const baseType = getBaseType(typeName);
assert(baseType);

const gqlReturnType = getGqlForSol(baseType);
assert(gqlReturnType);
const tsReturnType = getTsForGql(gqlReturnType);
assert(tsReturnType);
const pgReturnType = getPgForTs(tsReturnType);
assert(pgReturnType);

Expand Down
13 changes: 13 additions & 0 deletions packages/codegen/src/generate-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ import { getSubgraphConfig } from './utils/subgraph';
import { exportIndexBlock } from './index-block';
import { exportSubscriber } from './subscriber';
import { exportReset } from './reset';
import { writeFileToStream } from './utils/helpers';

const ASSET_DIR = path.resolve(__dirname, 'assets');

const main = async (): Promise<void> => {
const argv = await yargs(hideBin(process.argv))
Expand Down Expand Up @@ -234,6 +237,16 @@ function generateWatcher (visitor: Visitor, contracts: any[], config: any) {
: process.stdout;
exportReadme(path.basename(outputDir), config.port, outStream);

outStream = outputDir
? fs.createWriteStream(path.join(outputDir, 'LICENSE'))
: process.stdout;
writeFileToStream(path.join(ASSET_DIR, 'LICENSE'), outStream);

outStream = outputDir
? fs.createWriteStream(path.join(outputDir, '.gitignore'))
: process.stdout;
writeFileToStream(path.join(ASSET_DIR, '.gitignore'), outStream);

outStream = outputDir
? fs.createWriteStream(path.join(outputDir, 'src/job-runner.ts'))
: process.stdout;
Expand Down
30 changes: 21 additions & 9 deletions packages/codegen/src/indexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import Handlebars from 'handlebars';
import { Writable } from 'stream';
import _ from 'lodash';

import { getTsForSol } from './utils/type-mappings';
import { getGqlForSol, getTsForGql } from './utils/type-mappings';
import { Param } from './utils/types';
import { MODE_ETH_CALL, MODE_STORAGE } from './utils/constants';
import { getFieldType } from './utils/subgraph';
import { getBaseType, isArrayType } from './utils/helpers';

const TEMPLATE_FILE = './templates/indexer-template.handlebars';

Expand All @@ -37,22 +38,35 @@ export class Indexer {
* @param returnType Return type for the query.
* @param stateVariableType Type of the state variable in case of state variable query.
*/
addQuery (contract: string, mode: string, name: string, params: Array<Param>, returnType: string, stateVariableType?: string): void {
addQuery (contract: string, mode: string, name: string, params: Array<Param>, typeName: any, stateVariableType?: string): void {
// Check if the query is already added.
if (this._queries.some(query => query.name === name)) {
return;
}

const baseType = getBaseType(typeName);
assert(baseType);
const gqlReturnType = getGqlForSol(baseType);
assert(gqlReturnType);
let tsReturnType = getTsForGql(gqlReturnType);
assert(tsReturnType);

const isArray = isArrayType(typeName);
if (isArray) {
tsReturnType = tsReturnType.concat('[]');
}

const queryObject = {
name,
entityName: '',
getQueryName: '',
saveQueryName: '',
params: _.cloneDeep(params),
returnType,
returnType: tsReturnType,
mode,
stateVariableType,
contract
contract,
disableCaching: isArray
};

if (name.charAt(0) === '_') {
Expand All @@ -68,16 +82,14 @@ export class Indexer {
}

queryObject.params = queryObject.params.map((param) => {
const tsParamType = getTsForSol(param.type);
const gqlParamType = getGqlForSol(param.type);
assert(gqlParamType);
const tsParamType = getTsForGql(gqlParamType);
assert(tsParamType);
param.type = tsParamType;
return param;
});

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

if (stateVariableType) {
queryObject.stateVariableType = stateVariableType;
}
Expand Down
11 changes: 8 additions & 3 deletions packages/codegen/src/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import Handlebars from 'handlebars';
import assert from 'assert';
import _ from 'lodash';

import { getTsForSol } from './utils/type-mappings';
import { getGqlForSol, getTsForGql } from './utils/type-mappings';
import { Param } from './utils/types';
import { getBaseType } from './utils/helpers';

const TEMPLATE_FILE = './templates/resolvers-template.handlebars';

Expand All @@ -31,11 +32,13 @@ export class Resolvers {
* @param params Parameters to the query.
* @param returnType Return type for the query.
*/
addQuery (name: string, params: Array<Param>, returnType: string): void {
addQuery (name: string, params: Array<Param>, typeName: any): void {
// Check if the query is already added.
if (this._queries.some(query => query.name === name)) {
return;
}
const returnType = getBaseType(typeName);
assert(returnType);

const queryObject = {
name,
Expand All @@ -44,7 +47,9 @@ export class Resolvers {
};

queryObject.params = queryObject.params.map((param) => {
const tsParamType = getTsForSol(param.type);
const gqlParamType = getGqlForSol(param.type);
assert(gqlParamType);
const tsParamType = getTsForGql(gqlParamType);
assert(tsParamType);
param.type = tsParamType;
return param;
Expand Down
Loading

0 comments on commit 49d6789

Please sign in to comment.