From 08af21df18083aabb05735ec4ae7fc1c2f8b158f Mon Sep 17 00:00:00 2001 From: Saihajpreet Singh Date: Mon, 18 Sep 2023 16:31:21 -0700 Subject: [PATCH 1/3] fix: loading interfaces for derived loaders --- .../package.json | 4 +- packages/cli/src/codegen/schema.ts | 48 +++++++++++++++---- packages/cli/src/debug.ts | 4 +- packages/cli/src/type-generator.ts | 14 +++++- pnpm-lock.yaml | 22 ++++----- 5 files changed, 68 insertions(+), 24 deletions(-) diff --git a/examples/matic-lens-protocol-posts-subgraph/package.json b/examples/matic-lens-protocol-posts-subgraph/package.json index 294425bdf..8757d65c8 100644 --- a/examples/matic-lens-protocol-posts-subgraph/package.json +++ b/examples/matic-lens-protocol-posts-subgraph/package.json @@ -1,5 +1,6 @@ { "name": "lens-protocol-posts-sg", + "version": null, "license": "UNLICENSED", "private": true, "scripts": { @@ -17,6 +18,5 @@ }, "devDependencies": { "matchstick-as": "0.5.2" - }, - "version": null + } } diff --git a/packages/cli/src/codegen/schema.ts b/packages/cli/src/codegen/schema.ts index c3a1ca95f..994847f39 100644 --- a/packages/cli/src/codegen/schema.ts +++ b/packages/cli/src/codegen/schema.ts @@ -1,5 +1,4 @@ /* eslint-disable unicorn/no-array-for-each */ -import debug from 'debug'; import type { DefinitionNode, FieldDefinitionNode, @@ -8,6 +7,7 @@ import type { ObjectTypeDefinitionNode, TypeNode, } from 'graphql/language'; +import debug from '../debug'; import Schema from '../schema'; import * as typesCodegen from './types'; import * as tsCodegen from './typescript'; @@ -67,6 +67,8 @@ class IdField { } } +const schemaCodeGeneratorDebug = debug('graph-cli:SchemaCodeGenerator'); + export default class SchemaCodeGenerator { constructor(private schema: Schema) { this.schema = schema; @@ -99,6 +101,9 @@ export default class SchemaCodeGenerator { return this.schema.ast.definitions .map(def => { if (this._isEntityTypeDefinition(def)) { + schemaCodeGeneratorDebug.extend('generateTypes')( + `Generating entity type for ${def.name.value}`, + ); return this._generateEntityType(def); } }) @@ -116,7 +121,9 @@ export default class SchemaCodeGenerator { .filter(def => this._isDerivedField(def)) .filter(def => def?.type !== undefined) as FieldDefinitionNode[] ).map(def => this._getTypeNameForField(def.type)); - + schemaCodeGeneratorDebug.extend('generateDerivedLoaders')( + `Generating derived loaders for ${fields}`, + ); return [...new Set(fields)].map(typeName => { return this._generateDerivedLoader(typeName); }); @@ -131,8 +138,7 @@ export default class SchemaCodeGenerator { _isDerivedField(field: FieldDefinitionNode | undefined): boolean { return ( - field?.directives?.find((directive: any) => directive.name.value === 'derivedFrom') !== - undefined + field?.directives?.find(directive => directive.name.value === 'derivedFrom') !== undefined ); } _isInterfaceDefinition(def: DefinitionNode): def is InterfaceTypeDefinitionNode { @@ -287,13 +293,15 @@ export default class SchemaCodeGenerator { _generateEntityFieldGetter(_entityDef: ObjectTypeDefinitionNode, fieldDef: FieldDefinitionNode) { const isDerivedField = this._isDerivedField(fieldDef); - const codegenDebug = debug('codegen'); + const name = fieldDef.name.value; + if (isDerivedField) { - codegenDebug(`Generating derived field getter for ${fieldDef.name.value}`); + schemaCodeGeneratorDebug.extend('_generateEntityFieldGetter')( + `Generating derived field getter for ${name}`, + ); return this._generateDerivedFieldGetter(_entityDef, fieldDef); } - const name = fieldDef.name.value; const gqlType = fieldDef.type; const fieldValueType = this._valueTypeFromGraphQl(gqlType); const returnType = this._typeFromGraphQl(gqlType); @@ -329,16 +337,40 @@ export default class SchemaCodeGenerator { _generateDerivedFieldGetter(entityDef: ObjectTypeDefinitionNode, fieldDef: FieldDefinitionNode) { const entityName = entityDef.name.value; const name = fieldDef.name.value; + schemaCodeGeneratorDebug.extend('_generateDerivedFieldGetter')( + `Generating derived field '${name}' getter for Entity '${entityName}'`, + ); const gqlType = fieldDef.type; + schemaCodeGeneratorDebug.extend('_generateDerivedFieldGetter')( + "Derived field's type: %M", + gqlType, + ); const returnType = this._returnTypeForDervied(gqlType); + schemaCodeGeneratorDebug.extend('_generateDerivedFieldGetter')( + "Derived field's return type: %M", + returnType, + ); const obj = this.schema.ast.definitions.find(def => { - if (def.kind === 'ObjectTypeDefinition') { + if (def.kind === 'ObjectTypeDefinition' || def.kind === 'InterfaceTypeDefinition') { const defobj = def as ObjectTypeDefinitionNode; return defobj.name.value == this._baseType(gqlType); } return false; }) as ObjectTypeDefinitionNode; + if (!obj) { + schemaCodeGeneratorDebug.extend('_generateDerivedFieldGetter')( + "Could not find object type definition for derived field's base type: %M", + obj, + ); + return null; + } + + schemaCodeGeneratorDebug.extend('_generateDerivedFieldGetter')( + "Found object type definition for derived field's base type: %M", + obj, + ); + const idf = IdField.fromTypeDef(obj); const idIsBytes = idf.typeName() == 'Bytes'; const toValueString = idIsBytes ? '.toBytes().toHexString()' : '.toString()'; diff --git a/packages/cli/src/debug.ts b/packages/cli/src/debug.ts index bd80a5e5f..5c25970ad 100644 --- a/packages/cli/src/debug.ts +++ b/packages/cli/src/debug.ts @@ -5,11 +5,11 @@ debugFactory.formatters.L = immutableList => { }; debugFactory.formatters.M = immutableMap => { - if (immutableMap.toMap != null) { + if (immutableMap?.toMap != null) { return JSON.stringify(immutableMap.toMap()); } - if (typeof immutableMap.toJS === 'function') { + if (typeof immutableMap?.toJS === 'function') { return JSON.stringify(immutableMap.toJS()); } diff --git a/packages/cli/src/type-generator.ts b/packages/cli/src/type-generator.ts index 422cfbb61..e10083cb2 100644 --- a/packages/cli/src/type-generator.ts +++ b/packages/cli/src/type-generator.ts @@ -10,12 +10,15 @@ import DataSourceTemplateCodeGenerator from './codegen/template'; import { GENERATED_FILE_NOTE } from './codegen/typescript'; import { displayPath } from './command-helpers/fs'; import { Spinner, step, withSpinner } from './command-helpers/spinner'; +import debug from './debug'; import { applyMigrations } from './migrations'; import Protocol from './protocols'; import Schema from './schema'; import Subgraph from './subgraph'; import Watcher from './watcher'; +const typeGenDebug = debug('graph-cli:type-generator'); + export interface TypeGeneratorOptions { sourceDir?: string; subgraphManifest: string; @@ -52,6 +55,9 @@ export default class TypeGenerator { async generateTypes() { if (this.protocol.name === 'substreams') { + typeGenDebug.extend('generateTypes')( + 'Subgraph uses a substream datasource. Skipping code generation.', + ); toolbox.print.success( 'Subgraph uses a substream datasource. Codegeneration is not required.', ); @@ -70,10 +76,12 @@ export default class TypeGenerator { // Not all protocols support/have ABIs. if (this.protocol.hasABIs()) { + typeGenDebug.extend('generateTypes')('Generating types for ABIs'); const abis = await this.protocolTypeGenerator.loadABIs(subgraph); await this.protocolTypeGenerator.generateTypesForABIs(abis); } + typeGenDebug.extend('generateTypes')('Generating types for templates'); await this.generateTypesForDataSourceTemplates(subgraph); // Not all protocols support/have ABIs. @@ -83,6 +91,7 @@ export default class TypeGenerator { } const schema = await this.loadSchema(subgraph); + typeGenDebug.extend('generateTypes')('Generating types for schema'); await this.generateTypesForSchema(schema); toolbox.print.success('\nTypes generated successfully\n'); @@ -113,7 +122,10 @@ export default class TypeGenerator { } async loadSubgraph({ quiet } = { quiet: false }) { - const subgraphLoadOptions = { protocol: this.protocol, skipValidation: false }; + const subgraphLoadOptions = { + protocol: this.protocol, + skipValidation: false, + }; if (quiet) { return ( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 974f70ee8..a7d3363ec 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,7 +50,7 @@ importers: examples/arweave-blocks-transactions: devDependencies: '@graphprotocol/graph-cli': - specifier: 0.57.0 + specifier: 0.58.0 version: link:../../packages/cli '@graphprotocol/graph-ts': specifier: 0.31.0 @@ -66,7 +66,7 @@ importers: version: 6.26.0 devDependencies: '@graphprotocol/graph-cli': - specifier: 0.57.0 + specifier: 0.58.0 version: link:../../packages/cli '@graphprotocol/graph-ts': specifier: 0.31.0 @@ -85,7 +85,7 @@ importers: version: 6.26.0 devDependencies: '@graphprotocol/graph-cli': - specifier: 0.57.0 + specifier: 0.58.0 version: link:../../packages/cli '@graphprotocol/graph-ts': specifier: 0.31.0 @@ -104,7 +104,7 @@ importers: version: 6.26.0 devDependencies: '@graphprotocol/graph-cli': - specifier: 0.57.0 + specifier: 0.58.0 version: link:../../packages/cli '@graphprotocol/graph-ts': specifier: 0.31.0 @@ -123,7 +123,7 @@ importers: version: 6.26.0 devDependencies: '@graphprotocol/graph-cli': - specifier: 0.57.0 + specifier: 0.58.0 version: link:../../packages/cli '@graphprotocol/graph-ts': specifier: 0.31.0 @@ -175,7 +175,7 @@ importers: version: 5.0.4 devDependencies: '@graphprotocol/graph-cli': - specifier: 0.57.0 + specifier: 0.58.0 version: link:../../packages/cli '@graphprotocol/graph-ts': specifier: 0.31.0 @@ -193,7 +193,7 @@ importers: examples/ethereum-gravatar: devDependencies: '@graphprotocol/graph-cli': - specifier: 0.57.0 + specifier: 0.58.0 version: link:../../packages/cli '@graphprotocol/graph-ts': specifier: 0.31.0 @@ -214,7 +214,7 @@ importers: examples/matic-lens-protocol-posts-subgraph: dependencies: '@graphprotocol/graph-cli': - specifier: 0.57.0 + specifier: 0.58.0 version: link:../../packages/cli '@graphprotocol/graph-ts': specifier: 0.31.0 @@ -227,7 +227,7 @@ importers: examples/near-blocks: devDependencies: '@graphprotocol/graph-cli': - specifier: 0.57.0 + specifier: 0.58.0 version: link:../../packages/cli '@graphprotocol/graph-ts': specifier: 0.31.0 @@ -236,7 +236,7 @@ importers: examples/near-receipts: devDependencies: '@graphprotocol/graph-cli': - specifier: 0.57.0 + specifier: 0.58.0 version: link:../../packages/cli '@graphprotocol/graph-ts': specifier: 0.31.0 @@ -245,7 +245,7 @@ importers: examples/substreams-powered-subgraph: devDependencies: '@graphprotocol/graph-cli': - specifier: 0.57.0 + specifier: 0.58.0 version: link:../../packages/cli packages/cli: From 65df5b301a4004a41f23f19e2c907b4af2125fc8 Mon Sep 17 00:00:00 2001 From: Saihajpreet Singh Date: Mon, 18 Sep 2023 16:32:28 -0700 Subject: [PATCH 2/3] remove null version --- examples/matic-lens-protocol-posts-subgraph/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/matic-lens-protocol-posts-subgraph/package.json b/examples/matic-lens-protocol-posts-subgraph/package.json index 8757d65c8..30556b48a 100644 --- a/examples/matic-lens-protocol-posts-subgraph/package.json +++ b/examples/matic-lens-protocol-posts-subgraph/package.json @@ -1,6 +1,5 @@ { "name": "lens-protocol-posts-sg", - "version": null, "license": "UNLICENSED", "private": true, "scripts": { From f349bfd12da3feecc5227020d39eb5802a5d4e1c Mon Sep 17 00:00:00 2001 From: Saihajpreet Singh Date: Wed, 27 Sep 2023 17:16:18 -0400 Subject: [PATCH 3/3] do not generate loader for interfaces --- .changeset/four-pandas-ring.md | 5 +++++ packages/cli/src/codegen/schema.test.ts | 26 ++++++++++++++++++++++++- packages/cli/src/codegen/schema.ts | 22 ++++++++++++++++----- 3 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 .changeset/four-pandas-ring.md diff --git a/.changeset/four-pandas-ring.md b/.changeset/four-pandas-ring.md new file mode 100644 index 000000000..a5fecbcb2 --- /dev/null +++ b/.changeset/four-pandas-ring.md @@ -0,0 +1,5 @@ +--- +'@graphprotocol/graph-cli': minor +--- + +do not generate loader for interfaces diff --git a/packages/cli/src/codegen/schema.test.ts b/packages/cli/src/codegen/schema.test.ts index 679f2199b..99305fec1 100644 --- a/packages/cli/src/codegen/schema.test.ts +++ b/packages/cli/src/codegen/schema.test.ts @@ -1,3 +1,4 @@ +import assert from 'assert'; import * as graphql from 'graphql/language'; import prettier from 'prettier'; import Schema from '../schema'; @@ -27,7 +28,6 @@ const testEntity = (generatedTypes: any[], expectedEntity: any) => { expectedMethod.static ? expect(method instanceof StaticMethod).toBe(true) : expect(method instanceof Method).toBe(true); - expect(method.params).toStrictEqual(expectedMethod.params); expect(method.returnType).toStrictEqual(expectedMethod.returnType); expect(formatTS(method.body)).toBe(formatTS(expectedMethod.body)); @@ -536,4 +536,28 @@ describe('Schema code generator', () => { ], }); }); + + test('no derived loader for interface', () => { + const codegen = createSchemaCodeGen(` + interface IExample { + id: ID! + main: Main! + num: Int! + } + + type Example1 implements IExample @entity { + id: ID! + main: Main! + num: Int! + } + + type Main @entity { + id: ID! + examples: [IExample!]! @derivedFrom(field: "main") + } +`); + + const generateDerivedLoaders = codegen.generateDerivedLoaders().filter(Boolean); + assert(generateDerivedLoaders.length === 0); + }); }); diff --git a/packages/cli/src/codegen/schema.ts b/packages/cli/src/codegen/schema.ts index 994847f39..03b76459c 100644 --- a/packages/cli/src/codegen/schema.ts +++ b/packages/cli/src/codegen/schema.ts @@ -111,11 +111,19 @@ export default class SchemaCodeGenerator { } generateDerivedLoaders() { + // This gets all the interfaces in the schema + // We can think of more optimized ways to do this + const interfaces = ( + this.schema.ast.definitions.filter(def => + this._isInterfaceDefinition(def), + ) as InterfaceTypeDefinitionNode[] + ).map(def => def.name.value); + const fields = ( ( - this.schema.ast.definitions.filter(def => - this._isEntityTypeDefinition(def), - ) as ObjectTypeDefinitionNode[] + this.schema.ast.definitions.filter(def => { + return this._isEntityTypeDefinition(def); + }) as ObjectTypeDefinitionNode[] ) .flatMap((def: ObjectTypeDefinitionNode) => def.fields) .filter(def => this._isDerivedField(def)) @@ -124,8 +132,12 @@ export default class SchemaCodeGenerator { schemaCodeGeneratorDebug.extend('generateDerivedLoaders')( `Generating derived loaders for ${fields}`, ); + return [...new Set(fields)].map(typeName => { - return this._generateDerivedLoader(typeName); + // do not support interfaces + if (!interfaces.includes(typeName)) { + return this._generateDerivedLoader(typeName); + } }); } @@ -351,7 +363,7 @@ export default class SchemaCodeGenerator { returnType, ); const obj = this.schema.ast.definitions.find(def => { - if (def.kind === 'ObjectTypeDefinition' || def.kind === 'InterfaceTypeDefinition') { + if (def.kind === 'ObjectTypeDefinition') { const defobj = def as ObjectTypeDefinitionNode; return defobj.name.value == this._baseType(gqlType); }