Generate plural GQL query schema for subgraph entities (#443)

* Generate plural GQL queries for subgraph entities

* Use pluralize for plural query names

* Handle bigint values for filters in db query
This commit is contained in:
prathamesh0 2023-10-31 09:24:49 +05:30 committed by GitHub
parent a8e59eb6b9
commit 15ee523e71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 74 additions and 10 deletions

View File

@ -28,6 +28,7 @@
"gql-generator": "https://github.com/vulcanize/gql-generator.git", "gql-generator": "https://github.com/vulcanize/gql-generator.git",
"graphql": "^15.5.0", "graphql": "^15.5.0",
"graphql-compose": "^9.0.3", "graphql-compose": "^9.0.3",
"pluralize": "^8.0.0",
"handlebars": "^4.7.7", "handlebars": "^4.7.7",
"js-yaml": "^4.0.0", "js-yaml": "^4.0.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
@ -40,6 +41,7 @@
"devDependencies": { "devDependencies": {
"@openzeppelin/contracts": "^4.3.2", "@openzeppelin/contracts": "^4.3.2",
"@types/js-yaml": "^4.0.3", "@types/js-yaml": "^4.0.3",
"@types/pluralize": "^0.0.29",
"@types/lodash": "^4.14.168", "@types/lodash": "^4.14.168",
"@types/node": "^16.9.0", "@types/node": "^16.9.0",
"@types/yargs": "^17.0.0", "@types/yargs": "^17.0.0",

View File

@ -68,6 +68,7 @@ export class Resolvers {
queryName queryName
}; };
// TODO: Generate plural query resolvers
this._subgraphQueries.push(queryObject); this._subgraphQueries.push(queryObject);
} }
} }

View File

