feat(core-flows,medusa): Add API to update cart's customer (#10151)
what: - adds an endpoint that updates a cart's customer RESOLVES CMRC-718
This commit is contained in:
1 parent
41dc05d0c9
commit
b7044bb3b0
13 files changed
+462
-101
No files matched your search
@@ -1,3 +1,4 @@
|
|||||||
|
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
|
||||||
import {
|
import {
|
||||||
Modules,
|
Modules,
|
||||||
PriceListStatus,
|
PriceListStatus,
|
||||||
@@ -6,39 +7,19 @@ import {
|
|||||||
PromotionRuleOperator,
|
PromotionRuleOperator,
|
||||||
PromotionType,
|
PromotionType,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
|
|
||||||
import {
|
import {
|
||||||
createAdminUser,
|
createAdminUser,
|
||||||
generatePublishableKey,
|
generatePublishableKey,
|
||||||
generateStoreHeaders,
|
generateStoreHeaders,
|
||||||
} from "../../../../helpers/create-admin-user"
|
} from "../../../../helpers/create-admin-user"
|
||||||
import { setupTaxStructure } from "../../../../modules/__tests__/fixtures"
|
import { setupTaxStructure } from "../../../../modules/__tests__/fixtures"
|
||||||
|
import { createAuthenticatedCustomer } from "../../../../modules/helpers/create-authenticated-customer"
|
||||||
|
|
||||||
jest.setTimeout(100000)
|
jest.setTimeout(100000)
|
||||||
|
|
||||||
const env = { MEDUSA_FF_MEDUSA_V2: true }
|
const env = { MEDUSA_FF_MEDUSA_V2: true }
|
||||||
const adminHeaders = { headers: { "x-medusa-access-token": "test_token" } }
|
const adminHeaders = { headers: { "x-medusa-access-token": "test_token" } }
|
||||||
|
|
||||||
const generateStoreHeadersWithCustomer = async ({
|
|
||||||
api,
|
|
||||||
storeHeaders,
|
|
||||||
customer,
|
|
||||||
}) => {
|
|
||||||
const registeredCustomerToken = (
|
|
||||||
await api.post("/auth/customer/emailpass/register", {
|
|
||||||
email: customer.email,
|
|
||||||
password: "password",
|
|
||||||
})
|
|
||||||
).data.token
|
|
||||||
|
|
||||||
return {
|
|
||||||
headers: {
|
|
||||||
...storeHeaders.headers,
|
|
||||||
authorization: `Bearer ${registeredCustomerToken}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const shippingAddressData = {
|
const shippingAddressData = {
|
||||||
address_1: "test address 1",
|
address_1: "test address 1",
|
||||||
address_2: "test address 2",
|
address_2: "test address 2",
|
||||||
@@ -136,23 +117,20 @@ medusaIntegrationTestRunner({
|
|||||||
const publishableKey = await generatePublishableKey(appContainer)
|
const publishableKey = await generatePublishableKey(appContainer)
|
||||||
storeHeaders = generateStoreHeaders({ publishableKey })
|
storeHeaders = generateStoreHeaders({ publishableKey })
|
||||||
|
|
||||||
customer = (
|
const result = await createAuthenticatedCustomer(api, storeHeaders, {
|
||||||
await api.post(
|
first_name: "tony",
|
||||||
"/admin/customers",
|
last_name: "stark",
|
||||||
{
|
email: "tony@stark-industries.com",
|
||||||
first_name: "tony",
|
|
||||||
email: "tony@stark-industries.com",
|
|
||||||
},
|
|
||||||
adminHeaders
|
|
||||||
)
|
|
||||||
).data.customer
|
|
||||||
|
|
||||||
storeHeadersWithCustomer = await generateStoreHeadersWithCustomer({
|
|
||||||
storeHeaders,
|
|
||||||
api,
|
|
||||||
customer,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
customer = result.customer
|
||||||
|
storeHeadersWithCustomer = {
|
||||||
|
headers: {
|
||||||
|
...storeHeaders.headers,
|
||||||
|
authorization: `Bearer ${result.jwt}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
await setupTaxStructure(appContainer.resolve(Modules.TAX))
|
await setupTaxStructure(appContainer.resolve(Modules.TAX))
|
||||||
|
|
||||||
region = (
|
region = (
|
||||||
@@ -582,21 +560,17 @@ medusaIntegrationTestRunner({
|
|||||||
let otherRegion
|
let otherRegion
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
cart = (
|
const cartData = {
|
||||||
await api.post(
|
currency_code: "usd",
|
||||||
`/store/carts`,
|
sales_channel_id: salesChannel.id,
|
||||||
{
|
region_id: region.id,
|
||||||
email: "tony@stark.com",
|
shipping_address: shippingAddressData,
|
||||||
currency_code: "usd",
|
items: [{ variant_id: product.variants[0].id, quantity: 1 }],
|
||||||
sales_channel_id: salesChannel.id,
|
promo_codes: [promotion.code],
|
||||||
region_id: region.id,
|
}
|
||||||
shipping_address: shippingAddressData,
|
|
||||||
items: [{ variant_id: product.variants[0].id, quantity: 1 }],
|
cart = (await api.post(`/store/carts`, cartData, storeHeaders)).data
|
||||||
promo_codes: [promotion.code],
|
.cart
|
||||||
},
|
|
||||||
storeHeadersWithCustomer
|
|
||||||
)
|
|
||||||
).data.cart
|
|
||||||
|
|
||||||
otherRegion = (
|
otherRegion = (
|
||||||
await api.post(
|
await api.post(
|
||||||
@@ -751,7 +725,7 @@ medusaIntegrationTestRunner({
|
|||||||
it("should not generate tax lines if automatic taxes is false", async () => {
|
it("should not generate tax lines if automatic taxes is false", async () => {
|
||||||
let updated = await api.post(
|
let updated = await api.post(
|
||||||
`/store/carts/${cart.id}`,
|
`/store/carts/${cart.id}`,
|
||||||
{ email: "another@tax.com" },
|
{},
|
||||||
storeHeaders
|
storeHeaders
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -776,7 +750,7 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
updated = await api.post(
|
updated = await api.post(
|
||||||
`/store/carts/${cart.id}`,
|
`/store/carts/${cart.id}`,
|
||||||
{ email: "another@tax.com", region_id: noAutomaticRegion.id },
|
{ region_id: noAutomaticRegion.id },
|
||||||
storeHeaders
|
storeHeaders
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1236,6 +1210,242 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should update email irregardless of registered customer", async () => {
|
||||||
|
const updateEmailWithoutCustomer = await api.post(
|
||||||
|
`/store/carts/${cart.id}`,
|
||||||
|
{ email: "tony@stark.com" },
|
||||||
|
storeHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(updateEmailWithoutCustomer.data.cart).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: "tony@stark.com",
|
||||||
|
customer: expect.objectContaining({
|
||||||
|
email: "tony@stark.com",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const updateCartCustomer = await api.post(
|
||||||
|
`/store/carts/${cart.id}/customer`,
|
||||||
|
{},
|
||||||
|
storeHeadersWithCustomer
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(updateCartCustomer.data.cart).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: "tony@stark-industries.com",
|
||||||
|
customer: expect.objectContaining({
|
||||||
|
id: customer.id,
|
||||||
|
email: "tony@stark-industries.com",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const updateEmailWithCustomer = await api.post(
|
||||||
|
`/store/carts/${cart.id}`,
|
||||||
|
{ email: "new@stark.com" },
|
||||||
|
storeHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(updateEmailWithCustomer.data.cart).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: "new@stark.com",
|
||||||
|
customer: expect.objectContaining({
|
||||||
|
id: customer.id,
|
||||||
|
email: "tony@stark-industries.com",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("POST /store/carts/:id/customer", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
cart = (
|
||||||
|
await api.post(
|
||||||
|
`/store/carts`,
|
||||||
|
{
|
||||||
|
currency_code: "usd",
|
||||||
|
sales_channel_id: salesChannel.id,
|
||||||
|
region_id: region.id,
|
||||||
|
items: [{ variant_id: product.variants[0].id, quantity: 1 }],
|
||||||
|
},
|
||||||
|
storeHeaders
|
||||||
|
)
|
||||||
|
).data.cart
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw 401 when user is not logged in as a customer", async () => {
|
||||||
|
const { response } = await api
|
||||||
|
.post(`/store/carts/${cart.id}/customer`, {}, storeHeaders)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(401)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw error when cart does not exist", async () => {
|
||||||
|
const { response } = await api
|
||||||
|
.post(
|
||||||
|
`/store/carts/does-not-exist/customer`,
|
||||||
|
{},
|
||||||
|
storeHeadersWithCustomer
|
||||||
|
)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(404)
|
||||||
|
expect(response.data.message).toEqual(
|
||||||
|
"Cart id not found: does-not-exist"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw error when trying to update a cart that belongs to a customer that has an account", async () => {
|
||||||
|
const customerUpdate1 = await api.post(
|
||||||
|
`/store/carts/${cart.id}/customer`,
|
||||||
|
{},
|
||||||
|
storeHeadersWithCustomer
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(customerUpdate1.status).toEqual(200)
|
||||||
|
expect(customerUpdate1.data.cart).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: customer.email,
|
||||||
|
customer: expect.objectContaining({
|
||||||
|
id: customer.id,
|
||||||
|
email: customer.email,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const { jwt: jwt2 } = await createAuthenticatedCustomer(
|
||||||
|
api,
|
||||||
|
storeHeaders,
|
||||||
|
{
|
||||||
|
first_name: "tony2",
|
||||||
|
last_name: "stark",
|
||||||
|
email: "tony2@stark-industries.com",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const storeHeadersWithCustomer2 = {
|
||||||
|
headers: {
|
||||||
|
...storeHeaders.headers,
|
||||||
|
authorization: `Bearer ${jwt2}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const { response } = await api
|
||||||
|
.post(
|
||||||
|
`/store/carts/${cart.id}/customer`,
|
||||||
|
{},
|
||||||
|
storeHeadersWithCustomer2
|
||||||
|
)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(400)
|
||||||
|
expect(response.data.message).toEqual(
|
||||||
|
"Cannot update cart customer when its assigned to a different customer"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should successfully update cart customer when cart is without customer", async () => {
|
||||||
|
const updated = await api.post(
|
||||||
|
`/store/carts/${cart.id}/customer`,
|
||||||
|
{},
|
||||||
|
storeHeadersWithCustomer
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(updated.status).toEqual(200)
|
||||||
|
expect(updated.data.cart).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: customer.email,
|
||||||
|
customer: expect.objectContaining({
|
||||||
|
id: customer.id,
|
||||||
|
email: customer.email,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should successfully update cart customer when cart has a guest customer", async () => {
|
||||||
|
const guestEmail = "tony@guest.com"
|
||||||
|
const updatedCart = await api.post(
|
||||||
|
`/store/carts/${cart.id}`,
|
||||||
|
{ email: guestEmail },
|
||||||
|
storeHeadersWithCustomer
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(updatedCart.status).toEqual(200)
|
||||||
|
expect(updatedCart.data.cart).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: guestEmail,
|
||||||
|
customer: expect.objectContaining({
|
||||||
|
email: guestEmail,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const updated = await api.post(
|
||||||
|
`/store/carts/${cart.id}/customer`,
|
||||||
|
{},
|
||||||
|
storeHeadersWithCustomer
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(updated.status).toEqual(200)
|
||||||
|
expect(updated.data.cart).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: customer.email,
|
||||||
|
customer: expect.objectContaining({
|
||||||
|
id: customer.id,
|
||||||
|
email: customer.email,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should successfully update cart customer when customer already owns the cart", async () => {
|
||||||
|
const guestEmail = "tony@guest.com"
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
`/store/carts/${cart.id}/customer`,
|
||||||
|
{},
|
||||||
|
storeHeadersWithCustomer
|
||||||
|
)
|
||||||
|
|
||||||
|
const updatedCart = await api.post(
|
||||||
|
`/store/carts/${cart.id}`,
|
||||||
|
{ email: guestEmail },
|
||||||
|
storeHeadersWithCustomer
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(updatedCart.status).toEqual(200)
|
||||||
|
expect(updatedCart.data.cart).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: guestEmail,
|
||||||
|
customer: expect.objectContaining({
|
||||||
|
email: customer.email,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const updated = await api.post(
|
||||||
|
`/store/carts/${cart.id}/customer`,
|
||||||
|
{},
|
||||||
|
storeHeadersWithCustomer
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(updated.status).toEqual(200)
|
||||||
|
expect(updated.data.cart).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: guestEmail,
|
||||||
|
customer: expect.objectContaining({
|
||||||
|
id: customer.id,
|
||||||
|
email: customer.email,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -417,7 +417,8 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
it("should create cart with logged-in customer", async () => {
|
it("should create cart with logged-in customer", async () => {
|
||||||
const { customer, jwt } = await createAuthenticatedCustomer(
|
const { customer, jwt } = await createAuthenticatedCustomer(
|
||||||
appContainer
|
api,
|
||||||
|
storeHeaders
|
||||||
)
|
)
|
||||||
|
|
||||||
const response = await api.post(
|
const response = await api.post(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
|
||||||
import { ICustomerModuleService } from "@medusajs/types"
|
import { ICustomerModuleService } from "@medusajs/types"
|
||||||
import { Modules } from "@medusajs/utils"
|
import { Modules } from "@medusajs/utils"
|
||||||
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
|
|
||||||
import {
|
import {
|
||||||
generatePublishableKey,
|
generatePublishableKey,
|
||||||
generateStoreHeaders,
|
generateStoreHeaders,
|
||||||
@@ -31,7 +31,8 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
it("should create a customer address", async () => {
|
it("should create a customer address", async () => {
|
||||||
const { customer, jwt } = await createAuthenticatedCustomer(
|
const { customer, jwt } = await createAuthenticatedCustomer(
|
||||||
appContainer
|
api,
|
||||||
|
storeHeaders
|
||||||
)
|
)
|
||||||
|
|
||||||
const response = await api.post(
|
const response = await api.post(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
|
||||||
import { ICustomerModuleService } from "@medusajs/types"
|
import { ICustomerModuleService } from "@medusajs/types"
|
||||||
import { Modules } from "@medusajs/utils"
|
import { Modules } from "@medusajs/utils"
|
||||||
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
|
|
||||||
import {
|
import {
|
||||||
generatePublishableKey,
|
generatePublishableKey,
|
||||||
generateStoreHeaders,
|
generateStoreHeaders,
|
||||||
@@ -32,7 +32,8 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
it("should delete a customer address", async () => {
|
it("should delete a customer address", async () => {
|
||||||
const { customer, jwt } = await createAuthenticatedCustomer(
|
const { customer, jwt } = await createAuthenticatedCustomer(
|
||||||
appContainer
|
api,
|
||||||
|
storeHeaders
|
||||||
)
|
)
|
||||||
|
|
||||||
const address = await customerModuleService.createCustomerAddresses({
|
const address = await customerModuleService.createCustomerAddresses({
|
||||||
@@ -65,7 +66,7 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should fail to delete another customer's address", async () => {
|
it("should fail to delete another customer's address", async () => {
|
||||||
const { jwt } = await createAuthenticatedCustomer(appContainer)
|
const { jwt } = await createAuthenticatedCustomer(api, storeHeaders)
|
||||||
|
|
||||||
const otherCustomer = await customerModuleService.createCustomers({
|
const otherCustomer = await customerModuleService.createCustomers({
|
||||||
first_name: "Jane",
|
first_name: "Jane",
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
it("should retrieve auth user's customer", async () => {
|
it("should retrieve auth user's customer", async () => {
|
||||||
const { customer, jwt } = await createAuthenticatedCustomer(
|
const { customer, jwt } = await createAuthenticatedCustomer(
|
||||||
appContainer
|
api,
|
||||||
|
storeHeaders
|
||||||
)
|
)
|
||||||
|
|
||||||
const response = await api.get(`/store/customers/me`, {
|
const response = await api.get(`/store/customers/me`, {
|
||||||
@@ -41,7 +42,7 @@ medusaIntegrationTestRunner({
|
|||||||
id: customer.id,
|
id: customer.id,
|
||||||
first_name: "John",
|
first_name: "John",
|
||||||
last_name: "Doe",
|
last_name: "Doe",
|
||||||
email: "john@me.com",
|
email: "tony@start.com",
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
|
||||||
import { ICustomerModuleService } from "@medusajs/types"
|
import { ICustomerModuleService } from "@medusajs/types"
|
||||||
import { Modules } from "@medusajs/utils"
|
import { Modules } from "@medusajs/utils"
|
||||||
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
|
|
||||||
import {
|
import {
|
||||||
generatePublishableKey,
|
generatePublishableKey,
|
||||||
generateStoreHeaders,
|
generateStoreHeaders,
|
||||||
@@ -32,7 +32,8 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
it("should get all customer addresses and its count", async () => {
|
it("should get all customer addresses and its count", async () => {
|
||||||
const { customer, jwt } = await createAuthenticatedCustomer(
|
const { customer, jwt } = await createAuthenticatedCustomer(
|
||||||
appContainer
|
api,
|
||||||
|
storeHeaders
|
||||||
)
|
)
|
||||||
|
|
||||||
await customerModuleService.createCustomerAddresses([
|
await customerModuleService.createCustomerAddresses([
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
|
||||||
import { ICustomerModuleService } from "@medusajs/types"
|
import { ICustomerModuleService } from "@medusajs/types"
|
||||||
import { Modules } from "@medusajs/utils"
|
import { Modules } from "@medusajs/utils"
|
||||||
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
|
|
||||||
import {
|
import {
|
||||||
generatePublishableKey,
|
generatePublishableKey,
|
||||||
generateStoreHeaders,
|
generateStoreHeaders,
|
||||||
@@ -32,7 +32,8 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
it.only("should update a customer address", async () => {
|
it.only("should update a customer address", async () => {
|
||||||
const { customer, jwt } = await createAuthenticatedCustomer(
|
const { customer, jwt } = await createAuthenticatedCustomer(
|
||||||
appContainer
|
api,
|
||||||
|
storeHeaders
|
||||||
)
|
)
|
||||||
|
|
||||||
const address = await customerModuleService.createCustomerAddresses({
|
const address = await customerModuleService.createCustomerAddresses({
|
||||||
@@ -66,7 +67,7 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should fail to update another customer's address", async () => {
|
it("should fail to update another customer's address", async () => {
|
||||||
const { jwt } = await createAuthenticatedCustomer(appContainer)
|
const { jwt } = await createAuthenticatedCustomer(api, storeHeaders)
|
||||||
|
|
||||||
const otherCustomer = await customerModuleService.createCustomers({
|
const otherCustomer = await customerModuleService.createCustomers({
|
||||||
first_name: "Jane",
|
first_name: "Jane",
|
||||||
|
|||||||
@@ -1,43 +1,39 @@
|
|||||||
import { CreateCustomerDTO, MedusaContainer } from "@medusajs/types"
|
import { CreateCustomerDTO } from "@medusajs/types"
|
||||||
import { Modules } from "@medusajs/utils"
|
|
||||||
|
|
||||||
import jwt from "jsonwebtoken"
|
|
||||||
|
|
||||||
export const createAuthenticatedCustomer = async (
|
export const createAuthenticatedCustomer = async (
|
||||||
appContainer: MedusaContainer,
|
api: any,
|
||||||
|
storeHeaders: Record<any, any>,
|
||||||
customerData: Partial<CreateCustomerDTO> = {}
|
customerData: Partial<CreateCustomerDTO> = {}
|
||||||
) => {
|
) => {
|
||||||
const { http } = appContainer.resolve("configModule").projectConfig
|
const email = customerData.email ?? "tony@start.com"
|
||||||
const authService = appContainer.resolve(Modules.AUTH)
|
const signup = await api.post("/auth/customer/emailpass/register", {
|
||||||
const customerModuleService = appContainer.resolve(Modules.CUSTOMER)
|
email,
|
||||||
|
password: "secret_password",
|
||||||
const customer = await customerModuleService.createCustomers({
|
|
||||||
first_name: "John",
|
|
||||||
last_name: "Doe",
|
|
||||||
email: "john@me.com",
|
|
||||||
...customerData,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const authIdentity = await authService.createAuthIdentities({
|
const {
|
||||||
provider_identities: [
|
data: { customer },
|
||||||
{
|
} = await api.post(
|
||||||
entity_id: "store_user",
|
"/store/customers",
|
||||||
provider: "emailpass",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
app_metadata: {
|
|
||||||
customer_id: customer.id,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const token = jwt.sign(
|
|
||||||
{
|
{
|
||||||
actor_id: customer.id,
|
email,
|
||||||
actor_type: "customer",
|
first_name: "John",
|
||||||
auth_identity_id: authIdentity.id,
|
last_name: "Doe",
|
||||||
|
metadata: {},
|
||||||
|
...customerData,
|
||||||
},
|
},
|
||||||
http.jwtSecret
|
{
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${signup.data.token}`,
|
||||||
|
...storeHeaders.headers,
|
||||||
|
},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return { customer, authIdentity, jwt: token }
|
const signin = await api.post("/auth/customer/emailpass", {
|
||||||
|
email,
|
||||||
|
password: "secret_password",
|
||||||
|
})
|
||||||
|
|
||||||
|
return { customer, jwt: signin.data.token }
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,7 @@ export * from "./create-payment-collection-for-cart"
|
|||||||
export * from "./list-shipping-options-for-cart"
|
export * from "./list-shipping-options-for-cart"
|
||||||
export * from "./refresh-payment-collection"
|
export * from "./refresh-payment-collection"
|
||||||
export * from "./update-cart"
|
export * from "./update-cart"
|
||||||
|
export * from "./update-cart-customer"
|
||||||
export * from "./update-cart-promotions"
|
export * from "./update-cart-promotions"
|
||||||
export * from "./update-line-item-in-cart"
|
export * from "./update-line-item-in-cart"
|
||||||
export * from "./update-tax-lines"
|
export * from "./update-tax-lines"
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import { isDefined, MedusaError } from "@medusajs/framework/utils"
|
||||||
|
import {
|
||||||
|
createStep,
|
||||||
|
createWorkflow,
|
||||||
|
StepResponse,
|
||||||
|
transform,
|
||||||
|
when,
|
||||||
|
WorkflowData,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
|
import { CustomerDTO } from "@medusajs/types"
|
||||||
|
import { useQueryGraphStep } from "../../common"
|
||||||
|
import { updateCartsStep } from "../steps"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This step validates if cart should be updated
|
||||||
|
*/
|
||||||
|
export const validateCartCustomerUpdateStep = createStep(
|
||||||
|
"validate-cart-customer-update",
|
||||||
|
async function ({
|
||||||
|
cart,
|
||||||
|
customer,
|
||||||
|
}: {
|
||||||
|
cart: {
|
||||||
|
customer?: { id: string; has_account: boolean }
|
||||||
|
}
|
||||||
|
customer: CustomerDTO
|
||||||
|
}) {
|
||||||
|
// If cart customer is the same as the provided customer, succeed early
|
||||||
|
// Pass in a boolean to not perform a customer update
|
||||||
|
if (isDefined(cart.customer?.id) && cart.customer.id === customer.id) {
|
||||||
|
return new StepResponse(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the cart customer already has an account, we can safely assume that its already
|
||||||
|
// been assigned to a different customer. This falls under cart take over, which isn't being
|
||||||
|
// handled with this workflow
|
||||||
|
if (cart.customer?.has_account) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.NOT_ALLOWED,
|
||||||
|
`Cannot update cart customer when its assigned to a different customer`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StepResponse(true)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const updateCartCustomerWorkflowId = "update-cart-customer"
|
||||||
|
/**
|
||||||
|
* This workflow updates cart's customer.
|
||||||
|
*/
|
||||||
|
export const updateCartCustomerWorkflow = createWorkflow(
|
||||||
|
updateCartCustomerWorkflowId,
|
||||||
|
(input: WorkflowData<{ id: string; customer_id: string }>) => {
|
||||||
|
const cartQuery = useQueryGraphStep({
|
||||||
|
entity: "cart",
|
||||||
|
filters: { id: input.id },
|
||||||
|
fields: [
|
||||||
|
"id",
|
||||||
|
"email",
|
||||||
|
"customer_id",
|
||||||
|
"customer.has_account",
|
||||||
|
"shipping_address.*",
|
||||||
|
"region.*",
|
||||||
|
"region.countries.*",
|
||||||
|
],
|
||||||
|
options: { throwIfKeyNotFound: true },
|
||||||
|
}).config({ name: "get-cart" })
|
||||||
|
|
||||||
|
const cart = transform({ cartQuery }, ({ cartQuery }) => cartQuery.data[0])
|
||||||
|
|
||||||
|
const customerQuery = useQueryGraphStep({
|
||||||
|
entity: "customer",
|
||||||
|
filters: { id: input.customer_id },
|
||||||
|
fields: ["id", "email"],
|
||||||
|
options: { throwIfKeyNotFound: true },
|
||||||
|
}).config({ name: "get-customer" })
|
||||||
|
|
||||||
|
const customer = transform(
|
||||||
|
{ customerQuery },
|
||||||
|
({ customerQuery }) => customerQuery.data[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
const shouldUpdate = validateCartCustomerUpdateStep({ cart, customer })
|
||||||
|
|
||||||
|
when({ shouldUpdate }, ({ shouldUpdate }) => !!shouldUpdate).then(() => {
|
||||||
|
const cartInput = transform({ cart, customer }, ({ cart, customer }) => [
|
||||||
|
{
|
||||||
|
id: cart.id,
|
||||||
|
customer_id: customer.id,
|
||||||
|
email: customer.email,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
updateCartsStep(cartInput)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { updateCartCustomerWorkflow } from "@medusajs/core-flows"
|
||||||
|
import { HttpTypes } from "@medusajs/framework/types"
|
||||||
|
|
||||||
|
import {
|
||||||
|
AuthenticatedMedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "@medusajs/framework/http"
|
||||||
|
import { refetchCart } from "../../helpers"
|
||||||
|
|
||||||
|
export const POST = async (
|
||||||
|
req: AuthenticatedMedusaRequest,
|
||||||
|
res: MedusaResponse<HttpTypes.StoreCartResponse>
|
||||||
|
) => {
|
||||||
|
const workflow = updateCartCustomerWorkflow(req.scope)
|
||||||
|
|
||||||
|
await workflow.run({
|
||||||
|
input: {
|
||||||
|
id: req.params.id,
|
||||||
|
customer_id: req.auth_context?.actor_id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const cart = await refetchCart(
|
||||||
|
req.params.id,
|
||||||
|
req.scope,
|
||||||
|
req.remoteQueryConfig.fields
|
||||||
|
)
|
||||||
|
|
||||||
|
res.status(200).json({ cart })
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
import { MiddlewareRoute } from "@medusajs/framework/http"
|
import {
|
||||||
|
validateAndTransformBody,
|
||||||
|
validateAndTransformQuery,
|
||||||
|
} from "@medusajs/framework"
|
||||||
|
import { authenticate, MiddlewareRoute } from "@medusajs/framework/http"
|
||||||
import { ensurePublishableKeyAndSalesChannelMatch } from "../../utils/middlewares/common/ensure-pub-key-sales-channel-match"
|
import { ensurePublishableKeyAndSalesChannelMatch } from "../../utils/middlewares/common/ensure-pub-key-sales-channel-match"
|
||||||
import { maybeAttachPublishableKeyScopes } from "../../utils/middlewares/common/maybe-attach-pub-key-scopes"
|
import { maybeAttachPublishableKeyScopes } from "../../utils/middlewares/common/maybe-attach-pub-key-scopes"
|
||||||
import { validateAndTransformBody } from "@medusajs/framework"
|
|
||||||
import { validateAndTransformQuery } from "@medusajs/framework"
|
|
||||||
import * as OrderQueryConfig from "../orders/query-config"
|
import * as OrderQueryConfig from "../orders/query-config"
|
||||||
import { StoreGetOrderParams } from "../orders/validators"
|
import { StoreGetOrderParams } from "../orders/validators"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
@@ -15,6 +17,7 @@ import {
|
|||||||
StoreGetCartsCart,
|
StoreGetCartsCart,
|
||||||
StoreRemoveCartPromotions,
|
StoreRemoveCartPromotions,
|
||||||
StoreUpdateCart,
|
StoreUpdateCart,
|
||||||
|
StoreUpdateCartCustomer,
|
||||||
StoreUpdateCartLineItem,
|
StoreUpdateCartLineItem,
|
||||||
} from "./validators"
|
} from "./validators"
|
||||||
|
|
||||||
@@ -53,6 +56,18 @@ export const storeCartRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
method: ["POST"],
|
||||||
|
matcher: "/store/carts/:id/customer",
|
||||||
|
middlewares: [
|
||||||
|
authenticate("customer", ["session", "bearer"]),
|
||||||
|
validateAndTransformBody(StoreUpdateCartCustomer),
|
||||||
|
validateAndTransformQuery(
|
||||||
|
StoreGetCartsCart,
|
||||||
|
QueryConfig.retrieveTransformQueryConfig
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
method: ["POST"],
|
method: ["POST"],
|
||||||
matcher: "/store/carts/:id/line-items",
|
matcher: "/store/carts/:id/line-items",
|
||||||
|
|||||||
@@ -91,3 +91,8 @@ export type StoreCreateCartPaymentCollectionType = z.infer<
|
|||||||
typeof StoreCreateCartPaymentCollection
|
typeof StoreCreateCartPaymentCollection
|
||||||
>
|
>
|
||||||
export const StoreCreateCartPaymentCollection = z.object({}).strict()
|
export const StoreCreateCartPaymentCollection = z.object({}).strict()
|
||||||
|
|
||||||
|
export type StoreUpdateCartCustomerType = z.infer<
|
||||||
|
typeof StoreUpdateCartCustomer
|
||||||
|
>
|
||||||
|
export const StoreUpdateCartCustomer = z.object({}).strict()
|
||||||
Reference in new issue
Block a user