Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor indexer management API layer #859

Open
wants to merge 52 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
2ca42af
common: setting up GraphQL using Yoga and Codegen preset
saihaj Feb 21, 2024
35a172f
port over code model resolvers
saihaj Feb 21, 2024
e1d205f
port over more queries
saihaj Feb 21, 2024
3cede5c
more resolvers
saihaj Feb 23, 2024
897a88f
mutations
saihaj Feb 23, 2024
dea58e4
format
saihaj Feb 23, 2024
3f474ea
remove generate types
saihaj Feb 23, 2024
86af179
new line
saihaj Feb 23, 2024
b2e122c
fix build
saihaj Feb 23, 2024
1be1651
scalars
saihaj Feb 23, 2024
6831577
mutation
saihaj Feb 23, 2024
465f04f
remove unused
saihaj Feb 23, 2024
145fce5
remove Mutation.updateAction
saihaj Feb 23, 2024
57abcb2
make progress
saihaj Mar 18, 2024
5c23d34
Merge branch 'main' into saihaj/cleanup-resolvers
saihaj Mar 25, 2024
87acf85
finally some tests start to run
saihaj Mar 25, 2024
a84aa2f
fix actions test
saihaj Mar 26, 2024
504543d
more tests
saihaj Mar 26, 2024
229561a
fix install
saihaj Mar 26, 2024
d4a3957
fix cost model tests
saihaj Mar 26, 2024
1192c13
fix indexing rule test
saihaj Mar 26, 2024
5e568f5
fix poi disputes tests
saihaj Mar 26, 2024
06afc5e
remove grit
saihaj Mar 26, 2024
35aacef
fix some ts issues
saihaj Mar 27, 2024
abf8757
style prettier
saihaj Mar 27, 2024
d677bce
make tsc happy
saihaj Mar 27, 2024
50270d3
enable typechecking in tests
saihaj Mar 27, 2024
ade172d
fix tests
saihaj Mar 27, 2024
37c62ae
fix more type issues
saihaj Mar 27, 2024
ea7c704
format command
saihaj Mar 28, 2024
98f6698
fix some
saihaj Apr 1, 2024
cffb378
fix more
saihaj Apr 1, 2024
94926f7
mostly there
saihaj Apr 1, 2024
16ae8b4
fix
saihaj Apr 2, 2024
a8c3e6c
fix tests
saihaj Apr 2, 2024
463271b
works
saihaj Apr 2, 2024
cfd775a
fix a test
saihaj Apr 2, 2024
a49e672
fix test
saihaj Apr 2, 2024
4d0a0d3
remove console log
saihaj Apr 2, 2024
97666a2
fix issue
saihaj Apr 2, 2024
bd75175
prettier
saihaj Apr 2, 2024
5ce8a0c
remove urql client from indexer common
saihaj Apr 3, 2024
16c3ca3
bring back some deps because of typings
saihaj Apr 3, 2024
430cdeb
Merge branch 'main' into saihaj/cleanup-resolvers
saihaj Jul 8, 2024
02ca2ab
Fix test
saihaj Jul 8, 2024
72416d7
Merge branch 'main' into saihaj/cleanup-resolvers
saihaj Jul 9, 2024
572d48b
fix indexer agent
saihaj Jul 9, 2024
dd1832c
fix cli test
saihaj Jul 9, 2024
eeb644b
fix cli test
saihaj Jul 9, 2024
3269c95
Merge branch 'main' into saihaj/cleanup-resolvers
saihaj Aug 6, 2024
0ca52d4
fix some type issues and format
saihaj Aug 6, 2024
38a0eab
add contributor guide
saihaj Aug 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@

# Contributing to Indexer Components

Welcome to the Graph Protocol! Thanks a ton for your interest in contributing.

If you run into any problems feel free to create an issue. PRs are much appreciated for simple things. Here's [a list of good first issues](https://github.com/graphprotocol/indexer/labels/good%20first%20issue). If it's something more complex we'd appreciate having a quick chat in GitHub Issues or Discord.

