feat(medusa): Improve DX of validators extensions (#4397)

* feat(medusa): Allow to register extended validators seemlesly

* Create happy-points-yawn.md

* comment
This commit is contained in:
Adrien de Peretti
2023-06-26 11:05:23 +02:00
committed by GitHub
parent 579fcb8cd6
commit 01245ac89e
4 changed files with 76 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
---
"@medusajs/medusa": patch
---
feat(medusa): Allow to register extended validators seemlesly

View File

@@ -0,0 +1,40 @@
import { IsString } from "class-validator"
import { AdminPostProductsReq as MedusaAdminPostProductsReq } from "../../api/routes/admin/products/create-product"
import { registerOverriddenValidators, validator } from "../validator"
class AdminPostProductsReq extends MedusaAdminPostProductsReq {
@IsString()
custom_attribute: string
}
describe("Validator", function () {
it("should override the original validator", async function () {
let err = await validator(MedusaAdminPostProductsReq, {
title: "test",
})
.then(() => void 0)
.catch((err) => err)
expect(err).not.toBeDefined()
registerOverriddenValidators(AdminPostProductsReq)
err = await validator(MedusaAdminPostProductsReq, {
title: "test",
})
.then(() => void 0)
.catch((err) => err)
expect(err).toBeDefined()
expect(err.message).toEqual("custom_attribute must be a string")
err = await validator(MedusaAdminPostProductsReq, {
title: "test",
custom_attribute: "test",
})
.then(() => void 0)
.catch((err) => err)
expect(err).not.toBeDefined()
})
})

View File

@@ -13,3 +13,4 @@ export * from "./product-category"
export * from "./remove-undefined-properties"
export * from "./set-metadata"
export * from "./validate-id"
export { registerOverriddenValidators } from "./validator"

View File

@@ -1,6 +1,34 @@
import { ClassConstructor, plainToInstance } from "class-transformer"
import { validate, ValidationError, ValidatorOptions } from "class-validator"
import { MedusaError } from "medusa-core-utils"
import { Constructor } from "@medusajs/types"
const extendedValidators: Map<string, Constructor<any>> = new Map()
/**
* When overriding a validator, you can register it to be used instead of the original one.
* For example, the place where you are overriding the core validator, you can call this function
* @example
* ```ts
* // /src/api/routes/admin/products/create-product.ts
* import { registerOverriddenValidators } from "@medusajs/medusa"
* import { AdminPostProductsReq as MedusaAdminPostProductsReq } from "@medusajs/medusa/dist/api/routes/admin/products/create-product"
* import { IsString } from "class-validator"
*
* class AdminPostProductsReq extends MedusaAdminPostProductsReq {
* @IsString()
* test: string
* }
*
* registerOverriddenValidators(AdminPostProductsReq)
* ```
* @param extendedValidator
*/
export function registerOverriddenValidators(
extendedValidator: Constructor<any>
): void {
extendedValidators.set(extendedValidator.name, extendedValidator)
}
const reduceErrorMessages = (errs: ValidationError[]): string[] => {
return errs.reduce((acc: string[], next) => {
@@ -22,6 +50,8 @@ export async function validator<T, V>(
plain: V,
config: ValidatorOptions = {}
): Promise<T> {
typedClass = extendedValidators.get(typedClass.name) ?? typedClass
const toValidate = plainToInstance(typedClass, plain)
// @ts-ignore
const errors = await validate(toValidate, {