-
Notifications
You must be signed in to change notification settings - Fork 0
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
feat: implement EboActorsManager #25
Changes from all commits
496fdfa
8e53d2f
1a58060
c03c26d
7a439a2
41ae17f
fdddf82
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,43 @@ | ||
import { EboActor } from "./eboActor.js"; | ||
import { RequestAlreadyHandled } from "./exceptions/index.js"; | ||
|
||
export class EboActorsManager { | ||
private readonly requestActorMap: Map<string, EboActor>; | ||
|
||
constructor() { | ||
this.requestActorMap = new Map(); | ||
} | ||
|
||
/** | ||
* Registers the actor and makes it fetchable by the ID of the request the actor is handling. | ||
* | ||
* @param actor an `EboActor` instance that handles the request | ||
*/ | ||
public registerActor(actor: EboActor): void { | ||
const requestId = actor.getRequestId(); | ||
|
||
if (this.requestActorMap.has(requestId)) throw new RequestAlreadyHandled(requestId); | ||
|
||
this.requestActorMap.set(requestId, actor); | ||
} | ||
|
||
/** | ||
* Get the `EboActor` instance linked with the `requestId`. | ||
* | ||
* @param requestId request ID | ||
* @returns an `EboActor` instance if found by `requestId`, otherwise `undefined` | ||
*/ | ||
public getActor(requestId: string): EboActor | undefined { | ||
return this.requestActorMap.get(requestId); | ||
} | ||
|
||
/** | ||
* Deletes an actor from the manager, based on its linked request. | ||
* | ||
* @param requestId request ID | ||
* @returns `true` if there was a linked actor for the request ID and it was removed, or `false` if the request was not linked to any actor. | ||
*/ | ||
public deleteActor(requestId: string): boolean { | ||
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. why is returning a 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. same doubt, i guess on error false is returned instead of throwing 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. We can do I'm just following the I guess it doesn't hurt to keep the 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. yep, would be very helpful for the user of this class, should we add a warn or sth if the requestId doesn't exist? 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. Sounds good. The |
||
return this.requestActorMap.delete(requestId); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,4 @@ | ||
export * from "./rpcUrlsEmpty.exception.js"; | ||
export * from "./invalidActorState.exception.js"; | ||
export * from "./requestAlreadyHandled.exception.js"; | ||
export * from "./requestMismatch.js"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export class RequestAlreadyHandled extends Error { | ||
constructor(requestId: string) { | ||
super(`Request ${requestId} is already being handled by another actor.`); | ||
|
||
this.name = "RequestAlreadyHandled"; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { ILogger } from "@ebo-agent/shared"; | ||
import { describe, expect, it, vi } from "vitest"; | ||
|
||
import { EboActorsManager } from "../src/eboActorsManager.js"; | ||
import { RequestAlreadyHandled } from "../src/exceptions/index.js"; | ||
import { DEFAULT_MOCKED_REQUEST_CREATED_DATA } from "./eboActor/fixtures.js"; | ||
import mocks from "./mocks/index.js"; | ||
|
||
const logger: ILogger = mocks.mockLogger(); | ||
|
||
describe("EboActorsManager", () => { | ||
describe("registerActor", () => { | ||
it("registers the actor correctly", () => { | ||
const request = DEFAULT_MOCKED_REQUEST_CREATED_DATA; | ||
const { actor } = mocks.buildEboActor(request, logger); | ||
const actorsManager = new EboActorsManager(); | ||
const mockSetRequestActorMap = vi.spyOn(actorsManager["requestActorMap"], "set"); | ||
|
||
actorsManager.registerActor(actor); | ||
|
||
expect(mockSetRequestActorMap).toHaveBeenCalledWith(request.id, actor); | ||
expect(actorsManager.getActor(actor.getRequestId())).toBe(actor); | ||
}); | ||
|
||
it("throws if the request has already an actor linked to it", () => { | ||
const request = DEFAULT_MOCKED_REQUEST_CREATED_DATA; | ||
const { actor: firstActor } = mocks.buildEboActor(request, logger); | ||
const { actor: secondActor } = mocks.buildEboActor(request, logger); | ||
const actorsManager = new EboActorsManager(); | ||
|
||
actorsManager.registerActor(firstActor); | ||
|
||
expect(() => actorsManager.registerActor(secondActor)).toThrowError( | ||
RequestAlreadyHandled, | ||
); | ||
}); | ||
}); | ||
|
||
describe("getActor", () => { | ||
it("returns undefined if the request is not linked to any actor", () => { | ||
const actorsManager = new EboActorsManager(); | ||
|
||
expect(actorsManager.getActor("0x9999")).toBeUndefined(); | ||
}); | ||
|
||
it("returns the request's linked actor", () => { | ||
const request = DEFAULT_MOCKED_REQUEST_CREATED_DATA; | ||
const { actor } = mocks.buildEboActor(request, logger); | ||
const actorsManager = new EboActorsManager(); | ||
|
||
actorsManager.registerActor(actor); | ||
|
||
expect(actorsManager.getActor(request.id)).toBe(actor); | ||
}); | ||
}); | ||
|
||
describe("deleteActor", () => { | ||
it("deletes the actor linked to the request", () => { | ||
const request = DEFAULT_MOCKED_REQUEST_CREATED_DATA; | ||
const { actor } = mocks.buildEboActor(request, logger); | ||
const actorsManager = new EboActorsManager(); | ||
|
||
actorsManager.registerActor(actor); | ||
|
||
expect(actorsManager.getActor(request.id)).toBe(actor); | ||
|
||
actorsManager.deleteActor(request.id); | ||
|
||
expect(actorsManager.getActor(request.id)).toBeUndefined(); | ||
}); | ||
|
||
it("returns false if the request has no actors linked", () => { | ||
const requestId = "0x01"; | ||
const actorsManager = new EboActorsManager(); | ||
|
||
expect(actorsManager.getActor(requestId)).toBeUndefined(); | ||
expect(actorsManager.deleteActor(requestId)).toEqual(false); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { buildEboActor, buildResponse } from "./eboActor.js"; | ||
import { mockLogger } from "./logger.js"; | ||
|
||
export default { buildEboActor, buildResponse, mockLogger }; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { ILogger } from "@ebo-agent/shared"; | ||
import { vi } from "vitest"; | ||
|
||
export const mockLogger: () => ILogger = () => ({ | ||
info: vi.fn(), | ||
warn: vi.fn(), | ||
error: vi.fn(), | ||
debug: vi.fn(), | ||
}); |
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.
the manager implementation is always in-memory or is like the Registry that may in the future be a Redis or whatever makes sense? in that case, we should have the proper interface
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.
It's something interesting to think about; in this case I think The Graph should need to scale a lot for this to need something outside the in-memory solution used here. Worst case scenario, at the same there will be 1 item (ie 1
<request, actor>
tuple) for every indexed chain, which are not that many.