@ -3,16 +3,20 @@
// //
import assert from 'assert'; import assert from 'assert';
import { GraphQLSchema, parse, printSchema, print, GraphQLDirective, GraphQLInt, GraphQLBoolean } from 'graphql'; import { GraphQLSchema, parse, printSchema, print, GraphQLDirective, GraphQLInt, GraphQLBoolean, GraphQLEnumType, DefinitionNode } from 'graphql';
import { ObjectTypeComposer, NonNullComposer, ObjectTypeComposerDefinition, ObjectTypeComposerFieldConfigMapDefinition, SchemaComposer } from 'graphql-compose'; import { ObjectTypeComposer, NonNullComposer, ObjectTypeComposerDefinition, ObjectTypeComposerFieldConfigMapDefinition, SchemaComposer } from 'graphql-compose';
import { Writable } from 'stream'; import { Writable } from 'stream';
import { utils } from 'ethers'; import { utils } from 'ethers';
import { VariableDeclaration } from '@solidity-parser/parser/dist/src/ast-types'; import { VariableDeclaration } from '@solidity-parser/parser/dist/src/ast-types';
import pluralize from 'pluralize';
import { getGqlForSol } from './utils/type-mappings'; import { getGqlForSol } from './utils/type-mappings';
import { Param } from './utils/types'; import { Param } from './utils/types';
import { getBaseType, isArrayType } from './utils/helpers'; import { getBaseType, isArrayType } from './utils/helpers';
const OrderDirection = 'OrderDirection';
const BlockHeight = 'Block_height';
export class Schema { export class Schema {
_composer: SchemaComposer; _composer: SchemaComposer;
_events: Array<string>; _events: Array<string>;
@ -147,8 +151,8 @@ export class Schema {
this._composer.addTypeDefs(subgraphTypeDefsString); this._composer.addTypeDefs(subgraphTypeDefsString);
// Create the Block_height input needed in subgraph queries. // Create the Block_height input needed in subgraph queries.
const typeComposer = this._composer.createInputTC({ let typeComposer: any = this._composer.createInputTC({
name: 'Block_height', name: BlockHeight,
fields: { fields: {
hash: 'Bytes', hash: 'Bytes',
number: 'Int' number: 'Int'
@ -156,11 +160,22 @@ export class Schema {
}); });
this._composer.addSchemaMustHaveType(typeComposer); this._composer.addSchemaMustHaveType(typeComposer);
// Add the OrderDirection enum needed in subgraph plural queries.
const orderDirectionEnum = new GraphQLEnumType({
name: OrderDirection,
values: {
asc: {},
desc: {}
}
});
typeComposer = this._composer.createEnumTC(orderDirectionEnum);
this._composer.addSchemaMustHaveType(typeComposer);
// Add subgraph-schema entity queries to the schema composer. // Add subgraph-schema entity queries to the schema composer.
this._addSubgraphSchemaQueries(subgraphTypeDefs); this._addSubgraphSchemaQueries(subgraphTypeDefs);
} }
_addSubgraphSchemaQueries (subgraphTypeDefs: any): void { _addSubgraphSchemaQueries (subgraphTypeDefs: ReadonlyArray<DefinitionNode>): void {
for (const subgraphTypeDef of subgraphTypeDefs) { for (const subgraphTypeDef of subgraphTypeDefs) {
// Filtering out enums. // Filtering out enums.
if (subgraphTypeDef.kind !== 'ObjectTypeDefinition') { if (subgraphTypeDef.kind !== 'ObjectTypeDefinition') {
@ -178,7 +193,38 @@ export class Schema {
type: this._composer.getAnyTC(subgraphType).NonNull, type: this._composer.getAnyTC(subgraphType).NonNull,
args: { args: {
id: 'ID!', id: 'ID!',
block: 'Block_height' block: BlockHeight
}
};
// Add plural query
// Create the subgraphType_orderBy enum type
const subgraphTypeOrderByEnum = new GraphQLEnumType({
name: `${subgraphType}_orderBy`,
values: (subgraphTypeDef.fields || []).reduce((acc: any, field) => {
acc[field.name.value] = {};
return acc;
}, {})
});
this._composer.addSchemaMustHaveType(subgraphTypeOrderByEnum);
// Create plural query name
// Append suffix 's' if pluralized name is the same as singular name (eg. PoolDayData)
let pluralQueryName = pluralize(queryName);
pluralQueryName = (pluralQueryName === queryName) ? `${pluralQueryName}s` : pluralQueryName;
queryObject[pluralQueryName] = {
// Get type composer object for return type from the schema composer.
type: this._composer.getAnyTC(subgraphType).NonNull.List.NonNull,
args: {
block: BlockHeight,
// TODO: Create input type for where clause
// where: subgraphType_filter,
orderBy: subgraphTypeOrderByEnum,
orderDirection: OrderDirection,
first: { type: GraphQLInt, defaultValue: 100 },
skip: { type: GraphQLInt, defaultValue: 0 }
} }
}; };
@ -370,7 +416,7 @@ export class Schema {
_addEventsQuery (): void { _addEventsQuery (): void {
this._composer.Query.addFields({ this._composer.Query.addFields({
events: { events: {
type: [this._composer.getOTC('ResultEvent').NonNull], type: this._composer.getOTC('ResultEvent').NonNull.List,
args: { args: {
blockHash: 'String!', blockHash: 'String!',
contractAddress: 'String!', contractAddress: 'String!',
@ -381,7 +427,7 @@ export class Schema {
this._composer.Query.addFields({ this._composer.Query.addFields({
eventsInRange: { eventsInRange: {
type: [this._composer.getOTC('ResultEvent').NonNull], type: this._composer.getOTC('ResultEvent').NonNull.List,
args: { args: {
fromBlockNumber: 'Int!', fromBlockNumber: 'Int!',
toBlockNumber: 'Int!' toBlockNumber: 'Int!'

View File

@ -83,6 +83,7 @@
maxCompletionLagInSecs = 300 maxCompletionLagInSecs = 300
jobDelayInMilliSecs = 100 jobDelayInMilliSecs = 100
eventsInBatch = 50 eventsInBatch = 50
subgraphEventsOrder = true
blockDelayInMilliSecs = 2000 blockDelayInMilliSecs = 2000
prefetchBlocksInMem = true prefetchBlocksInMem = true
prefetchBlockCount = 10 prefetchBlockCount = 10

View File

@ -829,12 +829,10 @@ export class Database {
whereClause += `${OPERATOR_MAP[operator]} `; whereClause += `${OPERATOR_MAP[operator]} `;
value = this._transformBigIntValues(value);
if (operator === 'in') { if (operator === 'in') {
whereClause += '(:...'; whereClause += '(:...';
} else { } else {
// Convert to string type value as bigint type throws error in query.
value = value.toString();
whereClause += ':'; whereClause += ':';
} }
@ -900,4 +898,20 @@ export class Database {
eventCount.set(res); eventCount.set(res);
} }
_transformBigIntValues (value: any): any {
if (Array.isArray(value)) {
if (value.length > 0 && typeof value[0] === 'bigint') {
return value.map(val => {
return val.toString();
});
}
return value;
}
if (typeof value === 'bigint') {
return value.toString();
}
}
} }