diff --git a/packages/core/modules-sdk/src/loaders/__mocks__/@providers/default-with-fail-validation.ts b/packages/core/modules-sdk/src/loaders/__mocks__/@providers/default-with-fail-validation.ts new file mode 100644 index 0000000000000..2ce7c115537af --- /dev/null +++ b/packages/core/modules-sdk/src/loaders/__mocks__/@providers/default-with-fail-validation.ts @@ -0,0 +1,9 @@ +const service = class TestService { + static validateOptions(options: Record) { + throw new Error("Wrong options") + } +} + +export default { + services: [service], +} diff --git a/packages/core/modules-sdk/src/loaders/__tests__/module-provider-loader.ts b/packages/core/modules-sdk/src/loaders/__tests__/module-provider-loader.ts index 497697b787817..5fd04ba8c8ccf 100644 --- a/packages/core/modules-sdk/src/loaders/__tests__/module-provider-loader.ts +++ b/packages/core/modules-sdk/src/loaders/__tests__/module-provider-loader.ts @@ -1,5 +1,5 @@ import { createMedusaContainer } from "@medusajs/utils" -import { Lifetime, asFunction } from "awilix" +import { asFunction, Lifetime } from "awilix" import { moduleProviderLoader } from "../module-provider-loader" const logger = { @@ -34,6 +34,24 @@ describe("modules loader", () => { expect(testService.constructor.name).toEqual("TestService") }) + it("should fail to register the provider service", async () => { + const moduleProviders = [ + { + resolve: "@providers/default-with-fail-validation", + id: "default", + options: {}, + }, + ] + + const err = await moduleProviderLoader({ + container, + providers: moduleProviders, + }).catch((e) => e) + + expect(err).toBeTruthy() + expect(err.message).toBe("Wrong options") + }) + it("should register the provider service with custom register fn", async () => { const fn = async (klass, container, details) => { container.register({ @@ -64,6 +82,35 @@ describe("modules loader", () => { expect(testService.constructor.name).toEqual("TestService") }) + it("should fail to register the provider service with custom register fn", async () => { + const fn = async (klass, container, details) => { + container.register({ + [`testServiceCustomRegistration`]: asFunction( + (cradle) => new klass(cradle, details.options), + { + lifetime: Lifetime.SINGLETON, + } + ), + }) + } + const moduleProviders = [ + { + resolve: "@providers/default-with-fail-validation", + id: "default", + options: {}, + }, + ] + + const err = await moduleProviderLoader({ + container, + providers: moduleProviders, + registerServiceFn: fn, + }).catch((e) => e) + + expect(err).toBeTruthy() + expect(err.message).toBe("Wrong options") + }) + it("should log the errors if no service is defined", async () => { const moduleProviders = [ { diff --git a/packages/core/modules-sdk/src/loaders/module-provider-loader.ts b/packages/core/modules-sdk/src/loaders/module-provider-loader.ts index 00a18f7cd7354..e679b791381b9 100644 --- a/packages/core/modules-sdk/src/loaders/module-provider-loader.ts +++ b/packages/core/modules-sdk/src/loaders/module-provider-loader.ts @@ -60,8 +60,11 @@ export async function loadModuleProvider( ) } - const services = await promiseAll( + return await promiseAll( loadedProvider.services.map(async (service) => { + // Ask the provider to validate its options + await service.validateOptions?.(provider.options) + const name = lowerCaseFirst(service.name) if (registerServiceFn) { // Used to register the specific type of service in the provider @@ -83,6 +86,4 @@ export async function loadModuleProvider( return service }) ) - - return services } diff --git a/packages/core/utils/src/auth/abstract-auth-provider.ts b/packages/core/utils/src/auth/abstract-auth-provider.ts index 0e72a5ca29c73..40da662c0458b 100644 --- a/packages/core/utils/src/auth/abstract-auth-provider.ts +++ b/packages/core/utils/src/auth/abstract-auth-provider.ts @@ -1,7 +1,7 @@ import { - AuthIdentityProviderService, AuthenticationInput, AuthenticationResponse, + AuthIdentityProviderService, IAuthProvider, } from "@medusajs/types" @@ -74,6 +74,7 @@ export abstract class AbstractAuthModuleProvider implements IAuthProvider { * @ignore */ private static DISPLAY_NAME: string + /** * @ignore */ @@ -91,6 +92,12 @@ export abstract class AbstractAuthModuleProvider implements IAuthProvider { return (this.constructor as typeof AbstractAuthModuleProvider).DISPLAY_NAME } + /** + * Override this static method in order for the loader to validate the options provided to the module provider. + * @param options + */ + static validateOptions(options: Record): void | never {} + /** * @ignore * diff --git a/packages/core/utils/src/file/abstract-file-provider.ts b/packages/core/utils/src/file/abstract-file-provider.ts index 1b662faaa0cdc..51e0d9471186e 100644 --- a/packages/core/utils/src/file/abstract-file-provider.ts +++ b/packages/core/utils/src/file/abstract-file-provider.ts @@ -2,46 +2,46 @@ import { FileTypes, IFileProvider } from "@medusajs/types" /** * ### constructor - * + * * The constructor allows you to access resources from the module's container using the first parameter, * and the module's options using the second parameter. - * + * * If you're creating a client or establishing a connection with a third-party service, do it in the constructor. - * + * * #### Example - * + * * ```ts * import { Logger } from "@medusajs/types" * import { AbstractFileProviderService } from "@medusajs/utils" - * + * * type InjectedDependencies = { * logger: Logger * } - * + * * type Options = { * apiKey: string * } - * + * * class MyFileProviderService extends AbstractFileProviderService { * protected logger_: Logger * protected options_: Options * // assuming you're initializing a client * protected client - * + * * constructor ( * { logger }: InjectedDependencies, * options: Options * ) { * super() - * + * * this.logger_ = logger * this.options_ = options - * + * * // assuming you're initializing a client * this.client = new Client(options) * } * } - * + * * export default MyFileProviderService * ``` */ @@ -51,6 +51,12 @@ export class AbstractFileProviderService implements IFileProvider { */ static identifier: string + /** + * Override this static method in order for the loader to validate the options provided to the module provider. + * @param options + */ + static validateOptions(options: Record): void | never {} + /** * @ignore */ @@ -60,10 +66,10 @@ export class AbstractFileProviderService implements IFileProvider { /** * This method uploads a file using your provider's custom logic. - * + * * @param {FileTypes.ProviderUploadFileDTO} file - The file to upload * @returns {Promise} The uploaded file's details. - * + * * @example * class MyFileProviderService extends AbstractFileProviderService { * // ... @@ -72,7 +78,7 @@ export class AbstractFileProviderService implements IFileProvider { * ): Promise { * // TODO upload file to third-party provider * // or using custom logic - * + * * return { * url: "some-url.com", * key: "file-name" @@ -88,10 +94,10 @@ export class AbstractFileProviderService implements IFileProvider { /** * This method deletes the file from storage. - * + * * @param {FileTypes.ProviderDeleteFileDTO} file - The details of the file to delete. * @returns {Promise} Resolves when the file is deleted. - * + * * @example * class MyFileProviderService extends AbstractFileProviderService { * // ... @@ -108,16 +114,16 @@ export class AbstractFileProviderService implements IFileProvider { } /** - * This method is used to retrieve a download URL of the file. For some providers, + * This method is used to retrieve a download URL of the file. For some providers, * such as S3, a presigned URL indicates a temporary URL to get access to a file. - * - * If your provider doesn’t perform or offer a similar functionality, you can + * + * If your provider doesn’t perform or offer a similar functionality, you can * return the URL to download the file. - * - * @param {FileTypes.ProviderGetFileDTO} fileData - The details of the file to get its + * + * @param {FileTypes.ProviderGetFileDTO} fileData - The details of the file to get its * presigned URL. * @returns {Promise} The file's presigned URL. - * + * * @example * class MyFileProviderService extends AbstractFileProviderService { * // ... diff --git a/packages/core/utils/src/fulfillment/provider.ts b/packages/core/utils/src/fulfillment/provider.ts index fa66903c6f7fd..bf2e12b111222 100644 --- a/packages/core/utils/src/fulfillment/provider.ts +++ b/packages/core/utils/src/fulfillment/provider.ts @@ -11,6 +11,12 @@ export class AbstractFulfillmentProviderService return obj?.constructor?._isFulfillmentService } + /** + * Override this static method in order for the loader to validate the options provided to the module provider. + * @param options + */ + static validateOptions(options: Record): void | never {} + getIdentifier() { return (this.constructor as any).identifier } diff --git a/packages/core/utils/src/notification/abstract-notification-provider.ts b/packages/core/utils/src/notification/abstract-notification-provider.ts index 1895c382497b5..50e9ada76b46b 100644 --- a/packages/core/utils/src/notification/abstract-notification-provider.ts +++ b/packages/core/utils/src/notification/abstract-notification-provider.ts @@ -1,68 +1,74 @@ -import { NotificationTypes, INotificationProvider } from "@medusajs/types" +import { INotificationProvider, NotificationTypes } from "@medusajs/types" /** * ### constructor - * + * * The constructor allows you to access resources from the module's container using the first parameter, * and the module's options using the second parameter. - * + * * If you're creating a client or establishing a connection with a third-party service, do it in the constructor. - * + * * #### Example - * + * * ```ts * import { AbstractNotificationProviderService } from "@medusajs/utils" * import { Logger } from "@medusajs/types" - * + * * type InjectedDependencies = { * logger: Logger * } - * + * * type Options = { * apiKey: string * } - * + * * class MyNotificationProviderService extends AbstractNotificationProviderService { * protected logger_: Logger * protected options_: Options * // assuming you're initializing a client * protected client - * + * * constructor ( * { logger }: InjectedDependencies, * options: Options * ) { * super() - * + * * this.logger_ = logger * this.options_ = options - * + * * // assuming you're initializing a client * this.client = new Client(options) * } * } - * + * * export default MyNotificationProviderService * ``` */ export class AbstractNotificationProviderService implements INotificationProvider { + /** + * Override this static method in order for the loader to validate the options provided to the module provider. + * @param options + */ + static validateOptions(options: Record): void | never {} + /** * This method is used to send a notification using the third-party provider or your custom logic. - * + * * @param {NotificationTypes.ProviderSendNotificationDTO} notification - The details of the * notification to send. * @returns {Promise} The result of sending * the notification. - * + * * @example * // other imports... - * import { + * import { * ProviderSendNotificationDTO, * ProviderSendNotificationResultsDTO * } from "@medusajs/types" - * + * * class MyNotificationProviderService extends AbstractNotificationProviderService { * // ... * async send( diff --git a/packages/core/utils/src/payment/abstract-payment-provider.ts b/packages/core/utils/src/payment/abstract-payment-provider.ts index 4543e78d7e0ec..652ff1f5f45d0 100644 --- a/packages/core/utils/src/payment/abstract-payment-provider.ts +++ b/packages/core/utils/src/payment/abstract-payment-provider.ts @@ -7,13 +7,18 @@ import { PaymentSessionStatus, ProviderWebhookPayload, UpdatePaymentProviderSession, - WebhookActionResult + WebhookActionResult, } from "@medusajs/types" - export abstract class AbstractPaymentProvider> implements IPaymentProvider { + /** + * Override this static method in order for the loader to validate the options provided to the module provider. + * @param options + */ + static validateOptions(options: Record): void | never {} + /** * You can use the `constructor` of the provider's service to access resources in your module's container. * @@ -27,45 +32,45 @@ export abstract class AbstractPaymentProvider> * * @example * ```ts - * import { + * import { * AbstractPaymentProvider * } from "@medusajs/utils" * import { Logger } from "@medusajs/types" - * + * * type InjectedDependencies = { * logger: Logger * } - * + * * type Options = { * apiKey: string * } - * + * * class MyPaymentProviderService extends AbstractPaymentProvider< * Options * > { * protected logger_: Logger * protected options_: Options - * // Assuming you're using a client to integrate + * // Assuming you're using a client to integrate * // with a third-party service * protected client - * + * * constructor( * { logger }: InjectedDependencies, * options: Options * ) { * // @ts-ignore * super(...arguments) - * + * * this.logger_ = logger * this.options_ = options - * + * * // Assuming you're initializing a client * this.client = new Client(options) * } - * + * * // ... * } - * + * * export default MyPaymentProviderService * ``` */ @@ -88,7 +93,7 @@ export abstract class AbstractPaymentProvider> /** * Each payment provider has a unique identifier defined in its class. - * + * * @example * class MyPaymentProviderService extends AbstractPaymentProvider< * Options @@ -123,18 +128,18 @@ export abstract class AbstractPaymentProvider> * - A webhook event occurred that instructs the payment provider to capture the payment session. Learn more about handing webhook events in [this guide](https://docs.medusajs.com/v2/resources/commerce-modules/payment/webhook-events). * * In this method, use the third-party provider to capture the payment. - * + * * @param paymentData - The `data` property of the payment. Make sure to store in it * any helpful identification for your third-party integration. * @returns The new data to store in the payment's `data` property, or an error object. - * + * * @example * // other imports... - * import { - * PaymentProviderError, - * PaymentProviderSessionResponse, + * import { + * PaymentProviderError, + * PaymentProviderSessionResponse, * } from "@medusajs/types" - * + * * class MyPaymentProviderService extends AbstractPaymentProvider< * Options * > { @@ -142,10 +147,10 @@ export abstract class AbstractPaymentProvider> * paymentData: Record * ): Promise { * const externalId = paymentData.id - * + * * try { * const newData = await this.client.capturePayment(externalId) - * + * * return { * ...newData, * id: externalId @@ -158,7 +163,7 @@ export abstract class AbstractPaymentProvider> * } * } * } - * + * * // ... * } */ @@ -169,45 +174,45 @@ export abstract class AbstractPaymentProvider> /** * This method authorizes a payment session. When authorized successfully, a payment is created by the Payment * Module which can be later captured using the {@link capturePayment} method. - * + * * Refer to [this guide](https://docs.medusajs.com/v2/resources/commerce-modules/payment/payment-flow#3-authorize-payment-session) * to learn more about how this fits into the payment flow and how to handle required actions. - * + * * To automatically capture the payment after authorization, return the status `captured`. - * + * * @param paymentSessionData - The `data` property of the payment session. Make sure to store in it * any helpful identification for your third-party integration. - * @param context - The context in which the payment is being authorized. For example, in checkout, + * @param context - The context in which the payment is being authorized. For example, in checkout, * the context has a `cart_id` property indicating the ID of the associated cart. - * @returns Either an object of the new data to store in the created payment's `data` property and the + * @returns Either an object of the new data to store in the created payment's `data` property and the * payment's status, or an error object. Make sure to set in `data` anything useful to later retrieve the session. - * + * * @example * // other imports... - * import { - * PaymentProviderError, - * PaymentProviderSessionResponse, + * import { + * PaymentProviderError, + * PaymentProviderSessionResponse, * PaymentSessionStatus * } from "@medusajs/types" - * - * + * + * * class MyPaymentProviderService extends AbstractPaymentProvider< * Options * > { * async authorizePayment( - * paymentSessionData: Record, + * paymentSessionData: Record, * context: Record * ): Promise< - * PaymentProviderError | { + * PaymentProviderError | { * status: PaymentSessionStatus * data: PaymentProviderSessionResponse["data"] * } * > { * const externalId = paymentSessionData.id - * + * * try { * const paymentData = await this.client.authorizePayment(externalId) - * + * * return { * data: { * ...paymentData, @@ -223,7 +228,7 @@ export abstract class AbstractPaymentProvider> * } * } * } - * + * * // ... * } */ @@ -246,19 +251,19 @@ export abstract class AbstractPaymentProvider> /** * This method cancels a payment. - * + * * @param paymentData - The `data` property of the payment. Make sure to store in it * any helpful identification for your third-party integration. * @returns An error object if an error occurs, or the data received from the integration. - * + * * @example * // other imports... - * import { - * PaymentProviderError, - * PaymentProviderSessionResponse, + * import { + * PaymentProviderError, + * PaymentProviderSessionResponse, * } from "@medusajs/types" - * - * + * + * * class MyPaymentProviderService extends AbstractPaymentProvider< * Options * > { @@ -266,7 +271,7 @@ export abstract class AbstractPaymentProvider> * paymentData: Record * ): Promise { * const externalId = paymentData.id - * + * * try { * const paymentData = await this.client.cancelPayment(externalId) * } catch (e) { @@ -277,7 +282,7 @@ export abstract class AbstractPaymentProvider> * } * } * } - * + * * // ... * } */ @@ -288,36 +293,36 @@ export abstract class AbstractPaymentProvider> /** * This method is used when a payment session is created. It can be used to initiate the payment * in the third-party session, before authorizing or capturing the payment later. - * + * * @param context - The details of the payment session and its context. * @returns An object whose `data` property is set in the created payment session, or an error * object. Make sure to set in `data` anything useful to later retrieve the session. - * + * * @example * // other imports... - * import { - * PaymentProviderError, - * PaymentProviderSessionResponse, + * import { + * PaymentProviderError, + * PaymentProviderSessionResponse, * } from "@medusajs/types" - * - * + * + * * class MyPaymentProviderService extends AbstractPaymentProvider< * Options * > { * async initiatePayment( * context: CreatePaymentProviderSession * ): Promise { - * const { - * amount, - * currency_code, + * const { + * amount, + * currency_code, * context: customerDetails * } = context - * + * * try { * const response = await this.client.init( * amount, currency_code, customerDetails * ) - * + * * return { * ...response, * data: { @@ -332,7 +337,7 @@ export abstract class AbstractPaymentProvider> * } * } * } - * + * * // ... * } */ @@ -342,21 +347,21 @@ export abstract class AbstractPaymentProvider> /** * This method is used when a payment session is deleted, which can only happen if it isn't authorized, yet. - * + * * Use this to delete or cancel the payment in the third-party service. - * + * * @param paymentSessionData - The `data` property of the payment session. Make sure to store in it * any helpful identification for your third-party integration. * @returns An error object or the response from the third-party service. - * + * * @example * // other imports... - * import { - * PaymentProviderError, - * PaymentProviderSessionResponse, + * import { + * PaymentProviderError, + * PaymentProviderSessionResponse, * } from "@medusajs/types" - * - * + * + * * class MyPaymentProviderService extends AbstractPaymentProvider< * Options * > { @@ -366,7 +371,7 @@ export abstract class AbstractPaymentProvider> * PaymentProviderError | PaymentProviderSessionResponse["data"] * > { * const externalId = paymentSessionData.id - * + * * try { * await this.client.cancelPayment(externalId) * } catch (e) { @@ -377,7 +382,7 @@ export abstract class AbstractPaymentProvider> * } * } * } - * + * * // ... * } */ @@ -387,18 +392,18 @@ export abstract class AbstractPaymentProvider> /** * This method gets the status of a payment session based on the status in the third-party integration. - * + * * @param paymentSessionData - The `data` property of the payment session. Make sure to store in it * any helpful identification for your third-party integration. * @returns The payment session's status. - * + * * @example * // other imports... - * import { + * import { * PaymentSessionStatus * } from "@medusajs/types" - * - * + * + * * class MyPaymentProviderService extends AbstractPaymentProvider< * Options * > { @@ -406,10 +411,10 @@ export abstract class AbstractPaymentProvider> * paymentSessionData: Record * ): Promise { * const externalId = paymentSessionData.id - * + * * try { * const status = await this.client.getStatus(externalId) - * + * * switch (status) { * case "requires_capture": * return "authorized" @@ -424,7 +429,7 @@ export abstract class AbstractPaymentProvider> * return "error" * } * } - * + * * // ... * } */ @@ -434,37 +439,37 @@ export abstract class AbstractPaymentProvider> /** * This method refunds an amount of a payment previously captured. - * + * * @param paymentData - The `data` property of the payment. Make sure to store in it * any helpful identification for your third-party integration. * @param refundAmount The amount to refund. * @returns The new data to store in the payment's `data` property, or an error object. - * + * * @example * // other imports... - * import { - * PaymentProviderError, - * PaymentProviderSessionResponse, + * import { + * PaymentProviderError, + * PaymentProviderSessionResponse, * } from "@medusajs/types" - * - * + * + * * class MyPaymentProviderService extends AbstractPaymentProvider< * Options * > { * async refundPayment( - * paymentData: Record, + * paymentData: Record, * refundAmount: number * ): Promise< * PaymentProviderError | PaymentProviderSessionResponse["data"] * > { * const externalId = paymentData.id - * + * * try { * const newData = await this.client.refund( * externalId, * refundAmount * ) - * + * * return { * ...newData, * id: externalId @@ -477,7 +482,7 @@ export abstract class AbstractPaymentProvider> * } * } * } - * + * * // ... * } */ @@ -488,19 +493,19 @@ export abstract class AbstractPaymentProvider> /** * Retrieves the payment's data from the third-party service. - * + * * @param paymentSessionData - The `data` property of the payment. Make sure to store in it * any helpful identification for your third-party integration. * @returns An object to be stored in the payment's `data` property, or an error object. - * + * * @example * // other imports... - * import { - * PaymentProviderError, - * PaymentProviderSessionResponse, + * import { + * PaymentProviderError, + * PaymentProviderSessionResponse, * } from "@medusajs/types" - * - * + * + * * class MyPaymentProviderService extends AbstractPaymentProvider< * Options * > { @@ -510,7 +515,7 @@ export abstract class AbstractPaymentProvider> * PaymentProviderError | PaymentProviderSessionResponse["data"] * > { * const externalId = paymentSessionData.id - * + * * try { * return await this.client.retrieve(externalId) * } catch (e) { @@ -521,7 +526,7 @@ export abstract class AbstractPaymentProvider> * } * } * } - * + * * // ... * } */ @@ -531,34 +536,34 @@ export abstract class AbstractPaymentProvider> /** * Update a payment in the third-party service that was previously initiated with the {@link initiatePayment} method. - * + * * @param context - The details of the payment session and its context. * @returns An object whose `data` property is set in the updated payment session, or an error * object. Make sure to set in `data` anything useful to later retrieve the session. - * + * * @example * // other imports... - * import { + * import { * UpdatePaymentProviderSession, - * PaymentProviderError, - * PaymentProviderSessionResponse, + * PaymentProviderError, + * PaymentProviderSessionResponse, * } from "@medusajs/types" - * - * + * + * * class MyPaymentProviderService extends AbstractPaymentProvider< * Options * > { * async updatePayment( * context: UpdatePaymentProviderSession * ): Promise { - * const { - * amount, - * currency_code, + * const { + * amount, + * currency_code, * context: customerDetails, * data * } = context * const externalId = data.id - * + * * try { * const response = await this.client.update( * externalId, @@ -568,7 +573,7 @@ export abstract class AbstractPaymentProvider> * customerDetails * } * ) - * + * * return { * ...response, * data: { @@ -583,7 +588,7 @@ export abstract class AbstractPaymentProvider> * } * } * } - * + * * // ... * } */ @@ -594,24 +599,24 @@ export abstract class AbstractPaymentProvider> /** * This method is executed when a webhook event is received from the third-party payment provider. Use it * to process the action of the payment provider. - * + * * Learn more in [this documentation](https://docs.medusajs.com/v2/resources/commerce-modules/payment/webhook-events) - * + * * @param data - The webhook event's data * @returns The webhook result. If the `action`'s value is `captured`, the payment is captured within Medusa as well. * If the `action`'s value is `authorized`, the associated payment session is authorized within Medusa. - * + * * @example * // other imports... - * import { + * import { * BigNumber * } from "@medusajs/utils" - * import { + * import { * ProviderWebhookPayload, * WebhookActionResult * } from "@medusajs/types" - * - * + * + * * class MyPaymentProviderService extends AbstractPaymentProvider< * Options * > { @@ -623,7 +628,7 @@ export abstract class AbstractPaymentProvider> * rawData, * headers * } = payload - * + * * try { * switch(data.event_type) { * case "authorized_amount": @@ -657,7 +662,7 @@ export abstract class AbstractPaymentProvider> * } * } * } - * + * * // ... * } */ diff --git a/packages/modules/providers/auth-google/integration-tests/__tests__/services.spec.ts b/packages/modules/providers/auth-google/integration-tests/__tests__/services.spec.ts index 60b54bc21a5d1..8516f10e8c366 100644 --- a/packages/modules/providers/auth-google/integration-tests/__tests__/services.spec.ts +++ b/packages/modules/providers/auth-google/integration-tests/__tests__/services.spec.ts @@ -1,9 +1,10 @@ import { generateJwtToken, MedusaError } from "@medusajs/utils" import { GoogleAuthService } from "../../src/services/google" -jest.setTimeout(100000) import { http, HttpResponse } from "msw" import { setupServer } from "msw/node" +jest.setTimeout(100000) + const sampleIdPayload = { iss: "https://accounts.google.com", azp: "199301612397-l1lrg08vd6dvu98r43l7ul0ri2rd2b6r.apps.googleusercontent.com", @@ -97,15 +98,10 @@ describe("Google auth provider", () => { it("throw an error if required options are not passed", async () => { let msg = "" try { - new GoogleAuthService( - { - logger: console as any, - }, - { - clientID: "test", - clientSecret: "test", - } as any - ) + GoogleAuthService.validateOptions({ + clientID: "test", + clientSecret: "test", + } as any) } catch (e) { msg = e.message } diff --git a/packages/modules/providers/auth-google/src/services/google.ts b/packages/modules/providers/auth-google/src/services/google.ts index 05cf5e1498e52..1c2ac7de4b020 100644 --- a/packages/modules/providers/auth-google/src/services/google.ts +++ b/packages/modules/providers/auth-google/src/services/google.ts @@ -19,12 +19,25 @@ export class GoogleAuthService extends AbstractAuthModuleProvider { protected config_: LocalServiceConfig protected logger_: Logger + static validateOptions(options: GoogleAuthProviderOptions) { + if (!options.clientID) { + throw new Error("Google clientID is required") + } + + if (!options.clientSecret) { + throw new Error("Google clientSecret is required") + } + + if (!options.callbackURL) { + throw new Error("Google callbackUrl is required") + } + } + constructor( { logger }: InjectedDependencies, options: GoogleAuthProviderOptions ) { super({}, { provider: "google", displayName: "Google Authentication" }) - this.validateConfig(options) this.config_ = options this.logger_ = logger } @@ -173,18 +186,4 @@ export class GoogleAuthService extends AbstractAuthModuleProvider { return { success: true, location: authUrl.toString() } } - - private validateConfig(config: LocalServiceConfig) { - if (!config.clientID) { - throw new Error("Google clientID is required") - } - - if (!config.clientSecret) { - throw new Error("Google clientSecret is required") - } - - if (!config.callbackURL) { - throw new Error("Google callbackUrl is required") - } - } } diff --git a/packages/modules/providers/payment-stripe/src/core/stripe-base.ts b/packages/modules/providers/payment-stripe/src/core/stripe-base.ts index e0ee2e3f76355..0619ce8aa4b9b 100644 --- a/packages/modules/providers/payment-stripe/src/core/stripe-base.ts +++ b/packages/modules/providers/payment-stripe/src/core/stripe-base.ts @@ -13,12 +13,12 @@ import { } from "@medusajs/types" import { AbstractPaymentProvider, - MedusaError, - PaymentActions, - PaymentSessionStatus, isDefined, isPaymentProviderError, isPresent, + MedusaError, + PaymentActions, + PaymentSessionStatus, } from "@medusajs/utils" import { ErrorCodes, @@ -36,6 +36,12 @@ abstract class StripeBase extends AbstractPaymentProvider { protected stripe_: Stripe protected container_: MedusaContainer + static validateOptions(options: StripeOptions): void { + if (!isDefined(options.apiKey)) { + throw new Error("Required option `apiKey` is missing in Stripe plugin") + } + } + protected constructor(container: MedusaContainer, options: StripeOptions) { // @ts-ignore super(...arguments) @@ -43,23 +49,11 @@ abstract class StripeBase extends AbstractPaymentProvider { this.container_ = container this.options_ = options - this.stripe_ = this.init() - } - - protected init() { - this.validateOptions(this.config) - - return new Stripe(this.config.apiKey) + this.stripe_ = new Stripe(options.apiKey) } abstract get paymentIntentOptions(): PaymentIntentOptions - private validateOptions(options: StripeOptions): void { - if (!isDefined(options.apiKey)) { - throw new Error("Required option `apiKey` is missing in Stripe plugin") - } - } - get options(): StripeOptions { return this.options_ } @@ -370,7 +364,7 @@ abstract class StripeBase extends AbstractPaymentProvider { return this.stripe_.webhooks.constructEvent( data.rawData as string | Buffer, signature, - this.config.webhookSecret + this.options_.webhookSecret ) } protected buildError(