-
Notifications
You must be signed in to change notification settings - Fork 20
feat(indexer/distributions): define distributions GraphQL schema (#37) #50
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
Open
modrispath
wants to merge
2
commits into
Fundable-Protocol:dev
Choose a base branch
from
modrispath:feat/define-distributions-schema-37
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+170
−0
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,86 @@ | ||
| enum DistributionStatus { | ||
| ACTIVE | ||
| PAUSED | ||
| COMPLETED | ||
| CANCELLED | ||
| } | ||
|
|
||
| type DistributionBatch { | ||
| id: ID! | ||
| contractId: String! | ||
| distributor: String! | ||
| token: String! | ||
| totalAmount: String! | ||
| claimedAmount: String! | ||
| recipientCount: Int! | ||
| status: DistributionStatus! | ||
| pausedAt: String | ||
| resumedAt: String | ||
| uniqueRef: String! | ||
| ledgerNumber: Int! | ||
| txHash: String! | ||
| claims(pagination: PaginationInput): ClaimConnection! | ||
| createdAt: String! | ||
| updatedAt: String! | ||
| } | ||
|
|
||
| type ClaimAction { | ||
| id: ID! | ||
| batchId: ID! | ||
| claimant: String! | ||
| amount: String! | ||
| txHash: String! | ||
| ledgerNumber: Int! | ||
| eventTimestamp: String! | ||
| createdAt: String! | ||
| } | ||
|
|
||
| input DistributionFilterInput { | ||
| distributor: String | ||
| token: String | ||
| status: DistributionStatus | ||
| contractId: String | ||
| } | ||
|
|
||
| input ClaimFilterInput { | ||
| batchId: ID | ||
| claimant: String | ||
| } | ||
|
|
||
| input PaginationInput { | ||
| first: Int | ||
| after: String | ||
| } | ||
|
|
||
| type DistributionConnection { | ||
| nodes: [DistributionBatch!]! | ||
| pageInfo: PageInfo! | ||
| totalCount: Int! | ||
| } | ||
|
|
||
| type ClaimConnection { | ||
| nodes: [ClaimAction!]! | ||
| pageInfo: PageInfo! | ||
| totalCount: Int! | ||
| } | ||
|
|
||
| type PageInfo { | ||
| hasNextPage: Boolean! | ||
| endCursor: String | ||
| } | ||
|
|
||
| type Query { | ||
| distributionBatch(id: ID!): DistributionBatch | ||
| distributionBatches( | ||
| filter: DistributionFilterInput | ||
| pagination: PaginationInput | ||
| ): DistributionConnection! | ||
| distributionBatchesByDistributor( | ||
| distributor: String! | ||
| pagination: PaginationInput | ||
| ): DistributionConnection! | ||
| claimAction(id: ID!): ClaimAction | ||
| claims(filter: ClaimFilterInput, pagination: PaginationInput): ClaimConnection! | ||
| claimsByClaimant(claimant: String!, pagination: PaginationInput): ClaimConnection! | ||
| claimsByBatch(batchId: ID!, pagination: PaginationInput): ClaimConnection! | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| import { readFileSync } from "node:fs"; | ||
| import { dirname, join } from "node:path"; | ||
| import { fileURLToPath } from "node:url"; | ||
| import { describe, expect, test } from "vitest"; | ||
|
|
||
| const __dirname = dirname(fileURLToPath(import.meta.url)); | ||
| const schemaPath = join(__dirname, "../schema.graphql"); | ||
|
|
||
| describe("distributions GraphQL schema", () => { | ||
| const schemaContent = readFileSync(schemaPath, "utf-8"); | ||
|
|
||
| test("defines DistributionStatus enum", () => { | ||
| expect(schemaContent).toContain("enum DistributionStatus {"); | ||
| expect(schemaContent).toContain("ACTIVE"); | ||
| expect(schemaContent).toContain("PAUSED"); | ||
| expect(schemaContent).toContain("COMPLETED"); | ||
| expect(schemaContent).toContain("CANCELLED"); | ||
| }); | ||
|
|
||
| const definitionBody = (kind: "type" | "input" | "enum", name: string) => { | ||
| const match = schemaContent.match(new RegExp(`${kind}\\s+${name}\\s*\\{([\\s\\S]*?)\\n\\}`)); | ||
| expect(match).not.toBeNull(); | ||
| return match?.[1] ?? ""; | ||
| }; | ||
|
|
||
| test("defines DistributionBatch type aligned with database schema", () => { | ||
| const body = definitionBody("type", "DistributionBatch"); | ||
| expect(body).toContain("id: ID!"); | ||
| expect(body).toContain("contractId: String!"); | ||
| expect(body).toContain("distributor: String!"); | ||
| expect(body).toContain("token: String!"); | ||
| expect(body).toContain("totalAmount: String!"); | ||
| expect(body).toContain("claimedAmount: String!"); | ||
| expect(body).toContain("recipientCount: Int!"); | ||
| expect(body).toContain("status: DistributionStatus!"); | ||
| expect(body).toContain("pausedAt: String"); | ||
| expect(body).toContain("resumedAt: String"); | ||
| expect(body).toContain("uniqueRef: String!"); | ||
| expect(body).toContain("ledgerNumber: Int!"); | ||
| expect(body).toContain("txHash: String!"); | ||
| expect(body).toContain("claims(pagination: PaginationInput): ClaimConnection!"); | ||
| expect(body).toContain("createdAt: String!"); | ||
| expect(body).toContain("updatedAt: String!"); | ||
| }); | ||
|
|
||
| test("defines ClaimAction type aligned with database schema", () => { | ||
| const body = definitionBody("type", "ClaimAction"); | ||
| expect(body).toContain("id: ID!"); | ||
| expect(body).toContain("batchId: ID!"); | ||
| expect(body).toContain("claimant: String!"); | ||
| expect(body).toContain("amount: String!"); | ||
| expect(body).toContain("txHash: String!"); | ||
| expect(body).toContain("ledgerNumber: Int!"); | ||
| expect(body).toContain("eventTimestamp: String!"); | ||
| expect(body).toContain("createdAt: String!"); | ||
| }); | ||
|
|
||
| test("defines filter and pagination inputs", () => { | ||
| expect(schemaContent).toContain("input DistributionFilterInput {"); | ||
| expect(schemaContent).toContain("input ClaimFilterInput {"); | ||
| expect(schemaContent).toContain("input PaginationInput {"); | ||
| }); | ||
|
|
||
| test("defines connection and page info types", () => { | ||
| expect(schemaContent).toContain("type DistributionConnection {"); | ||
| expect(schemaContent).toContain("type ClaimConnection {"); | ||
| expect(schemaContent).toContain("type PageInfo {"); | ||
| }); | ||
|
|
||
| test("defines root Query queries", () => { | ||
| const normalized = schemaContent.replace(/\s+/g, " "); | ||
| expect(normalized).toContain("type Query {"); | ||
| expect(normalized).toContain("distributionBatch(id: ID!): DistributionBatch"); | ||
| expect(normalized).toContain( | ||
| "distributionBatches( filter: DistributionFilterInput pagination: PaginationInput ): DistributionConnection!", | ||
| ); | ||
| expect(normalized).toContain( | ||
| "distributionBatchesByDistributor( distributor: String! pagination: PaginationInput ): DistributionConnection!", | ||
| ); | ||
| expect(normalized).toContain("claimAction(id: ID!): ClaimAction"); | ||
| expect(normalized).toContain( | ||
| "claims(filter: ClaimFilterInput, pagination: PaginationInput): ClaimConnection!", | ||
| ); | ||
| expect(normalized).toContain( | ||
| "claimsByClaimant(claimant: String!, pagination: PaginationInput): ClaimConnection!", | ||
| ); | ||
| expect(normalized).toContain( | ||
| "claimsByBatch(batchId: ID!, pagination: PaginationInput): ClaimConnection!", | ||
| ); | ||
| }); | ||
| }); | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Parse the SDL instead of regex-matching raw text.
This helper lets the suite pass on malformed schema text as long as the expected substrings still exist somewhere in the file. That means syntax errors, duplicate definitions, or invalid field declarations can slip through here. Please build/parse the schema once and assert on the resulting type map/AST instead of extracting blocks with regex.
Suggested direction
🧰 Tools
🪛 ast-grep (0.44.0)
[warning] 20-20: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(
${kind}\\s+${name}\\s*\\{([\\s\\S]*?)\\n\\})Note: [CWE-1333] Inefficient Regular Expression Complexity
(regexp-from-variable)
🤖 Prompt for AI Agents