Skip to content

Commit

Permalink
feat(plugins): draft implementation of golem plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
SewerynKras committed Mar 27, 2024
1 parent bf1fab1 commit 195b624
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 0 deletions.
34 changes: 34 additions & 0 deletions src/plugins/examplePlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { MarketApi } from "ya-ts-client";
import { GlobalPluginManager, GolemPlugin, registerGlobalPlugin } from "./globalPluginManager";

const linearPricingOnlyPlugin: GolemPlugin = {
name: "linearPricingOnlyPlugin",
version: "1.0.0",
register(golem) {
golem.market.registerHook("beforeDemandPublished", (demand) => {
demand.properties["golem.com.pricing.model"] = "linear";
return demand;
});

golem.market.registerHook("filterInitialProposal", (proposal) => {
if (proposal.properties["golem.com.pricing.model"] !== "linear") {
return { isAccepted: false, reason: "Invalid pricing model" };
}
return { isAccepted: true };
});

golem.market.on("demandPublished", (demand) => {
console.log("demand has been published", demand);
});
},
};

registerGlobalPlugin(linearPricingOnlyPlugin);

// inside demand publishing logic
const createDemandDTO = () => ({}) as MarketApi.DemandDTO;
let demand = createDemandDTO();
const hooks = GlobalPluginManager.getHooks("beforeDemandPublished");
for (const hook of hooks) {
demand = await hook(demand);
}
46 changes: 46 additions & 0 deletions src/plugins/globalPluginManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import EventEmitter from "eventemitter3";
import { MarketEvents, MarketHooks, MarketPluginContext } from "./marketPluginContext";

type GolemPluginContext = {
market: MarketPluginContext;
};
export type GolemPlugin = {
name: string;
version: string;
register(context: GolemPluginContext): void;
};

type AllHooks = MarketHooks; // | DeploymentHooks | PaymentHooks | ...
type AllEvents = MarketEvents; // | DeploymentEvents | PaymentEvents | ...

export class GlobalPluginManager {
static eventEmitter = new EventEmitter<AllEvents>();
static hooks = new Map<keyof AllHooks, AllHooks[keyof AllHooks][]>();

static registerHook<T extends keyof AllHooks>(hookName: T, hook: AllHooks[T]) {
if (!this.hooks.has(hookName)) {
this.hooks.set(hookName, []);
}
this.hooks.get(hookName)!.push(hook as NonNullable<AllHooks[T]>);
}

static registerPlugin(plugin: GolemPlugin) {
const ctx = {
market: new MarketPluginContext(
(eventName, callback) => GlobalPluginManager.eventEmitter.on(eventName, callback),
(hookName, hook) => GlobalPluginManager.registerHook(hookName, hook),
),
// deployment: ...
// payment: ...
// ...
};
plugin.register(ctx);
}
static getHooks<T extends keyof AllHooks>(hookName: T): AllHooks[T][] {
return (this.hooks.get(hookName) || []) as AllHooks[T][];
}
}

export function registerGlobalPlugin(...plugins: GolemPlugin[]) {
plugins.forEach((plugin) => GlobalPluginManager.registerPlugin(plugin));
}
17 changes: 17 additions & 0 deletions src/plugins/marketPluginContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { PluginContext } from "./pluginContext";
import { MarketApi } from "ya-ts-client";
type DemandDTO = MarketApi.DemandDTO;
type ProposalDTO = MarketApi.ProposalDTO;

type OkOrNot = { isAccepted: true } | { isAccepted: false; reason: string };
export type MarketHooks = {
beforeDemandPublished: (demand: DemandDTO) => DemandDTO | Promise<DemandDTO>;
filterInitialProposal: (proposal: ProposalDTO) => OkOrNot | Promise<OkOrNot>;
};

export type MarketEvents = {
demandPublished: (demand: DemandDTO) => void;
initialProposalReceived: (proposal: ProposalDTO) => void;
};

export class MarketPluginContext extends PluginContext<MarketHooks, MarketEvents> {}
22 changes: 22 additions & 0 deletions src/plugins/pluginContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import EventEmitter from "eventemitter3";

export abstract class PluginContext<
Hooks extends { [key: string]: (...args: never[]) => unknown },
Events extends { [key: string]: (...args: never[]) => unknown },
> {
constructor(
private registerEventFn: <T extends EventEmitter.EventNames<Events>>(
eventName: T,
callback: EventEmitter.EventListener<Events, T>,
) => void,
private registerHookFn: (hookName: keyof Hooks, hook: Hooks[keyof Hooks]) => void,
) {}

registerHook<T extends keyof Hooks>(hookName: T, hook: Hooks[T]) {
this.registerHookFn(hookName, hook);
}

on<T extends EventEmitter.EventNames<Events>>(eventName: T, callback: EventEmitter.EventListener<Events, T>) {
this.registerEventFn(eventName, callback);
}
}

0 comments on commit 195b624

Please sign in to comment.