Join the conversation on our [Discord](https://discord.gg/graphprotocol).

Please follow the [Code of Conduct](https://github.com/graphprotocol/graph-node/blob/master/CODE_OF_CONDUCT.md) for all the communications and at events. Thank you!

## Development flow

#### Clone the repository and install the dependencies:

```sh
git clone [email protected]:graphprotocol/indexer.git
cd indexer
yarn
```

#### To run the tests:

```sh
yarn test
```

#### Creating new resolvers

The [`indexer-common`](./packages/indexer-common/) package is shared between the different indexer packages in this repository. If you are creating a new resolver, you should add it to the `indexer-common` package. This way, the resolver can be shared between the different indexer packages. To introduce a new resolver, you should:
1. Add the GraphQL schema in [`packages/indexer-common/src/indexer-management/schema.graphql`](./packages/indexer-common/src/indexer-management/schema.graphql).
2. Run `yarn codegen` this uses the [GraphQL Codegen Server preset](https://the-guild.dev/graphql/codegen/docs/guides/graphql-server-apollo-yoga-with-server-preset) to generate TypeScript types for the schema and resolvers.
3. Now you should see resolver in [`packages/indexer-common/src/schema/indexer-management/resolvers`](./packages/indexer-common/src/schema/indexer-management/resolvers/) directory and write the logic for the resolver in the file.

#### GraphQL Unit Testing

We use [Jest](https://jestjs.io/) for unit testing. You can write tests for your resolvers in the `__tests__` directory in the same directory as the resolver. For example, if you have a resolver in `packages/indexer-common/src/schema/indexer-management/resolvers/MyResolver.ts`, you should write tests for it in `packages/indexer-common/src/schema/indexer-management/resolvers/__tests__/MyResolver.test.ts`. The test setup utilizes [HTTP Injection from GraphQL Yoga](https://the-guild.dev/graphql/yoga-server/docs/features/testing).

## Commit messages and pull requests

We use the following format for commit messages:
`{package-name}: {Brief description of changes}`, for example: `indexer-cli: Remove l1 support`.

If multiple packages are being changed list them all like this: `all: `

If a shared package is touched and impacts multiple packages, you can use short-had like this: `*: `.

The body of the message can be terse, with just enough information to explain what the commit does overall. In a lot of cases, more extensive explanations of _how_ the commit achieves its goal are better as comments
in the code.

Commits in a pull request should be structured in such a way that each commit consists of a small logical step towards the overall goal of the pull request. Your pull request should make it as easy as possible for the
reviewer to follow each change you are making. For example, it is a good idea to separate simple mechanical changes like renaming a method that touches many files from logic changes. Your pull request should not be
structured into commits according to how you implemented your feature, often indicated by commit messages like 'Fix problem' or 'Cleanup'. Flex a bit, and make the world think that you implemented your feature perfectly, in small logical steps, in one sitting without ever having to touch up something you did earlier in the pull request. (In reality, that means you'll use `git rebase -i` a lot)
90 changes: 60 additions & 30 deletions packages/indexer-agent/src/__tests__/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ import {
consolidateAllocationDecisions,
} from '../agent'
import {
GeneratedGraphQLTypes,
INDEXING_RULE_GLOBAL,
IndexingDecisionBasis,
IndexingRuleAttributes,
SubgraphIdentifierType,
SubgraphVersion,
} from '@graphprotocol/indexer-common'
import { SubgraphDeploymentID } from '@graphprotocol/common-ts'
Expand All @@ -16,44 +14,56 @@ describe('Agent convenience function tests', () => {
const inputRules = [
{
identifier: INDEXING_RULE_GLOBAL,
identifierType: SubgraphIdentifierType.GROUP,
allocationAmount: '2300',
identifierType: 'group',
allocationAmount: BigInt(2300),
parallelAllocations: null,
maxAllocationPercentage: null,
minSignal: null,
maxSignal: null,
minStake: null,
minAverageQueryFees: null,
custom: null,
decisionBasis: IndexingDecisionBasis.RULES,
decisionBasis: 'rules',
autoRenewal: false,
requireSupported: false,
safety: false,
protocolNetwork: 'sepolia',
},
{
identifier: '0x0000000000000000000000000000000000000000-0',
identifierType: SubgraphIdentifierType.SUBGRAPH,
allocationAmount: '3000',
identifierType: 'subgraph',
allocationAmount: BigInt(3000),
parallelAllocations: null,
maxAllocationPercentage: null,
minSignal: null,
maxSignal: null,
minStake: null,
minAverageQueryFees: null,
custom: null,
decisionBasis: IndexingDecisionBasis.RULES,
decisionBasis: 'rules',
autoRenewal: false,
requireSupported: false,
safety: false,
protocolNetwork: 'sepolia',
},
{
identifier: 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr',
identifierType: SubgraphIdentifierType.DEPLOYMENT,
allocationAmount: '12000',
identifierType: 'deployment',
allocationAmount: BigInt(12000),
parallelAllocations: null,
maxAllocationPercentage: null,
minSignal: null,
maxSignal: null,
minStake: null,
minAverageQueryFees: null,
custom: null,
decisionBasis: IndexingDecisionBasis.RULES,
decisionBasis: 'rules',
protocolNetwork: 'sepolia',
autoRenewal: false,
requireSupported: false,
safety: false,
},
] as IndexingRuleAttributes[]
] satisfies GeneratedGraphQLTypes.IndexingRule[]

const subgraphs = [
{
Expand All @@ -73,45 +83,57 @@ describe('Agent convenience function tests', () => {
const expectedRules = [
{
identifier: INDEXING_RULE_GLOBAL,
identifierType: SubgraphIdentifierType.GROUP,
allocationAmount: '2300',
identifierType: 'group',
allocationAmount: BigInt(2300),
parallelAllocations: null,
maxAllocationPercentage: null,
minSignal: null,
maxSignal: null,
minStake: null,
minAverageQueryFees: null,
custom: null,
decisionBasis: IndexingDecisionBasis.RULES,
decisionBasis: 'rules',
protocolNetwork: 'sepolia',
autoRenewal: false,
requireSupported: false,
safety: false,
},
{
identifier:
'0xc9d18c59e4aaf2c1f86dfef16fbdc0f81eae8ada58d87a23d2666c45704b8823',
identifierType: SubgraphIdentifierType.DEPLOYMENT,
allocationAmount: '3000',
identifierType: 'deployment',
allocationAmount: BigInt(3000),
parallelAllocations: null,
maxAllocationPercentage: null,
minSignal: null,
maxSignal: null,
minStake: null,
minAverageQueryFees: null,
custom: null,
decisionBasis: IndexingDecisionBasis.RULES,
decisionBasis: 'rules',
protocolNetwork: 'sepolia',
autoRenewal: false,
requireSupported: false,
safety: false,
},
{
identifier: 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr',
identifierType: SubgraphIdentifierType.DEPLOYMENT,
allocationAmount: '12000',
identifierType: 'deployment',
allocationAmount: BigInt(12000),
parallelAllocations: null,
maxAllocationPercentage: null,
minSignal: null,
maxSignal: null,
minStake: null,
minAverageQueryFees: null,
custom: null,
decisionBasis: IndexingDecisionBasis.RULES,
decisionBasis: 'rules',
protocolNetwork: 'sepolia',
autoRenewal: false,
requireSupported: false,
safety: false,
},
] as IndexingRuleAttributes[]
] satisfies GeneratedGraphQLTypes.IndexingRule[]

expect(
convertSubgraphBasedRulesToDeploymentBased(inputRules, subgraphs, 1000),
Expand All @@ -122,31 +144,39 @@ describe('Agent convenience function tests', () => {
const inputRules = [
{
identifier: INDEXING_RULE_GLOBAL,
identifierType: SubgraphIdentifierType.GROUP,
allocationAmount: '2300',
identifierType: 'group',
allocationAmount: BigInt(2300),
parallelAllocations: null,
maxAllocationPercentage: null,
minSignal: null,
maxSignal: null,
minStake: null,
minAverageQueryFees: null,
custom: null,
decisionBasis: IndexingDecisionBasis.RULES,
decisionBasis: 'rules',
protocolNetwork: 'sepolia',
autoRenewal: false,
requireSupported: false,
safety: false,
},
{
identifier: 'QmZZtzZkfzCWMNrajxBf22q7BC9HzoT5iJUK3S8qA6zNZr',
identifierType: SubgraphIdentifierType.DEPLOYMENT,
allocationAmount: '12000',
identifierType: 'deployment',
allocationAmount: BigInt(12000),
parallelAllocations: null,
maxAllocationPercentage: null,
minSignal: null,
maxSignal: null,
minStake: null,
minAverageQueryFees: null,
custom: null,
decisionBasis: IndexingDecisionBasis.RULES,
decisionBasis: 'rules',
protocolNetwork: 'sepolia',
autoRenewal: false,
requireSupported: false,
safety: false,
},
] as IndexingRuleAttributes[]
] satisfies GeneratedGraphQLTypes.IndexingRule[]

const subgraphs = [
{
Expand Down
97 changes: 29 additions & 68 deletions packages/indexer-agent/src/__tests__/indexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import {
parseGRT,
} from '@graphprotocol/common-ts'
import {
createIndexerManagementClient,
defineIndexerManagementModels,
IndexerManagementClient,
IndexerManagementModels,
GraphNode,
Operator,
Expand All @@ -19,9 +17,9 @@ import {
QueryFeeModels,
defineQueryFeeModels,
MultiNetworks,
createIndexerManagementYogaClient,
loadTestYamlConfig,
} from '@graphprotocol/indexer-common'
import { BigNumber } from 'ethers'
import { Sequelize } from 'sequelize'

const TEST_DISPUTE_1: POIDisputeAttributes = {
Expand Down Expand Up @@ -67,52 +65,15 @@ const TEST_DISPUTE_2: POIDisputeAttributes = {
protocolNetwork: 'eip155:421614',
}

const POI_DISPUTES_CONVERTERS_FROM_GRAPHQL: Record<
keyof POIDisputeAttributes,
(x: never) => string | BigNumber | number | undefined
> = {
allocationID: x => x,
subgraphDeploymentID: x => x,
allocationIndexer: x => x,
allocationAmount: x => x,
allocationProof: x => x,
closedEpoch: x => +x,
closedEpochStartBlockHash: x => x,
closedEpochStartBlockNumber: x => +x,
closedEpochReferenceProof: x => x,
previousEpochStartBlockHash: x => x,
previousEpochStartBlockNumber: x => +x,
previousEpochReferenceProof: x => x,
status: x => x,
protocolNetwork: x => x,
}

/**
* Parses a POI dispute returned from the indexer management GraphQL
* API into normalized form.
*/
const disputeFromGraphQL = (
dispute: Partial<POIDisputeAttributes>,
): POIDisputeAttributes => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const obj = {} as any
for (const [key, value] of Object.entries(dispute)) {
if (key === '__typename') {
continue
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
obj[key] = (POI_DISPUTES_CONVERTERS_FROM_GRAPHQL as any)[key](value)
}
return obj as POIDisputeAttributes
}

declare const __DATABASE__: never

let sequelize: Sequelize
let models: IndexerManagementModels
let queryFeeModels: QueryFeeModels
let logger: Logger
let indexerManagementClient: IndexerManagementClient
let indexerManagementClient: Awaited<
ReturnType<typeof createIndexerManagementYogaClient>
>
let graphNode: GraphNode
let operator: Operator
let metrics: Metrics
Expand Down Expand Up @@ -157,7 +118,7 @@ const setup = async () => {
(n: Network) => n.specification.networkIdentifier,
)

indexerManagementClient = await createIndexerManagementClient({
indexerManagementClient = await createIndexerManagementYogaClient({
models,
graphNode,
logger,
Expand Down Expand Up @@ -217,34 +178,34 @@ describe('Indexer tests', () => {
test('Store POI Disputes is idempotent', async () => {
const disputes: POIDisputeAttributes[] = [TEST_DISPUTE_1, TEST_DISPUTE_2]

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const expectedResult = disputes.map((dispute: Record<string, any>) => {
return disputeFromGraphQL(dispute)
})
await expect(operator.storePoiDisputes(disputes)).resolves.toEqual(
expectedResult,
)
await expect(operator.storePoiDisputes(disputes)).resolves.toEqual(
expectedResult,
)
await expect(operator.storePoiDisputes(disputes)).resolves.toEqual(
expectedResult,
)
const result1 = (await operator.storePoiDisputes(disputes)).map(a => ({
...a,
allocationAmount: a.allocationAmount.toString(),
}))
expect(result1).toEqual(disputes)
const result2 = (await operator.storePoiDisputes(disputes)).map(a => ({
...a,
allocationAmount: a.allocationAmount.toString(),
}))
expect(result2).toEqual(disputes)
const result3 = (await operator.storePoiDisputes(disputes)).map(a => ({
...a,
allocationAmount: a.allocationAmount.toString(),
}))
expect(result3).toEqual(disputes)
})

test('Fetch POIDisputes', async () => {
const disputes: POIDisputeAttributes[] = [TEST_DISPUTE_1, TEST_DISPUTE_2]

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const expectedResult = disputes.map((dispute: Record<string, any>) => {
return disputeFromGraphQL(dispute)
})
const expectedFilteredResult = [disputeFromGraphQL(TEST_DISPUTE_2)]
await expect(operator.storePoiDisputes(disputes)).resolves.toEqual(
expectedResult,
)
await expect(
operator.fetchPOIDisputes('potential', 205, 'eip155:421614'),
).resolves.toEqual(expectedFilteredResult)
const result1 = (await operator.storePoiDisputes(disputes)).map(a => ({
...a,
allocationAmount: a.allocationAmount.toString(),
}))
expect(result1).toEqual(disputes)
const result2 = (
await operator.fetchPOIDisputes('potential', 205, 'eip155:421614')
).map(a => ({ ...a, allocationAmount: a.allocationAmount.toString() }))
expect(result2).toEqual([TEST_DISPUTE_2])
})
})
Loading
Loading