diff --git a/manifest-schema.graphql b/manifest-schema.graphql deleted file mode 100644 index f4cf25c46..000000000 --- a/manifest-schema.graphql +++ /dev/null @@ -1,122 +0,0 @@ -scalar String -scalar File -scalar BigInt - -type Schema { - file: File! -} - -union DataSource = - EthereumContractDataSource | - NearContractDataSource - -union DataSourceTemplate = EthereumContractDataSourceTemplate - -type EthereumContractDataSource { - kind: String! - name: String! - network: String - source: EthereumContractSource! - mapping: EthereumContractMapping! -} - -type NearContractDataSource { - kind: String! - name: String! - network: String - source: NearContractSource! - mapping: NearContractMapping! -} - -type EthereumContractSource { - address: String - abi: String! - startBlock: BigInt -} - -type NearContractSource { - account: String - startBlock: BigInt -} - -type EthereumContractMapping { - kind: String - apiVersion: String! - language: String! - file: File! - entities: [String!]! - abis: [EthereumContractAbi!]! - blockHandlers: [EthereumBlockHandler!] - callHandlers: [EthereumCallHandler!] - eventHandlers: [EthereumContractEventHandler!] -} - -type NearContractMapping { - apiVersion: String! - language: String! - file: File! - entities: [String!]! - blockHandlers: [NearBlockHandler!] - receiptHandlers: [NearReceiptHandler!] -} - -type EthereumContractAbi { - name: String! - file: File! -} - -type EthereumBlockHandler { - handler: String! - filter: EthereumBlockFilter -} - -type EthereumBlockFilter { - kind: String! -} - -type NearBlockHandler { - handler: String! -} - -type EthereumCallHandler { - function: String! - handler: String! -} - -type NearReceiptHandler { - handler: String! -} - -type EthereumContractEventHandler { - event: String! - topic0: String - handler: String! -} - -type Graft { - base: String! - block: BigInt! -} - -type SubgraphManifest { - specVersion: String! - features: [String!] - schema: Schema! - description: String - repository: String - graft: Graft - dataSources: [DataSource!]! - templates: [DataSourceTemplate!] -} - -type EthereumContractDataSourceTemplate { - kind: String! - name: String! - network: String - source: EthereumContractSourceTemplate! - mapping: EthereumContractMapping! -} - -type EthereumContractSourceTemplate { - abi: String! -} diff --git a/src/protocols/ethereum/manifest.graphql b/src/protocols/ethereum/manifest.graphql new file mode 100644 index 000000000..59a697f88 --- /dev/null +++ b/src/protocols/ethereum/manifest.graphql @@ -0,0 +1,92 @@ +# Each referenced type's in any of the types below must be listed +# here either as `scalar` or `type` for the validation code to work +# properly. +# +# That's why `String` is listed as a scalar even though it's built-in +# GraphQL basic types. +scalar String +scalar File +scalar BigInt + +type SubgraphManifest { + specVersion: String! + features: [String!] + schema: Schema! + description: String + repository: String + graft: Graft + dataSources: [DataSource!]! + templates: [DataSourceTemplate!] +} + +type Schema { + file: File! +} + +type DataSource { + kind: String! + name: String! + network: String + source: ContractSource! + mapping: ContractMapping! +} + +type ContractSource { + address: String + abi: String! + startBlock: BigInt +} + +type ContractMapping { + kind: String + apiVersion: String! + language: String! + file: File! + entities: [String!]! + abis: [ContractAbi!]! + blockHandlers: [BlockHandler!] + callHandlers: [CallHandler!] + eventHandlers: [ContractEventHandler!] +} + +type ContractAbi { + name: String! + file: File! +} + +type BlockHandler { + handler: String! + filter: BlockFilter +} + +type BlockFilter { + kind: String! +} + +type CallHandler { + function: String! + handler: String! +} + +type ContractEventHandler { + event: String! + topic0: String + handler: String! +} + +type Graft { + base: String! + block: BigInt! +} + +type DataSourceTemplate { + kind: String! + name: String! + network: String + source: ContractSourceTemplate! + mapping: ContractMapping! +} + +type ContractSourceTemplate { + abi: String! +} diff --git a/src/protocols/index.js b/src/protocols/index.js index 8bd77ecab..293513f79 100644 --- a/src/protocols/index.js +++ b/src/protocols/index.js @@ -60,10 +60,7 @@ module.exports = class Protocol { 'aurora', 'aurora-testnet', ], - near: [ - 'near-mainnet', - 'near-testnet' - ], + near: ['near-mainnet', 'near-testnet'], }) } @@ -108,6 +105,15 @@ module.exports = class Protocol { } } + hasTemplates() { + switch (this.name) { + case 'ethereum': + return true + case 'near': + return false + } + } + getTypeGenerator(options) { switch (this.name) { case 'ethereum': @@ -118,13 +124,17 @@ module.exports = class Protocol { } getTemplateCodeGen(template) { + if (!this.hasTemplates()) { + throw new Error( + `Template data sources with kind '${this.name}' are not supported yet`, + ) + } + switch (this.name) { case 'ethereum': return new EthereumTemplateCodeGen(template) default: - throw new Error( - `Template data sources with kind '${this.name}' are not supported yet`, - ) + throw new Error(`Template data sources with kind '${this.name}' is unknown`) } } diff --git a/src/protocols/near/manifest.graphql b/src/protocols/near/manifest.graphql new file mode 100644 index 000000000..a9fd91628 --- /dev/null +++ b/src/protocols/near/manifest.graphql @@ -0,0 +1,57 @@ +# Each referenced type's in any of the types below must be listed +# here either as `scalar` or `type` for the validation code to work +# properly. +# +# That's why `String` is listed as a scalar even though it's built-in +# GraphQL basic types. +scalar String +scalar File +scalar BigInt + +type SubgraphManifest { + specVersion: String! + schema: Schema! + description: String + repository: String + graft: Graft + dataSources: [DataSource!]! +} + +type Schema { + file: File! +} + +type DataSource { + kind: String! + name: String! + network: String + source: ContractSource! + mapping: ContractMapping! +} + +type ContractSource { + account: String + startBlock: BigInt +} + +type ContractMapping { + apiVersion: String! + language: String! + file: File! + entities: [String!]! + blockHandlers: [BlockHandler!] + receiptHandlers: [ReceiptHandler!] +} + +type BlockHandler { + handler: String! +} + +type ReceiptHandler { + handler: String! +} + +type Graft { + base: String! + block: BigInt! +} diff --git a/src/subgraph.js b/src/subgraph.js index 99f61b89a..fc9c66ca8 100644 --- a/src/subgraph.js +++ b/src/subgraph.js @@ -27,7 +27,7 @@ const buildCombinedWarning = (filename, warnings) => ? warnings.reduce( (msg, w) => `${msg} - + Path: ${w.get('path').size === 0 ? '/' : w.get('path').join(' > ')} ${w .get('message') @@ -39,9 +39,21 @@ const buildCombinedWarning = (filename, warnings) => module.exports = class Subgraph { static async validate(data, protocol, { resolveFile }) { + if (protocol.name == null) { + return immutable.fromJS([ + { + path: [], + message: `Unable to determine for which protocol manifest file is built for. Ensure you have at least one 'dataSources' and/or 'templates' elements defined in your subgraph.`, + }, + ]) + } + // Parse the default subgraph schema let schema = graphql.parse( - await fs.readFile(path.join(__dirname, '..', 'manifest-schema.graphql'), 'utf-8'), + await fs.readFile( + path.join(__dirname, 'protocols', protocol.name, `manifest.graphql`), + 'utf-8', + ), ) // Obtain the root `SubgraphManifest` type from the schema @@ -146,10 +158,7 @@ At least one such handler must be defined.`, } static validateContractValues(manifest, protocol) { - return validation.validateContractValues( - manifest, - protocol, - ) + return validation.validateContractValues(manifest, protocol) } // Validate that data source names are unique, so they don't overwrite each other. @@ -200,17 +209,13 @@ More than one template named '${name}', template names must be unique.`, return yaml.stringify(manifest.toJS()) } - static async load( - filename, - { protocol, skipValidation } = { skipValidation: false } - ) { + static async load(filename, { protocol, skipValidation } = { skipValidation: false }) { // Load and validate the manifest let data = null - if(filename.match(/.js$/)) { + if (filename.match(/.js$/)) { data = require(path.resolve(filename)) - } - else { + } else { data = yaml.parse(await fs.readFile(filename, 'utf-8')) } diff --git a/src/validation/manifest.js b/src/validation/manifest.js index 66c865bf5..1de702a54 100644 --- a/src/validation/manifest.js +++ b/src/validation/manifest.js @@ -52,8 +52,7 @@ const validators = immutable.fromJS({ validators.get(ctx.getIn(['type', 'name', 'value']))(value, ctx), UnionTypeDefinition: (value, ctx) => { - const unionVariants = ctx - .getIn(['type', 'types']) + const unionVariants = ctx.getIn(['type', 'types']) let errors = List() @@ -78,7 +77,10 @@ const validators = immutable.fromJS({ NonNullType: (value, ctx) => value !== null && value !== undefined - ? validateValue(value, ctx.update('type', type => type.get('type'))) + ? validateValue( + value, + ctx.update('type', type => type.get('type')), + ) : immutable.fromJS([ { path: ctx.get('path'), @@ -126,7 +128,7 @@ const validators = immutable.fromJS({ ), ) : errors.push( - key == 'templates' + key == 'templates' && ctx.get('protocol').hasTemplates() ? immutable.fromJS({ path: ctx.get('path'), message: @@ -211,7 +213,8 @@ const validateValue = (value, ctx) => { } const validateDataSourceForNetwork = (dataSources, protocol) => - dataSources.filter(dataSource => protocol.isValidKindName(dataSource.kind)) + dataSources + .filter(dataSource => protocol.isValidKindName(dataSource.kind)) .reduce( (networks, dataSource) => networks.update(dataSource.network, dataSources => @@ -235,11 +238,11 @@ const validateDataSourceNetworks = (value, protocol) => { ${networks .map( (dataSources, network) => - ` ${ + ` ${ network === undefined ? 'Data sources and templates having no network set' - : `Data sources and templates using '${network}'` - }:\n${dataSources.map(ds => ` - ${ds}`).join('\n')}`, + : `Data sources and templates using '${network}'` + }:\n${dataSources.map(ds => ` - ${ds}`).join('\n')}`, ) .join('\n')} Recommendation: Make all data sources and templates use the same network name.`, @@ -262,6 +265,7 @@ const validateManifest = (value, type, schema, protocol, { resolveFile }) => { path: [], errors: [], resolveFile, + protocol, }), ) : immutable.fromJS([ diff --git a/tests/cli/validation.test.js b/tests/cli/validation.test.js index 0dff02e1d..ef89def61 100644 --- a/tests/cli/validation.test.js +++ b/tests/cli/validation.test.js @@ -9,6 +9,14 @@ describe('Validation', () => { exitCode: 1, }, ) + cliTest( + 'Invalid subgraph manifest (cannot infer protocol)', + ['codegen', '--skip-migrations'], + 'validation/invalid-manifest-cannot-infer-protocol', + { + exitCode: 1, + }, + ) cliTest( 'ABI not found in data source', ['codegen', '--skip-migrations'], diff --git a/tests/cli/validation/example-values-found.stderr b/tests/cli/validation/example-values-found.stderr index 600bf2fcf..524095619 100644 --- a/tests/cli/validation/example-values-found.stderr +++ b/tests/cli/validation/example-values-found.stderr @@ -1,10 +1,10 @@ - Load subgraph from subgraph.yaml ⚠ Warnings while loading subgraph from subgraph.yaml: Warnings in subgraph.yaml: - + Path: repository The repository is still set to https://github.com/graphprotocol/example-subgraph. Please replace it with a link to your subgraph source code. - + Path: description The description is still the one from the example subgraph. Please update it to tell users more about your subgraph. diff --git a/tests/cli/validation/invalid-manifest-cannot-infer-protocol.stderr b/tests/cli/validation/invalid-manifest-cannot-infer-protocol.stderr new file mode 100644 index 000000000..6c3bc469e --- /dev/null +++ b/tests/cli/validation/invalid-manifest-cannot-infer-protocol.stderr @@ -0,0 +1,5 @@ +- Load subgraph from subgraph.yaml +✖ Failed to load subgraph from subgraph.yaml: Error in subgraph.yaml: + + Path: / + Unable to determine for which protocol manifest file is built for. Ensure you have at least one 'dataSources' and/or 'templates' elements defined in your subgraph. diff --git a/tests/cli/validation/invalid-manifest-cannot-infer-protocol/subgraph.yaml b/tests/cli/validation/invalid-manifest-cannot-infer-protocol/subgraph.yaml new file mode 100644 index 000000000..d4fa4ee08 --- /dev/null +++ b/tests/cli/validation/invalid-manifest-cannot-infer-protocol/subgraph.yaml @@ -0,0 +1,12 @@ +schema: + file: ./non-existent.grapqhl +dataSources: + - name: 5 + abis: + name: Foo + mapping: + - 12 + - 13 + - 14 +templates: + field: foo diff --git a/tests/cli/validation/invalid-manifest.stderr b/tests/cli/validation/invalid-manifest.stderr index f7794fb77..64cc78776 100644 --- a/tests/cli/validation/invalid-manifest.stderr +++ b/tests/cli/validation/invalid-manifest.stderr @@ -7,9 +7,6 @@ Path: schema > file File does not exist: non-existent.grapqhl - Path: dataSources > 0 > kind - No value provided - Path: dataSources > 0 > name Expected string, found number: 5 diff --git a/tests/cli/validation/invalid-manifest/subgraph.yaml b/tests/cli/validation/invalid-manifest/subgraph.yaml index d4fa4ee08..22d990eb5 100644 --- a/tests/cli/validation/invalid-manifest/subgraph.yaml +++ b/tests/cli/validation/invalid-manifest/subgraph.yaml @@ -1,7 +1,8 @@ schema: file: ./non-existent.grapqhl dataSources: - - name: 5 + - kind: ethereum/contract + name: 5 abis: name: Foo mapping: