-
Notifications
You must be signed in to change notification settings - Fork 4
feat: make saveLastProcessedEvent part of transaction #60
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| import { | ||
| IEventRegistryRepository, | ||
| ProcessedEventChangeset, | ||
| } from "@grants-stack-indexer/repository"; | ||
|
|
||
| import { ChangesetHandler } from "../types/index.js"; | ||
|
|
||
| /** | ||
| * Collection of handlers for application-related operations. | ||
| * Each handler corresponds to a specific Application changeset type. | ||
| */ | ||
| export type ProcessedEventHandlers = { | ||
| [K in ProcessedEventChangeset["type"]]: ChangesetHandler<K>; | ||
| }; | ||
|
|
||
| /** | ||
| * Creates handlers for managing application-related operations. | ||
| * | ||
| * @param repository - The application repository instance used for database operations | ||
| * @returns An object containing all application-related handlers | ||
| */ | ||
| export const createProcessedEventHandlers = ( | ||
| repository: IEventRegistryRepository, | ||
| ): ProcessedEventHandlers => ({ | ||
| InsertProcessedEvent: (async (changeset, txConnection): Promise<void> => { | ||
| await repository.saveLastProcessedEvent( | ||
| changeset.args.chainId, | ||
| changeset.args.processedEvent, | ||
| txConnection, | ||
| ); | ||
| }) satisfies ChangesetHandler<"InsertProcessedEvent">, | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ import { | |
| IApplicationPayoutRepository, | ||
| IApplicationRepository, | ||
| IDonationRepository, | ||
| IEventRegistryRepository, | ||
| IProjectRepository, | ||
| IRoundRepository, | ||
| ITransactionManager, | ||
|
|
@@ -41,6 +42,10 @@ describe("DataLoader", () => { | |
| insertApplicationPayout: vi.fn(), | ||
| } as IApplicationPayoutRepository; | ||
|
|
||
| const mockEventRegistryRepository = { | ||
| saveLastProcessedEvent: vi.fn(), | ||
| } as unknown as IEventRegistryRepository; | ||
|
Comment on lines
+45
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Add test cases for ProcessedEventChangeset handling in dataLoader.spec.ts The review comment is valid. While Suggested test cases to add:
🔗 Analysis chainAdd test cases for ProcessedEventChangeset handling. While the mock is correctly set up, there are no test cases verifying the behavior of
🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Description: Check if there are any test cases for ProcessedEventChangeset
rg -A 5 "InsertProcessedEvent" "packages/data-flow/test/**/*.spec.ts"
Length of output: 146 Script: #!/bin/bash
# Search for test files and relevant patterns
echo "=== Searching for saveLastProcessedEvent tests ==="
rg -A 5 "saveLastProcessedEvent" --type ts
echo -e "\n=== Searching for ProcessedEventChangeset tests ==="
rg -A 5 "ProcessedEventChangeset" --type ts
echo -e "\n=== Looking for related test files ==="
fd "dataLoader.spec.ts"
Length of output: 24168 |
||
|
|
||
| const logger: ILogger = { | ||
| debug: vi.fn(), | ||
| error: vi.fn(), | ||
|
|
@@ -61,6 +66,7 @@ describe("DataLoader", () => { | |
| application: mockApplicationRepository, | ||
| donation: mockDonationRepository, | ||
| applicationPayout: mockApplicationPayoutRepository, | ||
| eventRegistry: mockEventRegistryRepository, | ||
| }, | ||
| mockTransactionManager, | ||
| logger, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import { describe, expect, it, vi } from "vitest"; | ||
|
|
||
| import { IEventRegistryRepository, TransactionConnection } from "@grants-stack-indexer/repository"; | ||
| import { ChainId } from "@grants-stack-indexer/shared"; | ||
|
|
||
| import { createProcessedEventHandlers } from "../../../src/data-loader/handlers/processedEvent.handlers.js"; | ||
|
|
||
| describe("ProcessedEvent Handlers", () => { | ||
| const chainId = 1 as ChainId; | ||
| const mockEvent = { | ||
| blockNumber: 1, | ||
| blockTimestamp: 1234567890, | ||
| logIndex: 0, | ||
| rawEvent: {}, | ||
| }; | ||
|
|
||
| describe("InsertProcessedEvent", () => { | ||
| it("saves event to repository within transaction", async () => { | ||
| const saveLastProcessedEvent = vi.fn(); | ||
| const mockRepository = { | ||
| saveLastProcessedEvent, | ||
| getLastProcessedEvent: vi.fn(), | ||
| } as unknown as IEventRegistryRepository<TransactionConnection>; | ||
|
|
||
| const handlers = createProcessedEventHandlers(mockRepository); | ||
|
|
||
| const mockTx = {} as TransactionConnection; | ||
|
|
||
| await handlers.InsertProcessedEvent( | ||
| { | ||
| type: "InsertProcessedEvent", | ||
| args: { | ||
| chainId, | ||
| processedEvent: mockEvent, | ||
| }, | ||
| }, | ||
| mockTx, | ||
| ); | ||
|
|
||
| expect(saveLastProcessedEvent).toHaveBeenCalledWith(chainId, mockEvent, mockTx); | ||
| }); | ||
|
|
||
| it("propagates repository errors", async () => { | ||
| const error = new Error("Database error"); | ||
| const saveLastProcessedEvent = vi.fn().mockRejectedValue(error); | ||
| const mockRepository = { | ||
| saveLastProcessedEvent, | ||
| getLastProcessedEvent: vi.fn(), | ||
| } as unknown as IEventRegistryRepository<TransactionConnection>; | ||
|
|
||
| const handlers = createProcessedEventHandlers(mockRepository); | ||
|
|
||
| const mockTx = {} as TransactionConnection; | ||
|
|
||
| await expect( | ||
| handlers.InsertProcessedEvent( | ||
| { | ||
| type: "InsertProcessedEvent", | ||
| args: { | ||
| chainId, | ||
| processedEvent: mockEvent, | ||
| }, | ||
| }, | ||
| mockTx, | ||
| ), | ||
| ).rejects.toThrow(error); | ||
| }); | ||
| }); | ||
| }); |
Uh oh!
There was an error while loading. Please reload this page.
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.
Handle potential null reference in
eventThere is a risk of a null or undefined reference when using
event!in the async function passed tothis.retryHandler.execute. Ensure thateventis properly checked or handled to avoid runtime errors.Apply this diff to add null checking:
await this.retryHandler.execute( async () => { - const changesets = await this.handleEvent(event!); + if (!event) { + throw new Error("Event is undefined or null"); + } + const changesets = await this.handleEvent(event); if (changesets) { await this.dataLoader.applyChanges([ ...changesets, { type: "InsertProcessedEvent", args: { chainId: this.chainId, processedEvent: { - ...event!, + ...event, rawEvent: event, }, }, }, ]); } }, { abortSignal: signal }, );📝 Committable suggestion
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.
look at the prior lines, if event is undefined or null, we
continuethe loop prior to the the retryHandler execute method. this doesn't need to be fixedThere 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.
@0xnigir1 You're absolutely right. I apologize for the unnecessary suggestion. The code already has a proper null check before the
retryHandler.executecall, which ensures thateventis never null or undefined at that point.✏️ Learnings added