From 2a204d8a3215c7adc0129a8fd4588b1a09b60f84 Mon Sep 17 00:00:00 2001 From: nikugogoi Date: Thu, 23 Dec 2021 09:21:42 +0530 Subject: [PATCH] Handle additional subgraph types BigDecimal and Bytes in codegen (#89) * Handle additional subgraph types BigDecimal and Bytes * Use bigint and Decimal array transformers --- packages/codegen/src/entity.ts | 113 ++++++++++++++++-- packages/codegen/src/schema.ts | 37 ++++-- .../templates/database-template.handlebars | 4 +- packages/codegen/src/utils/subgraph.ts | 15 +-- packages/codegen/src/utils/type-mappings.ts | 3 + 5 files changed, 130 insertions(+), 42 deletions(-) diff --git a/packages/codegen/src/entity.ts b/packages/codegen/src/entity.ts index 375692aa..bcd6127d 100644 --- a/packages/codegen/src/entity.ts +++ b/packages/codegen/src/entity.ts @@ -244,6 +244,9 @@ export class Entity { // Add subgraph entity specific columns. entityObject = this._addSubgraphColumns(subgraphTypeDefs, entityObject, def); + // Add decimalTransformer column option if required. + this._addDecimalTransformerOption(entityObject); + // Add bigintTransformer column option if required. this._addBigIntTransformerOption(entityObject); @@ -282,9 +285,13 @@ export class Entity { } _addBigIntTransformerOption (entityObject: any): void { + let importObject = entityObject.imports.find((element: any) => { + return element.from === '@vulcanize/util'; + }); + entityObject.columns.forEach((column: any) => { - // Implement bigintTransformer for bigint types. - if (['bigint', 'bigint[]'].includes(column.tsType)) { + // Implement bigintTransformer for bigint type. + if (column.tsType === 'bigint') { column.columnOptions.push( { option: 'transformer', @@ -292,24 +299,106 @@ export class Entity { } ); - const importObject = entityObject.imports.find((element: any) => { - return element.from === '@vulcanize/util'; - }); - if (importObject) { importObject.toImport.add('bigintTransformer'); } else { - entityObject.imports.push( - { - toImport: new Set(['bigintTransformer']), - from: '@vulcanize/util' - } - ); + importObject = { + toImport: new Set(['bigintTransformer']), + from: '@vulcanize/util' + }; + + entityObject.imports.push(importObject); + } + } + + // Implement bigintArrayTransformer for array of bigint type. + if (column.tsType === 'bigint[]') { + column.columnOptions.push( + { + option: 'transformer', + value: 'bigintArrayTransformer' + } + ); + + if (importObject) { + importObject.toImport.add('bigintArrayTransformer'); + } else { + importObject = { + toImport: new Set(['bigintArrayTransformer']), + from: '@vulcanize/util' + }; + + entityObject.imports.push(importObject); } } }); } + _addDecimalTransformerOption (entityObject: any): void { + let importObject = entityObject.imports.find((element: any) => { + return element.from === '@vulcanize/util'; + }); + + let isDecimalRequired = false; + + entityObject.columns.forEach((column: any) => { + // Implement decimalTransformer for Decimal type. + if (column.tsType === 'Decimal') { + isDecimalRequired = true; + + column.columnOptions.push( + { + option: 'transformer', + value: 'decimalTransformer' + } + ); + + if (importObject) { + importObject.toImport.add('decimalTransformer'); + } else { + importObject = { + toImport: new Set(['decimalTransformer']), + from: '@vulcanize/util' + }; + + entityObject.imports.push(importObject); + } + } + + // Implement decimalArrayTransformer for array of Decimal type. + if (column.tsType === 'Decimal[]') { + isDecimalRequired = true; + + column.columnOptions.push( + { + option: 'transformer', + value: 'decimalArrayTransformer' + } + ); + + if (importObject) { + importObject.toImport.add('decimalArrayTransformer'); + } else { + importObject = { + toImport: new Set(['decimalArrayTransformer']), + from: '@vulcanize/util' + }; + + entityObject.imports.push(importObject); + } + } + }); + + if (isDecimalRequired) { + entityObject.imports.push( + { + toImport: new Set(['Decimal']), + from: 'decimal.js' + } + ); + } + } + _addSubgraphColumns (subgraphTypeDefs: any, entityObject: any, def: any): any { def.fields.forEach((field: any) => { if (field.directives.some((directive: any) => directive.name.value === 'derivedFrom')) { diff --git a/packages/codegen/src/schema.ts b/packages/codegen/src/schema.ts index 25d0f36a..037fc18a 100644 --- a/packages/codegen/src/schema.ts +++ b/packages/codegen/src/schema.ts @@ -198,6 +198,18 @@ export class Schema { }); this._composer.addSchemaMustHaveType(typeComposer); + // Create a scalar type composer to add the scalar BigDecimal in the schema composer. + typeComposer = this._composer.createScalarTC({ + name: 'BigDecimal' + }); + this._composer.addSchemaMustHaveType(typeComposer); + + // Create a scalar type composer to add the scalar Bytes in the schema composer. + typeComposer = this._composer.createScalarTC({ + name: 'Bytes' + }); + this._composer.addSchemaMustHaveType(typeComposer); + // Create a type composer to add the type Proof in the schema composer. typeComposer = this._composer.createObjectTC({ name: 'Proof', @@ -243,19 +255,10 @@ export class Schema { } }); this._composer.addSchemaMustHaveType(typeComposer); - } - /** - * Adds types 'ResultEvent' and 'WatchedEvent' to the schema. - */ - _addEventsRelatedTypes (): void { - let typeComposer; - - // Create Ethereum types. // Create the Block type. - const blockName = 'Block'; typeComposer = this._composer.createObjectTC({ - name: blockName, + name: '_Block_', fields: { cid: 'String!', hash: 'String!', @@ -265,9 +268,17 @@ export class Schema { } }); this._composer.addSchemaMustHaveType(typeComposer); + } + /** + * Adds types 'ResultEvent' and 'WatchedEvent' to the schema. + */ + _addEventsRelatedTypes (): void { + let typeComposer; + + // Create Ethereum types. // Create the Transaction type. - const transactionName = 'Transaction'; + const transactionName = '_Transaction_'; typeComposer = this._composer.createObjectTC({ name: transactionName, fields: { @@ -285,7 +296,7 @@ export class Schema { name: resultEventName, fields: { // Get type composer object for 'blockName' type from the schema composer. - block: () => this._composer.getOTC(blockName).NonNull, + block: () => this._composer.getOTC('_Block_').NonNull, tx: () => this._composer.getOTC(transactionName).NonNull, contract: 'String!', eventIndex: 'Int!', @@ -326,7 +337,7 @@ export class Schema { const typeComposer = this._composer.createObjectTC({ name: 'ResultIPLDBlock', fields: { - block: () => this._composer.getOTC('Block').NonNull, + block: () => this._composer.getOTC('_Block_').NonNull, contractAddress: 'String!', cid: 'String!', kind: 'String!', diff --git a/packages/codegen/src/templates/database-template.handlebars b/packages/codegen/src/templates/database-template.handlebars index 6630a3d0..0cabe8f6 100644 --- a/packages/codegen/src/templates/database-template.handlebars +++ b/packages/codegen/src/templates/database-template.handlebars @@ -17,10 +17,8 @@ import { IPLDBlock } from './entity/IPLDBlock'; {{#each queries as | query |}} import { {{query.entityName}} } from './entity/{{query.entityName}}'; -{{#unless @last}} - -{{/unless}} {{/each}} + export class Database implements IPLDDatabaseInterface { _config: ConnectionOptions; _conn!: Connection; diff --git a/packages/codegen/src/utils/subgraph.ts b/packages/codegen/src/utils/subgraph.ts index 19135355..1db72606 100644 --- a/packages/codegen/src/utils/subgraph.ts +++ b/packages/codegen/src/utils/subgraph.ts @@ -4,11 +4,6 @@ import fs from 'fs'; import { loadFilesSync } from '@graphql-tools/load-files'; -const SCALAR_MAPPING: any = { - BigDecimal: 'String', - Bytes: 'String' -}; - export function parseSubgraphSchema (subgraphPath: string): any { const subgraphSchemaPath = path.join(path.resolve(subgraphPath), '/schema.graphql'); @@ -51,15 +46,7 @@ export function getFieldType (typeNode: any): { typeName: string, array: boolean function parseType (typeNode: any): any { // Check if 'NamedType' is reached. - if (typeNode.kind === 'NamedType') { - const typeName: string = typeNode.name.value; - - // TODO Handle extra types provided by the graph. - // Replace unknown scalars using SCALAR_MAPPING. - if (typeName in SCALAR_MAPPING) { - typeNode.name.value = SCALAR_MAPPING[typeName]; - } - } else { + if (typeNode.kind !== 'NamedType') { typeNode.type = parseType(typeNode.type); } diff --git a/packages/codegen/src/utils/type-mappings.ts b/packages/codegen/src/utils/type-mappings.ts index b1bf2040..2922b3c5 100644 --- a/packages/codegen/src/utils/type-mappings.ts +++ b/packages/codegen/src/utils/type-mappings.ts @@ -33,12 +33,15 @@ _tsToPg.set('string', 'varchar'); _tsToPg.set('number', 'integer'); _tsToPg.set('bigint', 'numeric'); _tsToPg.set('boolean', 'boolean'); +_tsToPg.set('Decimal', 'numeric'); // Graphql to Typescript type-mapping. _gqlToTs.set('String', 'string'); _gqlToTs.set('Int', 'number'); _gqlToTs.set('BigInt', 'bigint'); _gqlToTs.set('Boolean', 'boolean'); +_gqlToTs.set('BigDecimal', 'Decimal'); +_gqlToTs.set('Bytes', 'string'); function getTsForSol (solType: string): string | undefined { return _solToTs.get(solType);