fix: Customer registration (#8896)
* fix: Customer registration * update test * one mroe test * chore: add transformation
This commit is contained in:
1 parent
99461e24ab
commit
cbb0a6adc7
7 files changed
+269
-10
No files matched your search
@@ -0,0 +1,196 @@
|
|||||||
|
import { MedusaContainer } from "@medusajs/types"
|
||||||
|
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||||
|
import {
|
||||||
|
adminHeaders,
|
||||||
|
createAdminUser,
|
||||||
|
} from "../../../../helpers/create-admin-user"
|
||||||
|
|
||||||
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
|
medusaIntegrationTestRunner({
|
||||||
|
testSuite: ({ dbConnection, api, getContainer }) => {
|
||||||
|
let appContainer: MedusaContainer
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
appContainer = getContainer()
|
||||||
|
await createAdminUser(dbConnection, adminHeaders, appContainer)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("POST /admin/customers", () => {
|
||||||
|
it("should fails to create a customer without an identity", async () => {
|
||||||
|
const customer = await api
|
||||||
|
.post("/store/customers", {
|
||||||
|
email: "newcustomer@medusa.js",
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
})
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(customer.response.status).toEqual(401)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should successfully create a customer with an identity", async () => {
|
||||||
|
const signup = await api.post("/auth/customer/emailpass/register", {
|
||||||
|
email: "newcustomer@medusa.js",
|
||||||
|
password: "secret_password",
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(signup.status).toEqual(200)
|
||||||
|
expect(signup.data).toEqual({ token: expect.any(String) })
|
||||||
|
|
||||||
|
const customer = await api.post(
|
||||||
|
"/store/customers",
|
||||||
|
{
|
||||||
|
email: "newcustomer@medusa.js",
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${signup.data.token}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(customer.status).toEqual(200)
|
||||||
|
expect(customer.data).toEqual({
|
||||||
|
customer: expect.objectContaining({
|
||||||
|
email: "newcustomer@medusa.js",
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
has_account: true,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should successfully create a customer with an identity even if the email is already taken by a non-registered customer", async () => {
|
||||||
|
const nonRegisteredCustomer = await api.post(
|
||||||
|
"/admin/customers",
|
||||||
|
{
|
||||||
|
email: "newcustomer@medusa.js",
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(nonRegisteredCustomer.status).toEqual(200)
|
||||||
|
expect(nonRegisteredCustomer.data).toEqual({
|
||||||
|
customer: expect.objectContaining({
|
||||||
|
email: "newcustomer@medusa.js",
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
has_account: false,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const signup = await api.post("/auth/customer/emailpass/register", {
|
||||||
|
email: "newcustomer@medusa.js",
|
||||||
|
password: "secret_password",
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(signup.status).toEqual(200)
|
||||||
|
expect(signup.data).toEqual({ token: expect.any(String) })
|
||||||
|
|
||||||
|
const customer = await api.post(
|
||||||
|
"/store/customers",
|
||||||
|
{
|
||||||
|
email: "newcustomer@medusa.js",
|
||||||
|
first_name: "Jane",
|
||||||
|
last_name: "Doe",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${signup.data.token}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(customer.status).toEqual(200)
|
||||||
|
expect(customer.data).toEqual({
|
||||||
|
customer: expect.objectContaining({
|
||||||
|
email: "newcustomer@medusa.js",
|
||||||
|
first_name: "Jane",
|
||||||
|
last_name: "Doe",
|
||||||
|
has_account: true,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Check that customers co-exist
|
||||||
|
const customers = await api.get("/admin/customers", adminHeaders)
|
||||||
|
|
||||||
|
expect(customers.status).toEqual(200)
|
||||||
|
expect(customers.data.customers).toHaveLength(2)
|
||||||
|
expect(customers.data.customers).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
first_name: "Jane",
|
||||||
|
last_name: "Doe",
|
||||||
|
email: "newcustomer@medusa.js",
|
||||||
|
has_account: true,
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
email: "newcustomer@medusa.js",
|
||||||
|
has_account: false,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fail to create a customer with an identity when the email is already taken by a registered customer", async () => {
|
||||||
|
const firstSignup = await api.post(
|
||||||
|
"/auth/customer/emailpass/register",
|
||||||
|
{
|
||||||
|
email: "newcustomer@medusa.js",
|
||||||
|
password: "secret_password",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(firstSignup.status).toEqual(200)
|
||||||
|
expect(firstSignup.data).toEqual({ token: expect.any(String) })
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
"/store/customers",
|
||||||
|
{
|
||||||
|
email: "newcustomer@medusa.js",
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${firstSignup.data.token}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const firstSignin = await api.post("/auth/customer/emailpass", {
|
||||||
|
email: "newcustomer@medusa.js",
|
||||||
|
password: "secret_password",
|
||||||
|
})
|
||||||
|
|
||||||
|
const customer = await api
|
||||||
|
.post(
|
||||||
|
"/store/customers",
|
||||||
|
{
|
||||||
|
email: "newcustomer@medusa.js",
|
||||||
|
first_name: "Jane",
|
||||||
|
last_name: "Doe",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${firstSignin.data.token}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(customer.response.status).toEqual(400)
|
||||||
|
expect(customer.response.data.message).toEqual(
|
||||||
|
"Request already authenticated as a customer."
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { MedusaError, ModuleRegistrationName } from "@medusajs/utils"
|
||||||
|
import { createStep } from "@medusajs/workflows-sdk"
|
||||||
|
import { CreateCustomerAccountWorkflowInput } from "../workflows"
|
||||||
|
|
||||||
|
export const validateCustomerAccountCreationStepId =
|
||||||
|
"validate-customer-account-creation"
|
||||||
|
|
||||||
|
export const validateCustomerAccountCreation = createStep(
|
||||||
|
validateCustomerAccountCreationStepId,
|
||||||
|
async (input: CreateCustomerAccountWorkflowInput, { container }) => {
|
||||||
|
const customerService = container.resolve(ModuleRegistrationName.CUSTOMER)
|
||||||
|
|
||||||
|
const { email } = input.customerData
|
||||||
|
|
||||||
|
if (!email) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
"Email is required to create a customer"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if customer with email already exists
|
||||||
|
const existingCustomers = await customerService.listCustomers({ email })
|
||||||
|
|
||||||
|
if (existingCustomers?.length) {
|
||||||
|
const hasExistingAccount = existingCustomers.some(
|
||||||
|
(customer) => customer.has_account
|
||||||
|
)
|
||||||
|
|
||||||
|
if (hasExistingAccount && input.authIdentityId) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.DUPLICATE_ERROR,
|
||||||
|
"Customer with this email already has an account"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasExistingAccount && !input.authIdentityId) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.DUPLICATE_ERROR,
|
||||||
|
"Guest customer with this email already exists"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
import { CreateCustomerDTO, CustomerDTO } from "@medusajs/types"
|
import { CreateCustomerDTO, CustomerDTO } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
createWorkflow,
|
createWorkflow,
|
||||||
|
transform,
|
||||||
WorkflowData,
|
WorkflowData,
|
||||||
WorkflowResponse,
|
WorkflowResponse,
|
||||||
} from "@medusajs/workflows-sdk"
|
} from "@medusajs/workflows-sdk"
|
||||||
import { createCustomersStep } from "../steps"
|
|
||||||
import { transform } from "@medusajs/workflows-sdk"
|
|
||||||
import { setAuthAppMetadataStep } from "../../auth"
|
import { setAuthAppMetadataStep } from "../../auth"
|
||||||
|
import { createCustomersStep } from "../steps"
|
||||||
|
import { validateCustomerAccountCreation } from "../steps/validate-customer-account-creation"
|
||||||
|
|
||||||
export type CreateCustomerAccountWorkflowInput = {
|
export type CreateCustomerAccountWorkflowInput = {
|
||||||
authIdentityId: string
|
authIdentityId: string
|
||||||
customersData: CreateCustomerDTO
|
customerData: CreateCustomerDTO
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createCustomerAccountWorkflowId = "create-customer-account"
|
export const createCustomerAccountWorkflowId = "create-customer-account"
|
||||||
@@ -19,8 +20,19 @@ export const createCustomerAccountWorkflowId = "create-customer-account"
|
|||||||
*/
|
*/
|
||||||
export const createCustomerAccountWorkflow = createWorkflow(
|
export const createCustomerAccountWorkflow = createWorkflow(
|
||||||
createCustomerAccountWorkflowId,
|
createCustomerAccountWorkflowId,
|
||||||
(input: WorkflowData<CreateCustomerAccountWorkflowInput>): WorkflowResponse<CustomerDTO> => {
|
(
|
||||||
const customers = createCustomersStep([input.customersData])
|
input: WorkflowData<CreateCustomerAccountWorkflowInput>
|
||||||
|
): WorkflowResponse<CustomerDTO> => {
|
||||||
|
validateCustomerAccountCreation(input)
|
||||||
|
|
||||||
|
const customerData = transform({ input }, (data) => {
|
||||||
|
return {
|
||||||
|
...data.input.customerData,
|
||||||
|
has_account: !!data.input.authIdentityId,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const customers = createCustomersStep([customerData])
|
||||||
|
|
||||||
const customer = transform(
|
const customer = transform(
|
||||||
customers,
|
customers,
|
||||||
|
|||||||
@@ -199,6 +199,11 @@ export interface CreateCustomerDTO {
|
|||||||
*/
|
*/
|
||||||
created_by?: string | null
|
created_by?: string | null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the customer has an account.
|
||||||
|
*/
|
||||||
|
has_account?: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The addresses of the customer.
|
* The addresses of the customer.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
ContainerRegistrationKeys,
|
ContainerRegistrationKeys,
|
||||||
remoteQueryObjectFromString,
|
remoteQueryObjectFromString,
|
||||||
@@ -6,7 +7,6 @@ import {
|
|||||||
AuthenticatedMedusaRequest,
|
AuthenticatedMedusaRequest,
|
||||||
MedusaResponse,
|
MedusaResponse,
|
||||||
} from "../../../types/routing"
|
} from "../../../types/routing"
|
||||||
import { HttpTypes } from "@medusajs/types"
|
|
||||||
|
|
||||||
export const GET = async (
|
export const GET = async (
|
||||||
req: AuthenticatedMedusaRequest<HttpTypes.AdminGetFulfillmentProvidersParams>,
|
req: AuthenticatedMedusaRequest<HttpTypes.AdminGetFulfillmentProvidersParams>,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const defaultStoreCustomersFields = [
|
|||||||
"last_name",
|
"last_name",
|
||||||
"phone",
|
"phone",
|
||||||
"metadata",
|
"metadata",
|
||||||
|
"has_account",
|
||||||
"created_by",
|
"created_by",
|
||||||
"deleted_at",
|
"deleted_at",
|
||||||
"created_at",
|
"created_at",
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
|
import { MedusaError } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
AuthenticatedMedusaRequest,
|
AuthenticatedMedusaRequest,
|
||||||
MedusaResponse,
|
MedusaResponse,
|
||||||
} from "../../../types/routing"
|
} from "../../../types/routing"
|
||||||
import { MedusaError } from "@medusajs/utils"
|
|
||||||
|
|
||||||
import { createCustomerAccountWorkflow } from "@medusajs/core-flows"
|
import { createCustomerAccountWorkflow } from "@medusajs/core-flows"
|
||||||
import { refetchCustomer } from "./helpers"
|
|
||||||
import { HttpTypes } from "@medusajs/types"
|
import { HttpTypes } from "@medusajs/types"
|
||||||
|
import { refetchCustomer } from "./helpers"
|
||||||
|
|
||||||
export const POST = async (
|
export const POST = async (
|
||||||
req: AuthenticatedMedusaRequest<HttpTypes.StoreCreateCustomer>,
|
req: AuthenticatedMedusaRequest<HttpTypes.StoreCreateCustomer>,
|
||||||
@@ -21,10 +21,10 @@ export const POST = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const createCustomers = createCustomerAccountWorkflow(req.scope)
|
const createCustomers = createCustomerAccountWorkflow(req.scope)
|
||||||
const customersData = req.validatedBody
|
const customerData = req.validatedBody
|
||||||
|
|
||||||
const { result } = await createCustomers.run({
|
const { result } = await createCustomers.run({
|
||||||
input: { customersData, authIdentityId: req.auth_context.auth_identity_id },
|
input: { customerData, authIdentityId: req.auth_context.auth_identity_id },
|
||||||
})
|
})
|
||||||
|
|
||||||
const customer = await refetchCustomer(
|
const customer = await refetchCustomer(
|
||||||
|
|||||||
Reference in new issue
Block a user