diff --git a/src/plugins/examplePlugin.ts b/src/plugins/examplePlugin.ts index 682168af0..fda573b4c 100644 --- a/src/plugins/examplePlugin.ts +++ b/src/plugins/examplePlugin.ts @@ -1,6 +1,8 @@ import { MarketApi } from "ya-ts-client"; -import { GlobalPluginManager, GolemPlugin, registerGlobalPlugin } from "./globalPluginManager"; +import { GolemPlugin, registerGlobalPlugin } from "./pluginManager"; +import { LocalPluginManager } from "./localPluginManager"; +// plugin that will be registered globally const linearPricingOnlyPlugin: GolemPlugin = { name: "linearPricingOnlyPlugin", version: "1.0.0", @@ -23,12 +25,47 @@ const linearPricingOnlyPlugin: GolemPlugin = { }, }; +// plugin that will be registered on a particular demand instead of globally +const cpuArchitecturePlugin: GolemPlugin = { + name: "cpuArchitecturePlugin", + version: "1.0.0", + register(golem) { + golem.market.registerHook("beforeDemandPublished", (demand) => { + demand.properties["golem.com.cpu.architecture"] = "x86_64"; + return 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); +class ExampleDemandManager { + private pluginManager = new LocalPluginManager(); + constructor(plugins?: GolemPlugin[]) { + if (plugins) { + plugins.forEach((plugin) => this.pluginManager.registerPlugin(plugin)); + } + } + + async publish() { + let demand: MarketApi.DemandDTO = { + properties: {}, + } as MarketApi.DemandDTO; + + const hooks = this.pluginManager.getHooks("beforeDemandPublished"); + for (const hook of hooks) { + demand = await hook(demand); + } + this.pluginManager.emitEvent("demandPublished", demand); + return demand; + } } + +const demandManager0 = new ExampleDemandManager([cpuArchitecturePlugin]); +const demandManager1 = new ExampleDemandManager(); + +const demandWithCpuArchitecture = await demandManager0.publish(); +const demandWithOnlyLinearPricing = await demandManager1.publish(); + +console.log(demandWithCpuArchitecture); +console.log(demandWithOnlyLinearPricing); diff --git a/src/plugins/localPluginManager.ts b/src/plugins/localPluginManager.ts new file mode 100644 index 000000000..366889fb4 --- /dev/null +++ b/src/plugins/localPluginManager.ts @@ -0,0 +1,23 @@ +import EventEmitter from "eventemitter3"; +import { MarketEvents, MarketHooks } from "./marketPluginContext"; +import { GlobalPluginManager, PluginManager } from "./pluginManager"; + +/** + * Plugin manager that will combine local and global plugins. + * Global plugins should be registered using `GlobalPluginManager`. + * Global hooks will be executed before local hooks, in order of registration. + */ +export class LocalPluginManager extends PluginManager { + getHooks(hookName: T): MarketHooks[T][] { + const localHooks = super.getHooks(hookName); + const globalHooks = GlobalPluginManager.getHooks(hookName); + return [...globalHooks, ...localHooks]; + } + public emitEvent( + eventName: T, + ...args: EventEmitter.ArgumentMap[Extract] + ): void { + GlobalPluginManager.emitEvent(eventName, ...args); + super.emitEvent(eventName, ...args); + } +} diff --git a/src/plugins/globalPluginManager.ts b/src/plugins/pluginManager.ts similarity index 54% rename from src/plugins/globalPluginManager.ts rename to src/plugins/pluginManager.ts index 9df604334..731c2548a 100644 --- a/src/plugins/globalPluginManager.ts +++ b/src/plugins/pluginManager.ts @@ -13,22 +13,22 @@ export type GolemPlugin = { type AllHooks = MarketHooks; // | DeploymentHooks | PaymentHooks | ... type AllEvents = MarketEvents; // | DeploymentEvents | PaymentEvents | ... -export class GlobalPluginManager { - static eventEmitter = new EventEmitter(); - static hooks = new Map(); +export class PluginManager { + private eventEmitter = new EventEmitter(); + private hooks = new Map(); - static registerHook(hookName: T, hook: AllHooks[T]) { + private registerHook(hookName: T, hook: AllHooks[T]) { if (!this.hooks.has(hookName)) { this.hooks.set(hookName, []); } this.hooks.get(hookName)!.push(hook as NonNullable); } - static registerPlugin(plugin: GolemPlugin) { + public registerPlugin(plugin: GolemPlugin) { const ctx = { market: new MarketPluginContext( - (eventName, callback) => GlobalPluginManager.eventEmitter.on(eventName, callback), - (hookName, hook) => GlobalPluginManager.registerHook(hookName, hook), + (eventName, callback) => this.eventEmitter.on(eventName, callback), + (hookName, hook) => this.registerHook(hookName, hook), ), // deployment: ... // payment: ... @@ -36,11 +36,20 @@ export class GlobalPluginManager { }; plugin.register(ctx); } - static getHooks(hookName: T): AllHooks[T][] { + public getHooks(hookName: T): AllHooks[T][] { return (this.hooks.get(hookName) || []) as AllHooks[T][]; } + public emitEvent( + eventName: T, + ...args: EventEmitter.ArgumentMap[Extract] + ) { + this.eventEmitter.emit(eventName, ...args); + } } +// eslint-disable-next-line @typescript-eslint/naming-convention +export const GlobalPluginManager = new PluginManager(); + export function registerGlobalPlugin(...plugins: GolemPlugin[]) { plugins.forEach((plugin) => GlobalPluginManager.registerPlugin(plugin)); }