feat: Add missing endpoints and normalize customer and currency endpoints for storefront (#7160)

This commit is contained in:
Stevche Radevski
2024-04-29 16:15:05 +02:00
committed by GitHub
parent 08a9297f76
commit 32c2a9d76b
24 changed files with 368 additions and 423 deletions

View File

@@ -37,7 +37,7 @@ medusaIntegrationTestRunner({
)
expect(response.status).toEqual(200)
expect(response.data.address).toEqual(
expect(response.data.customer.addresses[0]).toEqual(
expect.objectContaining({
id: expect.any(String),
first_name: "John",

View File

@@ -1,7 +1,7 @@
import { ICustomerModuleService } from "@medusajs/types"
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer"
import {medusaIntegrationTestRunner} from "medusa-test-utils";
import { medusaIntegrationTestRunner } from "medusa-test-utils"
const env = { MEDUSA_FF_MEDUSA_V2: true }

View File

@@ -42,7 +42,7 @@ medusaIntegrationTestRunner({
)
expect(response.status).toEqual(200)
expect(response.data.address).toEqual(
expect(response.data.customer.addresses[0]).toEqual(
expect.objectContaining({
id: address.id,
first_name: "Jane",

View File

@@ -1,6 +1,6 @@
import {
CreateCustomerAddressDTO,
CustomerAddressDTO,
UpdateCustomerAddressDTO,
FilterableCustomerAddressProps,
ICustomerModuleService,
} from "@medusajs/types"
@@ -13,7 +13,7 @@ type StepInput = {
create?: CreateCustomerAddressDTO[]
update?: {
selector: FilterableCustomerAddressProps
update: Partial<CustomerAddressDTO>
update: UpdateCustomerAddressDTO
}
}

View File

@@ -1,6 +1,6 @@
import {
CreateCustomerAddressDTO,
CustomerAddressDTO,
UpdateCustomerAddressDTO,
FilterableCustomerAddressProps,
ICustomerModuleService,
} from "@medusajs/types"
@@ -13,7 +13,7 @@ type StepInput = {
create?: CreateCustomerAddressDTO[]
update?: {
selector: FilterableCustomerAddressProps
update: Partial<CustomerAddressDTO>
update: UpdateCustomerAddressDTO
}
}

View File

@@ -1,6 +1,6 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import {
CustomerAddressDTO,
UpdateCustomerAddressDTO,
FilterableCustomerAddressProps,
ICustomerModuleService,
} from "@medusajs/types"
@@ -12,7 +12,7 @@ import { createStep, StepResponse } from "@medusajs/workflows-sdk"
type UpdateCustomerAddresseStepInput = {
selector: FilterableCustomerAddressProps
update: Partial<CustomerAddressDTO>
update: UpdateCustomerAddressDTO
}
export const updateCustomerAddresseStepId = "update-customer-addresses"

View File

@@ -1,5 +1,5 @@
import {
CustomerAddressDTO,
UpdateCustomerAddressDTO,
FilterableCustomerAddressProps,
ICustomerModuleService,
} from "@medusajs/types"
@@ -8,7 +8,7 @@ import { StepResponse } from "@medusajs/workflows-sdk"
export const unsetForUpdate = async (
data: {
selector: FilterableCustomerAddressProps
update: Partial<CustomerAddressDTO>
update: UpdateCustomerAddressDTO
},
customerService: ICustomerModuleService,
field: "is_default_billing" | "is_default_shipping"

View File

@@ -1,6 +1,7 @@
import {
FilterableCustomerAddressProps,
CustomerAddressDTO,
UpdateCustomerAddressDTO,
} from "@medusajs/types"
import {
WorkflowData,
@@ -16,7 +17,7 @@ import {
type WorkflowInput = {
selector: FilterableCustomerAddressProps
update: Partial<CustomerAddressDTO>
update: UpdateCustomerAddressDTO
}
export const updateCustomerAddressesWorkflowId = "update-customer-addresses"

View File

@@ -1,5 +1,6 @@
import {
ContainerRegistrationKeys,
MedusaError,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { MedusaRequest, MedusaResponse } from "../../../../types/routing"
@@ -16,5 +17,12 @@ export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
})
const [currency] = await remoteQuery(queryObject)
if (!currency) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Currency with code: ${req.params.code} was not found`
)
}
res.status(200).json({ currency })
}

View File

@@ -83,20 +83,11 @@ export const AdminCustomerAdressesParams = createFindParams({
}).merge(
z.object({
q: z.string().optional(),
address_name: z.union([z.string(), z.array(z.string())]).optional(),
is_default_shipping: z.boolean().optional(),
is_default_billing: z.boolean().optional(),
company: z.union([z.string(), z.array(z.string())]).optional(),
first_name: z.union([z.string(), z.array(z.string())]).optional(),
last_name: z.union([z.string(), z.array(z.string())]).optional(),
address_1: z.union([z.string(), z.array(z.string())]).optional(),
address_2: z.union([z.string(), z.array(z.string())]).optional(),
city: z.union([z.string(), z.array(z.string())]).optional(),
country_code: z.union([z.string(), z.array(z.string())]).optional(),
province: z.union([z.string(), z.array(z.string())]).optional(),
postal_code: z.union([z.string(), z.array(z.string())]).optional(),
phone: z.union([z.string(), z.array(z.string())]).optional(),
metadata: z.record(z.unknown()).optional(),
})
)

View File

@@ -1,18 +1,28 @@
import { remoteQueryObjectFromString } from "@medusajs/utils"
import {
ContainerRegistrationKeys,
MedusaError,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { MedusaRequest, MedusaResponse } from "../../../../types/routing"
import { defaultStoreCurrencyFields } from "../query-config"
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
const remoteQuery = req.scope.resolve("remoteQuery")
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const variables = { code: req.params.code }
const queryObject = remoteQueryObjectFromString({
entryPoint: "currency",
variables,
fields: defaultStoreCurrencyFields,
fields: req.remoteQueryConfig.fields,
})
const [currency] = await remoteQuery(queryObject)
if (!currency) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Currency with code: ${req.params.code} was not found`
)
}
res.status(200).json({ currency })
}

View File

@@ -1,18 +1,14 @@
import { transformQuery } from "../../../api/middlewares"
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
import { authenticate } from "../../../utils/authenticate-middleware"
import { validateAndTransformQuery } from "../../utils/validate-query"
import * as QueryConfig from "./query-config"
import {
StoreGetCurrenciesCurrencyParams,
StoreGetCurrenciesParams,
} from "./validators"
import { StoreGetCurrenciesParams, StoreGetCurrencyParams } from "./validators"
export const storeCurrencyRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["GET"],
matcher: "/store/currencies",
middlewares: [
transformQuery(
validateAndTransformQuery(
StoreGetCurrenciesParams,
QueryConfig.listTransformQueryConfig
),
@@ -22,8 +18,8 @@ export const storeCurrencyRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/store/currencies/:code",
middlewares: [
transformQuery(
StoreGetCurrenciesCurrencyParams,
validateAndTransformQuery(
StoreGetCurrencyParams,
QueryConfig.retrieveTransformQueryConfig
),
],

View File

@@ -1,20 +1,19 @@
export const defaultStoreCurrencyRelations = []
export const allowedStoreCurrencyRelations = []
export const defaultStoreCurrencyFields = [
"code",
"name",
"symbol",
"symbol_native",
"decimal_digits",
"rounding",
]
export const retrieveTransformQueryConfig = {
defaultFields: defaultStoreCurrencyFields,
defaultRelations: defaultStoreCurrencyRelations,
allowedRelations: allowedStoreCurrencyRelations,
defaults: defaultStoreCurrencyFields,
isList: false,
}
export const listTransformQueryConfig = {
defaultLimit: 20,
...retrieveTransformQueryConfig,
defaultLimit: 50,
isList: true,
}

View File

@@ -1,19 +1,19 @@
import { remoteQueryObjectFromString } from "@medusajs/utils"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { MedusaRequest, MedusaResponse } from "../../../types/routing"
import { defaultStoreCurrencyFields } from "./query-config"
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
const remoteQuery = req.scope.resolve("remoteQuery")
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "currency",
variables: {
filters: req.filterableFields,
order: req.listConfig.order,
skip: req.listConfig.skip,
take: req.listConfig.take,
...req.remoteQueryConfig.pagination,
},
fields: defaultStoreCurrencyFields,
fields: req.remoteQueryConfig.fields,
})
const { rows: currencies, metadata } = await remoteQuery(queryObject)

View File

@@ -1,30 +1,19 @@
import { Type } from "class-transformer"
import { IsOptional, IsString, ValidateNested } from "class-validator"
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
import { createFindParams, createSelectParams } from "../../utils/validators"
import { z } from "zod"
export class StoreGetCurrenciesCurrencyParams extends FindParams {}
/**
* Parameters used to filter and configure the pagination of the retrieved currencies.
*/
export class StoreGetCurrenciesParams extends extendedFindParamsMixin({
limit: 50,
export const StoreGetCurrencyParams = createSelectParams()
export type StoreGetCurrenciesParamsType = z.infer<
typeof StoreGetCurrenciesParams
>
export const StoreGetCurrenciesParams = createFindParams({
offset: 0,
}) {
/**
* Search parameter for currencies.
*/
@IsString({ each: true })
@IsOptional()
code?: string | string[]
// Additional filters from BaseFilterable
@IsOptional()
@ValidateNested({ each: true })
@Type(() => StoreGetCurrenciesParams)
$and?: StoreGetCurrenciesParams[]
@IsOptional()
@ValidateNested({ each: true })
@Type(() => StoreGetCurrenciesParams)
$or?: StoreGetCurrenciesParams[]
}
limit: 50,
}).merge(
z.object({
q: z.string().optional(),
code: z.union([z.string(), z.array(z.string())]).optional(),
$and: z.lazy(() => StoreGetCurrenciesParams.array()).optional(),
$or: z.lazy(() => StoreGetCurrenciesParams.array()).optional(),
})
)

View File

@@ -0,0 +1,23 @@
import { MedusaContainer } from "@medusajs/types"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
export const refetchCustomer = async (
customerId: string,
scope: MedusaContainer,
fields: string[]
) => {
const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "customer",
variables: {
filters: { id: customerId },
},
fields: fields,
})
const customers = await remoteQuery(queryObject)
return customers[0]
}

View File

@@ -1,51 +1,59 @@
import {
AuthenticatedMedusaRequest,
MedusaRequest,
MedusaResponse,
} from "../../../../../../types/routing"
import { CustomerAddressDTO, ICustomerModuleService } from "@medusajs/types"
import {
deleteCustomerAddressesWorkflow,
updateCustomerAddressesWorkflow,
} from "@medusajs/core-flows"
import { MedusaError } from "@medusajs/utils"
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import {
ContainerRegistrationKeys,
MedusaError,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { MedusaContainer } from "@medusajs/modules-sdk"
import {
StoreGetCustomerAddressParamsType,
StoreUpdateCustomerAddressType,
} from "../../../validators"
import { refetchCustomer } from "../../../helpers"
export const GET = async (
req: AuthenticatedMedusaRequest,
req: AuthenticatedMedusaRequest<StoreGetCustomerAddressParamsType>,
res: MedusaResponse
) => {
const id = req.auth.actor_id
const customerId = req.auth.actor_id
const customerModuleService = req.scope.resolve<ICustomerModuleService>(
ModuleRegistrationName.CUSTOMER
)
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "customer_address",
variables: {
filters: { id: req.params.address_id, customer_id: customerId },
},
fields: req.remoteQueryConfig.fields,
})
const [address] = await customerModuleService.listAddresses(
{ id: req.params.address_id, customer_id: id },
{
select: req.retrieveConfig.select,
relations: req.retrieveConfig.relations,
}
)
const [address] = await remoteQuery(queryObject)
if (!address) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Address with id: ${req.params.address_id} was not found`
)
}
res.status(200).json({ address })
}
export const POST = async (
req: AuthenticatedMedusaRequest<Partial<CustomerAddressDTO>>,
req: AuthenticatedMedusaRequest<StoreUpdateCustomerAddressType>,
res: MedusaResponse
) => {
const id = req.auth.actor_id!
const service = req.scope.resolve<ICustomerModuleService>(
ModuleRegistrationName.CUSTOMER
)
await validateCustomerAddress(service, id, req.params.address_id)
await validateCustomerAddress(req.scope, id, req.params.address_id)
const updateAddresses = updateCustomerAddressesWorkflow(req.scope)
const { result, errors } = await updateAddresses.run({
const { errors } = await updateAddresses.run({
input: {
selector: { id: req.params.address_id, customer_id: req.params.id },
update: req.validatedBody,
@@ -57,7 +65,13 @@ export const POST = async (
throw errors[0].error
}
res.status(200).json({ address: result[0] })
const customer = await refetchCustomer(
id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ customer })
}
export const DELETE = async (
@@ -65,14 +79,9 @@ export const DELETE = async (
res: MedusaResponse
) => {
const id = req.auth.actor_id
await validateCustomerAddress(req.scope, id, req.params.address_id)
const service = req.scope.resolve<ICustomerModuleService>(
ModuleRegistrationName.CUSTOMER
)
await validateCustomerAddress(service, id, req.params.address_id)
const deleteAddress = deleteCustomerAddressesWorkflow(req.scope)
const { errors } = await deleteAddress.run({
input: { ids: [req.params.address_id] },
throwOnError: false,
@@ -82,23 +91,35 @@ export const DELETE = async (
throw errors[0].error
}
const customer = await refetchCustomer(
id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({
id,
object: "address",
deleted: true,
parent: customer,
})
}
const validateCustomerAddress = async (
customerModuleService: ICustomerModuleService,
scope: MedusaContainer,
customerId: string,
addressId: string
) => {
const [address] = await customerModuleService.listAddresses(
{ id: addressId, customer_id: customerId },
{ select: ["id"] }
)
const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "customer_address",
variables: {
filters: { id: addressId, customer_id: customerId },
},
fields: ["id"],
})
const [address] = await remoteQuery(queryObject)
if (!address) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,

View File

@@ -1,43 +1,47 @@
import {
AuthenticatedMedusaRequest,
MedusaRequest,
MedusaResponse,
} from "../../../../../types/routing"
import {
CreateCustomerAddressDTO,
ICustomerModuleService,
} from "@medusajs/types"
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { createCustomerAddressesWorkflow } from "@medusajs/core-flows"
import {
StoreCreateCustomerAddressType,
StoreGetCustomerAddressesParamsType,
} from "../../validators"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { refetchCustomer } from "../../helpers"
export const GET = async (
req: AuthenticatedMedusaRequest,
req: AuthenticatedMedusaRequest<StoreGetCustomerAddressesParamsType>,
res: MedusaResponse
) => {
const customerId = req.auth.actor_id
const customerModuleService = req.scope.resolve<ICustomerModuleService>(
ModuleRegistrationName.CUSTOMER
)
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "customer_address",
variables: {
filters: { ...req.filterableFields, customer_id: customerId },
...req.remoteQueryConfig.pagination,
},
fields: req.remoteQueryConfig.fields,
})
const [addresses, count] = await customerModuleService.listAndCountAddresses(
{ ...req.filterableFields, customer_id: customerId },
req.listConfig
)
const { offset, limit } = req.validatedQuery
const { rows: addresses, metadata } = await remoteQuery(queryObject)
res.json({
count,
addresses,
offset,
limit,
count: metadata.count,
offset: metadata.skip,
limit: metadata.take,
})
}
export const POST = async (
req: AuthenticatedMedusaRequest<CreateCustomerAddressDTO>,
req: AuthenticatedMedusaRequest<StoreCreateCustomerAddressType>,
res: MedusaResponse
) => {
const customerId = req.auth.actor_id
@@ -50,7 +54,7 @@ export const POST = async (
},
]
const { result, errors } = await createAddresses.run({
const { errors } = await createAddresses.run({
input: { addresses },
throwOnError: false,
})
@@ -59,5 +63,11 @@ export const POST = async (
throw errors[0].error
}
res.status(200).json({ address: result[0] })
const customer = await refetchCustomer(
customerId,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ customer })
}

View File

@@ -3,20 +3,56 @@ import {
MedusaResponse,
} from "../../../../types/routing"
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import {
StoreGetCustomerParamsType,
StoreUpdateCustomerType,
} from "../validators"
import { refetchCustomer } from "../helpers"
import { MedusaError } from "@medusajs/utils"
import { updateCustomersWorkflow } from "@medusajs/core-flows"
export const GET = async (
req: AuthenticatedMedusaRequest,
req: AuthenticatedMedusaRequest<StoreGetCustomerParamsType>,
res: MedusaResponse
) => {
const id = req.auth.actor_id
const customer = await refetchCustomer(
id,
req.scope,
req.remoteQueryConfig.fields
)
const customerModule = req.scope.resolve(ModuleRegistrationName.CUSTOMER)
const customer = await customerModule.retrieve(id, {
select: req.retrieveConfig.select,
relations: req.retrieveConfig.relations,
})
if (!customer) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Customer with id: ${id} was not found`
)
}
res.json({ customer })
}
export const POST = async (
req: AuthenticatedMedusaRequest<StoreUpdateCustomerType>,
res: MedusaResponse
) => {
const customerId = req.auth.actor_id
const { errors } = await updateCustomersWorkflow(req.scope).run({
input: {
selector: { id: customerId },
update: req.validatedBody,
},
throwOnError: false,
})
if (Array.isArray(errors) && errors[0]) {
throw errors[0].error
}
const customer = await refetchCustomer(
req.params.id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ customer })
}

View File

@@ -1,63 +1,109 @@
import * as QueryConfig from "./query-config"
import {
StoreGetCustomersMeAddressesParams,
StoreGetCustomersMeParams,
StorePostCustomersMeAddressesAddressReq,
StorePostCustomersMeAddressesReq,
StorePostCustomersReq,
StoreCreateCustomer,
StoreCreateCustomerAddress,
StoreGetCustomerParams,
StoreGetCustomerAddressesParams,
StoreUpdateCustomer,
StoreUpdateCustomerAddress,
StoreGetCustomerAddressParams,
} from "./validators"
import { transformBody, transformQuery } from "../../../api/middlewares"
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
import { authenticate } from "../../../utils/authenticate-middleware"
import { validateAndTransformBody } from "../../utils/validate-body"
import { validateAndTransformQuery } from "../../utils/validate-query"
export const storeCustomerRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["POST"],
matcher: "/store/customers",
middlewares: [
authenticate("store", ["session", "bearer"], { allowUnregistered: true }),
validateAndTransformBody(StoreCreateCustomer),
validateAndTransformQuery(
StoreGetCustomerParams,
QueryConfig.retrieveTransformQueryConfig
),
],
},
{
method: "ALL",
matcher: "/store/customers/me*",
middlewares: [authenticate("store", ["session", "bearer"])],
},
{
method: "POST",
matcher: "/store/customers",
middlewares: [
authenticate("store", ["session", "bearer"], { allowUnregistered: true }),
],
},
{
method: ["POST"],
matcher: "/store/customers",
middlewares: [transformBody(StorePostCustomersReq)],
},
{
method: ["GET"],
matcher: "/store/customers/me",
middlewares: [
transformQuery(
StoreGetCustomersMeParams,
validateAndTransformQuery(
StoreGetCustomerParams,
QueryConfig.retrieveTransformQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/store/customers/me/addresses",
middlewares: [transformBody(StorePostCustomersMeAddressesReq)],
},
{
method: ["POST"],
matcher: "/store/customers/me/addresses/:address_id",
middlewares: [transformBody(StorePostCustomersMeAddressesAddressReq)],
matcher: "/store/customers/me",
middlewares: [
validateAndTransformBody(StoreUpdateCustomer),
validateAndTransformQuery(
StoreGetCustomerParams,
QueryConfig.retrieveTransformQueryConfig
),
],
},
{
method: ["GET"],
matcher: "/store/customers/me/addresses",
middlewares: [
transformQuery(
StoreGetCustomersMeAddressesParams,
validateAndTransformQuery(
StoreGetCustomerAddressesParams,
QueryConfig.listAddressesTransformQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/store/customers/me/addresses",
middlewares: [
validateAndTransformBody(StoreCreateCustomerAddress),
validateAndTransformQuery(
StoreGetCustomerParams,
QueryConfig.retrieveTransformQueryConfig
),
],
},
{
method: ["GET"],
matcher: "/store/customers/me/addresses/:address_id",
middlewares: [
validateAndTransformQuery(
StoreGetCustomerAddressParams,
QueryConfig.retrieveAddressTransformQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/store/customers/me/addresses/:address_id",
middlewares: [
validateAndTransformBody(StoreUpdateCustomerAddress),
validateAndTransformQuery(
StoreGetCustomerParams,
QueryConfig.retrieveTransformQueryConfig
),
],
},
{
method: ["DELETE"],
matcher: "/store/customers/me/addresses/:address_id",
middlewares: [
validateAndTransformQuery(
StoreGetCustomerAddressParams,
QueryConfig.retrieveAddressTransformQueryConfig
),
],
},
]

View File

@@ -1,8 +1,4 @@
import { CustomerDTO } from "@medusajs/types"
export const defaultStoreCustomersRelations = []
export const allowedStoreCustomersRelations = ["addresses", "groups"]
export const defaultStoreCustomersFields: (keyof CustomerDTO)[] = [
const defaultStoreCustomersFields = [
"id",
"email",
"company_name",
@@ -14,17 +10,14 @@ export const defaultStoreCustomersFields: (keyof CustomerDTO)[] = [
"deleted_at",
"created_at",
"updated_at",
"*addresses",
]
export const retrieveTransformQueryConfig = {
defaultFields: defaultStoreCustomersFields as string[],
defaultRelations: defaultStoreCustomersRelations,
allowedRelations: allowedStoreCustomersRelations,
defaults: defaultStoreCustomersFields,
isList: false,
}
export const defaultStoreCustomerAddressRelations = []
export const allowedStoreCustomerAddressRelations = ["customer"]
export const defaultStoreCustomerAddressFields = [
"id",
"company",
@@ -44,9 +37,7 @@ export const defaultStoreCustomerAddressFields = [
]
export const retrieveAddressTransformQueryConfig = {
defaultFields: defaultStoreCustomerAddressFields,
defaultRelations: defaultStoreCustomerAddressRelations,
allowedRelations: allowedStoreCustomerAddressRelations,
defaults: defaultStoreCustomerAddressFields,
isList: false,
}

View File

@@ -7,11 +7,12 @@ import {
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { CreateCustomerDTO } from "@medusajs/types"
import { createCustomerAccountWorkflow } from "@medusajs/core-flows"
import { refetchCustomer } from "./helpers"
import { StoreCreateCustomerType } from "./validators"
export const POST = async (
req: AuthenticatedMedusaRequest,
req: AuthenticatedMedusaRequest<StoreCreateCustomerType>,
res: MedusaResponse
) => {
if (req.auth.actor_id) {
@@ -32,7 +33,7 @@ export const POST = async (
}
const createCustomers = createCustomerAccountWorkflow(req.scope)
const customersData = req.validatedBody as CreateCustomerDTO
const customersData = req.validatedBody
const { result } = await createCustomers.run({
input: { customersData, authUserId: req.auth.auth_user_id },
@@ -42,5 +43,12 @@ export const POST = async (
if (req.session.auth_user) {
req.session.auth_user.app_metadata.customer_id = result.id
}
res.status(200).json({ customer: result })
const customer = await refetchCustomer(
result.id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ customer })
}

View File

@@ -1,244 +1,60 @@
import { OperatorMap } from "@medusajs/types"
import { Type } from "class-transformer"
import {
IsBoolean,
IsEmail,
IsNotEmpty,
IsObject,
IsOptional,
IsString,
ValidateNested,
} from "class-validator"
import { extendedFindParamsMixin, FindParams } from "../../../types/common"
import { OperatorMapValidator } from "../../../types/validators/operator-map"
import { createFindParams, createSelectParams } from "../../utils/validators"
import { z } from "zod"
import { AddressPayload } from "../../utils/common-validators"
export class StoreGetCustomersMeParams extends FindParams {}
export const StoreGetCustomerParams = createSelectParams()
export class StorePostCustomersReq {
@IsString()
@IsOptional()
first_name: string
export const StoreCreateCustomer = z.object({
email: z.string().email().optional(),
company_name: z.string().optional(),
first_name: z.string().optional(),
last_name: z.string().optional(),
phone: z.string().optional(),
})
@IsString()
@IsOptional()
last_name: string
export const StoreUpdateCustomer = z.object({
company_name: z.string().nullable().optional(),
first_name: z.string().nullable().optional(),
last_name: z.string().nullable().optional(),
phone: z.string().nullable().optional(),
})
@IsEmail()
email: string
export const StoreGetCustomerAddressParams = createSelectParams()
@IsString()
@IsOptional()
phone?: string
export const StoreCreateCustomerAddress = AddressPayload.merge(
z.object({
address_name: z.string().optional(),
is_default_shipping: z.boolean().optional(),
is_default_billing: z.boolean().optional(),
})
)
@IsString()
@IsOptional()
company_name?: string
export const StoreUpdateCustomerAddress = StoreCreateCustomerAddress
@IsObject()
@IsOptional()
metadata?: Record<string, unknown>
}
export const StoreGetCustomerAddressesParams = createFindParams({
offset: 0,
limit: 50,
}).merge(
z.object({
q: z.string().optional(),
city: z.union([z.string(), z.array(z.string())]).optional(),
country_code: z.union([z.string(), z.array(z.string())]).optional(),
postal_code: z.union([z.string(), z.array(z.string())]).optional(),
})
)
export class StorePostCustomersMeAddressesReq {
@IsNotEmpty()
@IsString()
@IsOptional()
address_name?: string
@IsBoolean()
@IsOptional()
is_default_shipping?: boolean
@IsBoolean()
@IsOptional()
is_default_billing?: boolean
@IsNotEmpty()
@IsString()
@IsOptional()
company?: string
@IsNotEmpty()
@IsString()
@IsOptional()
first_name?: string
@IsNotEmpty()
@IsString()
@IsOptional()
last_name?: string
@IsNotEmpty()
@IsString()
@IsOptional()
address_1?: string
@IsNotEmpty()
@IsString()
@IsOptional()
address_2?: string
@IsNotEmpty()
@IsString()
@IsOptional()
city?: string
@IsNotEmpty()
@IsString()
@IsOptional()
country_code?: string
@IsNotEmpty()
@IsString()
@IsOptional()
province?: string
@IsNotEmpty()
@IsString()
@IsOptional()
postal_code?: string
@IsNotEmpty()
@IsString()
@IsOptional()
phone?: string
@IsNotEmpty()
@IsString()
@IsOptional()
metadata?: Record<string, unknown>
}
export class StorePostCustomersMeAddressesAddressReq {
@IsNotEmpty()
@IsString()
@IsOptional()
address_name?: string
@IsBoolean()
@IsOptional()
is_default_shipping?: boolean
@IsBoolean()
@IsOptional()
is_default_billing?: boolean
@IsNotEmpty()
@IsString()
@IsOptional()
company?: string
@IsNotEmpty()
@IsString()
@IsOptional()
first_name?: string
@IsNotEmpty()
@IsString()
@IsOptional()
last_name?: string
@IsNotEmpty()
@IsString()
@IsOptional()
address_1?: string
@IsNotEmpty()
@IsString()
@IsOptional()
address_2?: string
@IsNotEmpty()
@IsString()
@IsOptional()
city?: string
@IsNotEmpty()
@IsString()
@IsOptional()
country_code?: string
@IsNotEmpty()
@IsString()
@IsOptional()
province?: string
@IsNotEmpty()
@IsString()
@IsOptional()
postal_code?: string
@IsNotEmpty()
@IsString()
@IsOptional()
phone?: string
@IsNotEmpty()
@IsString()
@IsOptional()
metadata?: Record<string, unknown>
}
export class StoreGetCustomersMeAddressesParams extends extendedFindParamsMixin(
{
limit: 100,
offset: 0,
}
) {
@IsOptional()
@IsString({ each: true })
address_name?: string | string[] | OperatorMap<string>
@IsOptional()
@IsBoolean()
is_default_shipping?: boolean
@IsOptional()
@IsBoolean()
is_default_billing?: boolean
@IsOptional()
@IsString({ each: true })
company?: string | string[] | OperatorMap<string> | null
@IsOptional()
@IsString({ each: true })
first_name?: string | string[] | OperatorMap<string> | null
@IsOptional()
@IsString({ each: true })
last_name?: string | string[] | OperatorMap<string> | null
@IsOptional()
@IsString({ each: true })
address_1?: string | string[] | OperatorMap<string> | null
@IsOptional()
@IsString({ each: true })
address_2?: string | string[] | OperatorMap<string> | null
@IsOptional()
@IsString({ each: true })
city?: string | string[] | OperatorMap<string> | null
@IsOptional()
@IsString({ each: true })
country_code?: string | string[] | OperatorMap<string> | null
@IsOptional()
@IsString({ each: true })
province?: string | string[] | OperatorMap<string> | null
@IsOptional()
@IsString({ each: true })
postal_code?: string | string[] | OperatorMap<string> | null
@IsOptional()
@IsString({ each: true })
phone?: string | string[] | OperatorMap<string> | null
@IsOptional()
@ValidateNested()
@Type(() => OperatorMapValidator)
metadata?: OperatorMap<Record<string, unknown>>
}
export type StoreGetCustomerParamsType = z.infer<typeof StoreGetCustomerParams>
export type StoreCreateCustomerType = z.infer<typeof StoreCreateCustomer>
export type StoreUpdateCustomerType = z.infer<typeof StoreUpdateCustomer>
export type StoreGetCustomerAddressParamsType = z.infer<
typeof StoreGetCustomerAddressParams
>
export type StoreGetCustomerAddressesParamsType = z.infer<
typeof StoreCreateCustomerAddress
>
export type StoreCreateCustomerAddressType = z.infer<
typeof StoreCreateCustomerAddress
>
export type StoreUpdateCustomerAddressType = z.infer<
typeof StoreUpdateCustomerAddress
>

View File

@@ -75,7 +75,7 @@ export interface CreateCustomerAddressDTO {
/**
* Holds custom data in key-value pairs.
*/
metadata?: Record<string, unknown>
metadata?: Record<string, unknown> | null
}
/**