From 87390704be5f3e82be08e1d8665102c29d357910 Mon Sep 17 00:00:00 2001 From: Sebastian Rindom Date: Mon, 29 Jan 2024 17:56:39 +0100 Subject: [PATCH] feat(customer): list customer (#6206) **What** - GET /admin/customers - GET /admin/customer-groups --- .../admin/list-customer-groups.spec.ts | 63 ++++++++ .../customer/admin/list-customers.spec.ts | 119 ++++++++++++++ integration-tests/plugins/medusa-config.js | 5 + integration-tests/plugins/package.json | 1 + packages/customer/tsconfig.json | 2 +- .../admin/customer-groups/middlewares.ts | 52 +++++++ .../admin/customer-groups/query-config.ts | 21 +++ .../src/api-v2/admin/customer-groups/route.ts | 24 +++ .../admin/customer-groups/validators.ts | 114 ++++++++++++++ .../src/api-v2/admin/customers/middlewares.ts | 46 ++++++ .../api-v2/admin/customers/query-config.ts | 29 ++++ .../src/api-v2/admin/customers/route.ts | 41 +++++ .../src/api-v2/admin/customers/validators.ts | 146 ++++++++++++++++++ packages/medusa/src/api-v2/middlewares.ts | 4 + .../src/types/validators/operator-map.ts | 67 ++++++++ packages/types/src/dal/index.ts | 4 +- yarn.lock | 3 +- 17 files changed, 737 insertions(+), 4 deletions(-) create mode 100644 integration-tests/plugins/__tests__/customer/admin/list-customer-groups.spec.ts create mode 100644 integration-tests/plugins/__tests__/customer/admin/list-customers.spec.ts create mode 100644 packages/medusa/src/api-v2/admin/customer-groups/middlewares.ts create mode 100644 packages/medusa/src/api-v2/admin/customer-groups/query-config.ts create mode 100644 packages/medusa/src/api-v2/admin/customer-groups/route.ts create mode 100644 packages/medusa/src/api-v2/admin/customer-groups/validators.ts create mode 100644 packages/medusa/src/api-v2/admin/customers/middlewares.ts create mode 100644 packages/medusa/src/api-v2/admin/customers/query-config.ts create mode 100644 packages/medusa/src/api-v2/admin/customers/route.ts create mode 100644 packages/medusa/src/api-v2/admin/customers/validators.ts create mode 100644 packages/medusa/src/types/validators/operator-map.ts diff --git a/integration-tests/plugins/__tests__/customer/admin/list-customer-groups.spec.ts b/integration-tests/plugins/__tests__/customer/admin/list-customer-groups.spec.ts new file mode 100644 index 0000000000..538caa7cbf --- /dev/null +++ b/integration-tests/plugins/__tests__/customer/admin/list-customer-groups.spec.ts @@ -0,0 +1,63 @@ +import { ModuleRegistrationName } from "@medusajs/modules-sdk" +import { ICustomerModuleService } from "@medusajs/types" +import path from "path" +import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app" +import { useApi } from "../../../../environment-helpers/use-api" +import { getContainer } from "../../../../environment-helpers/use-container" +import { initDb, useDb } from "../../../../environment-helpers/use-db" +import adminSeeder from "../../../../helpers/admin-seeder" + +const env = { MEDUSA_FF_MEDUSA_V2: true } +const adminHeaders = { + headers: { "x-medusa-access-token": "test_token" }, +} + +describe("GET /admin/customer-groups", () => { + let dbConnection + let appContainer + let shutdownServer + let customerModuleService: ICustomerModuleService + + beforeAll(async () => { + const cwd = path.resolve(path.join(__dirname, "..", "..", "..")) + dbConnection = await initDb({ cwd, env } as any) + shutdownServer = await startBootstrapApp({ cwd, env }) + appContainer = getContainer() + customerModuleService = appContainer.resolve( + ModuleRegistrationName.CUSTOMER + ) + }) + + afterAll(async () => { + const db = useDb() + await db.shutdown() + await shutdownServer() + }) + + beforeEach(async () => { + await adminSeeder(dbConnection) + }) + + afterEach(async () => { + const db = useDb() + await db.teardown() + }) + + it("should get all customer groups and its count", async () => { + await customerModuleService.createCustomerGroup({ + name: "Test", + }) + + const api = useApi() as any + const response = await api.get(`/admin/customer-groups`, adminHeaders) + + expect(response.status).toEqual(200) + expect(response.data.count).toEqual(1) + expect(response.data.groups).toEqual([ + expect.objectContaining({ + id: expect.any(String), + name: "Test", + }), + ]) + }) +}) diff --git a/integration-tests/plugins/__tests__/customer/admin/list-customers.spec.ts b/integration-tests/plugins/__tests__/customer/admin/list-customers.spec.ts new file mode 100644 index 0000000000..a5c0c47d54 --- /dev/null +++ b/integration-tests/plugins/__tests__/customer/admin/list-customers.spec.ts @@ -0,0 +1,119 @@ +import { ModuleRegistrationName } from "@medusajs/modules-sdk" +import { ICustomerModuleService } from "@medusajs/types" +import path from "path" +import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app" +import { useApi } from "../../../../environment-helpers/use-api" +import { getContainer } from "../../../../environment-helpers/use-container" +import { initDb, useDb } from "../../../../environment-helpers/use-db" +import adminSeeder from "../../../../helpers/admin-seeder" + +const env = { MEDUSA_FF_MEDUSA_V2: true } +const adminHeaders = { + headers: { "x-medusa-access-token": "test_token" }, +} + +describe("GET /admin/customers", () => { + let dbConnection + let appContainer + let shutdownServer + let customerModuleService: ICustomerModuleService + + beforeAll(async () => { + const cwd = path.resolve(path.join(__dirname, "..", "..", "..")) + dbConnection = await initDb({ cwd, env } as any) + shutdownServer = await startBootstrapApp({ cwd, env }) + appContainer = getContainer() + customerModuleService = appContainer.resolve( + ModuleRegistrationName.CUSTOMER + ) + }) + + afterAll(async () => { + const db = useDb() + await db.shutdown() + await shutdownServer() + }) + + beforeEach(async () => { + await adminSeeder(dbConnection) + }) + + afterEach(async () => { + const db = useDb() + await db.teardown() + }) + + it("should get all customers and its count", async () => { + await customerModuleService.create([ + { + first_name: "Test", + last_name: "Test", + email: "test@me.com", + }, + ]) + + const api = useApi() as any + const response = await api.get(`/admin/customers`, adminHeaders) + + expect(response.status).toEqual(200) + expect(response.data.count).toEqual(1) + expect(response.data.customers).toEqual([ + expect.objectContaining({ + id: expect.any(String), + first_name: "Test", + last_name: "Test", + email: "test@me.com", + }), + ]) + }) + + it("should filter customers by last name", async () => { + await customerModuleService.create([ + { + first_name: "Jane", + last_name: "Doe", + email: "jane@me.com", + }, + { + first_name: "John", + last_name: "Doe", + email: "john@me.com", + }, + { + first_name: "LeBron", + last_name: "James", + email: "lebron@me.com", + }, + { + first_name: "John", + last_name: "Silver", + email: "johns@me.com", + }, + ]) + + const api = useApi() as any + const response = await api.get( + `/admin/customers?last_name=Doe`, + adminHeaders + ) + + expect(response.status).toEqual(200) + expect(response.data.count).toEqual(2) + expect(response.data.customers).toContainEqual( + expect.objectContaining({ + id: expect.any(String), + first_name: "Jane", + last_name: "Doe", + email: "jane@me.com", + }) + ) + expect(response.data.customers).toContainEqual( + expect.objectContaining({ + id: expect.any(String), + first_name: "John", + last_name: "Doe", + email: "john@me.com", + }) + ) + }) +}) diff --git a/integration-tests/plugins/medusa-config.js b/integration-tests/plugins/medusa-config.js index 843f7264ed..2088ea4544 100644 --- a/integration-tests/plugins/medusa-config.js +++ b/integration-tests/plugins/medusa-config.js @@ -71,6 +71,11 @@ module.exports = { resources: "shared", resolve: "@medusajs/promotion", }, + [Modules.CUSTOMER]: { + scope: "internal", + resources: "shared", + resolve: "@medusajs/customer", + }, [Modules.SALES_CHANNEL]: { scope: "internal", resources: "shared", diff --git a/integration-tests/plugins/package.json b/integration-tests/plugins/package.json index b2b3a47a4b..74a9ec209c 100644 --- a/integration-tests/plugins/package.json +++ b/integration-tests/plugins/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@medusajs/cache-inmemory": "workspace:*", + "@medusajs/customer": "workspace:^", "@medusajs/event-bus-local": "workspace:*", "@medusajs/inventory": "workspace:^", "@medusajs/medusa": "workspace:*", diff --git a/packages/customer/tsconfig.json b/packages/customer/tsconfig.json index 6143fb1ef3..4b79cd6032 100644 --- a/packages/customer/tsconfig.json +++ b/packages/customer/tsconfig.json @@ -23,7 +23,7 @@ "@models": ["./src/models"], "@services": ["./src/services"], "@repositories": ["./src/repositories"], - "@types": ["./src/types"], + "@types": ["./src/types"] } }, "include": ["src"], diff --git a/packages/medusa/src/api-v2/admin/customer-groups/middlewares.ts b/packages/medusa/src/api-v2/admin/customer-groups/middlewares.ts new file mode 100644 index 0000000000..d1ca416a9f --- /dev/null +++ b/packages/medusa/src/api-v2/admin/customer-groups/middlewares.ts @@ -0,0 +1,52 @@ +import { MedusaV2Flag } from "@medusajs/utils" + +import { + isFeatureFlagEnabled, + transformBody, + transformQuery, +} from "../../../api/middlewares" +import { MiddlewareRoute } from "../../../loaders/helpers/routing/types" +import * as QueryConfig from "./query-config" +import { + AdminGetCustomerGroupsParams, + AdminGetCustomerGroupsGroupParams, + AdminPostCustomerGroupsReq, + AdminPostCustomerGroupsGroupReq, +} from "./validators" + +export const adminCustomerGroupRoutesMiddlewares: MiddlewareRoute[] = [ + { + matcher: "/admin/customer-groups*", + middlewares: [isFeatureFlagEnabled(MedusaV2Flag.key)], + }, + { + method: ["GET"], + matcher: "/admin/customer-groups", + middlewares: [ + transformQuery( + AdminGetCustomerGroupsParams, + QueryConfig.listTransformQueryConfig + ), + ], + }, + { + method: ["GET"], + matcher: "/admin/customer-groups/:id", + middlewares: [ + transformQuery( + AdminGetCustomerGroupsGroupParams, + QueryConfig.retrieveTransformQueryConfig + ), + ], + }, + { + method: ["POST"], + matcher: "/admin/customer-groups", + middlewares: [transformBody(AdminPostCustomerGroupsReq)], + }, + { + method: ["POST"], + matcher: "/admin/customer-groups/:id", + middlewares: [transformBody(AdminPostCustomerGroupsGroupReq)], + }, +] diff --git a/packages/medusa/src/api-v2/admin/customer-groups/query-config.ts b/packages/medusa/src/api-v2/admin/customer-groups/query-config.ts new file mode 100644 index 0000000000..db9aac9698 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/customer-groups/query-config.ts @@ -0,0 +1,21 @@ +export const defaultAdminCustomerGroupRelations = [] +export const allowedAdminCustomerGroupRelations = ["customers"] +export const defaultAdminCustomerGroupFields = [ + "id", + "name", + "created_at", + "updated_at", + "deleted_at", +] + +export const retrieveTransformQueryConfig = { + defaultFields: defaultAdminCustomerGroupFields, + defaultRelations: defaultAdminCustomerGroupRelations, + allowedRelations: allowedAdminCustomerGroupRelations, + isList: false, +} + +export const listTransformQueryConfig = { + ...retrieveTransformQueryConfig, + isList: true, +} diff --git a/packages/medusa/src/api-v2/admin/customer-groups/route.ts b/packages/medusa/src/api-v2/admin/customer-groups/route.ts new file mode 100644 index 0000000000..03cbdd4728 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/customer-groups/route.ts @@ -0,0 +1,24 @@ +import { ModuleRegistrationName } from "@medusajs/modules-sdk" +import { ICustomerModuleService } from "@medusajs/types" +import { MedusaRequest, MedusaResponse } from "../../../types/routing" + +export const GET = async (req: MedusaRequest, res: MedusaResponse) => { + const customerModuleService = req.scope.resolve( + ModuleRegistrationName.CUSTOMER + ) + + const [groups, count] = + await customerModuleService.listAndCountCustomerGroups( + req.filterableFields, + req.listConfig + ) + + const { offset, limit } = req.validatedQuery + + res.json({ + count, + groups, + offset, + limit, + }) +} diff --git a/packages/medusa/src/api-v2/admin/customer-groups/validators.ts b/packages/medusa/src/api-v2/admin/customer-groups/validators.ts new file mode 100644 index 0000000000..a0f5a24283 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/customer-groups/validators.ts @@ -0,0 +1,114 @@ +import { OperatorMap } from "@medusajs/types" +import { Type } from "class-transformer" +import { + IsNotEmpty, + IsOptional, + IsString, + ValidateNested, +} from "class-validator" +import { FindParams, extendedFindParamsMixin } from "../../../types/common" +import { OperatorMapValidator } from "../../../types/validators/operator-map" + +export class AdminGetCustomerGroupsGroupParams extends FindParams {} + +class FilterableCustomerPropsValidator { + @IsOptional() + @IsString({ each: true }) + id?: string | string[] + + @IsOptional() + @ValidateNested({ each: true }) + @Type(() => OperatorMapValidator) + email?: string | string[] | OperatorMap + + @IsOptional() + @IsString({ each: true }) + default_billing_address_id?: string | string[] | null + + @IsOptional() + @IsString({ each: true }) + default_shipping_address_id?: string | string[] | null + + @IsOptional() + @IsString({ each: true }) + company_name?: string | string[] | OperatorMap | null + + @IsOptional() + @IsString({ each: true }) + first_name?: string | string[] | OperatorMap | null + + @IsOptional() + @IsString({ each: true }) + last_name?: string | string[] | OperatorMap | null + + @IsOptional() + @IsString({ each: true }) + created_by?: string | string[] | null + + @IsOptional() + @ValidateNested() + @Type(() => OperatorMapValidator) + created_at?: OperatorMap + + @IsOptional() + @ValidateNested() + @Type(() => OperatorMapValidator) + updated_at?: OperatorMap +} + +export class AdminGetCustomerGroupsParams extends extendedFindParamsMixin({ + limit: 100, + offset: 0, +}) { + @IsOptional() + @IsString({ each: true }) + id?: string | string[] + + @IsOptional() + @ValidateNested({ each: true }) + @Type(() => OperatorMapValidator) + name?: string | OperatorMap + + @IsOptional() + @ValidateNested() + @Type(() => FilterableCustomerPropsValidator) + customers?: FilterableCustomerPropsValidator | string | string[] + + @IsOptional() + @IsString({ each: true }) + created_by?: string | string[] | null + + @IsOptional() + @ValidateNested() + @Type(() => OperatorMapValidator) + created_at?: OperatorMap + + @IsOptional() + @ValidateNested() + @Type(() => OperatorMapValidator) + updated_at?: OperatorMap + + // Additional filters from BaseFilterable + @IsOptional() + @ValidateNested({ each: true }) + @Type(() => AdminGetCustomerGroupsParams) + $and?: AdminGetCustomerGroupsParams[] + + @IsOptional() + @ValidateNested({ each: true }) + @Type(() => AdminGetCustomerGroupsParams) + $or?: AdminGetCustomerGroupsParams[] +} + +export class AdminPostCustomerGroupsReq { + @IsNotEmpty() + @IsString() + name: string +} + +export class AdminPostCustomerGroupsGroupReq { + @IsNotEmpty() + @IsString() + @IsOptional() + name?: string +} diff --git a/packages/medusa/src/api-v2/admin/customers/middlewares.ts b/packages/medusa/src/api-v2/admin/customers/middlewares.ts new file mode 100644 index 0000000000..962b1a4529 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/customers/middlewares.ts @@ -0,0 +1,46 @@ +import { MedusaV2Flag } from "@medusajs/utils" + +import { + isFeatureFlagEnabled, + transformBody, + transformQuery, +} from "../../../api/middlewares" +import { MiddlewareRoute } from "../../../loaders/helpers/routing/types" +import * as QueryConfig from "./query-config" +import { + AdminGetCustomersParams, + AdminGetCustomersCustomerParams, + AdminPostCustomersCustomerReq, +} from "./validators" + +export const adminCustomerRoutesMiddlewares: MiddlewareRoute[] = [ + { + matcher: "/admin/customers*", + middlewares: [isFeatureFlagEnabled(MedusaV2Flag.key)], + }, + { + method: ["GET"], + matcher: "/admin/customers", + middlewares: [ + transformQuery( + AdminGetCustomersParams, + QueryConfig.listTransformQueryConfig + ), + ], + }, + { + method: ["GET"], + matcher: "/admin/customers/:id", + middlewares: [ + transformQuery( + AdminGetCustomersCustomerParams, + QueryConfig.retrieveTransformQueryConfig + ), + ], + }, + { + method: ["POST"], + matcher: "/admin/customers/:id", + middlewares: [transformBody(AdminPostCustomersCustomerReq)], + }, +] diff --git a/packages/medusa/src/api-v2/admin/customers/query-config.ts b/packages/medusa/src/api-v2/admin/customers/query-config.ts new file mode 100644 index 0000000000..3702bea765 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/customers/query-config.ts @@ -0,0 +1,29 @@ +export const defaultAdminCustomerRelations = [] +export const allowedAdminCustomerRelations = [ + "groups", + "default_shipping_address", + "default_billing_address", + "addresses", +] +export const defaultAdminCustomerFields = [ + "id", + "company_name", + "first_name", + "last_name", + "email", + "created_at", + "updated_at", + "deleted_at", +] + +export const retrieveTransformQueryConfig = { + defaultFields: defaultAdminCustomerFields, + defaultRelations: defaultAdminCustomerRelations, + allowedRelations: allowedAdminCustomerRelations, + isList: false, +} + +export const listTransformQueryConfig = { + ...retrieveTransformQueryConfig, + isList: true, +} diff --git a/packages/medusa/src/api-v2/admin/customers/route.ts b/packages/medusa/src/api-v2/admin/customers/route.ts new file mode 100644 index 0000000000..8be7d30536 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/customers/route.ts @@ -0,0 +1,41 @@ +import { ModuleRegistrationName } from "@medusajs/modules-sdk" +import { ICustomerModuleService } from "@medusajs/types" +import { MedusaRequest, MedusaResponse } from "../../../types/routing" + +export const GET = async (req: MedusaRequest, res: MedusaResponse) => { + const customerModuleService = req.scope.resolve( + ModuleRegistrationName.CUSTOMER + ) + + const [customers, count] = await customerModuleService.listAndCount( + req.filterableFields, + req.listConfig + ) + + const { offset, limit } = req.validatedQuery + + // TODO: Replace with remote query + //const remoteQuery = req.scope.resolve("remoteQuery") + + //const variables = { + // filters: req.filterableFields, + // order: req.listConfig.order, + // skip: req.listConfig.skip, + // take: req.listConfig.take, + //} + + //const query = remoteQueryObjectFromString({ + // entryPoint: "customer", + // variables, + // fields: [...req.listConfig.select!, ...req.listConfig.relations!], + //}) + + //const results = await remoteQuery(query) + + res.json({ + count, + customers, + offset, + limit, + }) +} diff --git a/packages/medusa/src/api-v2/admin/customers/validators.ts b/packages/medusa/src/api-v2/admin/customers/validators.ts new file mode 100644 index 0000000000..4f1fda5ca0 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/customers/validators.ts @@ -0,0 +1,146 @@ +import { OperatorMap } from "@medusajs/types" +import { Transform, Type } from "class-transformer" +import { + IsNotEmpty, + IsOptional, + IsString, + ValidateNested, +} from "class-validator" +import { FindParams, extendedFindParamsMixin } from "../../../types/common" +import { OperatorMapValidator } from "../../../types/validators/operator-map" +import { IsType } from "../../../utils" + +export class AdminGetCustomersCustomerParams extends FindParams {} + +export class AdminGetCustomersParams extends extendedFindParamsMixin({ + limit: 100, + offset: 0, +}) { + @IsOptional() + @IsString({ each: true }) + id?: string | string[] + + @IsOptional() + @IsType([String, [String], OperatorMapValidator]) + email?: string | string[] | OperatorMap + + @IsOptional() + @ValidateNested() + @Type(() => FilterableCustomerGroupPropsValidator) + groups?: FilterableCustomerGroupPropsValidator | string | string[] + + @IsOptional() + @IsString({ each: true }) + default_billing_address_id?: string | string[] | null + + @IsOptional() + @IsString({ each: true }) + default_shipping_address_id?: string | string[] | null + + @IsOptional() + @IsString({ each: true }) + company_name?: string | string[] | OperatorMap | null + + @IsOptional() + @IsString({ each: true }) + first_name?: string | string[] | OperatorMap | null + + @IsOptional() + @IsType([String, [String], OperatorMapValidator]) + @Transform(({ value }) => (value === "null" ? null : value)) + last_name?: string | string[] | OperatorMap | null + + @IsOptional() + @IsString({ each: true }) + created_by?: string | string[] | null + + @IsOptional() + @ValidateNested() + @Type(() => OperatorMapValidator) + created_at?: OperatorMap + + @IsOptional() + @ValidateNested() + @Type(() => OperatorMapValidator) + updated_at?: OperatorMap + + // Additional filters from BaseFilterable + @IsOptional() + @ValidateNested({ each: true }) + @Type(() => AdminGetCustomersParams) + $and?: AdminGetCustomersParams[] + + @IsOptional() + @ValidateNested({ each: true }) + @Type(() => AdminGetCustomersParams) + $or?: AdminGetCustomersParams[] +} + +class FilterableCustomerGroupPropsValidator { + @IsOptional() + @IsString({ each: true }) + id?: string | string[] + + @IsOptional() + @ValidateNested({ each: true }) + @Type(() => OperatorMapValidator) + name?: string | OperatorMap + + @IsOptional() + @IsString({ each: true }) + created_by?: string | string[] | null + + @IsOptional() + @ValidateNested() + @Type(() => OperatorMapValidator) + created_at?: OperatorMap + + @IsOptional() + @ValidateNested() + @Type(() => OperatorMapValidator) + updated_at?: OperatorMap +} + +export class AdminPostCustomersReq { + @IsNotEmpty() + @IsString() + @IsOptional() + company_name?: string + + @IsNotEmpty() + @IsString() + @IsOptional() + first_name?: string + + @IsNotEmpty() + @IsString() + @IsOptional() + last_name?: string + + @IsNotEmpty() + @IsString() + @IsOptional() + email?: string +} + +export class AdminPostCustomersCustomerReq { + @IsNotEmpty() + @IsString() + @IsOptional() + company_name?: string + + @IsNotEmpty() + @IsString() + @IsOptional() + first_name?: string + + @IsNotEmpty() + @IsString() + @IsOptional() + last_name?: string + + @IsNotEmpty() + @IsString() + @IsOptional() + email?: string +} diff --git a/packages/medusa/src/api-v2/middlewares.ts b/packages/medusa/src/api-v2/middlewares.ts index 9c6ec41da5..59b5884fed 100644 --- a/packages/medusa/src/api-v2/middlewares.ts +++ b/packages/medusa/src/api-v2/middlewares.ts @@ -1,9 +1,13 @@ import { MiddlewaresConfig } from "../loaders/helpers/routing/types" import { adminCampaignRoutesMiddlewares } from "./admin/campaigns/middlewares" import { adminPromotionRoutesMiddlewares } from "./admin/promotions/middlewares" +import { adminCustomerRoutesMiddlewares } from "./admin/customers/middlewares" +import { adminCustomerGroupRoutesMiddlewares } from "./admin/customer-groups/middlewares" export const config: MiddlewaresConfig = { routes: [ + ...adminCustomerGroupRoutesMiddlewares, + ...adminCustomerRoutesMiddlewares, ...adminPromotionRoutesMiddlewares, ...adminCampaignRoutesMiddlewares, ], diff --git a/packages/medusa/src/types/validators/operator-map.ts b/packages/medusa/src/types/validators/operator-map.ts new file mode 100644 index 0000000000..82f50cfc73 --- /dev/null +++ b/packages/medusa/src/types/validators/operator-map.ts @@ -0,0 +1,67 @@ +import { IsArray, IsBoolean, IsOptional, IsString } from "class-validator" + +export class OperatorMapValidator { + @IsOptional() + @IsString() + $eq?: string | string[] + + @IsOptional() + @IsString() + $ne?: string + + @IsOptional() + @IsArray() + $in?: string[] + + @IsOptional() + @IsArray() + $nin?: string[] + + @IsOptional() + @IsString() + $like?: string + + @IsOptional() + @IsString() + $re?: string + + @IsOptional() + @IsString() + $ilike?: string + + @IsOptional() + @IsString() + $fulltext?: string + + @IsOptional() + @IsArray() + $overlap?: string[] + + @IsOptional() + @IsString() + $contains?: string + + @IsOptional() + @IsArray() + $contained?: string[] + + @IsOptional() + @IsBoolean() + $exists?: boolean + + @IsOptional() + @IsString() + $gt?: string + + @IsOptional() + @IsString() + $gte?: string + + @IsOptional() + @IsString() + $lt?: string + + @IsOptional() + @IsString() + $lte?: string +} diff --git a/packages/types/src/dal/index.ts b/packages/types/src/dal/index.ts index 32531476c6..341a50dbee 100644 --- a/packages/types/src/dal/index.ts +++ b/packages/types/src/dal/index.ts @@ -1,10 +1,10 @@ import { Dictionary, FilterQuery, Order } from "./utils" -export { FilterQuery } from "./utils" +export { FilterQuery, OperatorMap } from "./utils" /** * @interface - * + * * An object used to allow specifying flexible queries with and/or conditions. */ export interface BaseFilterable { diff --git a/yarn.lock b/yarn.lock index 575604621c..7a53985a90 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8009,7 +8009,7 @@ __metadata: languageName: unknown linkType: soft -"@medusajs/customer@workspace:packages/customer": +"@medusajs/customer@workspace:^, @medusajs/customer@workspace:packages/customer": version: 0.0.0-use.local resolution: "@medusajs/customer@workspace:packages/customer" dependencies: @@ -31378,6 +31378,7 @@ __metadata: "@babel/core": ^7.12.10 "@babel/node": ^7.12.10 "@medusajs/cache-inmemory": "workspace:*" + "@medusajs/customer": "workspace:^" "@medusajs/event-bus-local": "workspace:*" "@medusajs/inventory": "workspace:^" "@medusajs/medusa": "workspace:*"