Feat: Bulk add customers to customer group (#1095)
* fix babel transform-runtime regenerator required for migrations * add customer group model * add migration for customer group * add customer group model export * add customer group repository * add customer group service * add CustomerGroupRepository to "withTransaction" in CustomerGroupService * remove unnecessary argument to runtime plugin * service export ordering * add create customer group endpoint * add customergroup to route index in admin * add customer group service * add customer groups test * cleanup * add customers batch initial * batch creation of customer groups * integration testing batch creation * integration tests * chaining existing customers creation in repo * remove commented test * update unit tests to reflect change in idempotent behavior * ensure that exceptions are expected * update idempotency behavior * update formatting * update format * Update packages/medusa/src/repositories/customer-group.ts Co-authored-by: Sebastian Rindom <skrindom@gmail.com> * pr feedback * add In import * add seperate model dto * add integration test * error handling in repository * remove unused import * jsdoc * Update packages/medusa/src/api/routes/admin/customer-groups/add-customers-batch.ts Co-authored-by: Sebastian Rindom <skrindom@gmail.com> * Update packages/medusa/src/api/routes/admin/customer-groups/add-customers-batch.ts Co-authored-by: Sebastian Rindom <skrindom@gmail.com> * pr review comments * rename variable * fix: adds atomic phase clean up callback * fix: call error handler in new transaction block too * restore * error handling * fix: error handler in no isolation case * add integration test for missing group on update * final adjustments to test * fix pr feedback * cleanup core for pr * remove console.log * remove customergroupservice test from customers * Apply suggestions from code review Co-authored-by: Sebastian Rindom <skrindom@gmail.com> * add end bracket to customer tests * remove comments * change model decorator * fix integration test merge * onDelete cascade instead of cascade:true * remove verbose flag * fix: dedupe type * add save to customer groups * customer model delete cascade * add await to asyncronous save operations Co-authored-by: Sebastian Rindom <skrindom@gmail.com>
This commit is contained in:
@@ -130,7 +130,7 @@ describe("/admin/customer-groups", () => {
|
||||
.catch((error) => {
|
||||
expect(error.response.data.type).toEqual("not_found")
|
||||
expect(error.response.data.message).toEqual(
|
||||
`CustomerGroup with ${id} was not found`
|
||||
`CustomerGroup with id ${id} was not found`
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -193,6 +193,182 @@ describe("/admin/customer-groups", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("POST /admin/customer-groups/{id}/customers/batch", () => {
|
||||
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("adds multiple customers to a group", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const payload = {
|
||||
customer_ids: [{ id: "test-customer-1" }, { id: "test-customer-2" }],
|
||||
}
|
||||
|
||||
const batchAddResponse = await api.post(
|
||||
"/admin/customer-groups/customer-group-1/customers/batch",
|
||||
payload,
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(batchAddResponse.status).toEqual(200)
|
||||
expect(batchAddResponse.data.customer_group).toEqual(
|
||||
expect.objectContaining({
|
||||
name: "vip-customers",
|
||||
})
|
||||
)
|
||||
|
||||
const getCustomerResponse = await api.get(
|
||||
"/admin/customers?expand=groups",
|
||||
{
|
||||
headers: { Authorization: "Bearer test_token" },
|
||||
}
|
||||
)
|
||||
|
||||
expect(getCustomerResponse.data.customers).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: "test-customer-1",
|
||||
groups: [
|
||||
expect.objectContaining({
|
||||
name: "vip-customers",
|
||||
id: "customer-group-1",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-customer-2",
|
||||
groups: [
|
||||
expect.objectContaining({
|
||||
name: "vip-customers",
|
||||
id: "customer-group-1",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("presents a descriptive error when presented with a non-existing group", async () => {
|
||||
expect.assertions(2)
|
||||
|
||||
const api = useApi()
|
||||
|
||||
const payload = {
|
||||
customer_ids: [{ id: "test-customer-1" }, { id: "test-customer-2" }],
|
||||
}
|
||||
|
||||
await api
|
||||
.post(
|
||||
"/admin/customer-groups/non-existing-customer-group-1/customers/batch",
|
||||
payload,
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
expect(err.response.data.type).toEqual("not_found")
|
||||
expect(err.response.data.message).toEqual(
|
||||
"CustomerGroup with id non-existing-customer-group-1 was not found"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it("adds customers to a group idempotently", async () => {
|
||||
expect.assertions(3)
|
||||
|
||||
const api = useApi()
|
||||
|
||||
// add customer-1 to the customer group
|
||||
const payload_1 = {
|
||||
customer_ids: [{ id: "test-customer-1" }],
|
||||
}
|
||||
|
||||
await api
|
||||
.post(
|
||||
"/admin/customer-groups/customer-group-1/customers/batch",
|
||||
payload_1,
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => console.log(err))
|
||||
|
||||
// re-adding customer-1 to the customer group along with new addintion:
|
||||
// customer-2 and some non-existing customers should cause the request to fail
|
||||
const payload_2 = {
|
||||
customer_ids: [
|
||||
{ id: "test-customer-1" },
|
||||
{ id: "test-customer-27" },
|
||||
{ id: "test-customer-28" },
|
||||
{ id: "test-customer-2" },
|
||||
],
|
||||
}
|
||||
|
||||
await api
|
||||
.post(
|
||||
"/admin/customer-groups/customer-group-1/customers/batch",
|
||||
payload_2,
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
expect(err.response.data.type).toEqual("not_found")
|
||||
expect(err.response.data.message).toEqual(
|
||||
'The following customer ids do not exist: "test-customer-27, test-customer-28"'
|
||||
)
|
||||
})
|
||||
|
||||
// check that customer-1 is only added once and that customer-2 is added correctly
|
||||
const getCustomerResponse = await api.get(
|
||||
"/admin/customers?expand=groups",
|
||||
{
|
||||
headers: { Authorization: "Bearer test_token" },
|
||||
}
|
||||
)
|
||||
|
||||
expect(getCustomerResponse.data.customers).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: "test-customer-1",
|
||||
groups: [
|
||||
expect.objectContaining({
|
||||
name: "vip-customers",
|
||||
id: "customer-group-1",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-customer-2",
|
||||
groups: [],
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("POST /admin/customer-groups/:id", () => {
|
||||
beforeEach(async () => {
|
||||
try {
|
||||
@@ -336,8 +512,6 @@ describe("/admin/customer-groups", () => {
|
||||
})
|
||||
|
||||
it("throws error when a customer group doesn't exist", async () => {
|
||||
expect.assertions(3)
|
||||
|
||||
const api = useApi()
|
||||
|
||||
const id = "test-group-000"
|
||||
@@ -352,7 +526,7 @@ describe("/admin/customer-groups", () => {
|
||||
expect(err.response.status).toEqual(404)
|
||||
expect(err.response.data.type).toEqual("not_found")
|
||||
expect(err.response.data.message).toEqual(
|
||||
`CustomerGroup with ${id} was not found`
|
||||
`CustomerGroup with id ${id} was not found`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -229,6 +229,32 @@ describe("/admin/customers", () => {
|
||||
)
|
||||
})
|
||||
|
||||
it("fails when adding a customer group which doesn't exist", async () => {
|
||||
expect.assertions(3)
|
||||
// Try adding a non existing group
|
||||
const api = useApi()
|
||||
|
||||
await api
|
||||
.post(
|
||||
"/admin/customers/test-customer-3?expand=groups",
|
||||
{
|
||||
groups: [{ id: "fake-group-0" }],
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((error) => {
|
||||
expect(error.response.status).toEqual(404)
|
||||
expect(error.response.data.type).toEqual("not_found")
|
||||
expect(error.response.data.message).toEqual(
|
||||
"Customer_group with customer_group_id fake-group-0 does not exist."
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it("Correctly updates customer groups", async () => {
|
||||
const api = useApi()
|
||||
let response = await api
|
||||
@@ -254,32 +280,6 @@ describe("/admin/customers", () => {
|
||||
])
|
||||
)
|
||||
|
||||
// Try adding a non existing group
|
||||
|
||||
response = await api
|
||||
.post(
|
||||
"/admin/customers/test-customer-3?expand=groups",
|
||||
{
|
||||
groups: [{ id: "test-group-4" }, { id: "fake-group-0" }],
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.customer.groups.length).toEqual(1)
|
||||
expect(response.data.customer.groups).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ id: "test-group-4", name: "test-group-4" }),
|
||||
])
|
||||
)
|
||||
|
||||
// Delete all groups
|
||||
|
||||
response = await api
|
||||
|
||||
@@ -882,8 +882,8 @@ describe("/admin/discounts", () => {
|
||||
}
|
||||
)
|
||||
} catch (error) {
|
||||
expect(error.response.data.message).toMatch(
|
||||
/duplicate key value violates unique constraint/i
|
||||
expect(error.response.data.message).toEqual(
|
||||
"Discount with code TESTING already exists."
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1627,7 +1627,7 @@ describe("/admin/products", () => {
|
||||
})
|
||||
} catch (error) {
|
||||
expect(error.response.data.message).toMatch(
|
||||
/duplicate key value violates unique constraint/i
|
||||
"Product with handle test-product already exists."
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -1699,7 +1699,7 @@ describe("/admin/products", () => {
|
||||
})
|
||||
} catch (error) {
|
||||
expect(error.response.data.message).toMatch(
|
||||
/duplicate key value violates unique constraint/i
|
||||
"Product_collection with handle test-collection already exists."
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user