Skip to content

Commit

Permalink
Fix/docs (#63)
Browse files Browse the repository at this point in the history
* Enhanced the IEventBusManager

* renamed eventBusTestSuite to eventBusAdapterTestSuite

* Enhanced the IStorageManager

* renamed storageTestSuite to storageAdapterTestSuite

* Added extra documentation and updated som documentation

* Added changeset file
  • Loading branch information
yousif-khalil-abdulkarim authored Jan 5, 2025
1 parent 4a09bcc commit 0684e61
Show file tree
Hide file tree
Showing 23 changed files with 918 additions and 234 deletions.
7 changes: 7 additions & 0 deletions .changeset/ten-eagles-refuse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@daiso-tech/core": patch
---

## Changes
- Improved the IEventBusManager
- Improved the IStorageManager
262 changes: 227 additions & 35 deletions src/collection/contracts/async-collection.contract.ts

Large diffs are not rendered by default.

258 changes: 221 additions & 37 deletions src/collection/contracts/collection.contract.ts

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions src/event-bus/contracts/_shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ export class EventBusError extends Error {
}
}

/**
* @group Errors
*/
export class UnexpectedEventBusError extends EventBusError {
constructor(message: string, cause?: unknown) {
super(message, { cause });
this.name = UnexpectedEventBusError.name;
}
}

/**
* @group Errors
*/
Expand Down
128 changes: 118 additions & 10 deletions src/event-bus/contracts/event-bus-manager.contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,137 @@
* @module EventBus
*/

import type { Validator } from "@/utilities/_module";
import { type Validator } from "@/utilities/_module";
import type { INamespacedEventBus } from "@/event-bus/contracts/event-bus.contract";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import type { IEventBusAdapter } from "@/event-bus/contracts/event-bus-adapter.contract";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import type { IBaseEvent } from "@/event-bus/contracts/_shared";

/**
* The <i>IEventBusFactory</i> contract makes it easy to switch between different <i>{@link IEventBusAdapter | event adapters}</i> dynamically.
* @group Contracts
*/
export type EventBusManagerUseSettings<
TType,
export type IEventBusFactory<
TAdapters extends string = string,
TEvents extends IBaseEvent = IBaseEvent,
> = {
adapter?: TAdapters;
validator?: Validator<TType>;
/**
* @example
* ```ts
* import { EventBusManager, MemoryEventBusAdapter, RedisEventBusAdapter } from "@daiso-tech/core";
* import Redis from "ioredis";
*
* const eventBusManager = new EventBusManager({
* adapters: {
* memory: new MemoryEventBusAdapter(),
* redis: new RedisEventBusAdapter({
* dispatcherClient: new Redis();
* listenerClient: new Redis();
* }),
* },
* defaultAdapter: "memory",
* rootNamespace: "@storage/"
* });
*
* (async () => {
* // Will dispatch envent using the default adapter which is the memory addapter
* await eventBusManager
* .use()
* .dispatch({ type: "add", a: 1, b: 2 });
* // Will dispatch envent using the redis addapter
* await eventBusManager
* .use("redis")
* .dispatch({ type: "add", a: 1, b: 2 });
* })();
* ```
*/
use(adapter?: TAdapters): INamespacedEventBus<TEvents>;
};

