Feat: Add groups to list customer (#1113)
* add controllers directory and loaders * update integration tests * start groups * move controllers to pure functions * group filtering for customers * print errors * remove verbose flag * controller refactor * update imports * Feat/list customers by customer group (#1114) * move controllers to pure functions * add customer group test * add api for list customer group customers * remove unused imports * controller refactor * update imports * Update packages/medusa/src/api/routes/admin/customer-groups/get-customer-group-customers.ts Co-authored-by: Sebastian Rindom <skrindom@gmail.com> Co-authored-by: Sebastian Rindom <skrindom@gmail.com> * fix: dedupe Co-authored-by: Sebastian Rindom <skrindom@gmail.com>
This commit is contained in:
co-authored by
Sebastian Rindom
parent
8e7de6dd36
commit
dacc9c6c4b
@@ -193,6 +193,53 @@ describe("/admin/customer-groups", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("GET /admin/customer-groups/{id}/customers", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
try {
|
||||||
|
await adminSeeder(dbConnection)
|
||||||
|
await customerSeeder(dbConnection)
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("lists customers in group and count", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.get("/admin/customer-groups/test-group-5/customers", {
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.count).toEqual(3)
|
||||||
|
expect(response.data.customers).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "test-customer-5",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "test-customer-6",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "test-customer-7",
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("POST /admin/customer-groups/{id}/customers/batch", () => {
|
describe("POST /admin/customer-groups/{id}/customers/batch", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -75,6 +75,36 @@ describe("/admin/customers", () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("lists customers in group and count", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.get("/admin/customers?groups[]=test-group-5", {
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.count).toEqual(3)
|
||||||
|
expect(response.data.customers).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "test-customer-5",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "test-customer-6",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "test-customer-7",
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it("lists customers with specific query", async () => {
|
it("lists customers with specific query", async () => {
|
||||||
const api = useApi()
|
const api = useApi()
|
||||||
|
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ describe("/admin/tax-rates", () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test.only("fails with 404 on unknown rate", async () => {
|
test("fails with 404 on unknown rate", async () => {
|
||||||
await adminSeeder(dbConnection)
|
await adminSeeder(dbConnection)
|
||||||
const { tax_rates } = await createTaxRates(dbConnection, 1, 1, 200)
|
const { tax_rates } = await createTaxRates(dbConnection, 1, 1, 200)
|
||||||
const [rate] = tax_rates
|
const [rate] = tax_rates
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import CustomerController from "../../../../controllers/customers"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @oas [get] /customer-groups/{id}/customers
|
||||||
|
* operationId: "GetCustomerGroupsGroupCustomers"
|
||||||
|
* summary: "List Customers"
|
||||||
|
* description: "Retrieves a list of Customers."
|
||||||
|
* x-authenticated: true
|
||||||
|
* tags:
|
||||||
|
* - Customer
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: OK
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* properties:
|
||||||
|
* customer:
|
||||||
|
* $ref: "#/components/schemas/customer"
|
||||||
|
*/
|
||||||
|
export default async (req, res) => {
|
||||||
|
const { id } = req.params
|
||||||
|
|
||||||
|
req.query.groups = [id]
|
||||||
|
|
||||||
|
const result = await CustomerController.listAndCount(
|
||||||
|
req.scope,
|
||||||
|
req.query,
|
||||||
|
req.body
|
||||||
|
)
|
||||||
|
|
||||||
|
res.json(result)
|
||||||
|
}
|
||||||
@@ -24,6 +24,12 @@ export default (app) => {
|
|||||||
"/:id",
|
"/:id",
|
||||||
middlewares.wrap(require("./delete-customer-group").default)
|
middlewares.wrap(require("./delete-customer-group").default)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
route.get(
|
||||||
|
"/:id/customers",
|
||||||
|
middlewares.wrap(require("./get-customer-group-customers").default)
|
||||||
|
)
|
||||||
|
|
||||||
route.post(
|
route.post(
|
||||||
"/:id",
|
"/:id",
|
||||||
middlewares.wrap(require("./update-customer-group").default)
|
middlewares.wrap(require("./update-customer-group").default)
|
||||||
|
|||||||
@@ -8,7 +8,11 @@ const route = Router()
|
|||||||
export default (app) => {
|
export default (app) => {
|
||||||
app.use("/customers", route)
|
app.use("/customers", route)
|
||||||
|
|
||||||
route.get("/", middlewares.wrap(require("./list-customers").default))
|
route.get(
|
||||||
|
"/",
|
||||||
|
middlewares.normalizeQuery(),
|
||||||
|
middlewares.wrap(require("./list-customers").default)
|
||||||
|
)
|
||||||
route.get("/:id", middlewares.wrap(require("./get-customer").default))
|
route.get("/:id", middlewares.wrap(require("./get-customer").default))
|
||||||
|
|
||||||
route.post("/", middlewares.wrap(require("./create-customer").default))
|
route.post("/", middlewares.wrap(require("./create-customer").default))
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export class AdminGetCustomersParams extends AdminListCustomerSelector {
|
|||||||
@Type(() => Number)
|
@Type(() => Number)
|
||||||
limit = 50
|
limit = 50
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@Type(() => Number)
|
@Type(() => Number)
|
||||||
|
|||||||
@@ -2,4 +2,28 @@ import { EntityRepository, Repository } from "typeorm"
|
|||||||
import { Customer } from "../models/customer"
|
import { Customer } from "../models/customer"
|
||||||
|
|
||||||
@EntityRepository(Customer)
|
@EntityRepository(Customer)
|
||||||
export class CustomerRepository extends Repository<Customer> {}
|
export class CustomerRepository extends Repository<Customer> {
|
||||||
|
async listAndCount(query): Promise<[Customer[], number]> {
|
||||||
|
const groups = query?.where?.groups
|
||||||
|
delete query?.where?.groups
|
||||||
|
|
||||||
|
let qb = this.createQueryBuilder("customer")
|
||||||
|
.where(query.where)
|
||||||
|
.skip(query.skip)
|
||||||
|
.take(query.take)
|
||||||
|
|
||||||
|
if (groups) {
|
||||||
|
qb = qb
|
||||||
|
.leftJoinAndSelect("customer.groups", "group")
|
||||||
|
.andWhere(`group.id IN (:...ids)`, { ids: groups.value })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.relations?.length) {
|
||||||
|
query.relations.forEach((rel) => {
|
||||||
|
qb = qb.leftJoinAndSelect(`customer.${rel}`, rel)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return await qb.getManyAndCount()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -199,7 +199,6 @@ class CustomerService extends BaseService {
|
|||||||
|
|
||||||
query.where = (qb) => {
|
query.where = (qb) => {
|
||||||
qb.where(where)
|
qb.where(where)
|
||||||
|
|
||||||
qb.andWhere(
|
qb.andWhere(
|
||||||
new Brackets((qb) => {
|
new Brackets((qb) => {
|
||||||
qb.where({ email: ILike(`%${q}%`) })
|
qb.where({ email: ILike(`%${q}%`) })
|
||||||
@@ -210,8 +209,7 @@ class CustomerService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const [customers, count] = await customerRepo.findAndCount(query)
|
return await customerRepo.listAndCount(query)
|
||||||
return [customers, count]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,4 +4,8 @@ export class AdminListCustomerSelector {
|
|||||||
@IsString()
|
@IsString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
q?: string
|
q?: string
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString({ each: true })
|
||||||
|
groups?: string[]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user