From 1d600498906b246dede580289d700912ef117d64 Mon Sep 17 00:00:00 2001 From: 0xkenj1 Date: Tue, 12 Nov 2024 19:32:04 -0300 Subject: [PATCH] fix: pr comments --- .../exceptions/projectNotFound.exception.ts | 6 ++ .../handlers/profileCreated.handler.ts | 15 ++- .../profileMetadataUpdated.handler.ts | 29 ++++-- .../handlers/profileNameUpdated.handler.ts | 4 + .../handlers/profileOwnerUpdated.handler.ts | 6 ++ .../registry/handlers/roleRevoked.handler.ts | 11 ++- .../profileMetadataUpdated.handler.spec.ts | 15 ++- .../handlers/roleRevoked.handler.spec.ts | 4 +- .../test/registry/registry.processor.spec.ts | 91 ++++++++++++++++++- .../repository/src/types/project.types.ts | 2 +- 10 files changed, 160 insertions(+), 23 deletions(-) diff --git a/packages/processors/src/exceptions/projectNotFound.exception.ts b/packages/processors/src/exceptions/projectNotFound.exception.ts index ba9d367..9ff64cd 100644 --- a/packages/processors/src/exceptions/projectNotFound.exception.ts +++ b/packages/processors/src/exceptions/projectNotFound.exception.ts @@ -5,3 +5,9 @@ export class ProjectNotFound extends Error { super(`Project not found for chainId: ${chainId} and anchorAddress: ${anchorAddress}`); } } + +export class ProjectByRoleNotFound extends Error { + constructor(chainId: ChainId, role: string) { + super(`Project not found for chainId: ${chainId} and role: ${role}`); + } +} diff --git a/packages/processors/src/processors/registry/handlers/profileCreated.handler.ts b/packages/processors/src/processors/registry/handlers/profileCreated.handler.ts index a0796b1..0fc5eb1 100644 --- a/packages/processors/src/processors/registry/handlers/profileCreated.handler.ts +++ b/packages/processors/src/processors/registry/handlers/profileCreated.handler.ts @@ -12,6 +12,13 @@ type Dependencies = Pick< >; /** * Handles the ProfileCreated event for the Registry contract from Allo protocol. + * + * This handler performs the following steps: + * - Fetches the metadata for the profile from the metadata provider + * - Parses the metadata to extract the project type + * - Returns the changeset to insert the project with the metadata + * + * If the metadata is not valid, it sets the metadata to null and the project type to canonical. */ export class ProfileCreatedHandler implements IEventHandler<"Registry", "ProfileCreated"> { constructor( @@ -35,14 +42,6 @@ export class ProfileCreatedHandler implements IEventHandler<"Registry", "Profile projectType = this.getProjectTypeFromMetadata(parsedMetadata.data); isProgram = parsedMetadata.data.type === "program"; metadataValue = parsedMetadata.data; - } else { - //TODO: Replace with logger - // this.logger.warn({ - // msg: `ProfileCreated: Failed to parse metadata for profile ${profileId}`, - // event: this.event, - // metadataCid, - // metadata, - // }); } const createdBy = diff --git a/packages/processors/src/processors/registry/handlers/profileMetadataUpdated.handler.ts b/packages/processors/src/processors/registry/handlers/profileMetadataUpdated.handler.ts index cf68451..0da4627 100644 --- a/packages/processors/src/processors/registry/handlers/profileMetadataUpdated.handler.ts +++ b/packages/processors/src/processors/registry/handlers/profileMetadataUpdated.handler.ts @@ -8,8 +8,14 @@ type Dependencies = Pick< ProcessorDependencies, "projectRepository" | "evmProvider" | "metadataProvider" | "logger" >; + /** * Handles the ProfileMetadataUpdated event for the Registry contract from Allo protocol. + * + * This handler performs the following steps: + * - Fetches the metadata for the profile from the metadata provider + * - Parses the metadata to extract the project type + * - Returns the changeset to update the project with the metadata */ export class ProfileMetadataUpdatedHandler implements IEventHandler<"Registry", "ProfileMetadataUpdated"> @@ -19,6 +25,7 @@ export class ProfileMetadataUpdatedHandler readonly chainId: ChainId, private dependencies: Dependencies, ) {} + /* @inheritdoc */ async handle(): Promise { const { metadataProvider } = this.dependencies; @@ -27,13 +34,20 @@ export class ProfileMetadataUpdatedHandler const parsedMetadata = ProjectMetadataSchema.safeParse(metadata); if (!parsedMetadata.success) { - // logger.warn({ - // msg: `ProfileMetadataUpdated: Failed to parse metadata`, - // event, - // metadataCid, - // metadata, - // }); - return []; + return [ + { + type: "UpdateProject", + args: { + chainId: this.chainId, + projectId: this.event.params.profileId, + project: { + metadataCid: metadataCid, + metadata: null, + projectType: "canonical", + }, + }, + }, + ]; } const projectType = this.getProjectTypeFromMetadata(parsedMetadata.data); @@ -52,7 +66,6 @@ export class ProfileMetadataUpdatedHandler }, }, ]; - return []; } private getProjectTypeFromMetadata(metadata: ProjectMetadata): ProjectType { // if the metadata contains a canonical reference, it's a linked project diff --git a/packages/processors/src/processors/registry/handlers/profileNameUpdated.handler.ts b/packages/processors/src/processors/registry/handlers/profileNameUpdated.handler.ts index e7a9088..b246111 100644 --- a/packages/processors/src/processors/registry/handlers/profileNameUpdated.handler.ts +++ b/packages/processors/src/processors/registry/handlers/profileNameUpdated.handler.ts @@ -8,6 +8,9 @@ import { IEventHandler, ProcessorDependencies } from "../../../internal.js"; type Dependencies = Pick; /** * Handles the ProfileNameUpdated event for the Registry contract from Allo protocol. + * + * This handler performs the following steps: + * - Returns the changeset to update the project with the new name */ export class ProfileNameUpdatedHandler implements IEventHandler<"Registry", "ProfileNameUpdated"> { constructor( @@ -15,6 +18,7 @@ export class ProfileNameUpdatedHandler implements IEventHandler<"Registry", "Pro readonly chainId: ChainId, private dependencies: Dependencies, ) {} + /* @inheritdoc */ async handle(): Promise { return [ { diff --git a/packages/processors/src/processors/registry/handlers/profileOwnerUpdated.handler.ts b/packages/processors/src/processors/registry/handlers/profileOwnerUpdated.handler.ts index 38aff7b..45fefca 100644 --- a/packages/processors/src/processors/registry/handlers/profileOwnerUpdated.handler.ts +++ b/packages/processors/src/processors/registry/handlers/profileOwnerUpdated.handler.ts @@ -8,6 +8,11 @@ import { IEventHandler, ProcessorDependencies } from "../../../internal.js"; type Dependencies = Pick; /** * Handles the ProfileOwnerUpdated event for the Registry contract from Allo protocol. + * + * This handler performs the following steps: + * + * - Returns the changeset to delete all project roles with the role "owner" + * for the profile and insert a new project role with the new owner address. */ export class ProfileOwnerUpdatedHandler implements IEventHandler<"Registry", "ProfileOwnerUpdated"> @@ -17,6 +22,7 @@ export class ProfileOwnerUpdatedHandler readonly chainId: ChainId, private dependencies: Dependencies, ) {} + /* @inheritdoc */ async handle(): Promise { return [ { diff --git a/packages/processors/src/processors/registry/handlers/roleRevoked.handler.ts b/packages/processors/src/processors/registry/handlers/roleRevoked.handler.ts index c5fd532..dc22880 100644 --- a/packages/processors/src/processors/registry/handlers/roleRevoked.handler.ts +++ b/packages/processors/src/processors/registry/handlers/roleRevoked.handler.ts @@ -3,11 +3,18 @@ import { getAddress } from "viem"; import { Changeset } from "@grants-stack-indexer/repository"; import { ChainId, ProcessorEvent } from "@grants-stack-indexer/shared"; -import { IEventHandler, ProcessorDependencies } from "../../../internal.js"; +import { IEventHandler, ProcessorDependencies, ProjectByRoleNotFound } from "../../../internal.js"; type Dependencies = Pick; /** * Handles the RoleRevoked event for the Registry contract from Allo protocol. + * + * This handler performs the following steps: + * + * - Returns the changeset to delete all project roles with the role "member" + * for the profile and address. + * + * If the project with the role id doesn't exist, it throws an error. */ export class RoleRevokedHandler implements IEventHandler<"Registry", "RoleRevoked"> { constructor( @@ -25,7 +32,7 @@ export class RoleRevokedHandler implements IEventHandler<"Registry", "RoleRevoke // which is the project id in this database. // If we don't find a project with that id we can't remove the role. if (!project) { - return []; + throw new ProjectByRoleNotFound(this.chainId, role); } return [ diff --git a/packages/processors/test/registry/handlers/profileMetadataUpdated.handler.spec.ts b/packages/processors/test/registry/handlers/profileMetadataUpdated.handler.spec.ts index a2165d2..710666c 100644 --- a/packages/processors/test/registry/handlers/profileMetadataUpdated.handler.spec.ts +++ b/packages/processors/test/registry/handlers/profileMetadataUpdated.handler.spec.ts @@ -65,7 +65,20 @@ describe("ProfileMetadataUpdatedHandler", () => { const handler = new ProfileMetadataUpdatedHandler(mockEvent, chainId, mockDependencies); const result = await handler.handle(); - expect(result).toEqual([]); + expect(result).toEqual([ + { + type: "UpdateProject", + args: { + chainId, + projectId: mockEvent.params.profileId, + project: { + metadataCid: mockCid, + metadata: null, + projectType: "canonical", + }, + }, + }, + ]); }); it("throws an error if getMetadata fails", async () => { diff --git a/packages/processors/test/registry/handlers/roleRevoked.handler.spec.ts b/packages/processors/test/registry/handlers/roleRevoked.handler.spec.ts index 6a0b80b..432c43d 100644 --- a/packages/processors/test/registry/handlers/roleRevoked.handler.spec.ts +++ b/packages/processors/test/registry/handlers/roleRevoked.handler.spec.ts @@ -3,6 +3,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { IProjectRepository, Project } from "@grants-stack-indexer/repository"; import { ChainId, ILogger, ProcessorEvent } from "@grants-stack-indexer/shared"; +import { ProjectByRoleNotFound } from "../../../src/internal.js"; import { RoleRevokedHandler } from "../../../src/processors/registry/handlers/roleRevoked.handler.js"; describe("RoleRevokedHandler", () => { @@ -64,9 +65,8 @@ describe("RoleRevokedHandler", () => { ); const handler = new RoleRevokedHandler(mockEvent, mockChainId, mockDependencies); - const result = await handler.handle(); - expect(result).toEqual([]); + await expect(handler.handle()).rejects.toThrow(ProjectByRoleNotFound); }); it("throw an error when getProjectById fails", async () => { diff --git a/packages/processors/test/registry/registry.processor.spec.ts b/packages/processors/test/registry/registry.processor.spec.ts index 80c0b98..321e0b1 100644 --- a/packages/processors/test/registry/registry.processor.spec.ts +++ b/packages/processors/test/registry/registry.processor.spec.ts @@ -4,7 +4,11 @@ import type { ChainId, ProcessorEvent, RegistryEvent } from "@grants-stack-index import { ProcessorDependencies, UnsupportedEventException } from "../../src/internal.js"; import { ProfileCreatedHandler } from "../../src/processors/registry/handlers/profileCreated.handler.js"; +import { ProfileMetadataUpdatedHandler } from "../../src/processors/registry/handlers/profileMetadataUpdated.handler.js"; +import { ProfileNameUpdatedHandler } from "../../src/processors/registry/handlers/profileNameUpdated.handler.js"; +import { ProfileOwnerUpdatedHandler } from "../../src/processors/registry/handlers/profileOwnerUpdated.handler.js"; import { RoleGrantedHandler } from "../../src/processors/registry/handlers/roleGranted.handler.js"; +import { RoleRevokedHandler } from "../../src/processors/registry/handlers/roleRevoked.handler.js"; import { RegistryProcessor } from "../../src/processors/registry/registry.processor.js"; // Mock the handlers and their handle methods @@ -17,7 +21,6 @@ vi.mock("../../src/processors/registry/handlers/roleGranted.handler.js", () => { }; }); -// Mock the handlers and their handle methods vi.mock("../../src/processors/registry/handlers/profileCreated.handler.js", () => { const ProfileCreatedHandler = vi.fn(); // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access @@ -27,6 +30,40 @@ vi.mock("../../src/processors/registry/handlers/profileCreated.handler.js", () = }; }); +vi.mock("../../src/processors/registry/handlers/profileOwnerUpdated.handler.js", () => { + const ProfileOwnerUpdatedHandler = vi.fn(); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + ProfileOwnerUpdatedHandler.prototype.handle = vi.fn(); + return { + ProfileOwnerUpdatedHandler, + }; +}); + +vi.mock("../../src/processors/registry/handlers/roleRevoked.handler.js", () => { + const RoleRevokedHandler = vi.fn(); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + RoleRevokedHandler.prototype.handle = vi.fn(); + return { + RoleRevokedHandler, + }; +}); +vi.mock("../../src/processors/registry/handlers/profileMetadataUpdated.handler.js", () => { + const ProfileMetadataUpdatedHandler = vi.fn(); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + ProfileMetadataUpdatedHandler.prototype.handle = vi.fn(); + return { + ProfileMetadataUpdatedHandler, + }; +}); +vi.mock("../../src/processors/registry/handlers/profileNameUpdated.handler.js", () => { + const ProfileNameUpdatedHandler = vi.fn(); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + ProfileNameUpdatedHandler.prototype.handle = vi.fn(); + return { + ProfileNameUpdatedHandler, + }; +}); + describe("RegistryProcessor", () => { const chainId: ChainId = 10 as ChainId; // Replace with appropriate chainId const dependencies: ProcessorDependencies = {} as ProcessorDependencies; // Replace with actual dependencies @@ -72,4 +109,56 @@ describe("RegistryProcessor", () => { expect(RoleGrantedHandler.prototype.handle).toHaveBeenCalled(); expect(result).toEqual([]); // Check if handle returns [] }); + it("calls ProfileOwnerUpdatedHandler", async () => { + const event: ProcessorEvent<"Registry", "ProfileOwnerUpdated"> = { + eventName: "ProfileOwnerUpdated", + } as ProcessorEvent<"Registry", "ProfileOwnerUpdated">; + + vi.spyOn(ProfileOwnerUpdatedHandler.prototype, "handle").mockResolvedValue([]); + + const processor = new RegistryProcessor(chainId, dependencies); + const result = await processor.process(event); + + expect(ProfileOwnerUpdatedHandler.prototype.handle).toHaveBeenCalled(); + expect(result).toEqual([]); // Check if handle returns [] + }); + it("calls RoleRevokedHandler", async () => { + const event: ProcessorEvent<"Registry", "RoleRevoked"> = { + eventName: "RoleRevoked", + } as ProcessorEvent<"Registry", "RoleRevoked">; + + vi.spyOn(RoleRevokedHandler.prototype, "handle").mockResolvedValue([]); + + const processor = new RegistryProcessor(chainId, dependencies); + const result = await processor.process(event); + + expect(RoleRevokedHandler.prototype.handle).toHaveBeenCalled(); + expect(result).toEqual([]); // Check if handle returns [] + }); + it("calls ProfileMetadataUpdatedHandler", async () => { + const event: ProcessorEvent<"Registry", "ProfileMetadataUpdated"> = { + eventName: "ProfileMetadataUpdated", + } as ProcessorEvent<"Registry", "ProfileMetadataUpdated">; + + vi.spyOn(ProfileMetadataUpdatedHandler.prototype, "handle").mockResolvedValue([]); + + const processor = new RegistryProcessor(chainId, dependencies); + const result = await processor.process(event); + + expect(ProfileMetadataUpdatedHandler.prototype.handle).toHaveBeenCalled(); + expect(result).toEqual([]); // Check if handle returns [] + }); + it("calls ProfileNameUpdatedHandler", async () => { + const event: ProcessorEvent<"Registry", "ProfileNameUpdated"> = { + eventName: "ProfileNameUpdated", + } as ProcessorEvent<"Registry", "ProfileNameUpdated">; + + vi.spyOn(ProfileNameUpdatedHandler.prototype, "handle").mockResolvedValue([]); + + const processor = new RegistryProcessor(chainId, dependencies); + const result = await processor.process(event); + + expect(ProfileNameUpdatedHandler.prototype.handle).toHaveBeenCalled(); + expect(result).toEqual([]); // Check if handle returns [] + }); }); diff --git a/packages/repository/src/types/project.types.ts b/packages/repository/src/types/project.types.ts index e4d4d79..e4a87ca 100644 --- a/packages/repository/src/types/project.types.ts +++ b/packages/repository/src/types/project.types.ts @@ -1,6 +1,6 @@ import { Address, ChainId } from "@grants-stack-indexer/shared"; -export type ProjectType = "canonical" | "linked"; +export type ProjectType = "canonical" | "linked" | "unknown"; export type Project = { id: string;