/**
* The <i>IEventBusManager</i> contract makes it easy to switch between different <i>{@link IEventBusAdapter | event bus adapters}</i> dynamically.
* The <i>IEventBusManager</i> contract makes it easy to configure and switch between different <i>{@link IEventBusAdapter | event adapters}</i> dynamically.
* @group Contracts
*/
export type IEventBusManager<TAdapters extends string = string> = {
use<TEvents extends IBaseEvent = IBaseEvent>(
settings?: EventBusManagerUseSettings<TEvents, TAdapters>,
): INamespacedEventBus<TEvents>;
export type IEventBusManager<
TAdapters extends string = string,
TEvents extends IBaseEvent = IBaseEvent,
> = IEventBusFactory<TAdapters, TEvents> & {
/**
* The <i>withValidation</i> method is used to set a <i>validator</i>, which validates the event during runtime.
* The type is inferred from the provided <i>validator</i>.
* @example
* ```ts
* import { EventBusManager, MemoryEventBusAdapter, RedisEventBusAdapter, zodValidator } from "@daiso-tech/core";
* import Redis from "ioredis";
* import { z } from "zod"
*
* const eventBusManager = new EventBusManager({
* adapters: {
* memory: new MemoryEventBusAdapter(),
* redis: new RedisEventBusAdapter({
* dispatcherClient: new Redis();
* listenerClient: new Redis();
* }),
* },
* defaultAdapter: "memory",
* rootNamespace: "@storage/"
* });
*
* (async () => {
* const addEventSchema = z.zod({
* type: z.literal("add"),
* a: z.number(),
* b: z.number(),
* });
* await eventBusManager
* .withValidation(zodValidator(addEventSchema))
* .use()
* // You will se an typescript error and get runtime erorr
* .dispatch({ type: "add" });
* })();
* ```
*/
withValidation<TOutput extends TEvents = TEvents>(
validator: Validator<TOutput>,
): IEventBusFactory<TAdapters, TOutput>;

/**
* The <i>withTypes</i> method is used to set the event types of the <i>{@link IEventBus}</i>.
* @example
* ```ts
* import { EventBusManager, MemoryEventBusAdapter, RedisEventBusAdapter, zodValidator } from "@daiso-tech/core";
* import Redis from "ioredis";
* import { z } from "zod"
*
* const eventBusManager = new EventBusManager({
* adapters: {
* memory: new MemoryEventBusAdapter(),
* redis: new RedisEventBusAdapter({
* dispatcherClient: new Redis();
* listenerClient: new Redis();
* }),
* },
* defaultAdapter: "memory",
* rootNamespace: "@storage/"
* });
*
* (async () => {
* type AddEvent = {
* type: "add";
* a: number;
* b: number;
* };
* await eventBusManager
* .withTypes<AddEvent>()
* .use()
* // You will se an typescript error
* .dispatch({ type: "add" });
* })();
* ```
*/
withType<TOutput extends TEvents = TEvents>(): IEventBusFactory<
TAdapters,
TOutput
>;
};
74 changes: 51 additions & 23 deletions src/event-bus/contracts/event-bus.contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,12 @@ export type SelectEvent<
export type Unsubscribe = () => PromiseLike<void>;
/**
* The <i>IListenable</i> contract defines a way listening to events independent of underlying technology
* @throws {EventBusError} {@link EventBusError}
* @throws {AddListenerEventBusError} {@link AddListenerEventBusError}
* @throws {RemoveListenerEventBusError} {@link RemoveListenerEventBusError}
* @group Contracts
*/
export type IListenable<TEvents extends IBaseEvent = IBaseEvent> = {
/**
* The <i>addListener</i> method is used for adding <i>{@link Listener | listener}</i> for certain <i>event</i>.
* A listener can only be added once for a specific event. Adding the same listener multiple times will have no effect and nothing will occur.
* @throws {EventBusError} {@link EventBusError}
* @throws {AddListenerEventBusError} {@link AddListenerEventBusError}
*/
addListener<TEventType extends TEvents["type"]>(
Expand All @@ -46,7 +42,6 @@ export type IListenable<TEvents extends IBaseEvent = IBaseEvent> = {
/**
* The <i>addListenerMany</i> method is used for adding multiple <i>{@link Listener | listeners}</i> for certain <i>events</i>.
* A listener can only be added once for a specific event. Adding the same listener multiple times will have no effect and nothing will occur.
* @throws {EventBusError} {@link EventBusError}
* @throws {AddListenerEventBusError} {@link AddListenerEventBusError}
*/
addListenerMany<TEventType extends TEvents["type"]>(
Expand All @@ -57,7 +52,6 @@ export type IListenable<TEvents extends IBaseEvent = IBaseEvent> = {
/**
* The <i>removeListener</i> method is used for removing <i>{@link Listener | listener}</i> for certain <i>event</i>.
* Removing unadded listener will have no effect and nothing will occur.
* @throws {EventBusError} {@link EventBusError}
* @throws {RemoveListenerEventBusError} {@link RemoveListenerEventBusError}
*/
removeListener<TEventType extends TEvents["type"]>(
Expand All @@ -68,7 +62,6 @@ export type IListenable<TEvents extends IBaseEvent = IBaseEvent> = {
/**
* The <i>removeListener</i> method is used for removing multiple <i>{@link Listener | listeners}</i> for certain <i>event</i>.
* Removing unadded listener will have no effect and nothing will occur.
* @throws {EventBusError} {@link EventBusError}
* @throws {RemoveListenerEventBusError} {@link RemoveListenerEventBusError}
*/
removeListenerMany<TEventType extends TEvents["type"]>(
Expand All @@ -79,14 +72,16 @@ export type IListenable<TEvents extends IBaseEvent = IBaseEvent> = {
/**
* The <i>subscribe</i> method is used for adding <i>{@link Listener | listener}</i> for certain <i>event</i>.
* A listener can only be added once for a specific event. Adding the same listener multiple times will have no effect and nothing will occur.
* @throws {EventBusError} {@link EventBusError}
* @throws {RemoveListenerEventBusError} {@link RemoveListenerEventBusError}
*/
subscribe<TEventType extends TEvents["type"]>(
event: TEventType,
listener: Listener<SelectEvent<TEvents, TEventType>>,
): PromiseLike<Unsubscribe>;

/**
* The <i>subscribeMany</i> method is used for adding <i>{@link Listener | listener}</i> for multiple <i>events</i>.
* A listener can only be added once for a specific event. Adding the same listener multiple times will have no effect and nothing will occur.
*/
subscribeMany<TEventType extends TEvents["type"]>(
events: TEventType[],
listener: Listener<SelectEvent<TEvents, TEventType>>,
Expand All @@ -96,41 +91,74 @@ export type IListenable<TEvents extends IBaseEvent = IBaseEvent> = {
/**
* The <i>IEventBus</i> contract defines a way for dispatching and listening to events independent of underlying technology.
* It commes with more convient methods compared to <i>IEventBusAdapter</i>.
* @throws {EventBusError} {@link EventBusError}
* @throws {DispatchEventBusError} {@link DispatchEventBusError}
* @throws {AddListenerEventBusError} {@link AddListenerEventBusError}
* @throws {RemoveListenerEventBusError} {@link RemoveListenerEventBusError}
* @group Contracts
*/
export type IEventBus<TEvents extends IBaseEvent = IBaseEvent> =
IListenable<TEvents> & {
/**
* The <i>dispatch</i> method is used for dispatching one or multiple <i>events</i>.
* @throws {EventBusError} {@link EventBusError}
* @throws {DispatchEventBusError} {@link DispatchEventBusError}
*/
dispatch(events: OneOrMore<TEvents>): PromiseLike<void>;

/**
* The <i>getNamespace</i> method return the complete namespace.
* @example
* ```ts
* const eventBus = new EventBus(new MemoryEventBusAdapter(), {
* rootNamespace: "@root/"
* });
*
* // Will be "@root/"
* console.log(eventBus.getNamespace())
*
* const eventBusA = eventBus.withNamespace("a/");
*
* // Will be "@root/a/"
* console.log(eventBusA.getNamespace())
* ```
*/
getNamespace(): string;
};

/**
* The <i>INamespacedEventBus</i> contract defines a way for dispatching and listening to events independent of underlying technology.
* It commes with one extra method which is useful for multitennat applications compared to <i>IEventBus</i>.
* @throws {EventBusError} {@link EventBusError}
* @throws {DispatchEventBusError} {@link DispatchEventBusError}
* @throws {AddListenerEventBusError} {@link AddListenerEventBusError}
* @throws {RemoveListenerEventBusError} {@link RemoveListenerEventBusError}
* @group Contracts
*/
export type INamespacedEventBus<TEvents extends IBaseEvent = IBaseEvent> =
IEventBus<TEvents> & {
/**
* The <i>withNamespace</i> method returns new instance of <i>{@link IEventBus}</i> where all the events names will be prefixed with a given <i>namespace</i>.
* This useful for multitennat applications.
* @example
* ```ts
* import { EventBus, MemoryEventBusAdapter } from "@daiso-tech/core";
*
* (async () => {
* const eventBus = new EventBus(new MemoryEventBusAdapter());
*
* const eventBusA = eventBus.withNamespace("a");
* await eventBusA.subscribe("add", (event) => {
* // This will be logged
* console.log("eventBusA:", event);
* });
*
* const eventBusB = eventBus.withNamespace("b");
* await eventBusB.subscribe("add", (event) => {
* // This will never be logged
* console.log("eventBusB:", event);
* });
*
* await eventBusA.dispatch({
* type: "add",
* a: 1,
* b: 2
* })
* })();
*
* ```
*/
withNamespace(namespace: string): IEventBus<TEvents>;

/**
* The <i>getNamespace</i> method return the complete namespace.
*/
getNamespace(): string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,29 @@ export type EventBusTestSuiteSettings = {
beforeEach: typeof beforeEach;
createAdapter: () => Promisable<IEventBusAdapter>;
};

/**
* The <i>eventBusAdapterTestSuite</i> function simplifies the process of testing your custom implementation of <i>{@link IEventBusAdapter}</i> with vitest.
* @example
* ```ts
* import { eventBusAdapterTestSuite, MemoryEventBusAdapter } from "@daiso-tech/core";
* import { expext, test, describe, beforeEach } from "vitest";
*
* describe("class: MemoryEventBusAdapter", () => {
* eventBusAdapterTestSuite({
* createAdapter: () => new MemoryEventBusAdapter(),
* test,
* beforeEach,
* expect,
* describe,
* });
* });
* ```
* @group Utilities
*/
export function eventBusTestSuite(settings: EventBusTestSuiteSettings): void {
export function eventBusAdapterTestSuite(
settings: EventBusTestSuiteSettings,
): void {
const { expect, test, createAdapter, beforeEach } = settings;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let eventBusAdapter: IEventBusAdapter;
Expand Down
Loading

0 comments on commit 0684e61

Please sign in to comment.