Skip to content

Commit 3c4a600

Browse files
committed
protocols: Centralize name validation/comparison with data sources 'kind'
1 parent eedff93 commit 3c4a600

File tree

6 files changed

+69
-48
lines changed

6 files changed

+69
-48
lines changed

src/command-helpers/data-sources.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ const fromFilePath = async manifestPath => {
1111
return dataSources.concat(templates)
1212
}
1313

14-
const extractDataSourceByType = (manifest, dataSourceType, protocolName) =>
14+
const extractDataSourceByType = (manifest, dataSourceType, protocol) =>
1515
manifest
1616
.get(dataSourceType, immutable.List())
1717
.reduce(
1818
(dataSources, dataSource, dataSourceIndex) =>
19-
dataSource.get('kind') === protocolName
19+
protocol.isValidKindName(dataSource.get('kind'))
2020
? dataSources.push(
2121
immutable.Map({ path: [dataSourceType, dataSourceIndex], dataSource }),
2222
)
@@ -25,9 +25,9 @@ const extractDataSourceByType = (manifest, dataSourceType, protocolName) =>
2525
)
2626

2727
// Extracts data sources and templates from a immutable manifest data structure
28-
const fromManifest = (manifest, protocolName) => {
29-
const dataSources = extractDataSourceByType(manifest, 'dataSources', protocolName)
30-
const templates = extractDataSourceByType(manifest, 'templates', protocolName)
28+
const fromManifest = (manifest, protocol) => {
29+
const dataSources = extractDataSourceByType(manifest, 'dataSources', protocol)
30+
const templates = extractDataSourceByType(manifest, 'templates', protocol)
3131

3232
return dataSources.concat(templates)
3333
}

src/protocols/ethereum/subgraph.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
const immutable = require('immutable')
22
const ABI = require('./abi')
3-
const DataSourceExtractor = require('../../command-helpers/data-sources')
3+
const DataSourcesExtractor = require('../../command-helpers/data-sources')
44
const { validateContractAddresses } = require('../../validation')
55

66
module.exports = class EthereumSubgraph {
77
constructor(options = {}) {
88
this.manifest = options.manifest
99
this.resolveFile = options.resolveFile
10+
this.protocol = options.protocol
1011
}
1112

1213
validateManifest() {
@@ -17,7 +18,7 @@ module.exports = class EthereumSubgraph {
1718
}
1819

1920
validateAbis() {
20-
const dataSourcesAndTemplates = DataSourceExtractor.fromManifest(this.manifest, 'ethereum/contract')
21+
const dataSourcesAndTemplates = DataSourcesExtractor.fromManifest(this.manifest, this.protocol)
2122

2223
return dataSourcesAndTemplates.reduce(
2324
(errors, dataSourceOrTemplate) =>
@@ -76,14 +77,14 @@ ${abiNames
7677

7778
return validateContractAddresses(
7879
this.manifest,
79-
'ethereum/contract',
80+
this.protocol,
8081
address => ethereumAddressPattern.test(address),
8182
"Must be 40 hexadecimal characters, with an optional '0x' prefix.",
8283
)
8384
}
8485

8586
validateEvents() {
86-
const dataSourcesAndTemplates = DataSourceExtractor.fromManifest(this.manifest, 'ethereum/contract')
87+
const dataSourcesAndTemplates = DataSourcesExtractor.fromManifest(this.manifest, this.protocol)
8788

8889
return dataSourcesAndTemplates
8990
.reduce((errors, dataSourceOrTemplate) => {
@@ -144,7 +145,7 @@ ${abiEvents
144145
validateCallFunctions() {
145146
return this.manifest
146147
.get('dataSources')
147-
.filter(dataSource => dataSource.get('kind') === 'ethereum/contract')
148+
.filter(dataSource => this.protocol.isValidKindName(dataSource.get('kind')))
148149
.reduce((errors, dataSource, dataSourceIndex) => {
149150
let path = ['dataSources', dataSourceIndex, 'callHandlers']
150151

src/protocols/index.js

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const immutable = require('immutable')
12
const EthereumTypeGenerator = require('./ethereum/type-generator')
23
const EthereumTemplateCodeGen = require('./ethereum/codegen/template')
34
const NearTemplateCodeGen = require('./near/codegen/template')
@@ -12,7 +13,29 @@ module.exports = class Protocol {
1213
}
1314

1415
constructor(name) {
15-
this.name = name
16+
this.name = this.normalizeName(name)
17+
}
18+
19+
availableProtocols() {
20+
return immutable.fromJS({
21+
// `ethereum/contract` is kept for backwards compatibility.
22+
// New networks (or protocol perhaps) shouldn't have the `/contract` anymore (unless a new case makes use of it).
23+
ethereum: ['ethereum', 'ethereum/contract'],
24+
near: ['near'],
25+
})
26+
}
27+
28+
normalizeName(name) {
29+
return this.availableProtocols()
30+
.findKey(possibleNames => possibleNames.includes(name))
31+
}
32+
33+
// Receives a data source kind, and checks if it's valid
34+
// for the given protocol instance (this).
35+
isValidKindName(kind) {
36+
return this.availableProtocols()
37+
.get(this.name, immutable.List())
38+
.includes(kind)
1639
}
1740

1841
hasABIs() {
@@ -59,13 +82,15 @@ module.exports = class Protocol {
5982
}
6083
}
6184

62-
getSubgraph(options) {
85+
getSubgraph(options = {}) {
86+
const optionsWithProtocol = { ...options, protocol: this }
87+
6388
switch (this.name) {
6489
case 'ethereum':
6590
case 'ethereum/contract':
66-
return new EthereumSubgraph(options)
91+
return new EthereumSubgraph(optionsWithProtocol)
6792
case 'near':
68-
return new NearSubgraph(options)
93+
return new NearSubgraph(optionsWithProtocol)
6994
}
7095
}
7196
}

src/protocols/near/subgraph.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module.exports = class NearSubgraph {
55
constructor(options = {}) {
66
this.manifest = options.manifest
77
this.resolveFile = options.resolveFile
8+
this.protocol = options.protocol
89
}
910

1011
validateManifest() {
@@ -22,7 +23,7 @@ module.exports = class NearSubgraph {
2223

2324
return validateContractAddresses(
2425
this.manifest,
25-
'near',
26+
this.protocol,
2627
accountId => validateLength(accountId) && nearAccountIdPattern.test(accountId),
2728
`Must be between '${MINIMUM_ACCOUNT_ID_LENGTH}' and '${MAXIMUM_ACCOUNT_ID_LENGTH}' characters
2829
An Account ID consists of Account ID parts separated by '.' (dots)

src/subgraph.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const buildCombinedWarning = (filename, warnings) =>
3838
: null
3939

4040
module.exports = class Subgraph {
41-
static async validate(data, { resolveFile }) {
41+
static async validate(data, protocol, { resolveFile }) {
4242
// Parse the default subgraph schema
4343
let schema = graphql.parse(
4444
await fs.readFile(path.join(__dirname, '..', 'manifest-schema.graphql'), 'utf-8'),
@@ -50,7 +50,7 @@ module.exports = class Subgraph {
5050
})
5151

5252
// Validate the subgraph manifest using this schema
53-
return validation.validateManifest(data, rootType, schema, { resolveFile })
53+
return validation.validateManifest(data, rootType, schema, protocol, { resolveFile })
5454
}
5555

5656
static validateSchema(manifest, { resolveFile }) {
@@ -115,10 +115,10 @@ Please update it to tell users more about your subgraph.`,
115115
: immutable.List()
116116
}
117117

118-
static validateHandlers(manifest, protocolName, protocolSubgraph) {
118+
static validateHandlers(manifest, protocol, protocolSubgraph) {
119119
return manifest
120120
.get('dataSources')
121-
.filter(dataSource => dataSource.get('kind') === protocolName)
121+
.filter(dataSource => protocol.isValidKindName(dataSource.get('kind')))
122122
.reduce((errors, dataSource, dataSourceIndex) => {
123123
let path = ['dataSources', dataSourceIndex, 'mapping']
124124

@@ -130,12 +130,14 @@ Please update it to tell users more about your subgraph.`,
130130
.map(handlerType => mapping.get(handlerType, immutable.List()))
131131
.every(handlers => handlers.isEmpty())
132132

133+
const handlerNamesWithoutLast = handlerTypes.pop().join(', ')
134+
133135
return areAllHandlersEmpty
134136
? errors.push(
135137
immutable.fromJS({
136138
path: path,
137139
message: `\
138-
Mapping has no blockHandlers, callHandlers or eventHandlers.
140+
Mapping has no ${handlerNamesWithoutLast} or ${handlerTypes.get(-1)}.
139141
At least one such handler must be defined.`,
140142
}),
141143
)
@@ -209,7 +211,7 @@ More than one template named '${name}', template names must be unique.`,
209211
let resolveFile = maybeRelativeFile =>
210212
path.resolve(path.dirname(filename), maybeRelativeFile)
211213

212-
let manifestErrors = await Subgraph.validate(data, { resolveFile })
214+
let manifestErrors = await Subgraph.validate(data, protocol, { resolveFile })
213215
if (manifestErrors.size > 0) {
214216
throwCombinedError(filename, manifestErrors)
215217
}
@@ -231,7 +233,7 @@ More than one template named '${name}', template names must be unique.`,
231233
...protocolSubgraph.validateManifest(),
232234
...Subgraph.validateUniqueDataSourceNames(manifest),
233235
...Subgraph.validateUniqueTemplateNames(manifest),
234-
...Subgraph.validateHandlers(manifest, protocol.name, protocolSubgraph),
236+
...Subgraph.validateHandlers(manifest, protocol, protocolSubgraph),
235237
)
236238

237239
if (errors.size > 0) {

src/validation/manifest.js

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -210,15 +210,8 @@ const validateValue = (value, ctx) => {
210210
}
211211
}
212212

213-
const availableNetworks = immutable.fromJS({
214-
// `ethereum/contract` is kept for backwards compatibility.
215-
// New networks (or protocol perhaps) shouldn't have the `/contract` anymore (unless a new case makes use of it).
216-
ethereum: ['ethereum', 'ethereum/contract'],
217-
near: ['near'],
218-
})
219-
220-
const validateDataSourceForNetwork = (dataSources, network) =>
221-
dataSources.filter(dataSource => availableNetworks.get(network, List()).includes(dataSource.kind))
213+
const validateDataSourceForNetwork = (dataSources, protocol) =>
214+
dataSources.filter(dataSource => protocol.isValidKindName(dataSource.kind))
222215
.reduce(
223216
(networks, dataSource) =>
224217
networks.update(dataSource.network, dataSources =>
@@ -227,38 +220,37 @@ const validateDataSourceForNetwork = (dataSources, network) =>
227220
immutable.OrderedMap(),
228221
)
229222

230-
const validateDataSourceNetworks = value => {
223+
const validateDataSourceNetworks = (value, protocol) => {
231224
const dataSources = [...value.dataSources, ...(value.templates || [])]
232225

233-
const ethereumNetworks = validateDataSourceForNetwork(dataSources, 'ethereum')
234-
const nearNetworks = validateDataSourceForNetwork(dataSources, 'near')
226+
// Networks found valid for a protocol.
227+
// By searching through all data sources and templates.
228+
const networks = validateDataSourceForNetwork(dataSources, protocol)
235229

236-
for (const networks of [ethereumNetworks, nearNetworks]) {
237-
if (networks.size > 1) {
238-
return immutable.fromJS([
239-
{
240-
path: [],
241-
message: `Conflicting networks used in data sources and templates:
230+
if (networks.size > 1) {
231+
return immutable.fromJS([
232+
{
233+
path: [],
234+
message: `Conflicting networks used in data sources and templates:
242235
${networks
243236
.map(
244237
(dataSources, network) =>
245238
` ${
246239
network === undefined
247240
? 'Data sources and templates having no network set'
248-
: `Data sources and templates using '${network}'`
241+
: `Data sources and templates using '${network}'`
249242
}:\n${dataSources.map(ds => ` - ${ds}`).join('\n')}`,
250243
)
251244
.join('\n')}
252245
Recommendation: Make all data sources and templates use the same network name.`,
253-
},
254-
])
255-
}
246+
},
247+
])
256248
}
257249

258250
return List()
259251
}
260252

261-
const validateManifest = (value, type, schema, { resolveFile }) => {
253+
const validateManifest = (value, type, schema, protocol, { resolveFile }) => {
262254
// Validate manifest using the GraphQL schema that defines its structure
263255
let errors =
264256
value !== null && value !== undefined
@@ -287,13 +279,13 @@ const validateManifest = (value, type, schema, { resolveFile }) => {
287279

288280
// Validate that all data sources are for the same `network` (this includes
289281
// _no_ network at all)
290-
return validateDataSourceNetworks(value)
282+
return validateDataSourceNetworks(value, protocol)
291283
}
292284

293-
const validateContractAddresses = (manifest, protocolName, validator, errorMessage) =>
285+
const validateContractAddresses = (manifest, protocol, validator, errorMessage) =>
294286
manifest
295287
.get('dataSources')
296-
.filter(dataSource => dataSource.get('kind') === protocolName)
288+
.filter(dataSource => protocol.isValidKindName(dataSource.get('kind')))
297289
.reduce((errors, dataSource, dataSourceIndex) => {
298290
let path = ['dataSources', dataSourceIndex, 'source', 'address']
299291

0 commit comments

Comments
 (0)