From fbad99235d66fd122d1f15bfb4572d0d0a4248f5 Mon Sep 17 00:00:00 2001 From: Kasper Date: Mon, 6 Nov 2023 15:02:24 +0100 Subject: [PATCH 01/15] workking subscribers API --- .../src/loaders/helpers/subscribers/index.ts | 198 ++++++++++++++++++ packages/medusa/src/loaders/plugins.ts | 32 +-- 2 files changed, 217 insertions(+), 13 deletions(-) create mode 100644 packages/medusa/src/loaders/helpers/subscribers/index.ts diff --git a/packages/medusa/src/loaders/helpers/subscribers/index.ts b/packages/medusa/src/loaders/helpers/subscribers/index.ts new file mode 100644 index 0000000000000..82eed72c5e9a1 --- /dev/null +++ b/packages/medusa/src/loaders/helpers/subscribers/index.ts @@ -0,0 +1,198 @@ +import { Subscriber } from "@medusajs/types" +import { kebabCase } from "@medusajs/utils" +import { readdir } from "fs/promises" +import { join } from "path" + +import { EventBusService } from "../../../services" +import { MedusaContainer } from "../../../types/global" + +type Config = { + event: string | string[] + subscriberId?: string +} + +type Handler = ( + data: T, + eventName: string, + container: MedusaContainer +) => Promise + +type SubscriberConfig = { + config: Config + handler: Handler +} + +export class SubscriberRegistrar { + protected container_: MedusaContainer + protected pluginOptions_: Record + protected rootDir_: string + protected excludes: RegExp[] = [ + /\.DS_Store/, + /(\.ts\.map|\.js\.map|\.d\.ts)/, + /^_[^/\\]*(\.[^/\\]+)?$/, + ] + + protected subscriberDescriptors_: Map> = + new Map() + + constructor( + rootDir: string, + container: MedusaContainer, + options: Record = {} + ) { + this.rootDir_ = rootDir + this.pluginOptions_ = options + this.container_ = container + } + + private async validateSubscriber() { + return null + } + + private async createDescriptor(absolutePath: string, entry: string) { + return await import(absolutePath).then((module_) => { + // Check if module has a default export of type function + const handler = module_.default + + if (typeof handler !== "function") { + throw new Error( + `Subscriber ${absolutePath} does not have a default export of type function` + ) + } + + // check if the handler accepts more than 3 arguments + if (handler.length > 3) { + throw new Error( + `Subscriber ${absolutePath} has a default export that accepts more than 3 arguments` + ) + } + + // Check if module has a named export called config + const config = module_.config + + if (!config) { + throw new Error( + `Subscriber ${absolutePath} is missing a named export called config` + ) + } + + this.subscriberDescriptors_.set(absolutePath, { + config, + handler, + }) + }) + } + + private async createMap(dirPath: string) { + await Promise.all( + await readdir(dirPath, { withFileTypes: true }).then((entries) => { + return entries + .filter((entry) => { + if ( + this.excludes.length && + this.excludes.some((exclude) => exclude.test(entry.name)) + ) { + return false + } + + return true + }) + .map(async (entry) => { + const fullPath = join(dirPath, entry.name) + + if (entry.isDirectory()) { + return this.createMap(fullPath) + } + + return this.createDescriptor(fullPath, entry.name) + }) + .flat(Infinity) + }) + ) + } + + private inferIdentifier( + fileName: string, + config: Config, + handler: Handler + ) { + const { subscriberId } = config + + /** + * If subscriberId is provided, use that + */ + if (subscriberId) { + return subscriberId + } + + const handlerName = handler.name + + /** + * If the handler is not anonymous, use the name + */ + if (handlerName) { + return kebabCase(handlerName) + } + + /** + * If the handler is anonymous, use the file name + */ + return kebabCase(fileName) + } + + private async createSubscriber({ + fileName, + config, + handler, + }: { + fileName: string + config: Config + handler: Handler + }) { + const eventBusService: EventBusService = + this.container_.resolve("eventBusService") + + const { event } = config + + const events = Array.isArray(event) ? event : [event] + + const subscriber: Subscriber = async (data: T, eventName: string) => { + return handler(data, eventName, this.container_) + } + + const subscriberId = this.inferIdentifier(fileName, config, handler) + + for (const e of events) { + eventBusService.subscribe(e, subscriber as Subscriber, { + subscriberId, + }) + } + } + + async register() { + let hasSubscriberDir = false + + try { + await readdir(this.rootDir_) + hasSubscriberDir = true + } catch (err) { + hasSubscriberDir = false + } + + if (!hasSubscriberDir) { + return + } + + await this.createMap(this.rootDir_) + + const map = this.subscriberDescriptors_ + + for (const [fileName, { config, handler }] of map.entries()) { + await this.createSubscriber({ + fileName, + config, + handler, + }) + } + } +} diff --git a/packages/medusa/src/loaders/plugins.ts b/packages/medusa/src/loaders/plugins.ts index c1d9d5911657b..0711646579cd1 100644 --- a/packages/medusa/src/loaders/plugins.ts +++ b/packages/medusa/src/loaders/plugins.ts @@ -39,6 +39,7 @@ import { EntitySchema } from "typeorm" import { MiddlewareService } from "../services" import { getModelExtensionsMap } from "./helpers/get-model-extension-map" import { RoutesLoader } from "./helpers/routing" +import { SubscriberRegistrar } from "./helpers/subscribers" import logger from "./logger" type Options = { @@ -93,7 +94,7 @@ export default async ({ activityId ) registerCoreRouters(pluginDetails, container) - registerSubscribers(pluginDetails, container) + await registerSubscribers(pluginDetails, container) }) ) @@ -536,20 +537,25 @@ export async function registerServices( * registered * @return {void} */ -function registerSubscribers( +async function registerSubscribers( pluginDetails: PluginDetails, container: MedusaContainer -): void { - const files = glob.sync(`${pluginDetails.resolve}/subscribers/*.js`, {}) - files.forEach((fn) => { - const loaded = require(fn).default - - container.build( - asFunction( - (cradle) => new loaded(cradle, pluginDetails.options) - ).singleton() - ) - }) +): Promise { + await new SubscriberRegistrar( + path.join(pluginDetails.resolve, "subscribers"), + container + ).register() + + // const files = glob.sync(`${pluginDetails.resolve}/subscribers/*.js`, {}) + // files.forEach((fn) => { + // const loaded = require(fn).default + + // container.build( + // asFunction( + // (cradle) => new loaded(cradle, pluginDetails.options) + // ).singleton() + // ) + // }) } /** From 00be27ab70160c060d25975e3aa1eb33bbf7a720 Mon Sep 17 00:00:00 2001 From: Kasper Date: Mon, 6 Nov 2023 16:57:58 +0100 Subject: [PATCH 02/15] progress --- .../medusa/src/loaders/helpers/jobs/index.ts | 33 ++++++++ .../src/loaders/helpers/subscribers/index.ts | 82 ++++++++++++------- packages/medusa/src/loaders/plugins.ts | 8 +- 3 files changed, 90 insertions(+), 33 deletions(-) create mode 100644 packages/medusa/src/loaders/helpers/jobs/index.ts diff --git a/packages/medusa/src/loaders/helpers/jobs/index.ts b/packages/medusa/src/loaders/helpers/jobs/index.ts new file mode 100644 index 0000000000000..be8b8c79cda42 --- /dev/null +++ b/packages/medusa/src/loaders/helpers/jobs/index.ts @@ -0,0 +1,33 @@ +import { MedusaContainer } from "@medusajs/types" + +class JobsRegistrar { + protected container_: MedusaContainer + protected pluginOptions_: Record + protected rootDir_: string + + constructor( + rootDir: string, + container: MedusaContainer, + options: Record = {} + ) { + this.rootDir_ = rootDir + this.pluginOptions_ = options + this.container_ = container + } + + private async validateJob() { + return null + } + + // private async createJob({}: { + // handler: ScheduledJobHandler + // }) {} + + // async register() { + // const jobScheduler: JobSchedulerService = this.container_.resolve( + // "jobSchedulerService" + // ) + + // jobScheduler.create + // } +} diff --git a/packages/medusa/src/loaders/helpers/subscribers/index.ts b/packages/medusa/src/loaders/helpers/subscribers/index.ts index 82eed72c5e9a1..f5c0415f6683f 100644 --- a/packages/medusa/src/loaders/helpers/subscribers/index.ts +++ b/packages/medusa/src/loaders/helpers/subscribers/index.ts @@ -1,10 +1,10 @@ -import { Subscriber } from "@medusajs/types" +import { MedusaContainer, Subscriber } from "@medusajs/types" import { kebabCase } from "@medusajs/utils" import { readdir } from "fs/promises" import { join } from "path" import { EventBusService } from "../../../services" -import { MedusaContainer } from "../../../types/global" +import logger from "../../logger" type Config = { event: string | string[] @@ -25,6 +25,7 @@ type SubscriberConfig = { export class SubscriberRegistrar { protected container_: MedusaContainer protected pluginOptions_: Record + protected activityId_: string protected rootDir_: string protected excludes: RegExp[] = [ /\.DS_Store/, @@ -38,47 +39,69 @@ export class SubscriberRegistrar { constructor( rootDir: string, container: MedusaContainer, - options: Record = {} + options: Record = {}, + activityId: string ) { this.rootDir_ = rootDir this.pluginOptions_ = options this.container_ = container + this.activityId_ = activityId } - private async validateSubscriber() { - return null - } + private validateSubscriber( + subscriber: any + ): subscriber is { default: Handler; config: Config } { + const handler = subscriber.default - private async createDescriptor(absolutePath: string, entry: string) { - return await import(absolutePath).then((module_) => { - // Check if module has a default export of type function - const handler = module_.default + if (!handler || typeof handler !== "function") { + /** + * If the handler is not a function, we can't use it + */ + return false + } - if (typeof handler !== "function") { - throw new Error( - `Subscriber ${absolutePath} does not have a default export of type function` - ) - } + const config = subscriber.config - // check if the handler accepts more than 3 arguments - if (handler.length > 3) { - throw new Error( - `Subscriber ${absolutePath} has a default export that accepts more than 3 arguments` - ) - } + if (!config) { + /** + * If the subscriber is missing a config, we can't use it + */ + logger.warn(`The subscriber is missing a config. Skipping registration.`) + return false + } - // Check if module has a named export called config - const config = module_.config + if (!config.event) { + /** + * If the subscriber is missing an event, we can't use it + */ + return false + } - if (!config) { - throw new Error( - `Subscriber ${absolutePath} is missing a named export called config` - ) + if ( + typeof config.event !== "string" && + !Array.isArray(config.event) && + !config.event.every((e: unknown) => typeof e === "string") + ) { + /** + * If the subscribers event is not a string or an array of strings, we can't use it + */ + return false + } + + return true + } + + private async createDescriptor(absolutePath: string, entry: string) { + return await import(absolutePath).then((module_) => { + const isValid = this.validateSubscriber(module_) + + if (!isValid) { + return } this.subscriberDescriptors_.set(absolutePath, { - config, - handler, + config: module_.config, + handler: module_.default, }) }) } @@ -106,7 +129,6 @@ export class SubscriberRegistrar { return this.createDescriptor(fullPath, entry.name) }) - .flat(Infinity) }) ) } diff --git a/packages/medusa/src/loaders/plugins.ts b/packages/medusa/src/loaders/plugins.ts index 0711646579cd1..66d2974512757 100644 --- a/packages/medusa/src/loaders/plugins.ts +++ b/packages/medusa/src/loaders/plugins.ts @@ -94,7 +94,7 @@ export default async ({ activityId ) registerCoreRouters(pluginDetails, container) - await registerSubscribers(pluginDetails, container) + await registerSubscribers(pluginDetails, container, activityId) }) ) @@ -539,11 +539,13 @@ export async function registerServices( */ async function registerSubscribers( pluginDetails: PluginDetails, - container: MedusaContainer + container: MedusaContainer, + activityId: string ): Promise { await new SubscriberRegistrar( path.join(pluginDetails.resolve, "subscribers"), - container + container, + activityId ).register() // const files = glob.sync(`${pluginDetails.resolve}/subscribers/*.js`, {}) From bef93084da99e8679394c1d6fc57c4237c50e192 Mon Sep 17 00:00:00 2001 From: Kasper Date: Tue, 7 Nov 2023 10:32:07 +0100 Subject: [PATCH 03/15] update registrar args --- packages/medusa/src/loaders/plugins.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/medusa/src/loaders/plugins.ts b/packages/medusa/src/loaders/plugins.ts index 66d2974512757..6f4fa644e9618 100644 --- a/packages/medusa/src/loaders/plugins.ts +++ b/packages/medusa/src/loaders/plugins.ts @@ -545,6 +545,7 @@ async function registerSubscribers( await new SubscriberRegistrar( path.join(pluginDetails.resolve, "subscribers"), container, + pluginDetails.options, activityId ).register() From d073dd2922a74dccaf403c2a31529b2d5cdd9ae5 Mon Sep 17 00:00:00 2001 From: Kasper Date: Tue, 7 Nov 2023 17:13:26 +0100 Subject: [PATCH 04/15] cleanup --- packages/medusa/src/index.js | 9 +-- .../src/loaders/helpers/subscribers/index.ts | 70 +++++++++++-------- packages/medusa/src/loaders/plugins.ts | 31 +++++--- packages/medusa/src/types/subscribers.ts | 17 +++++ 4 files changed, 83 insertions(+), 44 deletions(-) create mode 100644 packages/medusa/src/types/subscribers.ts diff --git a/packages/medusa/src/index.js b/packages/medusa/src/index.js index 9a46e91dfa490..4398c79cde6d2 100644 --- a/packages/medusa/src/index.js +++ b/packages/medusa/src/index.js @@ -1,14 +1,15 @@ export * from "./api" export * from "./api/middlewares" export * from "./interfaces" +export * from "./joiner-config" export * from "./models" +export * from "./modules-config" export * from "./services" export * from "./types/batch-job" export * from "./types/common" -export * from "./types/middlewares" -export * from "./types/routing" export * from "./types/global" +export * from "./types/middlewares" export * from "./types/price-list" +export * from "./types/routing" +export * from "./types/subscribers" export * from "./utils" -export * from "./joiner-config" -export * from "./modules-config" diff --git a/packages/medusa/src/loaders/helpers/subscribers/index.ts b/packages/medusa/src/loaders/helpers/subscribers/index.ts index f5c0415f6683f..13fbef7a0f1ac 100644 --- a/packages/medusa/src/loaders/helpers/subscribers/index.ts +++ b/packages/medusa/src/loaders/helpers/subscribers/index.ts @@ -1,25 +1,15 @@ import { MedusaContainer, Subscriber } from "@medusajs/types" import { kebabCase } from "@medusajs/utils" import { readdir } from "fs/promises" -import { join } from "path" +import { extname, join, sep } from "path" import { EventBusService } from "../../../services" +import { SubscriberConfig, SubscriberHandler } from "../../../types/subscribers" import logger from "../../logger" -type Config = { - event: string | string[] - subscriberId?: string -} - -type Handler = ( - data: T, - eventName: string, - container: MedusaContainer -) => Promise - -type SubscriberConfig = { - config: Config - handler: Handler +type SubscriberModule = { + config: SubscriberConfig + handler: SubscriberHandler } export class SubscriberRegistrar { @@ -33,7 +23,7 @@ export class SubscriberRegistrar { /^_[^/\\]*(\.[^/\\]+)?$/, ] - protected subscriberDescriptors_: Map> = + protected subscriberDescriptors_: Map> = new Map() constructor( @@ -48,9 +38,10 @@ export class SubscriberRegistrar { this.activityId_ = activityId } - private validateSubscriber( - subscriber: any - ): subscriber is { default: Handler; config: Config } { + private validateSubscriber(subscriber: any): subscriber is { + default: SubscriberHandler + config: SubscriberConfig + } { const handler = subscriber.default if (!handler || typeof handler !== "function") { @@ -72,8 +63,19 @@ export class SubscriberRegistrar { if (!config.event) { /** - * If the subscriber is missing an event, we can't use it + * If the subscriber is missing an event, we can't use it. + * In production we throw an error, else we log a warning */ + if (process.env.NODE_ENV === "production") { + throw new Error( + "The subscriber is missing an event. Skipping registration." + ) + } else { + logger.warn( + `The subscriber is missing an event. Skipping registration.` + ) + } + return false } @@ -135,16 +137,16 @@ export class SubscriberRegistrar { private inferIdentifier( fileName: string, - config: Config, - handler: Handler + config: SubscriberConfig, + handler: SubscriberHandler ) { - const { subscriberId } = config + const { context } = config /** * If subscriberId is provided, use that */ - if (subscriberId) { - return subscriberId + if (context?.subscriberId) { + return context.subscriberId } const handlerName = handler.name @@ -159,17 +161,19 @@ export class SubscriberRegistrar { /** * If the handler is anonymous, use the file name */ + fileName = fileName.replace(extname(fileName), "").replace(sep, "-") + return kebabCase(fileName) } - private async createSubscriber({ + private createSubscriber({ fileName, config, handler, }: { fileName: string - config: Config - handler: Handler + config: SubscriberConfig + handler: SubscriberHandler }) { const eventBusService: EventBusService = this.container_.resolve("eventBusService") @@ -179,7 +183,7 @@ export class SubscriberRegistrar { const events = Array.isArray(event) ? event : [event] const subscriber: Subscriber = async (data: T, eventName: string) => { - return handler(data, eventName, this.container_) + return handler(data, eventName, this.container_, this.pluginOptions_) } const subscriberId = this.inferIdentifier(fileName, config, handler) @@ -210,11 +214,17 @@ export class SubscriberRegistrar { const map = this.subscriberDescriptors_ for (const [fileName, { config, handler }] of map.entries()) { - await this.createSubscriber({ + this.createSubscriber({ fileName, config, handler, }) } + + /** + * Return the file paths of the registered subscribers, to prevent the + * backwards compatible loader from trying to register them. + */ + return [...map.keys()] } } diff --git a/packages/medusa/src/loaders/plugins.ts b/packages/medusa/src/loaders/plugins.ts index 6f4fa644e9618..93ba47a8d31ba 100644 --- a/packages/medusa/src/loaders/plugins.ts +++ b/packages/medusa/src/loaders/plugins.ts @@ -542,23 +542,34 @@ async function registerSubscribers( container: MedusaContainer, activityId: string ): Promise { - await new SubscriberRegistrar( + const exclude: string[] = [] + + const loadedFiles = await new SubscriberRegistrar( path.join(pluginDetails.resolve, "subscribers"), container, pluginDetails.options, activityId ).register() - // const files = glob.sync(`${pluginDetails.resolve}/subscribers/*.js`, {}) - // files.forEach((fn) => { - // const loaded = require(fn).default + /** + * Exclude any files that have already been loaded by the registrar + */ + exclude.push(...(loadedFiles ?? [])) + + console.log("exclude", exclude, "loadedFiles", loadedFiles, "loadedFiles") - // container.build( - // asFunction( - // (cradle) => new loaded(cradle, pluginDetails.options) - // ).singleton() - // ) - // }) + const files = glob.sync(`${pluginDetails.resolve}/subscribers/*.js`, {}) + files + .filter((file) => !exclude.includes(file)) + .forEach((fn) => { + const loaded = require(fn).default + + container.build( + asFunction( + (cradle) => new loaded(cradle, pluginDetails.options) + ).singleton() + ) + }) } /** diff --git a/packages/medusa/src/types/subscribers.ts b/packages/medusa/src/types/subscribers.ts new file mode 100644 index 0000000000000..b655d5f554474 --- /dev/null +++ b/packages/medusa/src/types/subscribers.ts @@ -0,0 +1,17 @@ +import { MedusaContainer } from "@medusajs/types" + +interface SubscriberContext extends Record { + subscriberId?: string +} + +export type SubscriberConfig = { + event: string | string[] + context?: SubscriberContext +} + +export type SubscriberHandler = ( + data: T, + eventName: string, + container: MedusaContainer, + pluginOptions: Record +) => Promise From 268b8c9a397fb2d5111dc11e6e5d960f94021771 Mon Sep 17 00:00:00 2001 From: Kasper Date: Thu, 9 Nov 2023 12:56:13 +0100 Subject: [PATCH 05/15] progress --- .../icons/stats/@medusajs/icons-min.html | 4838 +++++++++++++++++ .../icons/stats/@medusajs/icons.html | 4838 +++++++++++++++++ .../jobs/__fixtures__/jobs/every-hour.ts | 14 + .../jobs/__fixtures__/jobs/every-minute.ts | 14 + .../loaders/helpers/jobs/__mocks__/index.ts | 16 + .../helpers/jobs/__tests__/index.spec.ts | 23 + .../medusa/src/loaders/helpers/jobs/index.ts | 168 +- .../src/loaders/helpers/subscribers/index.ts | 4 +- packages/medusa/src/loaders/plugins.ts | 18 + 9 files changed, 9919 insertions(+), 14 deletions(-) create mode 100644 packages/design-system/icons/stats/@medusajs/icons-min.html create mode 100644 packages/design-system/icons/stats/@medusajs/icons.html create mode 100644 packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-hour.ts create mode 100644 packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-minute.ts create mode 100644 packages/medusa/src/loaders/helpers/jobs/__mocks__/index.ts create mode 100644 packages/medusa/src/loaders/helpers/jobs/__tests__/index.spec.ts diff --git a/packages/design-system/icons/stats/@medusajs/icons-min.html b/packages/design-system/icons/stats/@medusajs/icons-min.html new file mode 100644 index 0000000000000..500b28a22a075 --- /dev/null +++ b/packages/design-system/icons/stats/@medusajs/icons-min.html @@ -0,0 +1,4838 @@ + + + + + + + + Rollup Visualizer + + + +
+ + + + + diff --git a/packages/design-system/icons/stats/@medusajs/icons.html b/packages/design-system/icons/stats/@medusajs/icons.html new file mode 100644 index 0000000000000..d09ef501b5110 --- /dev/null +++ b/packages/design-system/icons/stats/@medusajs/icons.html @@ -0,0 +1,4838 @@ + + + + + + + + Rollup Visualizer + + + +
+ + + + + diff --git a/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-hour.ts b/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-hour.ts new file mode 100644 index 0000000000000..a4087a2992848 --- /dev/null +++ b/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-hour.ts @@ -0,0 +1,14 @@ +import { MedusaContainer } from "@medusajs/types" + +export default async function ( + container: MedusaContainer, + pluginOptions: Record +) { + // noop: We can't test the actual job running, but we can test that the job is registered + return {} +} + +export const config = { + name: "every-hour", + cron_schedule: "0 * * * *", +} diff --git a/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-minute.ts b/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-minute.ts new file mode 100644 index 0000000000000..75a1bdc319700 --- /dev/null +++ b/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-minute.ts @@ -0,0 +1,14 @@ +import { MedusaContainer } from "@medusajs/types" + +export default async function ( + container: MedusaContainer, + pluginOptions: Record +) { + // noop: We can't test the actual job running, but we can test that the job is registered + return {} +} + +export const config = { + name: "every-hour", + cron_schedule: "* * * * *", +} diff --git a/packages/medusa/src/loaders/helpers/jobs/__mocks__/index.ts b/packages/medusa/src/loaders/helpers/jobs/__mocks__/index.ts new file mode 100644 index 0000000000000..23565d82cad90 --- /dev/null +++ b/packages/medusa/src/loaders/helpers/jobs/__mocks__/index.ts @@ -0,0 +1,16 @@ +export const jobSchedulerServiceMock = { + create: jest.fn().mockImplementation((...args) => { + return Promise.resolve(args) + }), +} + +export const containerMock = { + // mock .resolve method so if its called with "jobSchedulerService" it returns the mock + resolve: jest.fn().mockImplementation((name: string) => { + if (name === "jobSchedulerService") { + return jobSchedulerServiceMock + } else { + return {} + } + }), +} diff --git a/packages/medusa/src/loaders/helpers/jobs/__tests__/index.spec.ts b/packages/medusa/src/loaders/helpers/jobs/__tests__/index.spec.ts new file mode 100644 index 0000000000000..01c17aa9c9d85 --- /dev/null +++ b/packages/medusa/src/loaders/helpers/jobs/__tests__/index.spec.ts @@ -0,0 +1,23 @@ +import { MedusaContainer } from "@medusajs/types" +import { join } from "path" +import { containerMock, jobSchedulerServiceMock } from "../__mocks__" +import ScheduledJobsRegistrar from "../index" + +describe("ScheduledJobsRegistrar", () => { + it("should register every job in '/jobs'", async () => { + const rootDir = join(__dirname, "../__fixtures__", "jobs") + const pluginOptions = { + job_scheduler: { + enabled: true, + }, + } + + await new ScheduledJobsRegistrar( + rootDir, + containerMock as unknown as MedusaContainer, + pluginOptions + ).load() + + expect(jobSchedulerServiceMock.create).toHaveBeenCalledTimes(2) + }) +}) diff --git a/packages/medusa/src/loaders/helpers/jobs/index.ts b/packages/medusa/src/loaders/helpers/jobs/index.ts index be8b8c79cda42..660cf822f9a24 100644 --- a/packages/medusa/src/loaders/helpers/jobs/index.ts +++ b/packages/medusa/src/loaders/helpers/jobs/index.ts @@ -1,9 +1,37 @@ import { MedusaContainer } from "@medusajs/types" +import { readdir } from "fs/promises" +import { join } from "path" +import JobSchedulerService from "../../../services/job-scheduler" +import logger from "../../logger" -class JobsRegistrar { +type ScheduledJobConfig = { + name: string + cron_schedule: string + data?: Record + keep_existing?: boolean +} + +type ScheduledJobHandler = ( + container: MedusaContainer, + pluginOptions: Record +) => Promise + +type ScheduledJobModule = { + config: ScheduledJobConfig + handler: ScheduledJobHandler +} + +export default class ScheduledJobsRegistrar { protected container_: MedusaContainer protected pluginOptions_: Record protected rootDir_: string + protected excludes: RegExp[] = [ + /\.DS_Store/, + /(\.ts\.map|\.js\.map|\.d\.ts)/, + /^_[^/\\]*(\.[^/\\]+)?$/, + ] + + protected jobDescriptors_: Map = new Map() constructor( rootDir: string, @@ -15,19 +43,135 @@ class JobsRegistrar { this.container_ = container } - private async validateJob() { - return null + private validateJob(job: any): job is { + default: ScheduledJobHandler + config: ScheduledJobConfig + } { + const handler = job.default + + if (!handler || typeof handler !== "function") { + /** + * If the handler is not a function, we can't use it + */ + return false + } + + const config = job.config + + if (!config) { + /** + * If the job is missing a config, we can't use it + */ + logger.warn(`The job is missing a config. Skipping registration.`) + return false + } + + if (!config.cron_schedule) { + /** + * If the job is missing a cron_schedule, we can't use it + */ + logger.warn(`The job is missing a cron_schedule. Skipping registration.`) + return false + } + + if (!config.name) { + /** + * If the job is missing a name, we can't use it + */ + logger.warn(`The job is missing a name. Skipping registration.`) + return false + } + + return true + } + + private async createDescriptor(absolutePath: string, entry: string) { + return await import(absolutePath).then((module_) => { + const isValid = this.validateJob(module_) + + if (!isValid) { + return + } + + this.jobDescriptors_.set(absolutePath, { + config: module_.config, + handler: module_.default, + }) + }) } - // private async createJob({}: { - // handler: ScheduledJobHandler - // }) {} + private async createMap(dirPath: string) { + await Promise.all( + await readdir(dirPath, { withFileTypes: true }).then(async (entries) => { + return entries + .filter((entry) => { + if ( + this.excludes.length && + this.excludes.some((exclude) => exclude.test(entry.name)) + ) { + return false + } - // async register() { - // const jobScheduler: JobSchedulerService = this.container_.resolve( - // "jobSchedulerService" - // ) + return true + }) + .map(async (entry) => { + const fullPath = join(dirPath, entry.name) - // jobScheduler.create - // } + if (entry.isDirectory()) { + return this.createMap(fullPath) + } + + return await this.createDescriptor(fullPath, entry.name) + }) + }) + ) + } + + private async runJobs() { + const jobs = Array.from(this.jobDescriptors_.values()) + + if (!jobs.length) { + return + } + + const jobSchedulerService: JobSchedulerService = this.container_.resolve( + "jobSchedulerService" + ) + + for (const job of jobs) { + const { + name, + data = {}, + cron_schedule, + keep_existing = false, + } = job.config + + const handler = async () => { + await job.handler(this.container_, this.pluginOptions_) + } + + await jobSchedulerService.create(name, data, cron_schedule, handler, { + keepExisting: keep_existing, + }) + } + } + + async load(): Promise { + let hasJobsDir = false + + try { + await readdir(this.rootDir_) + hasJobsDir = true + } catch (_err) { + hasJobsDir = false + } + + if (!hasJobsDir) { + return + } + + await this.createMap(this.rootDir_) + + await this.runJobs() + } } diff --git a/packages/medusa/src/loaders/helpers/subscribers/index.ts b/packages/medusa/src/loaders/helpers/subscribers/index.ts index 13fbef7a0f1ac..2e7c299d863dd 100644 --- a/packages/medusa/src/loaders/helpers/subscribers/index.ts +++ b/packages/medusa/src/loaders/helpers/subscribers/index.ts @@ -110,7 +110,7 @@ export class SubscriberRegistrar { private async createMap(dirPath: string) { await Promise.all( - await readdir(dirPath, { withFileTypes: true }).then((entries) => { + await readdir(dirPath, { withFileTypes: true }).then(async (entries) => { return entries .filter((entry) => { if ( @@ -129,7 +129,7 @@ export class SubscriberRegistrar { return this.createMap(fullPath) } - return this.createDescriptor(fullPath, entry.name) + return await this.createDescriptor(fullPath, entry.name) }) }) ) diff --git a/packages/medusa/src/loaders/plugins.ts b/packages/medusa/src/loaders/plugins.ts index 93ba47a8d31ba..7376abd59a2b9 100644 --- a/packages/medusa/src/loaders/plugins.ts +++ b/packages/medusa/src/loaders/plugins.ts @@ -38,6 +38,7 @@ import path from "path" import { EntitySchema } from "typeorm" import { MiddlewareService } from "../services" import { getModelExtensionsMap } from "./helpers/get-model-extension-map" +import ScheduledJobsRegistrar from "./helpers/jobs" import { RoutesLoader } from "./helpers/routing" import { SubscriberRegistrar } from "./helpers/subscribers" import logger from "./logger" @@ -102,6 +103,12 @@ export default async ({ resolved.map(async (pluginDetails) => runLoaders(pluginDetails, container)) ) + await Promise.all( + resolved.map(async (pluginDetails) => { + await registerScheduledJobs(pluginDetails, container) + }) + ) + resolved.forEach((plugin) => trackInstallation(plugin.name, "plugin")) } @@ -184,6 +191,17 @@ async function runLoaders( ) } +async function registerScheduledJobs( + pluginDetails: PluginDetails, + container: MedusaContainer +): Promise { + await new ScheduledJobsRegistrar( + path.join(pluginDetails.resolve, "jobs"), + container, + pluginDetails.options + ).load() +} + async function registerMedusaApi( pluginDetails: PluginDetails, container: MedusaContainer From 1c54e64e53215c32347137f5912a1bbf02bc3926 Mon Sep 17 00:00:00 2001 From: Kasper Date: Thu, 9 Nov 2023 16:29:40 +0100 Subject: [PATCH 06/15] progress --- packages/medusa/src/index.js | 1 + .../medusa/src/loaders/helpers/jobs/index.ts | 56 ++++++++++--------- packages/medusa/src/types/scheduled-jobs.ts | 22 ++++++++ 3 files changed, 52 insertions(+), 27 deletions(-) create mode 100644 packages/medusa/src/types/scheduled-jobs.ts diff --git a/packages/medusa/src/index.js b/packages/medusa/src/index.js index 4398c79cde6d2..84748367af176 100644 --- a/packages/medusa/src/index.js +++ b/packages/medusa/src/index.js @@ -11,5 +11,6 @@ export * from "./types/global" export * from "./types/middlewares" export * from "./types/price-list" export * from "./types/routing" +export * from "./types/scheduled-jobs" export * from "./types/subscribers" export * from "./utils" diff --git a/packages/medusa/src/loaders/helpers/jobs/index.ts b/packages/medusa/src/loaders/helpers/jobs/index.ts index 660cf822f9a24..0446f2f429350 100644 --- a/packages/medusa/src/loaders/helpers/jobs/index.ts +++ b/packages/medusa/src/loaders/helpers/jobs/index.ts @@ -2,20 +2,12 @@ import { MedusaContainer } from "@medusajs/types" import { readdir } from "fs/promises" import { join } from "path" import JobSchedulerService from "../../../services/job-scheduler" +import { + ScheduledJobConfig, + ScheduledJobHandler, +} from "../../../types/scheduled-jobs" import logger from "../../logger" -type ScheduledJobConfig = { - name: string - cron_schedule: string - data?: Record - keep_existing?: boolean -} - -type ScheduledJobHandler = ( - container: MedusaContainer, - pluginOptions: Record -) => Promise - type ScheduledJobModule = { config: ScheduledJobConfig handler: ScheduledJobHandler @@ -66,11 +58,11 @@ export default class ScheduledJobsRegistrar { return false } - if (!config.cron_schedule) { + if (!config.schedule) { /** * If the job is missing a cron_schedule, we can't use it */ - logger.warn(`The job is missing a cron_schedule. Skipping registration.`) + logger.warn(`The job is missing a schedule. Skipping registration.`) return false } @@ -82,6 +74,14 @@ export default class ScheduledJobsRegistrar { return false } + if (config.data && typeof config.data !== "object") { + /** + * If the job has data but it's not an object, we can't use it + */ + logger.warn(`The job data is not an object. Skipping registration.`) + return false + } + return true } @@ -139,20 +139,22 @@ export default class ScheduledJobsRegistrar { ) for (const job of jobs) { - const { - name, - data = {}, - cron_schedule, - keep_existing = false, - } = job.config - - const handler = async () => { - await job.handler(this.container_, this.pluginOptions_) + try { + const { name, data = {}, schedule } = job.config + + const handler = async () => { + await job.handler(this.container_, this.pluginOptions_) + } + + await jobSchedulerService.create(name, data, schedule, handler, { + keepExisting: false, // For now, we do not support changing this flag + }) + } catch (err) { + logger.error( + `An error occured while registering job ${job.config.name}`, + err + ) } - - await jobSchedulerService.create(name, data, cron_schedule, handler, { - keepExisting: keep_existing, - }) } } diff --git a/packages/medusa/src/types/scheduled-jobs.ts b/packages/medusa/src/types/scheduled-jobs.ts new file mode 100644 index 0000000000000..e4063d5ef4739 --- /dev/null +++ b/packages/medusa/src/types/scheduled-jobs.ts @@ -0,0 +1,22 @@ +import { MedusaContainer } from "@medusajs/types" + +export type ScheduledJobConfig = { + /** + * The name of the job + */ + name: string + /** + * The cron schedule of the job, e.g. `0 0 * * *` for running every day at midnight. + */ + schedule: string + /** + * An optional data object to pass to the job handler + */ + data?: T +} + +export type ScheduledJobHandler = ( + container: MedusaContainer, + pluginOptions?: Record, + data?: T +) => Promise From 29c44d52eb1f46a3f7ca6f9ad6679bb19fef57b4 Mon Sep 17 00:00:00 2001 From: Kasper Date: Mon, 13 Nov 2023 17:29:31 +0100 Subject: [PATCH 07/15] tests --- .../jobs/__fixtures__/jobs/every-hour.ts | 4 +- .../jobs/__fixtures__/jobs/every-minute.ts | 6 +- .../helpers/jobs/__tests__/index.spec.ts | 40 +++++- .../medusa/src/loaders/helpers/jobs/index.ts | 15 --- .../subscribers/order-notifier.ts | 22 ++++ .../subscribers/product-updater.ts | 13 ++ .../subscribers/variant-created.ts | 10 ++ .../helpers/subscribers/__mocks__/index.ts | 16 +++ .../subscribers/__tests__/index.spec.ts | 120 ++++++++++++++++++ .../src/loaders/helpers/subscribers/index.ts | 18 ++- packages/medusa/src/types/subscribers.ts | 10 +- 11 files changed, 237 insertions(+), 37 deletions(-) create mode 100644 packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/order-notifier.ts create mode 100644 packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/product-updater.ts create mode 100644 packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/variant-created.ts create mode 100644 packages/medusa/src/loaders/helpers/subscribers/__mocks__/index.ts create mode 100644 packages/medusa/src/loaders/helpers/subscribers/__tests__/index.spec.ts diff --git a/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-hour.ts b/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-hour.ts index a4087a2992848..41d64f6c8095e 100644 --- a/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-hour.ts +++ b/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-hour.ts @@ -4,11 +4,11 @@ export default async function ( container: MedusaContainer, pluginOptions: Record ) { - // noop: We can't test the actual job running, but we can test that the job is registered + // noop return {} } export const config = { name: "every-hour", - cron_schedule: "0 * * * *", + schedule: "0 * * * *", } diff --git a/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-minute.ts b/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-minute.ts index 75a1bdc319700..82f5243914d1a 100644 --- a/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-minute.ts +++ b/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-minute.ts @@ -4,11 +4,11 @@ export default async function ( container: MedusaContainer, pluginOptions: Record ) { - // noop: We can't test the actual job running, but we can test that the job is registered + // noop return {} } export const config = { - name: "every-hour", - cron_schedule: "* * * * *", + name: "every-minute", + schedule: "* * * * *", } diff --git a/packages/medusa/src/loaders/helpers/jobs/__tests__/index.spec.ts b/packages/medusa/src/loaders/helpers/jobs/__tests__/index.spec.ts index 01c17aa9c9d85..eeb3b715a5b90 100644 --- a/packages/medusa/src/loaders/helpers/jobs/__tests__/index.spec.ts +++ b/packages/medusa/src/loaders/helpers/jobs/__tests__/index.spec.ts @@ -4,20 +4,46 @@ import { containerMock, jobSchedulerServiceMock } from "../__mocks__" import ScheduledJobsRegistrar from "../index" describe("ScheduledJobsRegistrar", () => { - it("should register every job in '/jobs'", async () => { - const rootDir = join(__dirname, "../__fixtures__", "jobs") - const pluginOptions = { - job_scheduler: { - enabled: true, - }, - } + const rootDir = join(__dirname, "../__fixtures__", "jobs") + + const pluginOptions = { + important_data: { + enabled: true, + }, + } + + beforeAll(async () => { + jest.clearAllMocks() await new ScheduledJobsRegistrar( rootDir, containerMock as unknown as MedusaContainer, pluginOptions ).load() + }) + it("should register every job in '/jobs'", async () => { + // As '/jobs' contains 2 jobs, we expect the create method to be called twice expect(jobSchedulerServiceMock.create).toHaveBeenCalledTimes(2) }) + + it("should register every job with the correct props", async () => { + // Registering every-hour.ts + expect(jobSchedulerServiceMock.create).toHaveBeenCalledWith( + "every-hour", + {}, + "0 * * * *", + expect.any(Function), + { keepExisting: false } + ) + + // Registering every-minute.ts + expect(jobSchedulerServiceMock.create).toHaveBeenCalledWith( + "every-minute", + {}, + "* * * * *", + expect.any(Function), + { keepExisting: false } + ) + }) }) diff --git a/packages/medusa/src/loaders/helpers/jobs/index.ts b/packages/medusa/src/loaders/helpers/jobs/index.ts index 0446f2f429350..baf3879c348ec 100644 --- a/packages/medusa/src/loaders/helpers/jobs/index.ts +++ b/packages/medusa/src/loaders/helpers/jobs/index.ts @@ -42,42 +42,27 @@ export default class ScheduledJobsRegistrar { const handler = job.default if (!handler || typeof handler !== "function") { - /** - * If the handler is not a function, we can't use it - */ return false } const config = job.config if (!config) { - /** - * If the job is missing a config, we can't use it - */ logger.warn(`The job is missing a config. Skipping registration.`) return false } if (!config.schedule) { - /** - * If the job is missing a cron_schedule, we can't use it - */ logger.warn(`The job is missing a schedule. Skipping registration.`) return false } if (!config.name) { - /** - * If the job is missing a name, we can't use it - */ logger.warn(`The job is missing a name. Skipping registration.`) return false } if (config.data && typeof config.data !== "object") { - /** - * If the job has data but it's not an object, we can't use it - */ logger.warn(`The job data is not an object. Skipping registration.`) return false } diff --git a/packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/order-notifier.ts b/packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/order-notifier.ts new file mode 100644 index 0000000000000..e32ebba718cc5 --- /dev/null +++ b/packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/order-notifier.ts @@ -0,0 +1,22 @@ +import { OrderService } from "../../../../../services" +import { + SubscriberArgs, + SubscriberConfig, +} from "../../../../../types/subscribers" + +export default async function orderNotifier({ + data, + eventName, + container, + pluginOptions, +}: SubscriberArgs) { + return Promise.resolve() +} + +export const config: SubscriberConfig = { + event: [ + OrderService.Events.PLACED, + OrderService.Events.CANCELED, + OrderService.Events.COMPLETED, + ], +} diff --git a/packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/product-updater.ts b/packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/product-updater.ts new file mode 100644 index 0000000000000..43296e511030c --- /dev/null +++ b/packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/product-updater.ts @@ -0,0 +1,13 @@ +import { ProductService } from "../../../../../services" +import { SubscriberConfig } from "../../../../../types/subscribers" + +export default async function productUpdater() { + return Promise.resolve() +} + +export const config: SubscriberConfig = { + event: ProductService.Events.UPDATED, + context: { + subscriberId: "product-updater", + }, +} diff --git a/packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/variant-created.ts b/packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/variant-created.ts new file mode 100644 index 0000000000000..964d33b8b8840 --- /dev/null +++ b/packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/variant-created.ts @@ -0,0 +1,10 @@ +import { ProductVariantService } from "../../../../../services" +import { SubscriberConfig } from "../../../../../types/subscribers" + +export default async function () { + return Promise.resolve() +} + +export const config: SubscriberConfig = { + event: ProductVariantService.Events.CREATED, +} diff --git a/packages/medusa/src/loaders/helpers/subscribers/__mocks__/index.ts b/packages/medusa/src/loaders/helpers/subscribers/__mocks__/index.ts new file mode 100644 index 0000000000000..e930fd6788a57 --- /dev/null +++ b/packages/medusa/src/loaders/helpers/subscribers/__mocks__/index.ts @@ -0,0 +1,16 @@ +export const eventBusServiceMock = { + subscribe: jest.fn().mockImplementation((...args) => { + return Promise.resolve(args) + }), +} + +export const containerMock = { + // mock .resolve method so if its called with "eventBusService" it returns the mock + resolve: jest.fn().mockImplementation((name: string) => { + if (name === "eventBusService") { + return eventBusServiceMock + } else { + return {} + } + }), +} diff --git a/packages/medusa/src/loaders/helpers/subscribers/__tests__/index.spec.ts b/packages/medusa/src/loaders/helpers/subscribers/__tests__/index.spec.ts new file mode 100644 index 0000000000000..111aa72943993 --- /dev/null +++ b/packages/medusa/src/loaders/helpers/subscribers/__tests__/index.spec.ts @@ -0,0 +1,120 @@ +import { MedusaContainer } from "@medusajs/types" +import { join } from "path" +import { + OrderService, + ProductService, + ProductVariantService, +} from "../../../../services" +import { containerMock, eventBusServiceMock } from "../__mocks__" +import { SubscriberRegistrar } from "../index" + +describe("SubscriberRegistrar", () => { + const rootDir = join(__dirname, "../__fixtures__", "subscribers") + + const pluginOptions = { + important_data: { + enabled: true, + }, + } + + let registeredPaths: string[] = [] + + beforeAll(async () => { + jest.clearAllMocks() + + const paths = await new SubscriberRegistrar( + rootDir, + containerMock as unknown as MedusaContainer, + pluginOptions, + "id-load-subscribers" + ).register() + + if (paths) { + registeredPaths = [...registeredPaths, ...paths] + } + }) + + it("should register each subscriber in the '/subscribers' folder", async () => { + // As '/subscribers' contains 2 subscribers, we expect the number of registered paths to be 2 + expect(registeredPaths.length).toEqual(3) + }) + + it("should have registered subscribers for 5 events", async () => { + /** + * The 'product-updater.ts' subscriber is registered for the following events: + * - "product.created" + * The 'order-updater.ts' subscriber is registered for the following events: + * - "order.placed" + * - "order.canceled" + * - "order.completed" + * The 'variant-created.ts' subscriber is registered for the following events: + * - "variant.created" + * + * This means that we expect the eventBusServiceMock.subscribe method to have + * been called 4 times, once for 'product-updater.ts', once for 'variant-created.ts', + * and 3 times for 'order-updater.ts'. + */ + expect(eventBusServiceMock.subscribe).toHaveBeenCalledTimes(5) + }) + + it("should have registered subscribers with the correct props", async () => { + /** + * The 'product-updater.ts' subscriber is registered + * with a explicit subscriberId of "product-updater". + */ + expect(eventBusServiceMock.subscribe).toHaveBeenCalledWith( + ProductService.Events.UPDATED, + expect.any(Function), + { + subscriberId: "product-updater", + } + ) + + /** + * The 'order-updater.ts' subscriber is registered + * without an explicit subscriberId, which means that + * the loader tries to infer one from either the handler + * functions name or the file name. In this case, the + * handler function is named 'orderUpdater' and is used + * to infer the subscriberId. + */ + expect(eventBusServiceMock.subscribe).toHaveBeenCalledWith( + OrderService.Events.PLACED, + expect.any(Function), + { + subscriberId: "order-notifier", + } + ) + + expect(eventBusServiceMock.subscribe).toHaveBeenCalledWith( + OrderService.Events.CANCELED, + expect.any(Function), + { + subscriberId: "order-notifier", + } + ) + + expect(eventBusServiceMock.subscribe).toHaveBeenCalledWith( + OrderService.Events.COMPLETED, + expect.any(Function), + { + subscriberId: "order-notifier", + } + ) + + /** + * The 'variant-created.ts' subscriber is registered + * without an explicit subscriberId, and with an anonymous + * handler function. This means that the loader tries to + * infer the subscriberId from the file name, which in this + * case is 'variant-created.ts'. + */ + expect(eventBusServiceMock.subscribe).toHaveBeenCalledWith( + ProductVariantService.Events.CREATED, + expect.any(Function), + { + subscriberId: "variant-created", + } + ) + }) +}) diff --git a/packages/medusa/src/loaders/helpers/subscribers/index.ts b/packages/medusa/src/loaders/helpers/subscribers/index.ts index 2e7c299d863dd..dc37d5f6a50cb 100644 --- a/packages/medusa/src/loaders/helpers/subscribers/index.ts +++ b/packages/medusa/src/loaders/helpers/subscribers/index.ts @@ -4,9 +4,11 @@ import { readdir } from "fs/promises" import { extname, join, sep } from "path" import { EventBusService } from "../../../services" -import { SubscriberConfig, SubscriberHandler } from "../../../types/subscribers" +import { SubscriberArgs, SubscriberConfig } from "../../../types/subscribers" import logger from "../../logger" +type SubscriberHandler = (args: SubscriberArgs) => Promise + type SubscriberModule = { config: SubscriberConfig handler: SubscriberHandler @@ -154,16 +156,17 @@ export class SubscriberRegistrar { /** * If the handler is not anonymous, use the name */ - if (handlerName) { + if (handlerName && !handlerName.startsWith("default")) { return kebabCase(handlerName) } /** * If the handler is anonymous, use the file name */ - fileName = fileName.replace(extname(fileName), "").replace(sep, "-") + const idFromFile = + fileName.split(sep).pop()?.replace(extname(fileName), "") ?? "" - return kebabCase(fileName) + return kebabCase(idFromFile) } private createSubscriber({ @@ -183,7 +186,12 @@ export class SubscriberRegistrar { const events = Array.isArray(event) ? event : [event] const subscriber: Subscriber = async (data: T, eventName: string) => { - return handler(data, eventName, this.container_, this.pluginOptions_) + return handler({ + data, + eventName, + container: this.container_, + pluginOptions: this.pluginOptions_, + }) } const subscriberId = this.inferIdentifier(fileName, config, handler) diff --git a/packages/medusa/src/types/subscribers.ts b/packages/medusa/src/types/subscribers.ts index b655d5f554474..265ea6b12bca6 100644 --- a/packages/medusa/src/types/subscribers.ts +++ b/packages/medusa/src/types/subscribers.ts @@ -9,9 +9,9 @@ export type SubscriberConfig = { context?: SubscriberContext } -export type SubscriberHandler = ( - data: T, - eventName: string, - container: MedusaContainer, +export type SubscriberArgs = { + data: T + eventName: string + container: MedusaContainer pluginOptions: Record -) => Promise +} From 1475ff45a2819f45c0e44b91c3724fa0d6989b7f Mon Sep 17 00:00:00 2001 From: Kasper Date: Mon, 13 Nov 2023 17:32:04 +0100 Subject: [PATCH 08/15] rename to loaders --- .../loaders/helpers/jobs/__tests__/index.spec.ts | 6 +++--- packages/medusa/src/loaders/helpers/jobs/index.ts | 2 +- .../helpers/subscribers/__tests__/index.spec.ts | 8 ++++---- .../src/loaders/helpers/subscribers/index.ts | 4 ++-- packages/medusa/src/loaders/plugins.ts | 14 ++++++-------- 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/packages/medusa/src/loaders/helpers/jobs/__tests__/index.spec.ts b/packages/medusa/src/loaders/helpers/jobs/__tests__/index.spec.ts index eeb3b715a5b90..f644bf46e017d 100644 --- a/packages/medusa/src/loaders/helpers/jobs/__tests__/index.spec.ts +++ b/packages/medusa/src/loaders/helpers/jobs/__tests__/index.spec.ts @@ -1,9 +1,9 @@ import { MedusaContainer } from "@medusajs/types" import { join } from "path" import { containerMock, jobSchedulerServiceMock } from "../__mocks__" -import ScheduledJobsRegistrar from "../index" +import ScheduledJobsLoader from "../index" -describe("ScheduledJobsRegistrar", () => { +describe("ScheduledJobsLoader", () => { const rootDir = join(__dirname, "../__fixtures__", "jobs") const pluginOptions = { @@ -15,7 +15,7 @@ describe("ScheduledJobsRegistrar", () => { beforeAll(async () => { jest.clearAllMocks() - await new ScheduledJobsRegistrar( + await new ScheduledJobsLoader( rootDir, containerMock as unknown as MedusaContainer, pluginOptions diff --git a/packages/medusa/src/loaders/helpers/jobs/index.ts b/packages/medusa/src/loaders/helpers/jobs/index.ts index baf3879c348ec..b0392536c7f0b 100644 --- a/packages/medusa/src/loaders/helpers/jobs/index.ts +++ b/packages/medusa/src/loaders/helpers/jobs/index.ts @@ -13,7 +13,7 @@ type ScheduledJobModule = { handler: ScheduledJobHandler } -export default class ScheduledJobsRegistrar { +export default class ScheduledJobsLoader { protected container_: MedusaContainer protected pluginOptions_: Record protected rootDir_: string diff --git a/packages/medusa/src/loaders/helpers/subscribers/__tests__/index.spec.ts b/packages/medusa/src/loaders/helpers/subscribers/__tests__/index.spec.ts index 111aa72943993..57c151bb81f8a 100644 --- a/packages/medusa/src/loaders/helpers/subscribers/__tests__/index.spec.ts +++ b/packages/medusa/src/loaders/helpers/subscribers/__tests__/index.spec.ts @@ -6,9 +6,9 @@ import { ProductVariantService, } from "../../../../services" import { containerMock, eventBusServiceMock } from "../__mocks__" -import { SubscriberRegistrar } from "../index" +import { SubscriberLoader } from "../index" -describe("SubscriberRegistrar", () => { +describe("SubscriberLoader", () => { const rootDir = join(__dirname, "../__fixtures__", "subscribers") const pluginOptions = { @@ -22,12 +22,12 @@ describe("SubscriberRegistrar", () => { beforeAll(async () => { jest.clearAllMocks() - const paths = await new SubscriberRegistrar( + const paths = await new SubscriberLoader( rootDir, containerMock as unknown as MedusaContainer, pluginOptions, "id-load-subscribers" - ).register() + ).load() if (paths) { registeredPaths = [...registeredPaths, ...paths] diff --git a/packages/medusa/src/loaders/helpers/subscribers/index.ts b/packages/medusa/src/loaders/helpers/subscribers/index.ts index dc37d5f6a50cb..36a6a3482ea3a 100644 --- a/packages/medusa/src/loaders/helpers/subscribers/index.ts +++ b/packages/medusa/src/loaders/helpers/subscribers/index.ts @@ -14,7 +14,7 @@ type SubscriberModule = { handler: SubscriberHandler } -export class SubscriberRegistrar { +export class SubscriberLoader { protected container_: MedusaContainer protected pluginOptions_: Record protected activityId_: string @@ -203,7 +203,7 @@ export class SubscriberRegistrar { } } - async register() { + async load() { let hasSubscriberDir = false try { diff --git a/packages/medusa/src/loaders/plugins.ts b/packages/medusa/src/loaders/plugins.ts index 7376abd59a2b9..1050e47a97d4a 100644 --- a/packages/medusa/src/loaders/plugins.ts +++ b/packages/medusa/src/loaders/plugins.ts @@ -38,9 +38,9 @@ import path from "path" import { EntitySchema } from "typeorm" import { MiddlewareService } from "../services" import { getModelExtensionsMap } from "./helpers/get-model-extension-map" -import ScheduledJobsRegistrar from "./helpers/jobs" +import ScheduledJobsLoader from "./helpers/jobs" import { RoutesLoader } from "./helpers/routing" -import { SubscriberRegistrar } from "./helpers/subscribers" +import { SubscriberLoader } from "./helpers/subscribers" import logger from "./logger" type Options = { @@ -195,7 +195,7 @@ async function registerScheduledJobs( pluginDetails: PluginDetails, container: MedusaContainer ): Promise { - await new ScheduledJobsRegistrar( + await new ScheduledJobsLoader( path.join(pluginDetails.resolve, "jobs"), container, pluginDetails.options @@ -562,20 +562,18 @@ async function registerSubscribers( ): Promise { const exclude: string[] = [] - const loadedFiles = await new SubscriberRegistrar( + const loadedFiles = await new SubscriberLoader( path.join(pluginDetails.resolve, "subscribers"), container, pluginDetails.options, activityId - ).register() + ).load() /** - * Exclude any files that have already been loaded by the registrar + * Exclude any files that have already been loaded by the subscriber loader */ exclude.push(...(loadedFiles ?? [])) - console.log("exclude", exclude, "loadedFiles", loadedFiles, "loadedFiles") - const files = glob.sync(`${pluginDetails.resolve}/subscribers/*.js`, {}) files .filter((file) => !exclude.includes(file)) From 5eafc378c077e54a66ce834a5ba917531246c5f9 Mon Sep 17 00:00:00 2001 From: Kasper Date: Mon, 13 Nov 2023 17:35:32 +0100 Subject: [PATCH 09/15] rm build artifacts --- .../icons/stats/@medusajs/icons-min.html | 4838 ----------------- .../icons/stats/@medusajs/icons.html | 4838 ----------------- 2 files changed, 9676 deletions(-) delete mode 100644 packages/design-system/icons/stats/@medusajs/icons-min.html delete mode 100644 packages/design-system/icons/stats/@medusajs/icons.html diff --git a/packages/design-system/icons/stats/@medusajs/icons-min.html b/packages/design-system/icons/stats/@medusajs/icons-min.html deleted file mode 100644 index 500b28a22a075..0000000000000 --- a/packages/design-system/icons/stats/@medusajs/icons-min.html +++ /dev/null @@ -1,4838 +0,0 @@ - - - - - - - - Rollup Visualizer - - - -
- - - - - diff --git a/packages/design-system/icons/stats/@medusajs/icons.html b/packages/design-system/icons/stats/@medusajs/icons.html deleted file mode 100644 index d09ef501b5110..0000000000000 --- a/packages/design-system/icons/stats/@medusajs/icons.html +++ /dev/null @@ -1,4838 +0,0 @@ - - - - - - - - Rollup Visualizer - - - -
- - - - - From 46c73d70ad1094697becafe44d557d56cb06b6b3 Mon Sep 17 00:00:00 2001 From: Kasper Date: Tue, 14 Nov 2023 10:13:58 +0100 Subject: [PATCH 10/15] improve validation and change jobs args to object --- .../jobs/__fixtures__/jobs/every-hour.ts | 7 ++--- .../jobs/__fixtures__/jobs/every-minute.ts | 7 ++--- .../medusa/src/loaders/helpers/jobs/index.ts | 28 +++++++++++++------ .../subscribers/product-updater.ts | 12 ++++++-- .../subscribers/variant-created.ts | 12 ++++++-- .../src/loaders/helpers/subscribers/index.ts | 21 ++++++++------ packages/medusa/src/types/scheduled-jobs.ts | 8 +++--- 7 files changed, 60 insertions(+), 35 deletions(-) diff --git a/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-hour.ts b/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-hour.ts index 41d64f6c8095e..67211910fa6a5 100644 --- a/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-hour.ts +++ b/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-hour.ts @@ -1,9 +1,6 @@ -import { MedusaContainer } from "@medusajs/types" +import { ScheduledJobArgs } from "../../../../../types/scheduled-jobs" -export default async function ( - container: MedusaContainer, - pluginOptions: Record -) { +export default async function ({ container, pluginOptions }: ScheduledJobArgs) { // noop return {} } diff --git a/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-minute.ts b/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-minute.ts index 82f5243914d1a..5f0958f5b01fb 100644 --- a/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-minute.ts +++ b/packages/medusa/src/loaders/helpers/jobs/__fixtures__/jobs/every-minute.ts @@ -1,9 +1,6 @@ -import { MedusaContainer } from "@medusajs/types" +import { ScheduledJobArgs } from "../../../../../types/scheduled-jobs" -export default async function ( - container: MedusaContainer, - pluginOptions: Record -) { +export default async function ({ container, pluginOptions }: ScheduledJobArgs) { // noop return {} } diff --git a/packages/medusa/src/loaders/helpers/jobs/index.ts b/packages/medusa/src/loaders/helpers/jobs/index.ts index b0392536c7f0b..51ffb13f682da 100644 --- a/packages/medusa/src/loaders/helpers/jobs/index.ts +++ b/packages/medusa/src/loaders/helpers/jobs/index.ts @@ -3,11 +3,15 @@ import { readdir } from "fs/promises" import { join } from "path" import JobSchedulerService from "../../../services/job-scheduler" import { + ScheduledJobArgs, ScheduledJobConfig, - ScheduledJobHandler, } from "../../../types/scheduled-jobs" import logger from "../../logger" +type ScheduledJobHandler = ( + args: ScheduledJobArgs +) => Promise + type ScheduledJobModule = { config: ScheduledJobConfig handler: ScheduledJobHandler @@ -35,35 +39,39 @@ export default class ScheduledJobsLoader { this.container_ = container } - private validateJob(job: any): job is { + private validateJob( + job: any, + path: string + ): job is { default: ScheduledJobHandler config: ScheduledJobConfig } { const handler = job.default if (!handler || typeof handler !== "function") { + logger.warn(`The job in ${path} is not a function.`) return false } const config = job.config if (!config) { - logger.warn(`The job is missing a config. Skipping registration.`) + logger.warn(`The job in ${path} is missing a config.`) return false } if (!config.schedule) { - logger.warn(`The job is missing a schedule. Skipping registration.`) + logger.warn(`The job in ${path} is missing a schedule.`) return false } if (!config.name) { - logger.warn(`The job is missing a name. Skipping registration.`) + logger.warn(`The job in ${path} is missing a name.`) return false } if (config.data && typeof config.data !== "object") { - logger.warn(`The job data is not an object. Skipping registration.`) + logger.warn(`The job data in ${path} is not an object.`) return false } @@ -72,7 +80,7 @@ export default class ScheduledJobsLoader { private async createDescriptor(absolutePath: string, entry: string) { return await import(absolutePath).then((module_) => { - const isValid = this.validateJob(module_) + const isValid = this.validateJob(module_, absolutePath) if (!isValid) { return @@ -128,7 +136,11 @@ export default class ScheduledJobsLoader { const { name, data = {}, schedule } = job.config const handler = async () => { - await job.handler(this.container_, this.pluginOptions_) + await job.handler({ + container: this.container_, + data, + pluginOptions: this.pluginOptions_, + }) } await jobSchedulerService.create(name, data, schedule, handler, { diff --git a/packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/product-updater.ts b/packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/product-updater.ts index 43296e511030c..8e83ba4ee5099 100644 --- a/packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/product-updater.ts +++ b/packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/product-updater.ts @@ -1,7 +1,15 @@ import { ProductService } from "../../../../../services" -import { SubscriberConfig } from "../../../../../types/subscribers" +import { + SubscriberArgs, + SubscriberConfig, +} from "../../../../../types/subscribers" -export default async function productUpdater() { +export default async function productUpdater({ + data, + eventName, + container, + pluginOptions, +}: SubscriberArgs) { return Promise.resolve() } diff --git a/packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/variant-created.ts b/packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/variant-created.ts index 964d33b8b8840..b56fa7aa9758f 100644 --- a/packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/variant-created.ts +++ b/packages/medusa/src/loaders/helpers/subscribers/__fixtures__/subscribers/variant-created.ts @@ -1,7 +1,15 @@ import { ProductVariantService } from "../../../../../services" -import { SubscriberConfig } from "../../../../../types/subscribers" +import { + SubscriberArgs, + SubscriberConfig, +} from "../../../../../types/subscribers" -export default async function () { +export default async function ({ + data, + eventName, + container, + pluginOptions, +}: SubscriberArgs) { return Promise.resolve() } diff --git a/packages/medusa/src/loaders/helpers/subscribers/index.ts b/packages/medusa/src/loaders/helpers/subscribers/index.ts index 36a6a3482ea3a..b942229fa2c38 100644 --- a/packages/medusa/src/loaders/helpers/subscribers/index.ts +++ b/packages/medusa/src/loaders/helpers/subscribers/index.ts @@ -40,7 +40,10 @@ export class SubscriberLoader { this.activityId_ = activityId } - private validateSubscriber(subscriber: any): subscriber is { + private validateSubscriber( + subscriber: any, + path: string + ): subscriber is { default: SubscriberHandler config: SubscriberConfig } { @@ -50,6 +53,7 @@ export class SubscriberLoader { /** * If the handler is not a function, we can't use it */ + logger.warn(`The subscriber in ${path} is not a function.`) return false } @@ -59,7 +63,7 @@ export class SubscriberLoader { /** * If the subscriber is missing a config, we can't use it */ - logger.warn(`The subscriber is missing a config. Skipping registration.`) + logger.warn(`The subscriber in ${path} is missing a config.`) return false } @@ -69,13 +73,9 @@ export class SubscriberLoader { * In production we throw an error, else we log a warning */ if (process.env.NODE_ENV === "production") { - throw new Error( - "The subscriber is missing an event. Skipping registration." - ) + throw new Error(`The subscriber in ${path} is missing an event.`) } else { - logger.warn( - `The subscriber is missing an event. Skipping registration.` - ) + logger.warn(`The subscriber in ${path} is missing an event.`) } return false @@ -89,6 +89,9 @@ export class SubscriberLoader { /** * If the subscribers event is not a string or an array of strings, we can't use it */ + logger.warn( + `The subscriber in ${path} has an invalid event. The event must be a string or an array of strings.` + ) return false } @@ -97,7 +100,7 @@ export class SubscriberLoader { private async createDescriptor(absolutePath: string, entry: string) { return await import(absolutePath).then((module_) => { - const isValid = this.validateSubscriber(module_) + const isValid = this.validateSubscriber(module_, absolutePath) if (!isValid) { return diff --git a/packages/medusa/src/types/scheduled-jobs.ts b/packages/medusa/src/types/scheduled-jobs.ts index e4063d5ef4739..591e6e3664d83 100644 --- a/packages/medusa/src/types/scheduled-jobs.ts +++ b/packages/medusa/src/types/scheduled-jobs.ts @@ -15,8 +15,8 @@ export type ScheduledJobConfig = { data?: T } -export type ScheduledJobHandler = ( - container: MedusaContainer, - pluginOptions?: Record, +export type ScheduledJobArgs = { + container: MedusaContainer data?: T -) => Promise + pluginOptions?: Record +} From 9ad0f8ee1352976e6878706fb19d715dced3d0b9 Mon Sep 17 00:00:00 2001 From: Kasper Date: Tue, 14 Nov 2023 10:26:32 +0100 Subject: [PATCH 11/15] spread context on subscribe --- packages/medusa/src/loaders/helpers/subscribers/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/medusa/src/loaders/helpers/subscribers/index.ts b/packages/medusa/src/loaders/helpers/subscribers/index.ts index b942229fa2c38..8ea7ccae0de2b 100644 --- a/packages/medusa/src/loaders/helpers/subscribers/index.ts +++ b/packages/medusa/src/loaders/helpers/subscribers/index.ts @@ -201,6 +201,7 @@ export class SubscriberLoader { for (const e of events) { eventBusService.subscribe(e, subscriber as Subscriber, { + ...(config.context ?? {}), subscriberId, }) } From 40d73abb3053ccf25f14b0fc3a8f5872822d6e05 Mon Sep 17 00:00:00 2001 From: Kasper Date: Tue, 14 Nov 2023 13:15:16 +0100 Subject: [PATCH 12/15] add changeset --- .changeset/gold-suits-march.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/gold-suits-march.md diff --git a/.changeset/gold-suits-march.md b/.changeset/gold-suits-march.md new file mode 100644 index 0000000000000..e6e2860cb93bb --- /dev/null +++ b/.changeset/gold-suits-march.md @@ -0,0 +1,5 @@ +--- +"@medusajs/medusa": patch +--- + +feat(medusa): Adds a new alternative syntax for creating Subscribers in Medusa, as well as adding a new API for creating scheduled jobs. From 4c4b1abeec9d2f2da94c66b52a62a5c7da07eeb1 Mon Sep 17 00:00:00 2001 From: Kasper Date: Wed, 15 Nov 2023 11:23:46 +0100 Subject: [PATCH 13/15] address comments --- packages/medusa/src/loaders/helpers/jobs/index.ts | 4 ++-- .../src/loaders/helpers/subscribers/__tests__/index.spec.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/medusa/src/loaders/helpers/jobs/index.ts b/packages/medusa/src/loaders/helpers/jobs/index.ts index 51ffb13f682da..a994cf8a3ce30 100644 --- a/packages/medusa/src/loaders/helpers/jobs/index.ts +++ b/packages/medusa/src/loaders/helpers/jobs/index.ts @@ -120,7 +120,7 @@ export default class ScheduledJobsLoader { ) } - private async runJobs() { + private async createScheduledJobs() { const jobs = Array.from(this.jobDescriptors_.values()) if (!jobs.length) { @@ -171,6 +171,6 @@ export default class ScheduledJobsLoader { await this.createMap(this.rootDir_) - await this.runJobs() + await this.createScheduledJobs() } } diff --git a/packages/medusa/src/loaders/helpers/subscribers/__tests__/index.spec.ts b/packages/medusa/src/loaders/helpers/subscribers/__tests__/index.spec.ts index 57c151bb81f8a..a6be26e9fcf54 100644 --- a/packages/medusa/src/loaders/helpers/subscribers/__tests__/index.spec.ts +++ b/packages/medusa/src/loaders/helpers/subscribers/__tests__/index.spec.ts @@ -35,7 +35,7 @@ describe("SubscriberLoader", () => { }) it("should register each subscriber in the '/subscribers' folder", async () => { - // As '/subscribers' contains 2 subscribers, we expect the number of registered paths to be 2 + // As '/subscribers' contains 3 subscribers, we expect the number of registered paths to be 3 expect(registeredPaths.length).toEqual(3) }) @@ -51,7 +51,7 @@ describe("SubscriberLoader", () => { * - "variant.created" * * This means that we expect the eventBusServiceMock.subscribe method to have - * been called 4 times, once for 'product-updater.ts', once for 'variant-created.ts', + * been called times, once for 'product-updater.ts', once for 'variant-created.ts', * and 3 times for 'order-updater.ts'. */ expect(eventBusServiceMock.subscribe).toHaveBeenCalledTimes(5) From fcc2f34a127cbf177906ae11b35b44794352aa29 Mon Sep 17 00:00:00 2001 From: Kasper Date: Thu, 16 Nov 2023 12:13:24 +0100 Subject: [PATCH 14/15] fix spelling of warning and add warning on Redis not enabled for Scheduled Jobs --- .../medusa/src/loaders/helpers/jobs/index.ts | 8 +++----- .../src/loaders/helpers/subscribers/index.ts | 2 +- packages/medusa/src/loaders/plugins.ts | 16 +++++++++++----- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/medusa/src/loaders/helpers/jobs/index.ts b/packages/medusa/src/loaders/helpers/jobs/index.ts index a994cf8a3ce30..b49cf7bf7b58f 100644 --- a/packages/medusa/src/loaders/helpers/jobs/index.ts +++ b/packages/medusa/src/loaders/helpers/jobs/index.ts @@ -8,9 +8,7 @@ import { } from "../../../types/scheduled-jobs" import logger from "../../logger" -type ScheduledJobHandler = ( - args: ScheduledJobArgs -) => Promise +type ScheduledJobHandler = (args: ScheduledJobArgs) => Promise type ScheduledJobModule = { config: ScheduledJobConfig @@ -133,7 +131,7 @@ export default class ScheduledJobsLoader { for (const job of jobs) { try { - const { name, data = {}, schedule } = job.config + const { name, data, schedule } = job.config const handler = async () => { await job.handler({ @@ -148,7 +146,7 @@ export default class ScheduledJobsLoader { }) } catch (err) { logger.error( - `An error occured while registering job ${job.config.name}`, + `An error occurred while registering job ${job.config.name}`, err ) } diff --git a/packages/medusa/src/loaders/helpers/subscribers/index.ts b/packages/medusa/src/loaders/helpers/subscribers/index.ts index 8ea7ccae0de2b..784bddd9940c5 100644 --- a/packages/medusa/src/loaders/helpers/subscribers/index.ts +++ b/packages/medusa/src/loaders/helpers/subscribers/index.ts @@ -190,8 +190,8 @@ export class SubscriberLoader { const subscriber: Subscriber = async (data: T, eventName: string) => { return handler({ - data, eventName, + data, container: this.container_, pluginOptions: this.pluginOptions_, }) diff --git a/packages/medusa/src/loaders/plugins.ts b/packages/medusa/src/loaders/plugins.ts index 23d9ae9d37e27..356c5d3f5ce3c 100644 --- a/packages/medusa/src/loaders/plugins.ts +++ b/packages/medusa/src/loaders/plugins.ts @@ -103,11 +103,17 @@ export default async ({ resolved.map(async (pluginDetails) => runLoaders(pluginDetails, container)) ) - await Promise.all( - resolved.map(async (pluginDetails) => { - await registerScheduledJobs(pluginDetails, container) - }) - ) + if (configModule.projectConfig.redis_url) { + await Promise.all( + resolved.map(async (pluginDetails) => { + await registerScheduledJobs(pluginDetails, container) + }) + ) + } else { + logger.warn( + "You don't have Redis configured. Scheduled jobs will not be enabled." + ) + } resolved.forEach((plugin) => trackInstallation(plugin.name, "plugin")) } From e177e67afbf3b922cb58853f7fa19a2b948213e2 Mon Sep 17 00:00:00 2001 From: Kasper Date: Thu, 16 Nov 2023 13:25:56 +0100 Subject: [PATCH 15/15] fix tests --- .../medusa/src/loaders/helpers/jobs/__tests__/index.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/medusa/src/loaders/helpers/jobs/__tests__/index.spec.ts b/packages/medusa/src/loaders/helpers/jobs/__tests__/index.spec.ts index f644bf46e017d..33304a08d80e8 100644 --- a/packages/medusa/src/loaders/helpers/jobs/__tests__/index.spec.ts +++ b/packages/medusa/src/loaders/helpers/jobs/__tests__/index.spec.ts @@ -31,7 +31,7 @@ describe("ScheduledJobsLoader", () => { // Registering every-hour.ts expect(jobSchedulerServiceMock.create).toHaveBeenCalledWith( "every-hour", - {}, + undefined, "0 * * * *", expect.any(Function), { keepExisting: false } @@ -40,7 +40,7 @@ describe("ScheduledJobsLoader", () => { // Registering every-minute.ts expect(jobSchedulerServiceMock.create).toHaveBeenCalledWith( "every-minute", - {}, + undefined, "* * * * *", expect.any(Function), { keepExisting: false }