Changes in codegen to remove lint warnings in generated watcher (#368)

* Codegen declare resultDataTypes before queries in gql schema

* Update codegen templates to remove lint warnings

* Add flag to overwrite existing watcher in target directory

* Codegen set up pre-commit lint

* Codegen generate .npmrc

* Codegen refactor  code for pre-commit lint

* Update codegen templates with eslint rule exceptions

* Fix missing comma in codegen template

* Set husky preCommit file permission to executable

---------

Co-authored-by: Dhruv Srivastava <dhruvdhs.ds@gmail.com>
This commit is contained in:
Nabarun Gogoi 2023-04-24 12:04:24 +05:30 committed by GitHub
parent 49d67899be
commit 096a0081e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 111 additions and 18 deletions

View File

@ -0,0 +1 @@
@cerc-io:registry=https://git.vdb.to/api/packages/cerc-io/npm/

View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
yarn lint

View File

@ -57,6 +57,13 @@ const main = async (): Promise<void> => {
describe: 'Continue generating watcher if unhandled types encountered', describe: 'Continue generating watcher if unhandled types encountered',
type: 'boolean' type: 'boolean'
}) })
.option('overwrite', {
alias: 'o',
demandOption: false,
default: false,
describe: 'Overwrite previously generated watcher',
type: 'boolean'
})
.argv; .argv;
const config = getConfig(path.resolve(argv['config-file'])); const config = getConfig(path.resolve(argv['config-file']));
@ -108,11 +115,13 @@ const main = async (): Promise<void> => {
} }
const continueOnError = argv['continue-on-error']; const continueOnError = argv['continue-on-error'];
const overwriteExisting = argv.overwrite;
const visitor = new Visitor(continueOnError); const visitor = new Visitor(continueOnError);
parseAndVisit(visitor, contracts, config.mode); parseAndVisit(visitor, contracts, config.mode);
generateWatcher(visitor, contracts, config); generateWatcher(visitor, contracts, config, overwriteExisting);
}; };
function parseAndVisit (visitor: Visitor, contracts: any[], mode: string) { function parseAndVisit (visitor: Visitor, contracts: any[], mode: string) {
@ -148,12 +157,23 @@ function parseAndVisit (visitor: Visitor, contracts: any[], mode: string) {
} }
} }
function generateWatcher (visitor: Visitor, contracts: any[], config: any) { function generateWatcher (visitor: Visitor, contracts: any[], config: any, overWriteExisting = false) {
// Prepare directory structure for the watcher. // Prepare directory structure for the watcher.
let outputDir = ''; let outputDir = '';
if (config.outputFolder) { if (config.outputFolder) {
outputDir = path.resolve(config.outputFolder); outputDir = path.resolve(config.outputFolder);
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
if (fs.existsSync(outputDir)) {
if (!overWriteExisting) {
throw new Error('Watcher already exists in output folder. Run with --overwrite flag to overwrite');
}
} else {
fs.mkdirSync(outputDir, { recursive: true });
}
const huskyDir = path.join(outputDir, '.husky');
if (!fs.existsSync(huskyDir)) fs.mkdirSync(huskyDir);
const environmentsFolder = path.join(outputDir, 'environments'); const environmentsFolder = path.join(outputDir, 'environments');
if (!fs.existsSync(environmentsFolder)) fs.mkdirSync(environmentsFolder); if (!fs.existsSync(environmentsFolder)) fs.mkdirSync(environmentsFolder);
@ -247,6 +267,23 @@ function generateWatcher (visitor: Visitor, contracts: any[], config: any) {
: process.stdout; : process.stdout;
writeFileToStream(path.join(ASSET_DIR, '.gitignore'), outStream); writeFileToStream(path.join(ASSET_DIR, '.gitignore'), outStream);
outStream = outputDir
? fs.createWriteStream(path.join(outputDir, '.npmrc'))
: process.stdout;
writeFileToStream(path.join(ASSET_DIR, '.npmrc'), outStream);
const huskyPreCommitFilePath = path.join(outputDir, '.husky/pre-commit');
outStream = outputDir
? fs.createWriteStream(huskyPreCommitFilePath)
: process.stdout;
writeFileToStream(path.join(ASSET_DIR, 'pre-commit'), outStream);
if (fs.existsSync(huskyPreCommitFilePath)) {
// Set file permission to executable
fs.chmodSync(huskyPreCommitFilePath, '775');
}
outStream = outputDir outStream = outputDir
? fs.createWriteStream(path.join(outputDir, 'src/job-runner.ts')) ? fs.createWriteStream(path.join(outputDir, 'src/job-runner.ts'))
: process.stdout; : process.stdout;

View File

@ -22,11 +22,15 @@ export class Indexer {
_events: Array<any>; _events: Array<any>;
_subgraphEntities: Array<any>; _subgraphEntities: Array<any>;
_templateString: string; _templateString: string;
_hasStateVariableElementaryType: boolean;
_hasStateVariableMappingType: boolean;
constructor () { constructor () {
this._queries = []; this._queries = [];
this._events = []; this._events = [];
this._subgraphEntities = []; this._subgraphEntities = [];
this._hasStateVariableElementaryType = false;
this._hasStateVariableMappingType = false;
this._templateString = fs.readFileSync(path.resolve(__dirname, TEMPLATE_FILE)).toString(); this._templateString = fs.readFileSync(path.resolve(__dirname, TEMPLATE_FILE)).toString();
} }
@ -92,6 +96,15 @@ export class Indexer {
if (stateVariableType) { if (stateVariableType) {
queryObject.stateVariableType = stateVariableType; queryObject.stateVariableType = stateVariableType;
switch (stateVariableType) {
case 'ElementaryTypeName':
this._hasStateVariableElementaryType = true;
break;
case 'Mapping':
this._hasStateVariableMappingType = true;
break;
}
} }
this._queries.push(queryObject); this._queries.push(queryObject);
@ -173,6 +186,8 @@ export class Indexer {
contracts, contracts,
queries: this._queries, queries: this._queries,
subgraphEntities: this._subgraphEntities, subgraphEntities: this._subgraphEntities,
hasStateVariableElementaryType: this._hasStateVariableElementaryType,
hasStateVariableMappingType: this._hasStateVariableMappingType,
constants: { constants: {
MODE_ETH_CALL, MODE_ETH_CALL,
MODE_STORAGE MODE_STORAGE

View File

@ -257,7 +257,12 @@ export class Schema {
value: (isArray) ? `[${value}]!` : value, value: (isArray) ? `[${value}]!` : value,
proof: () => this._composer.getOTC('Proof') proof: () => this._composer.getOTC('Proof')
}); });
}); }
);
// Using this to declare result types before queries
this._composer.addSchemaMustHaveType(typeComposer);
return typeComposer; return typeComposer;
} }

View File

@ -37,7 +37,7 @@ export const SUBGRAPH_ENTITIES = new Set([
{{~/each}}]); {{~/each}}]);
{{/if}} {{/if}}
export const ENTITIES = [ export const ENTITIES = [
{{~#each queries as | query |}}{{~#if @index }}, {{/if}}{{query.entityName}}{{/each}} {{~#each queries as | query |}}{{query.entityName}}{{#unless @last}}, {{/unless}}{{/each}}{{#if (subgraphPath)}}, {{/if}}
{{~#if (subgraphPath)}}...SUBGRAPH_ENTITIES{{/if}}]; {{~#if (subgraphPath)}}...SUBGRAPH_ENTITIES{{/if}}];
{{#if (subgraphPath)}} {{#if (subgraphPath)}}
// Map: Entity to suitable query type. // Map: Entity to suitable query type.

View File

@ -4,7 +4,13 @@
import assert from 'assert'; import assert from 'assert';
import { updateStateForMappingType, updateStateForElementaryType, ResultEvent } from '@cerc-io/util'; import {
ResultEvent,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
updateStateForMappingType,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
updateStateForElementaryType
} from '@cerc-io/util';
import { Indexer } from './indexer'; import { Indexer } from './indexer';

View File

@ -3,9 +3,11 @@
// //
import assert from 'assert'; import assert from 'assert';
import { DeepPartial, FindConditions, FindManyOptions{{#if (subgraphPath)}}, ObjectLiteral{{/if}} } from 'typeorm';
import debug from 'debug'; import debug from 'debug';
import { DeepPartial, FindConditions, FindManyOptions, ObjectLiteral } from 'typeorm'; {{#if queries}}
import JSONbig from 'json-bigint'; import JSONbig from 'json-bigint';
{{/if}}
import { ethers } from 'ethers'; import { ethers } from 'ethers';
{{#if (subgraphPath)}} {{#if (subgraphPath)}}
import { SelectionNode } from 'graphql'; import { SelectionNode } from 'graphql';
@ -23,8 +25,12 @@ import {
JobQueue, JobQueue,
Where, Where,
QueryOptions, QueryOptions,
{{#if hasStateVariableElementaryType}}
updateStateForElementaryType, updateStateForElementaryType,
{{/if}}
{{#if hasStateVariableMappingType}}
updateStateForMappingType, updateStateForMappingType,
{{/if}}
{{#if (subgraphPath)}} {{#if (subgraphPath)}}
BlockHeight, BlockHeight,
updateSubgraphState, updateSubgraphState,
@ -53,15 +59,21 @@ import { SyncStatus } from './entity/SyncStatus';
import { StateSyncStatus } from './entity/StateSyncStatus'; import { StateSyncStatus } from './entity/StateSyncStatus';
import { BlockProgress } from './entity/BlockProgress'; import { BlockProgress } from './entity/BlockProgress';
import { State } from './entity/State'; import { State } from './entity/State';
{{#if (subgraphPath)}}
/* eslint-disable @typescript-eslint/no-unused-vars */
{{#each subgraphEntities as | subgraphEntity |}} {{#each subgraphEntities as | subgraphEntity |}}
import { {{subgraphEntity.className}} } from './entity/{{subgraphEntity.className}}'; import { {{subgraphEntity.className}} } from './entity/{{subgraphEntity.className}}';
{{/each}} {{/each}}
{{#if (subgraphPath)}} /* eslint-enable @typescript-eslint/no-unused-vars */
import { FrothyEntity } from './entity/FrothyEntity'; import { FrothyEntity } from './entity/FrothyEntity';
{{/if}} {{/if}}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const log = debug('vulcanize:indexer'); const log = debug('vulcanize:indexer');
{{#if queries}}
const JSONbigNative = JSONbig({ useNativeBigInt: true }); const JSONbigNative = JSONbig({ useNativeBigInt: true });
{{/if}}
{{#each contracts as | contract |}} {{#each contracts as | contract |}}
const KIND_{{capitalize contract.contractName}} = '{{contract.contractKind}}'; const KIND_{{capitalize contract.contractName}} = '{{contract.contractKind}}';
@ -178,12 +190,12 @@ export class Indexer implements IndexerInterface {
}; };
} }
{{/unless}}
log('{{query.name}}: db miss, fetching from upstream server');
const { block: { number } } = await this._ethClient.getBlockByHash(blockHash); const { block: { number } } = await this._ethClient.getBlockByHash(blockHash);
const blockNumber = ethers.BigNumber.from(number).toNumber(); const blockNumber = ethers.BigNumber.from(number).toNumber();
{{/unless}}
log('{{query.name}}: db miss, fetching from upstream server');
{{#if (compare query.mode @root.constants.MODE_ETH_CALL)}} {{#if (compare query.mode @root.constants.MODE_ETH_CALL)}}
const abi = this._abiMap.get(KIND_{{capitalize query.contract}}); const abi = this._abiMap.get(KIND_{{capitalize query.contract}});
assert(abi); assert(abi);
@ -279,7 +291,7 @@ export class Indexer implements IndexerInterface {
return createStateCheckpoint(this, contractAddress, blockHash); return createStateCheckpoint(this, contractAddress, blockHash);
} }
async processCanonicalBlock (blockHash: string, blockNumber: number): Promise<void> { async processCanonicalBlock (blockHash: string{{~#if (subgraphPath)}}, blockNumber: number{{/if}}): Promise<void> {
console.time('time:indexer#processCanonicalBlock-finalize_auto_diffs'); console.time('time:indexer#processCanonicalBlock-finalize_auto_diffs');
// Finalize staged diff blocks if any. // Finalize staged diff blocks if any.
await this._baseIndexer.finalizeDiffStaged(blockHash); await this._baseIndexer.finalizeDiffStaged(blockHash);

View File

@ -8,6 +8,7 @@
"lint": "eslint --max-warnings=0 .", "lint": "eslint --max-warnings=0 .",
"build": "yarn clean && tsc && yarn copy-assets", "build": "yarn clean && tsc && yarn copy-assets",
"clean": "rm -rf ./dist", "clean": "rm -rf ./dist",
"prepare": "husky install",
"copy-assets": "copyfiles -u 1 src/**/*.gql dist/", "copy-assets": "copyfiles -u 1 src/**/*.gql dist/",
"server": "DEBUG=vulcanize:* YARN_CHILD_PROCESS=true node --enable-source-maps dist/server.js", "server": "DEBUG=vulcanize:* YARN_CHILD_PROCESS=true node --enable-source-maps dist/server.js",
"server:dev": "DEBUG=vulcanize:* YARN_CHILD_PROCESS=true ts-node src/server.ts", "server:dev": "DEBUG=vulcanize:* YARN_CHILD_PROCESS=true ts-node src/server.ts",
@ -72,6 +73,7 @@
"eslint-plugin-node": "^11.1.0", "eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.0", "eslint-plugin-promise": "^5.1.0",
"eslint-plugin-standard": "^5.0.0", "eslint-plugin-standard": "^5.0.0",
"husky": "^7.0.2",
"ts-node": "^10.2.1", "ts-node": "^10.2.1",
"typescript": "^5.0.2", "typescript": "^5.0.2",
"copyfiles": "^2.4.1" "copyfiles": "^2.4.1"

View File

@ -6,18 +6,26 @@ import assert from 'assert';
import BigInt from 'apollo-type-bigint'; import BigInt from 'apollo-type-bigint';
import debug from 'debug'; import debug from 'debug';
import Decimal from 'decimal.js'; import Decimal from 'decimal.js';
import { GraphQLResolveInfo, GraphQLScalarType } from 'graphql'; import {
GraphQLScalarType,
GraphQLResolveInfo
} from 'graphql';
import { import {
{{#if queries}}
ValueResult, ValueResult,
BlockHeight, {{/if}}
gqlTotalQueryCount, gqlTotalQueryCount,
gqlQueryCount, gqlQueryCount,
jsonBigIntStringReplacer,
getResultState, getResultState,
setGQLCacheHints,
IndexerInterface, IndexerInterface,
EventWatcher {{#if (subgraphPath)}}
BlockHeight,
jsonBigIntStringReplacer,
{{/if}}
EventWatcher,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
setGQLCacheHints
} from '@cerc-io/util'; } from '@cerc-io/util';
import { Indexer } from './indexer'; import { Indexer } from './indexer';
@ -33,6 +41,7 @@ const log = debug('vulcanize:resolver');
export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher: EventWatcher): Promise<any> => { export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher: EventWatcher): Promise<any> => {
const indexer = indexerArg as Indexer; const indexer = indexerArg as Indexer;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const gqlCacheConfig = indexer.serverConfig.gqlCache; const gqlCacheConfig = indexer.serverConfig.gqlCache;
return { return {
@ -81,7 +90,9 @@ export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher
{ blockHash, contractAddress { blockHash, contractAddress
{{~#each this.params}}, {{this.name~}} {{/each}} }: { blockHash: string, contractAddress: string {{~#each this.params}}, {{this.name~}} {{/each}} }: { blockHash: string, contractAddress: string
{{~#each this.params}}, {{this.name}}: {{this.type~}} {{/each}} }, {{~#each this.params}}, {{this.name}}: {{this.type~}} {{/each}} },
// eslint-disable-next-line @typescript-eslint/no-unused-vars
__: any, __: any,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
info: GraphQLResolveInfo info: GraphQLResolveInfo
): Promise<ValueResult> => { ): Promise<ValueResult> => {
log('{{this.name}}', blockHash, contractAddress log('{{this.name}}', blockHash, contractAddress