chore: Move several modules to use zod for validation and cleanup routes (#7079)

This commit is contained in:
Stevche Radevski
2024-04-16 18:06:56 +02:00
committed by GitHub
parent 0a9b9b073d
commit a7df6235f9
48 changed files with 854 additions and 1331 deletions

View File

@@ -269,9 +269,9 @@ medusaIntegrationTestRunner({
.catch((e) => e)
expect(err.response.status).toEqual(400)
expect(err.response.data.message).toEqual(
"name must be a string, currency_code must be a string"
)
// expect(err.response.data.message).toEqual(
// "name must be a string, currency_code must be a string"
// )
})
it("should throw on unknown properties in create", async () => {
@@ -288,9 +288,9 @@ medusaIntegrationTestRunner({
.catch((e) => e)
expect(error.response.status).toEqual(400)
expect(error.response.data.message).toEqual(
"property foo should not exist"
)
// expect(error.response.data.message).toEqual(
// "property foo should not exist"
// )
})
it("should throw on unknown properties in update", async () => {
@@ -312,9 +312,9 @@ medusaIntegrationTestRunner({
.catch((e) => e)
expect(error.response.status).toEqual(400)
expect(error.response.data.message).toEqual(
"property foo should not exist"
)
// expect(error.response.data.message).toEqual(
// "property foo should not exist"
// )
})
it("should get all regions and count", async () => {

View File

@@ -7,31 +7,27 @@ import {
MedusaResponse,
} from "../../../../types/routing"
import { UpdateProductTypeDTO } from "@medusajs/types"
import { remoteQueryObjectFromString } from "@medusajs/utils"
import { refetchProductType } from "../helpers"
import {
AdminGetProductTypeParamsType,
AdminUpdateProductTypeType,
} from "../validators"
export const GET = async (
req: AuthenticatedMedusaRequest,
req: AuthenticatedMedusaRequest<AdminGetProductTypeParamsType>,
res: MedusaResponse
) => {
const remoteQuery = req.scope.resolve("remoteQuery")
const productType = await refetchProductType(
req.params.id,
req.scope,
req.remoteQueryConfig.fields
)
const variables = { id: req.params.id }
const queryObject = remoteQueryObjectFromString({
entryPoint: "product_type",
variables,
fields: req.remoteQueryConfig.fields,
})
const [product_type] = await remoteQuery(queryObject)
res.status(200).json({ product_type })
res.status(200).json({ product_type: productType })
}
export const POST = async (
req: AuthenticatedMedusaRequest<UpdateProductTypeDTO>,
req: AuthenticatedMedusaRequest<AdminUpdateProductTypeType>,
res: MedusaResponse
) => {
const { result, errors } = await updateProductTypesWorkflow(req.scope).run({

View File

@@ -1,15 +1,14 @@
import * as QueryConfig from "./query-config"
import {
AdminGetProductTypesProductTypeParams,
AdminGetProductTypesParams,
AdminPostProductTypesProductTypeReq,
AdminPostProductTypesReq,
} from "./validators"
import { transformBody, transformQuery } from "../../../api/middlewares"
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
import { authenticate } from "../../../utils/authenticate-middleware"
import { validateAndTransformQuery } from "../../utils/validate-query"
import {
AdminCreateProductType,
AdminGetProductTypeParams,
AdminGetProductTypesParams,
AdminUpdateProductType,
} from "./validators"
import { validateAndTransformBody } from "../../utils/validate-body"
export const adminProductTypeRoutesMiddlewares: MiddlewareRoute[] = [
{
@@ -22,7 +21,7 @@ export const adminProductTypeRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/admin/product-types",
middlewares: [
transformQuery(
validateAndTransformQuery(
AdminGetProductTypesParams,
QueryConfig.listProductTypesTransformQueryConfig
),
@@ -32,8 +31,8 @@ export const adminProductTypeRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/admin/product-types/:id",
middlewares: [
transformQuery(
AdminGetProductTypesProductTypeParams,
validateAndTransformQuery(
AdminGetProductTypeParams,
QueryConfig.retrieveProductTypeTransformQueryConfig
),
],
@@ -43,9 +42,9 @@ export const adminProductTypeRoutesMiddlewares: MiddlewareRoute[] = [
method: ["POST"],
matcher: "/admin/product-types",
middlewares: [
transformBody(AdminPostProductTypesReq),
transformQuery(
AdminGetProductTypesParams,
validateAndTransformBody(AdminCreateProductType),
validateAndTransformQuery(
AdminGetProductTypeParams,
QueryConfig.retrieveProductTypeTransformQueryConfig
),
],
@@ -54,9 +53,9 @@ export const adminProductTypeRoutesMiddlewares: MiddlewareRoute[] = [
method: ["POST"],
matcher: "/admin/product-types/:id",
middlewares: [
transformBody(AdminPostProductTypesProductTypeReq),
transformQuery(
AdminGetProductTypesProductTypeParams,
validateAndTransformBody(AdminUpdateProductType),
validateAndTransformQuery(
AdminGetProductTypeParams,
QueryConfig.retrieveProductTypeTransformQueryConfig
),
],

View File

@@ -7,15 +7,15 @@ import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import {
AdminGetProductTypesParams,
AdminPostProductTypesReq,
} from "./validators"
import { createProductTypesWorkflow } from "@medusajs/core-flows"
import { refetchProductType } from "./helpers"
import {
AdminCreateProductTypeType,
AdminGetProductTypesParamsType,
} from "./validators"
export const GET = async (
req: AuthenticatedMedusaRequest<AdminGetProductTypesParams>,
req: AuthenticatedMedusaRequest<AdminGetProductTypesParamsType>,
res: MedusaResponse
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
@@ -39,7 +39,7 @@ export const GET = async (
}
export const POST = async (
req: AuthenticatedMedusaRequest<AdminPostProductTypesReq>,
req: AuthenticatedMedusaRequest<AdminCreateProductTypeType>,
res: MedusaResponse
) => {
const input = [req.validatedBody]

View File

@@ -1,113 +1,48 @@
import { OperatorMap } from "@medusajs/types"
import { Type } from "class-transformer"
import { z } from "zod"
import {
IsNotEmpty,
IsObject,
IsOptional,
IsString,
ValidateNested,
} from "class-validator"
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
import { OperatorMapValidator } from "../../../types/validators/operator-map"
createFindParams,
createOperatorMap,
createSelectParams,
} from "../../utils/validators"
export class AdminGetProductTypesProductTypeParams extends FindParams {}
export type AdminGetProductTypeParamsType = z.infer<
typeof AdminGetProductTypeParams
>
export const AdminGetProductTypeParams = createSelectParams()
/**
* Parameters used to filter and configure the pagination of the retrieved product types.
*/
export class AdminGetProductTypesParams extends extendedFindParamsMixin({
export type AdminGetProductTypesParamsType = z.infer<
typeof AdminGetProductTypesParams
>
export const AdminGetProductTypesParams = createFindParams({
limit: 10,
offset: 0,
}) {
/**
* Term to search product product types by their title and handle.
*/
@IsString()
@IsOptional()
q?: string
}).merge(
z.object({
q: z.string().optional(),
id: z.union([z.string(), z.array(z.string())]).optional(),
value: z.union([z.string(), z.array(z.string())]).optional(),
// TODO: To be added in next iteration
// discount_condition_id: z.string().optional(),
created_at: createOperatorMap().optional(),
updated_at: createOperatorMap().optional(),
deleted_at: createOperatorMap().optional(),
$and: z.lazy(() => AdminGetProductTypesParams.array()).optional(),
$or: z.lazy(() => AdminGetProductTypesParams.array()).optional(),
})
)
/**
* Id to filter product product types by.
*/
@IsOptional()
@IsString()
id?: string | string[]
export type AdminCreateProductTypeType = z.infer<typeof AdminCreateProductType>
export const AdminCreateProductType = z
.object({
value: z.string(),
metadata: z.record(z.string(), z.unknown()).optional(),
})
.strict()
/**
* Title to filter product product types by.
*/
@IsOptional()
@IsString()
value?: string | string[] | OperatorMap<string>
/**
* Handle to filter product product types by.
*/
@IsOptional()
@IsString()
handle?: string | string[]
/**
* Date filters to apply on the product product types' `created_at` date.
*/
@IsOptional()
@ValidateNested()
@Type(() => OperatorMapValidator)
created_at?: OperatorMap<string>
/**
* Date filters to apply on the product product types' `updated_at` date.
*/
@IsOptional()
@ValidateNested()
@Type(() => OperatorMapValidator)
updated_at?: OperatorMap<string>
/**
* Date filters to apply on the product product types' `deleted_at` date.
*/
@ValidateNested()
@IsOptional()
@Type(() => OperatorMapValidator)
deleted_at?: OperatorMap<string>
// TODO: To be added in next iteration
// /**
// * Filter product types by their associated discount condition's ID.
// */
// @IsString()
// @IsOptional()
// discount_condition_id?: string
// Note: These are new in v2
// Additional filters from BaseFilterable
@IsOptional()
@ValidateNested({ each: true })
@Type(() => AdminGetProductTypesParams)
$and?: AdminGetProductTypesParams[]
@IsOptional()
@ValidateNested({ each: true })
@Type(() => AdminGetProductTypesParams)
$or?: AdminGetProductTypesParams[]
}
export class AdminPostProductTypesReq {
@IsString()
@IsNotEmpty()
value: string
@IsObject()
@IsOptional()
metadata?: Record<string, unknown>
}
export class AdminPostProductTypesProductTypeReq {
@IsString()
@IsOptional()
value?: string
@IsObject()
@IsOptional()
metadata?: Record<string, unknown>
}
export type AdminUpdateProductTypeType = z.infer<typeof AdminUpdateProductType>
export const AdminUpdateProductType = z
.object({
value: z.string().optional(),
metadata: z.record(z.string(), z.unknown()).optional(),
})
.strict()

View File

@@ -7,7 +7,10 @@ import {
updateProductOptionsWorkflow,
} from "@medusajs/core-flows"
import { remoteQueryObjectFromString } from "@medusajs/utils"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { refetchProduct, remapProductResponse } from "../../../helpers"
import { AdminUpdateProductOptionType } from "../../../validators"
@@ -15,7 +18,7 @@ export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const remoteQuery = req.scope.resolve("remoteQuery")
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const productId = req.params.id
const optionId = req.params.option_id

View File

@@ -4,7 +4,10 @@ import {
} from "../../../../../types/routing"
import { createProductOptionsWorkflow } from "@medusajs/core-flows"
import { remoteQueryObjectFromString } from "@medusajs/utils"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { refetchProduct, remapProductResponse } from "../../helpers"
import { AdminCreateProductOptionType } from "../../validators"
@@ -12,7 +15,7 @@ export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const remoteQuery = req.scope.resolve("remoteQuery")
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const productId = req.params.id
const queryObject = remoteQueryObjectFromString({

View File

@@ -7,7 +7,10 @@ import {
updateProductsWorkflow,
} from "@medusajs/core-flows"
import { remoteQueryObjectFromString } from "@medusajs/utils"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import {
refetchProduct,
remapKeysForProduct,
@@ -20,7 +23,7 @@ export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const remoteQuery = req.scope.resolve("remoteQuery")
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const variables = { id: req.params.id }

View File

@@ -7,7 +7,10 @@ import {
updateProductVariantsWorkflow,
} from "@medusajs/core-flows"
import { remoteQueryObjectFromString } from "@medusajs/utils"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import {
refetchProduct,
remapKeysForVariant,
@@ -20,7 +23,7 @@ export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const remoteQuery = req.scope.resolve("remoteQuery")
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
// TODO: Should we allow fetching a variant without knowing the product ID? In such case we'll need to change the route to /admin/products/variants/:id
const productId = req.params.id

View File

@@ -6,34 +6,23 @@ import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../types/routing"
import { UpdateRegionDTO } from "@medusajs/types"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { refetchRegion } from "../helpers"
import { AdminGetRegionParamsType, AdminUpdateRegionType } from "../validators"
export const GET = async (
req: AuthenticatedMedusaRequest,
req: AuthenticatedMedusaRequest<AdminGetRegionParamsType>,
res: MedusaResponse
) => {
const remoteQuery = req.scope.resolve("remoteQuery")
const variables = { id: req.params.id }
const queryObject = remoteQueryObjectFromString({
entryPoint: "region",
variables,
fields: req.remoteQueryConfig.fields,
})
const [region] = await remoteQuery(queryObject)
const region = await refetchRegion(
req.params.id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ region })
}
export const POST = async (
req: AuthenticatedMedusaRequest<UpdateRegionDTO>,
req: AuthenticatedMedusaRequest<AdminUpdateRegionType>,
res: MedusaResponse
) => {
const { result, errors } = await updateRegionsWorkflow(req.scope).run({
@@ -48,19 +37,13 @@ export const POST = async (
throw errors[0].error
}
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const region = await refetchRegion(
result[0].id,
req.scope,
req.remoteQueryConfig.fields
)
const queryObject = remoteQueryObjectFromString({
entryPoint: "region",
variables: {
filters: { id: req.params.id },
},
fields: req.remoteQueryConfig.fields,
})
const regions = await remoteQuery(queryObject)
res.status(200).json({ region: regions[0] })
res.status(200).json({ region })
}
export const DELETE = async (

View File

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

View File

@@ -1,15 +1,14 @@
import * as QueryConfig from "./query-config"
import { transformBody, transformQuery } from "../../../api/middlewares"
import {
AdminGetRegionsParams,
AdminGetRegionsRegionParams,
AdminPostRegionsRegionReq,
AdminPostRegionsReq,
} from "./validators"
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
import { authenticate } from "../../../utils/authenticate-middleware"
import { validateAndTransformQuery } from "../../utils/validate-query"
import {
AdminCreateRegion,
AdminGetRegionParams,
AdminGetRegionsParams,
AdminUpdateRegion,
} from "./validators"
import { validateAndTransformBody } from "../../utils/validate-body"
export const adminRegionRoutesMiddlewares: MiddlewareRoute[] = [
{
@@ -21,7 +20,7 @@ export const adminRegionRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/admin/regions",
middlewares: [
transformQuery(
validateAndTransformQuery(
AdminGetRegionsParams,
QueryConfig.listTransformQueryConfig
),
@@ -31,8 +30,8 @@ export const adminRegionRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/admin/regions/:id",
middlewares: [
transformQuery(
AdminGetRegionsRegionParams,
validateAndTransformQuery(
AdminGetRegionParams,
QueryConfig.retrieveTransformQueryConfig
),
],
@@ -41,22 +40,22 @@ export const adminRegionRoutesMiddlewares: MiddlewareRoute[] = [
method: ["POST"],
matcher: "/admin/regions",
middlewares: [
transformQuery(
AdminGetRegionsRegionParams,
validateAndTransformBody(AdminCreateRegion),
validateAndTransformQuery(
AdminGetRegionParams,
QueryConfig.retrieveTransformQueryConfig
),
transformBody(AdminPostRegionsReq),
],
},
{
method: ["POST"],
matcher: "/admin/regions/:id",
middlewares: [
transformQuery(
AdminGetRegionsRegionParams,
validateAndTransformBody(AdminUpdateRegion),
validateAndTransformQuery(
AdminGetRegionParams,
QueryConfig.retrieveTransformQueryConfig
),
transformBody(AdminPostRegionsRegionReq),
],
},
]

View File

@@ -2,16 +2,16 @@ import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../types/routing"
import { CreateRegionDTO } from "@medusajs/types"
import { createRegionsWorkflow } from "@medusajs/core-flows"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { AdminCreateRegionType, AdminGetRegionsParamsType } from "./validators"
import { refetchRegion } from "./helpers"
export const GET = async (
req: AuthenticatedMedusaRequest,
req: AuthenticatedMedusaRequest<AdminGetRegionsParamsType>,
res: MedusaResponse
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
@@ -36,14 +36,10 @@ export const GET = async (
}
export const POST = async (
req: AuthenticatedMedusaRequest<CreateRegionDTO>,
req: AuthenticatedMedusaRequest<AdminCreateRegionType>,
res: MedusaResponse
) => {
const input = [
{
...req.validatedBody,
},
]
const input = [req.validatedBody]
const { result, errors } = await createRegionsWorkflow(req.scope).run({
input: { regions: input },
@@ -54,17 +50,11 @@ export const POST = async (
throw errors[0].error
}
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const region = await refetchRegion(
result[0].id,
req.scope,
req.remoteQueryConfig.fields
)
const queryObject = remoteQueryObjectFromString({
entryPoint: "region",
variables: {
filters: { id: result[0].id },
},
fields: req.remoteQueryConfig.fields,
})
const regions = await remoteQuery(queryObject)
res.status(200).json({ region: regions[0] })
res.status(200).json({ region })
}

View File

@@ -1,130 +1,50 @@
import { OperatorMap } from "@medusajs/types"
import { Type } from "class-transformer"
import { z } from "zod"
import {
IsArray,
IsBoolean,
IsObject,
IsOptional,
IsString,
ValidateNested,
} from "class-validator"
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
import { OperatorMapValidator } from "../../../types/validators/operator-map"
createFindParams,
createOperatorMap,
createSelectParams,
} from "../../utils/validators"
export class AdminGetRegionsRegionParams extends FindParams {}
export type AdminGetRegionParamsType = z.infer<typeof AdminGetRegionParams>
export const AdminGetRegionParams = createSelectParams()
/**
* Parameters used to filter and configure the pagination of the retrieved regions.
*/
export class AdminGetRegionsParams extends extendedFindParamsMixin({
export type AdminGetRegionsParamsType = z.infer<typeof AdminGetRegionsParams>
export const AdminGetRegionsParams = createFindParams({
limit: 50,
offset: 0,
}) {
/**
* Search parameter for regions.
*/
@IsString({ each: true })
@IsOptional()
id?: string | string[]
}).merge(
z.object({
id: z.union([z.string(), z.array(z.string())]).optional(),
code: z.union([z.string(), z.array(z.string())]).optional(),
name: z.union([z.string(), z.array(z.string())]).optional(),
created_at: createOperatorMap().optional(),
updated_at: createOperatorMap().optional(),
deleted_at: createOperatorMap().optional(),
$and: z.lazy(() => AdminGetRegionsParams.array()).optional(),
$or: z.lazy(() => AdminGetRegionsParams.array()).optional(),
})
)
/**
* Filter by currency code
*/
@IsString({ each: true })
@IsOptional()
code?: string | string[]
export type AdminCreateRegionType = z.infer<typeof AdminCreateRegion>
export const AdminCreateRegion = z
.object({
name: z.string(),
currency_code: z.string(),
countries: z.array(z.string()).optional(),
automatic_taxes: z.boolean().optional(),
payment_providers: z.array(z.string()).optional(),
metadata: z.record(z.string(), z.unknown()).optional(),
})
.strict()
/**
* Filter by region name
*/
@IsString({ each: true })
@IsOptional()
name?: string | string[]
/**
* Date filters to apply on the regions' `created_at` date.
*/
@IsOptional()
@ValidateNested()
@Type(() => OperatorMapValidator)
created_at?: OperatorMap<string>
/**
* Date filters to apply on the regions' `updated_at` date.
*/
@IsOptional()
@ValidateNested()
@Type(() => OperatorMapValidator)
updated_at?: OperatorMap<string>
/**
* Date filters to apply on the regions' `deleted_at` date.
*/
@ValidateNested()
@IsOptional()
@Type(() => OperatorMapValidator)
deleted_at?: OperatorMap<string>
// Additional filters from BaseFilterable
@IsOptional()
@ValidateNested({ each: true })
@Type(() => AdminGetRegionsParams)
$and?: AdminGetRegionsParams[]
@IsOptional()
@ValidateNested({ each: true })
@Type(() => AdminGetRegionsParams)
$or?: AdminGetRegionsParams[]
}
export class AdminPostRegionsReq {
@IsString()
name: string
@IsString()
currency_code: string
@IsArray()
@IsOptional()
countries?: string[]
@IsBoolean()
@IsOptional()
automatic_taxes?: boolean
@IsObject()
@IsOptional()
metadata?: Record<string, unknown>
@IsOptional()
@IsArray()
@IsString({ each: true })
payment_providers?: string[]
}
export class AdminPostRegionsRegionReq {
@IsString()
@IsOptional()
name?: string
@IsString()
@IsOptional()
currency_code?: string
@IsArray()
@IsOptional()
countries?: string[]
@IsBoolean()
@IsOptional()
automatic_taxes?: boolean
@IsObject()
@IsOptional()
metadata?: Record<string, unknown>
@IsOptional()
@IsArray()
@IsString({ each: true })
payment_providers?: string[]
}
export type AdminUpdateRegionType = z.infer<typeof AdminUpdateRegion>
export const AdminUpdateRegion = z
.object({
name: z.string().optional(),
currency_code: z.string().optional(),
countries: z.array(z.string()).optional(),
automatic_taxes: z.boolean().optional(),
payment_providers: z.array(z.string()).optional(),
metadata: z.record(z.string(), z.unknown()).optional(),
})
.strict()

View File

@@ -3,31 +3,25 @@ import {
MedusaResponse,
} from "../../../../types/routing"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { AdminPostReservationsReservationReq } from "../validators"
AdminGetReservationParamsType,
AdminUpdateReservationType,
} from "../validators"
import { MedusaError } from "@medusajs/utils"
import { deleteReservationsWorkflow } from "@medusajs/core-flows"
import { updateReservationsWorkflow } from "@medusajs/core-flows"
import { refetchReservation } from "../helpers"
export const GET = async (
req: AuthenticatedMedusaRequest,
req: AuthenticatedMedusaRequest<AdminGetReservationParamsType>,
res: MedusaResponse
) => {
const { id } = req.params
const remoteQuery = req.scope.resolve("remoteQuery")
const variables = { id }
const queryObject = remoteQueryObjectFromString({
entryPoint: "reservation",
variables,
fields: req.remoteQueryConfig.fields,
})
const [reservation] = await remoteQuery(queryObject)
const reservation = await refetchReservation(
id,
req.scope,
req.remoteQueryConfig.fields
)
if (!reservation) {
throw new MedusaError(
@@ -40,7 +34,7 @@ export const GET = async (
}
export const POST = async (
req: AuthenticatedMedusaRequest<AdminPostReservationsReservationReq>,
req: AuthenticatedMedusaRequest<AdminUpdateReservationType>,
res: MedusaResponse
) => {
const { id } = req.params
@@ -55,18 +49,11 @@ export const POST = async (
throw errors[0].error
}
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "reservation",
variables: {
filters: { id: req.params.id },
},
fields: req.remoteQueryConfig.fields,
})
const [reservation] = await remoteQuery(queryObject)
const reservation = await refetchReservation(
id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ reservation })
}

View File

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

View File

@@ -1,15 +1,15 @@
import * as QueryConfig from "./query-config"
import {
AdminGetReservationsParams,
AdminGetReservationsReservationParams,
AdminPostReservationsReq,
AdminPostReservationsReservationReq,
} from "./validators"
import { transformBody, transformQuery } from "../../../api/middlewares"
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
import { authenticate } from "../../../utils/authenticate-middleware"
import { validateAndTransformQuery } from "../../utils/validate-query"
import {
AdminCreateReservation,
AdminGetReservationParams,
AdminGetReservationsParams,
AdminUpdateReservation,
} from "./validators"
import { validateAndTransformBody } from "../../utils/validate-body"
export const adminReservationRoutesMiddlewares: MiddlewareRoute[] = [
{
@@ -21,7 +21,7 @@ export const adminReservationRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/admin/reservations",
middlewares: [
transformQuery(
validateAndTransformQuery(
AdminGetReservationsParams,
QueryConfig.listTransformQueryConfig
),
@@ -31,8 +31,8 @@ export const adminReservationRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/admin/reservations/:id",
middlewares: [
transformQuery(
AdminGetReservationsReservationParams,
validateAndTransformQuery(
AdminGetReservationParams,
QueryConfig.retrieveTransformQueryConfig
),
],
@@ -41,22 +41,22 @@ export const adminReservationRoutesMiddlewares: MiddlewareRoute[] = [
method: ["POST"],
matcher: "/admin/reservations",
middlewares: [
transformQuery(
AdminGetReservationsReservationParams,
validateAndTransformBody(AdminCreateReservation),
validateAndTransformQuery(
AdminGetReservationParams,
QueryConfig.retrieveTransformQueryConfig
),
transformBody(AdminPostReservationsReq),
],
},
{
method: ["POST"],
matcher: "/admin/reservations/:id",
middlewares: [
transformQuery(
AdminGetReservationsReservationParams,
validateAndTransformBody(AdminUpdateReservation),
validateAndTransformQuery(
AdminGetReservationParams,
QueryConfig.retrieveTransformQueryConfig
),
transformBody(AdminPostReservationsReservationReq),
],
},
]

View File

@@ -7,11 +7,15 @@ import {
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { AdminPostReservationsReq } from "./validators"
import { createReservationsWorkflow } from "@medusajs/core-flows"
import {
AdminCreateReservationType,
AdminGetReservationsParamsType,
} from "./validators"
import { refetchReservation } from "./helpers"
export const GET = async (
req: AuthenticatedMedusaRequest,
req: AuthenticatedMedusaRequest<AdminGetReservationsParamsType>,
res: MedusaResponse
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
@@ -36,14 +40,10 @@ export const GET = async (
}
export const POST = async (
req: AuthenticatedMedusaRequest<AdminPostReservationsReq>,
req: AuthenticatedMedusaRequest<AdminCreateReservationType>,
res: MedusaResponse
) => {
const input = [
{
...req.validatedBody,
},
]
const input = [req.validatedBody]
const { result, errors } = await createReservationsWorkflow(req.scope).run({
input: { reservations: input },
@@ -54,17 +54,10 @@ export const POST = async (
throw errors[0].error
}
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "reservation",
variables: {
filters: { id: result[0].id },
},
fields: req.remoteQueryConfig.fields,
})
const [reservation] = await remoteQuery(queryObject)
const reservation = await refetchReservation(
result[0].id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ reservation })
}

View File

@@ -1,129 +1,53 @@
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
import {
IsArray,
IsBoolean,
IsNumber,
IsObject,
IsOptional,
IsString,
ValidateNested,
} from "class-validator"
createFindParams,
createOperatorMap,
createSelectParams,
} from "../../utils/validators"
import { z } from "zod"
import { IsType } from "../../../utils"
import { OperatorMap } from "@medusajs/types"
import { OperatorMapValidator } from "../../../types/validators/operator-map"
import { Type } from "class-transformer"
export type AdminGetReservationParamsType = z.infer<
typeof AdminGetReservationParams
>
export const AdminGetReservationParams = createSelectParams()
// TODO: naming
export class AdminGetReservationsReservationParams extends FindParams {}
/**
* Parameters used to filter and configure the pagination of the retrieved reservations.
*/
export class AdminGetReservationsParams extends extendedFindParamsMixin({
export type AdminGetReservationsParamsType = z.infer<
typeof AdminGetReservationsParams
>
export const AdminGetReservationsParams = createFindParams({
limit: 20,
offset: 0,
}) {
/**
* Location IDs to filter reservations by.
*/
@IsOptional()
@IsType([String, [String]])
location_id?: string | string[]
}).merge(
z.object({
location_id: z.union([z.string(), z.array(z.string())]).optional(),
inventory_item_id: z.union([z.string(), z.array(z.string())]).optional(),
line_item_id: z.union([z.string(), z.array(z.string())]).optional(),
created_by: z.union([z.string(), z.array(z.string())]).optional(),
description: z.union([z.string(), createOperatorMap()]).optional(),
quantity: createOperatorMap(z.number(), parseFloat).optional(),
created_at: createOperatorMap().optional(),
updated_at: createOperatorMap().optional(),
deleted_at: createOperatorMap().optional(),
})
)
/**
* Inventory item IDs to filter reservations by.
*/
@IsArray()
@IsString({ each: true })
@IsOptional()
inventory_item_id?: string[]
export type AdminCreateReservationType = z.infer<typeof AdminCreateReservation>
export const AdminCreateReservation = z
.object({
line_item_id: z.string().optional(),
location_id: z.string(),
inventory_item_id: z.string(),
quantity: z.number(),
description: z.string().optional(),
metadata: z.record(z.string(), z.unknown()).optional(),
})
.strict()
/**
* Line item IDs to filter reservations by.
*/
@IsArray()
@IsString({ each: true })
@IsOptional()
line_item_id?: string[]
/**
* "Create by" user IDs to filter reservations by.
*/
@IsArray()
@IsString({ each: true })
@IsOptional()
created_by?: string[]
/**
* Numerical filters to apply on the reservations' `quantity` field.
*/
@IsOptional()
@ValidateNested()
@Type(() => OperatorMapValidator)
quantity?: OperatorMap<number>
/**
* Date filters to apply on the reservations' `created_at` field.
*/
@IsOptional()
@ValidateNested()
@Type(() => OperatorMapValidator)
created_at?: OperatorMap<Date>
/**
* Date filters to apply on the reservations' `updated_at` field.
*/
@IsOptional()
@ValidateNested()
@Type(() => OperatorMapValidator)
updated_at?: OperatorMap<Date>
/**
* String filters to apply on the reservations' `description` field.
*/
@IsOptional()
@IsType([OperatorMapValidator, String])
description?: string | OperatorMap<string>
}
export class AdminPostReservationsReq {
@IsString()
@IsOptional()
line_item_id?: string
@IsString()
location_id: string
@IsString()
inventory_item_id: string
@IsNumber()
quantity: number
@IsString()
@IsOptional()
description?: string
@IsObject()
@IsOptional()
metadata?: Record<string, unknown>
}
export class AdminPostReservationsReservationReq {
@IsNumber()
@IsOptional()
quantity?: number
@IsString()
@IsOptional()
location_id?: string
@IsString()
@IsOptional()
description?: string
@IsObject()
@IsOptional()
metadata?: Record<string, unknown>
}
export type AdminUpdateReservationType = z.infer<typeof AdminUpdateReservation>
export const AdminUpdateReservation = z
.object({
location_id: z.string().optional(),
quantity: z.number().optional(),
description: z.string().optional(),
metadata: z.record(z.string(), z.unknown()).optional(),
})
.strict()

View File

@@ -1,27 +1,21 @@
import { addProductsToSalesChannelsWorkflow } from "@medusajs/core-flows"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../../../../types/routing"
import { defaultAdminSalesChannelFields } from "../../../../query-config"
import { AdminPostSalesChannelsChannelProductsBatchReq } from "../../../../validators"
import { refetchSalesChannel } from "../../../../helpers"
import { AdminSetSalesChannelProductsBatchType } from "../../../../validators"
export const POST = async (
req: AuthenticatedMedusaRequest,
req: AuthenticatedMedusaRequest<AdminSetSalesChannelProductsBatchType>,
res: MedusaResponse
) => {
const body =
req.validatedBody as AdminPostSalesChannelsChannelProductsBatchReq
const workflowInput = {
data: [
{
sales_channel_id: req.params.id,
product_ids: body.product_ids,
product_ids: req.validatedBody.product_ids,
},
],
}
@@ -35,15 +29,10 @@ export const POST = async (
throw errors[0].error
}
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "sales_channels",
variables: { id: req.params.id },
fields: defaultAdminSalesChannelFields,
})
const [sales_channel] = await remoteQuery(queryObject)
res.status(200).json({ sales_channel })
const salesChannel = await refetchSalesChannel(
req.params.id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ sales_channel: salesChannel })
}

View File

@@ -1,27 +1,20 @@
import { removeProductsFromSalesChannelsWorkflow } from "@medusajs/core-flows"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../../../../types/routing"
import { defaultAdminSalesChannelFields } from "../../../../query-config"
import { AdminPostSalesChannelsChannelProductsBatchReq } from "../../../../validators"
import { AdminSetSalesChannelProductsBatchType } from "../../../../validators"
import { refetchSalesChannel } from "../../../../helpers"
export const POST = async (
req: AuthenticatedMedusaRequest,
req: AuthenticatedMedusaRequest<AdminSetSalesChannelProductsBatchType>,
res: MedusaResponse
) => {
const body =
req.validatedBody as AdminPostSalesChannelsChannelProductsBatchReq
const workflowInput = {
data: [
{
sales_channel_id: req.params.id,
product_ids: body.product_ids,
product_ids: req.validatedBody.product_ids,
},
],
}
@@ -37,15 +30,10 @@ export const POST = async (
throw errors[0].error
}
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "sales_channels",
variables: { id: req.params.id },
fields: defaultAdminSalesChannelFields,
})
const [sales_channel] = await remoteQuery(queryObject)
res.status(200).json({ sales_channel })
const salesChannel = await refetchSalesChannel(
req.params.id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ sales_channel: salesChannel })
}

View File

@@ -2,39 +2,31 @@ import {
deleteSalesChannelsWorkflow,
updateSalesChannelsWorkflow,
} from "@medusajs/core-flows"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../types/routing"
import { defaultAdminSalesChannelFields } from "../query-config"
import {
AdminGetSalesChannelParamsType,
AdminUpdateSalesChannelType,
} from "../validators"
import { refetchSalesChannel } from "../helpers"
export const GET = async (
req: AuthenticatedMedusaRequest,
req: AuthenticatedMedusaRequest<AdminGetSalesChannelParamsType>,
res: MedusaResponse
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const salesChannel = await refetchSalesChannel(
req.params.id,
req.scope,
req.remoteQueryConfig.fields
)
const variables = {
id: req.params.id,
}
const queryObject = remoteQueryObjectFromString({
entryPoint: "sales_channels",
variables,
fields: req.remoteQueryConfig.fields,
})
const [sales_channel] = await remoteQuery(queryObject)
res.json({ sales_channel })
res.json({ sales_channel: salesChannel })
}
export const POST = async (
req: AuthenticatedMedusaRequest,
req: AuthenticatedMedusaRequest<AdminUpdateSalesChannelType>,
res: MedusaResponse
) => {
const { errors } = await updateSalesChannelsWorkflow(req.scope).run({
@@ -49,17 +41,12 @@ export const POST = async (
throw errors[0].error
}
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "sales_channels",
variables: { id: req.params.id },
fields: defaultAdminSalesChannelFields,
})
const [sales_channel] = await remoteQuery(queryObject)
res.status(200).json({ sales_channel })
const salesChannel = await refetchSalesChannel(
req.params.id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ sales_channel: salesChannel })
}
export const DELETE = async (

View File

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

View File

@@ -1,14 +1,15 @@
import { transformBody, transformQuery } from "../../../api/middlewares"
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
import { authenticate } from "../../../utils/authenticate-middleware"
import { maybeApplyLinkFilter } from "../../utils/maybe-apply-link-filter"
import { validateAndTransformBody } from "../../utils/validate-body"
import { validateAndTransformQuery } from "../../utils/validate-query"
import * as QueryConfig from "./query-config"
import {
AdminCreateSalesChannel,
AdminGetSalesChannelParams,
AdminGetSalesChannelsParams,
AdminGetSalesChannelsSalesChannelParams,
AdminPostSalesChannelsChannelProductsBatchReq,
AdminPostSalesChannelsReq,
AdminPostSalesChannelsSalesChannelReq,
AdminSetSalesChannelProductsBatch,
AdminUpdateSalesChannel,
} from "./validators"
export const adminSalesChannelRoutesMiddlewares: MiddlewareRoute[] = [
@@ -21,7 +22,7 @@ export const adminSalesChannelRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/admin/sales-channels",
middlewares: [
transformQuery(
validateAndTransformQuery(
AdminGetSalesChannelsParams,
QueryConfig.listTransformQueryConfig
),
@@ -41,8 +42,8 @@ export const adminSalesChannelRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/admin/sales-channels/:id",
middlewares: [
transformQuery(
AdminGetSalesChannelsSalesChannelParams,
validateAndTransformQuery(
AdminGetSalesChannelParams,
QueryConfig.retrieveTransformQueryConfig
),
],
@@ -51,9 +52,9 @@ export const adminSalesChannelRoutesMiddlewares: MiddlewareRoute[] = [
method: ["POST"],
matcher: "/admin/sales-channels",
middlewares: [
transformBody(AdminPostSalesChannelsReq),
transformQuery(
AdminGetSalesChannelsSalesChannelParams,
validateAndTransformBody(AdminCreateSalesChannel),
validateAndTransformQuery(
AdminGetSalesChannelParams,
QueryConfig.retrieveTransformQueryConfig
),
],
@@ -61,7 +62,13 @@ export const adminSalesChannelRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["POST"],
matcher: "/admin/sales-channels/:id",
middlewares: [transformBody(AdminPostSalesChannelsSalesChannelReq)],
middlewares: [
validateAndTransformBody(AdminUpdateSalesChannel),
validateAndTransformQuery(
AdminGetSalesChannelParams,
QueryConfig.retrieveTransformQueryConfig
),
],
},
{
method: ["DELETE"],
@@ -71,11 +78,23 @@ export const adminSalesChannelRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["POST"],
matcher: "/admin/sales-channels/:id/products/batch/add",
middlewares: [transformBody(AdminPostSalesChannelsChannelProductsBatchReq)],
middlewares: [
validateAndTransformBody(AdminSetSalesChannelProductsBatch),
validateAndTransformQuery(
AdminGetSalesChannelParams,
QueryConfig.retrieveTransformQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/admin/sales-channels/:id/products/batch/remove",
middlewares: [transformBody(AdminPostSalesChannelsChannelProductsBatchReq)],
middlewares: [
validateAndTransformBody(AdminSetSalesChannelProductsBatch),
validateAndTransformQuery(
AdminGetSalesChannelParams,
QueryConfig.retrieveTransformQueryConfig
),
],
},
]

View File

@@ -1,5 +1,4 @@
import { createSalesChannelsWorkflow } from "@medusajs/core-flows"
import { CreateSalesChannelDTO } from "@medusajs/types"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
@@ -8,21 +7,24 @@ import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../types/routing"
import { refetchSalesChannel } from "./helpers"
import {
AdminCreateSalesChannelType,
AdminGetSalesChannelsParamsType,
} from "./validators"
export const GET = async (
req: AuthenticatedMedusaRequest,
req: AuthenticatedMedusaRequest<AdminGetSalesChannelsParamsType>,
res: MedusaResponse
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const variables = {
filters: req.filterableFields,
...req.remoteQueryConfig.pagination,
}
const queryObject = remoteQueryObjectFromString({
entryPoint: "sales_channels",
variables,
variables: {
filters: req.filterableFields,
...req.remoteQueryConfig.pagination,
},
fields: req.remoteQueryConfig.fields,
})
@@ -37,7 +39,7 @@ export const GET = async (
}
export const POST = async (
req: AuthenticatedMedusaRequest<CreateSalesChannelDTO>,
req: AuthenticatedMedusaRequest<AdminCreateSalesChannelType>,
res: MedusaResponse
) => {
const salesChannelsData = [req.validatedBody]
@@ -51,17 +53,11 @@ export const POST = async (
throw errors[0].error
}
const salesChannel = result[0]
const salesChannel = await refetchSalesChannel(
result[0].id,
req.scope,
req.remoteQueryConfig.fields
)
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "sales_channels",
variables: { id: salesChannel.id },
fields: req.remoteQueryConfig.fields,
})
const [sales_channel] = await remoteQuery(queryObject)
res.status(200).json({ sales_channel })
res.status(200).json({ sales_channel: salesChannel })
}

View File

@@ -1,122 +1,59 @@
import { OperatorMap } from "@medusajs/types"
import { Type } from "class-transformer"
import { z } from "zod"
import {
IsArray,
IsBoolean,
IsNotEmpty,
IsOptional,
IsString,
ValidateNested,
} from "class-validator"
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
import { OperatorMapValidator } from "../../../types/validators/operator-map"
createFindParams,
createOperatorMap,
createSelectParams,
} from "../../utils/validators"
export class AdminGetSalesChannelsSalesChannelParams extends FindParams {}
export type AdminGetSalesChannelParamsType = z.infer<
typeof AdminGetSalesChannelParams
>
export const AdminGetSalesChannelParams = createSelectParams()
export class AdminGetSalesChannelsParams extends extendedFindParamsMixin({
export type AdminGetSalesChannelsParamsType = z.infer<
typeof AdminGetSalesChannelsParams
>
export const AdminGetSalesChannelsParams = createFindParams({
limit: 20,
offset: 0,
}) {
/**
* ID to filter sales channels by.
*/
@IsString()
@IsOptional()
id?: string
}).merge(
z.object({
id: z.union([z.string(), z.array(z.string())]).optional(),
name: z.union([z.string(), z.array(z.string())]).optional(),
description: z.string().optional(),
created_at: createOperatorMap().optional(),
updated_at: createOperatorMap().optional(),
deleted_at: createOperatorMap().optional(),
location_id: z.union([z.string(), z.array(z.string())]).optional(),
publishable_key_id: z.union([z.string(), z.array(z.string())]).optional(),
$and: z.lazy(() => AdminGetSalesChannelsParams.array()).optional(),
$or: z.lazy(() => AdminGetSalesChannelsParams.array()).optional(),
})
)
/**
* Name to filter sales channels by.
*/
@IsOptional()
@IsString()
name?: string
export type AdminCreateSalesChannelType = z.infer<
typeof AdminCreateSalesChannel
>
export const AdminCreateSalesChannel = z.object({
name: z.string(),
description: z.string().optional(),
is_disabled: z.boolean().optional(),
metadata: z.record(z.string(), z.unknown()).optional(),
})
/**
* Description to filter sales channels by.
*/
@IsOptional()
@IsString()
description?: string
export type AdminUpdateSalesChannelType = z.infer<
typeof AdminUpdateSalesChannel
>
export const AdminUpdateSalesChannel = z.object({
name: z.string().optional(),
description: z.string().optional(),
is_disabled: z.boolean().optional(),
metadata: z.record(z.string(), z.unknown()).optional(),
})
/**
* Date filters to apply on sales channels' `created_at` field.
*/
@IsOptional()
@ValidateNested()
@Type(() => OperatorMapValidator)
created_at?: OperatorMap<string>
/**
* Filter by location id
*/
@IsString({ each: true })
@IsOptional()
location_id?: string | string[]
/**
* Filter by publishable api keys
*/
@IsString({ each: true })
@IsOptional()
publishable_key_id?: string | string[]
/**
* Date filters to apply on sales channels' `updated_at` field.
*/
@IsOptional()
@ValidateNested()
@Type(() => OperatorMapValidator)
updated_at?: OperatorMap<string>
@IsOptional()
@ValidateNested({ each: true })
@Type(() => AdminGetSalesChannelsParams)
$and?: AdminGetSalesChannelsParams[]
@IsOptional()
@ValidateNested({ each: true })
@Type(() => AdminGetSalesChannelsParams)
$or?: AdminGetSalesChannelsParams[]
}
export class AdminPostSalesChannelsReq {
@IsString()
name: string
@IsString()
@IsOptional()
description: string
@IsBoolean()
@IsOptional()
is_disabled?: boolean
@IsNotEmpty()
@IsString()
@IsOptional()
metadata?: Record<string, unknown>
}
export class AdminPostSalesChannelsSalesChannelReq {
@IsOptional()
@IsString()
name?: string
@IsOptional()
@IsString()
description?: string
@IsBoolean()
@IsOptional()
is_disabled?: boolean
@IsNotEmpty()
@IsString()
@IsOptional()
metadata?: Record<string, unknown>
}
export class AdminPostSalesChannelsChannelProductsBatchReq {
@IsArray()
product_ids: string[]
}
export type AdminSetSalesChannelProductsBatchType = z.infer<
typeof AdminSetSalesChannelProductsBatch
>
export const AdminSetSalesChannelProductsBatch = z.object({
product_ids: z.array(z.string()),
})

View File

@@ -1,7 +1,3 @@
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import {
AdminShippingOptionDeleteResponse,
AdminShippingOptionRetrieveResponse,
@@ -15,6 +11,7 @@ import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../types/routing"
import { refetchShippingOption } from "../helpers"
export const POST = async (
req: AuthenticatedMedusaRequest<AdminUpdateShippingOptionType>,
@@ -33,18 +30,11 @@ export const POST = async (
throw errors[0].error
}
const shippingOptionId = result[0].id
const query = remoteQueryObjectFromString({
entryPoint: "shipping_options",
variables: {
id: shippingOptionId,
},
fields: req.remoteQueryConfig.fields,
})
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const [shippingOption] = await remoteQuery(query)
const shippingOption = await refetchShippingOption(
result[0].id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ shipping_option: shippingOption })
}

View File

@@ -1,19 +1,14 @@
import { addRulesToFulfillmentShippingOptionWorkflow } from "@medusajs/core-flows"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { AdminShippingOptionRetrieveResponse } from "@medusajs/types"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../../../../types/routing"
import { AdminShippingOptionRulesBatchAddType } from "../../../../validators"
import { refetchShippingOption } from "../../../../helpers"
export const POST = async (
req: AuthenticatedMedusaRequest<
AdminShippingOptionRulesBatchAddType
>,
req: AuthenticatedMedusaRequest<AdminShippingOptionRulesBatchAddType>,
res: MedusaResponse<AdminShippingOptionRetrieveResponse>
) => {
const id = req.params.id
@@ -33,16 +28,10 @@ export const POST = async (
throw errors[0].error
}
const query = remoteQueryObjectFromString({
entryPoint: "shipping_options",
variables: {
id: req.params.id,
},
fields: req.remoteQueryConfig.fields,
})
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const [shippingOption] = await remoteQuery(query)
const shippingOption = await refetchShippingOption(
req.params.id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ shipping_option: shippingOption })
}

View File

@@ -1,14 +1,11 @@
import { removeRulesFromFulfillmentShippingOptionWorkflow } from "@medusajs/core-flows"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { AdminShippingOptionRetrieveResponse } from "@medusajs/types"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../../../../types/routing"
import { AdminShippingOptionRulesBatchRemoveType } from "../../../../validators"
import { refetchShippingOption } from "../../../../helpers"
export const POST = async (
req: AuthenticatedMedusaRequest<AdminShippingOptionRulesBatchRemoveType>,
@@ -25,16 +22,10 @@ export const POST = async (
throw errors[0].error
}
const query = remoteQueryObjectFromString({
entryPoint: "shipping_options",
variables: {
id: req.params.id,
},
fields: req.remoteQueryConfig.fields,
})
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const [shippingOption] = await remoteQuery(query)
const shippingOption = await refetchShippingOption(
req.params.id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ shipping_option: shippingOption })
}

View File

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

View File

@@ -3,7 +3,7 @@ import { authenticate } from "../../../utils/authenticate-middleware"
import {
AdminCreateShippingOption,
AdminGetShippingOptionParams,
AdminListShippingOptionParams,
AdminGetShippingOptionsParams,
AdminShippingOptionRulesBatchAdd,
AdminShippingOptionRulesBatchRemove,
AdminUpdateShippingOption,
@@ -20,68 +20,62 @@ export const adminShippingOptionRoutesMiddlewares: MiddlewareRoute[] = [
matcher: "/admin/shipping-options*",
middlewares: [authenticate("admin", ["bearer", "session"])],
},
{
method: ["GET"],
matcher: "/admin/shipping-options",
middlewares: [
validateAndTransformQuery(
AdminListShippingOptionParams,
AdminGetShippingOptionsParams,
listTransformQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/admin/shipping-options",
middlewares: [
validateAndTransformBody(AdminCreateShippingOption),
validateAndTransformQuery(
AdminGetShippingOptionParams,
retrieveTransformQueryConfig
),
validateAndTransformBody(AdminCreateShippingOption),
],
},
{
method: ["POST"],
matcher: "/admin/shipping-options/:id",
middlewares: [
validateAndTransformBody(AdminUpdateShippingOption),
validateAndTransformQuery(
AdminGetShippingOptionParams,
retrieveTransformQueryConfig
),
validateAndTransformBody(AdminUpdateShippingOption),
],
},
{
method: ["DELETE"],
matcher: "/admin/shipping-options/:id",
},
{
method: ["POST"],
matcher: "/admin/shipping-options/:id/rules/batch/add",
middlewares: [
validateAndTransformBody(AdminShippingOptionRulesBatchAdd),
validateAndTransformQuery(
AdminGetShippingOptionParams,
retrieveTransformQueryConfig
),
validateAndTransformBody(AdminShippingOptionRulesBatchAdd),
],
},
{
method: ["POST"],
matcher: "/admin/shipping-options/:id/rules/batch/remove",
middlewares: [
validateAndTransformBody(AdminShippingOptionRulesBatchRemove),
validateAndTransformQuery(
AdminGetShippingOptionParams,
retrieveTransformQueryConfig
),
validateAndTransformBody(AdminShippingOptionRulesBatchRemove),
],
},
]

View File

@@ -5,28 +5,30 @@ import {
} from "@medusajs/utils"
import {
AdminShippingOptionListResponse,
AdminShippingOptionRetrieveResponse
AdminShippingOptionRetrieveResponse,
} from "@medusajs/types"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../types/routing"
import { AdminCreateShippingOptionType } from "./validators"
import {
AdminCreateShippingOptionType,
AdminGetShippingOptionsParamsType,
} from "./validators"
import { refetchShippingOption } from "./helpers"
export const GET = async (
req: AuthenticatedMedusaRequest,
req: AuthenticatedMedusaRequest<AdminGetShippingOptionsParamsType>,
res: MedusaResponse<AdminShippingOptionListResponse>
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const variables = {
filters: req.filterableFields,
...req.remoteQueryConfig.pagination,
}
const queryObject = remoteQueryObjectFromString({
entryPoint: "shipping_options",
variables,
variables: {
filters: req.filterableFields,
...req.remoteQueryConfig.pagination,
},
fields: req.remoteQueryConfig.fields,
})
@@ -57,18 +59,11 @@ export const POST = async (
throw errors[0].error
}
const shippingOptionId = result[0].id
const query = remoteQueryObjectFromString({
entryPoint: "shipping_options",
variables: {
id: shippingOptionId,
},
fields: req.remoteQueryConfig.fields,
})
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const [shippingOption] = await remoteQuery(query)
const shippingOption = await refetchShippingOption(
result[0].id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ shipping_option: shippingOption })
}

View File

@@ -5,8 +5,15 @@ import {
import { z } from "zod"
import { createFindParams, createSelectParams } from "../../utils/validators"
export type AdminGetShippingOptionParamsType = z.infer<
typeof AdminGetShippingOptionParams
>
export const AdminGetShippingOptionParams = createSelectParams()
export const AdminListShippingOptionParams = createFindParams({
export type AdminGetShippingOptionsParamsType = z.infer<
typeof AdminGetShippingOptionsParams
>
export const AdminGetShippingOptionsParams = createFindParams({
offset: 0,
limit: 20,
})
@@ -15,6 +22,9 @@ export const AdminListShippingOptionParams = createFindParams({
* SHIPPING OPTIONS RULES
*/
export type AdminCreateShippingOptionRuleType = z.infer<
typeof AdminCreateShippingOptionRule
>
export const AdminCreateShippingOptionRule = z
.object({
operator: z.nativeEnum(RuleOperator),
@@ -23,26 +33,24 @@ export const AdminCreateShippingOptionRule = z
})
.strict()
export type AdminShippingOptionRulesBatchAddType = z.infer<
typeof AdminShippingOptionRulesBatchAdd
>
export const AdminShippingOptionRulesBatchAdd = z
.object({
rules: AdminCreateShippingOptionRule.array(),
})
.strict()
export type AdminShippingOptionRulesBatchAddType = z.infer<
typeof AdminShippingOptionRulesBatchAdd
export type AdminShippingOptionRulesBatchRemoveType = z.infer<
typeof AdminShippingOptionRulesBatchRemove
>
export const AdminShippingOptionRulesBatchRemove = z
.object({
rule_ids: z.array(z.string()),
})
.strict()
export type AdminShippingOptionRulesBatchRemoveType = z.infer<
typeof AdminShippingOptionRulesBatchRemove
>
/**
* SHIPPING OPTIONS
*/
@@ -70,7 +78,7 @@ export const AdminCreateShippingOptionPriceWithRegion = z
})
.strict()
export const AdminUpdateShippingOptionPriceWithCurrency =z
export const AdminUpdateShippingOptionPriceWithCurrency = z
.object({
id: z.string().optional(),
currency_code: z.string().optional(),
@@ -78,15 +86,17 @@ export const AdminUpdateShippingOptionPriceWithCurrency =z
})
.strict()
export const AdminUpdateShippingOptionPriceWithRegion =
z
.object({
id: z.string().optional(),
region_id: z.string().optional(),
amount: z.number().optional(),
})
.strict()
export const AdminUpdateShippingOptionPriceWithRegion = z
.object({
id: z.string().optional(),
region_id: z.string().optional(),
amount: z.number().optional(),
})
.strict()
export type AdminCreateShippingOptionType = z.infer<
typeof AdminCreateShippingOption
>
export const AdminCreateShippingOption = z
.object({
name: z.string(),
@@ -103,10 +113,9 @@ export const AdminCreateShippingOption = z
})
.strict()
export type AdminCreateShippingOptionType = z.infer<
typeof AdminCreateShippingOption
export type AdminUpdateShippingOptionType = z.infer<
typeof AdminUpdateShippingOption
>
export const AdminUpdateShippingOption = z
.object({
id: z.string(),
@@ -122,7 +131,3 @@ export const AdminUpdateShippingOption = z
.optional(),
})
.strict()
export type AdminUpdateShippingOptionType = z.infer<
typeof AdminUpdateShippingOption
>

View File

@@ -5,28 +5,22 @@ import {
IFulfillmentModuleService,
} from "@medusajs/types"
import { deleteShippingProfileWorkflow } from "@medusajs/core-flows"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../types/routing"
import { AdminGetShippingProfileParamsType } from "../validators"
import { refetchShippingProfile } from "../helpers"
export const GET = async (
req: AuthenticatedMedusaRequest,
req: AuthenticatedMedusaRequest<AdminGetShippingProfileParamsType>,
res: MedusaResponse<AdminShippingProfileResponse>
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const query = remoteQueryObjectFromString({
entryPoint: "shipping_profiles",
variables: { id: req.params.id },
fields: req.remoteQueryConfig.fields,
})
const [shippingProfile] = await remoteQuery(query)
const shippingProfile = await refetchShippingProfile(
req.params.id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ shipping_profile: shippingProfile })
}

View File

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

View File

@@ -8,8 +8,8 @@ import {
} from "./query-config"
import {
AdminCreateShippingProfile,
AdminShippingProfileParams,
AdminShippingProfilesParams,
AdminGetShippingProfileParams,
AdminGetShippingProfilesParams,
} from "./validators"
export const adminShippingProfilesMiddlewares: MiddlewareRoute[] = [
@@ -21,11 +21,11 @@ export const adminShippingProfilesMiddlewares: MiddlewareRoute[] = [
method: ["POST"],
matcher: "/admin/shipping-profiles",
middlewares: [
validateAndTransformBody(AdminCreateShippingProfile),
validateAndTransformQuery(
AdminShippingProfilesParams,
AdminGetShippingProfilesParams,
retrieveTransformQueryConfig
),
validateAndTransformBody(AdminCreateShippingProfile),
],
},
{
@@ -33,7 +33,7 @@ export const adminShippingProfilesMiddlewares: MiddlewareRoute[] = [
matcher: "/admin/shipping-profiles",
middlewares: [
validateAndTransformQuery(
AdminShippingProfilesParams,
AdminGetShippingProfilesParams,
listTransformQueryConfig
),
],
@@ -43,7 +43,7 @@ export const adminShippingProfilesMiddlewares: MiddlewareRoute[] = [
matcher: "/admin/shipping-profiles/:id",
middlewares: [
validateAndTransformQuery(
AdminShippingProfileParams,
AdminGetShippingProfileParams,
retrieveTransformQueryConfig
),
],

View File

@@ -11,7 +11,11 @@ import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../types/routing"
import { AdminCreateShippingProfileType } from "./validators"
import {
AdminCreateShippingProfileType,
AdminGetShippingProfilesParamsType,
} from "./validators"
import { refetchShippingProfile } from "./helpers"
export const POST = async (
req: AuthenticatedMedusaRequest<AdminCreateShippingProfileType>,
@@ -30,23 +34,17 @@ export const POST = async (
throw errors[0].error
}
const shippingProfileId = result?.[0].id
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const query = remoteQueryObjectFromString({
entryPoint: "shipping_profiles",
variables: { id: shippingProfileId },
fields: req.remoteQueryConfig.fields,
})
const [shippingProfile] = await remoteQuery(query)
const shippingProfile = await refetchShippingProfile(
result?.[0].id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ shipping_profile: shippingProfile })
}
export const GET = async (
req: AuthenticatedMedusaRequest,
req: AuthenticatedMedusaRequest<AdminGetShippingProfilesParamsType>,
res: MedusaResponse<AdminShippingProfilesResponse>
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)

View File

@@ -1,8 +1,15 @@
import { z } from "zod"
import { createFindParams, createSelectParams } from "../../utils/validators"
export const AdminShippingProfileParams = createSelectParams()
export const AdminShippingProfilesParams = createFindParams({
export type AdminGetShippingProfileParamsType = z.infer<
typeof AdminGetShippingProfileParams
>
export const AdminGetShippingProfileParams = createSelectParams()
export type AdminGetShippingProfilesParamsType = z.infer<
typeof AdminGetShippingProfilesParams
>
export const AdminGetShippingProfilesParams = createFindParams({
limit: 20,
offset: 0,
}).merge(
@@ -12,6 +19,9 @@ export const AdminShippingProfilesParams = createFindParams({
})
)
export type AdminCreateShippingProfileType = z.infer<
typeof AdminCreateShippingProfile
>
export const AdminCreateShippingProfile = z
.object({
name: z.string(),
@@ -19,7 +29,3 @@ export const AdminCreateShippingProfile = z
metadata: z.record(z.string(), z.unknown()).optional(),
})
.strict()
export type AdminCreateShippingProfileType = z.infer<
typeof AdminCreateShippingProfile
>

View File

@@ -1,18 +1,12 @@
import { createLocationFulfillmentSetWorkflow } from "@medusajs/core-flows"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { z } from "zod"
import { MedusaRequest, MedusaResponse } from "../../../../../types/routing"
import { AdminCreateStockLocationFulfillmentSet } from "../../validators"
import { AdminCreateStockLocationFulfillmentSetType } from "../../validators"
import { refetchStockLocation } from "../../helpers"
export const POST = async (
req: MedusaRequest<z.infer<typeof AdminCreateStockLocationFulfillmentSet>>,
req: MedusaRequest<AdminCreateStockLocationFulfillmentSetType>,
res: MedusaResponse
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const { errors } = await createLocationFulfillmentSetWorkflow(req.scope).run({
input: {
location_id: req.params.id,
@@ -28,15 +22,11 @@ export const POST = async (
throw errors[0].error
}
const [stock_location] = await remoteQuery(
remoteQueryObjectFromString({
entryPoint: "stock_locations",
variables: {
id: req.params.id,
},
fields: req.remoteQueryConfig.fields,
})
const stockLocation = await refetchStockLocation(
req.params.id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ stock_location })
res.status(200).json({ stock_location: stockLocation })
}

View File

@@ -1,19 +1,21 @@
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { MedusaRequest, MedusaResponse } from "../../../../types/routing"
import { deleteStockLocationsWorkflow, updateStockLocationsWorkflow } from "@medusajs/core-flows"
import {
deleteStockLocationsWorkflow,
updateStockLocationsWorkflow,
} from "@medusajs/core-flows"
import { MedusaError } from "@medusajs/utils"
import { AdminPostStockLocationsLocationReq } from "../validators"
import {
AdminGetStockLocationParamsType,
AdminUpdateStockLocationType,
} from "../validators"
import { refetchStockLocation } from "../helpers"
export const POST = async (
req: MedusaRequest<AdminPostStockLocationsLocationReq>,
req: MedusaRequest<AdminUpdateStockLocationType>,
res: MedusaResponse
) => {
const { id } = req.params
await updateStockLocationsWorkflow(req.scope).run({
input: {
selector: { id: req.params.id },
@@ -21,45 +23,37 @@ export const POST = async (
},
})
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const [stock_location] = await remoteQuery(
remoteQueryObjectFromString({
entryPoint: "stock_locations",
variables: {
id,
},
fields: req.remoteQueryConfig.fields,
})
const stockLocation = await refetchStockLocation(
id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({
stock_location,
stock_location: stockLocation,
})
}
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
export const GET = async (
req: MedusaRequest<AdminGetStockLocationParamsType>,
res: MedusaResponse
) => {
const { id } = req.params
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const [stock_location] = await remoteQuery(
remoteQueryObjectFromString({
entryPoint: "stock_locations",
variables: {
id,
},
fields: req.remoteQueryConfig.fields,
})
const stockLocation = await refetchStockLocation(
id,
req.scope,
req.remoteQueryConfig.fields
)
if (!stock_location) {
if (!stockLocation) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Stock location with id: ${id} was not found`
)
}
res.status(200).json({ stock_location })
res.status(200).json({ stock_location: stockLocation })
}
export const DELETE = async (req: MedusaRequest, res: MedusaResponse) => {

View File

@@ -1,17 +1,14 @@
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import {
MedusaRequest,
MedusaResponse,
} from "../../../../../../../types/routing"
import { AdminStockLocationsLocationSalesChannelBatchReq } from "../../../../validators"
import { AdminStockLocationsSalesChannelType } from "../../../../validators"
import { addLocationsToSalesChannelWorkflow } from "@medusajs/core-flows"
import { refetchStockLocation } from "../../../../helpers"
export const POST = async (
req: MedusaRequest<AdminStockLocationsLocationSalesChannelBatchReq>,
req: MedusaRequest<AdminStockLocationsSalesChannelType>,
res: MedusaResponse
) => {
const workflowInput = {
@@ -30,15 +27,11 @@ export const POST = async (
throw errors[0].error
}
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const stockLocation = await refetchStockLocation(
req.params.id,
req.scope,
req.remoteQueryConfig.fields
)
const queryObject = remoteQueryObjectFromString({
entryPoint: "stock_locations",
variables: { id: req.params.id },
fields: req.remoteQueryConfig.fields,
})
const [stock_location] = await remoteQuery(queryObject)
res.status(200).json({ stock_location })
res.status(200).json({ stock_location: stockLocation })
}

View File

@@ -1,17 +1,14 @@
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import {
MedusaRequest,
MedusaResponse,
} from "../../../../../../../types/routing"
import { AdminStockLocationsLocationSalesChannelBatchReq } from "../../../../validators"
import { AdminStockLocationsSalesChannelType } from "../../../../validators"
import { removeLocationsFromSalesChannelWorkflow } from "@medusajs/core-flows"
import { refetchStockLocation } from "../../../../helpers"
export const POST = async (
req: MedusaRequest<AdminStockLocationsLocationSalesChannelBatchReq>,
req: MedusaRequest<AdminStockLocationsSalesChannelType>,
res: MedusaResponse
) => {
const workflowInput = {
@@ -32,15 +29,11 @@ export const POST = async (
throw errors[0].error
}
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const stockLocation = await refetchStockLocation(
req.params.id,
req.scope,
req.remoteQueryConfig.fields
)
const queryObject = remoteQueryObjectFromString({
entryPoint: "stock_locations",
variables: { id: req.params.id },
fields: req.remoteQueryConfig.fields,
})
const [stock_location] = await remoteQuery(queryObject)
res.status(200).json({ stock_location })
res.status(200).json({ stock_location: stockLocation })
}

View File

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

View File

@@ -1,18 +1,16 @@
import { transformBody, transformQuery } from "../../../api/middlewares"
import { MiddlewareRoute } from "../../../types/middlewares"
import { authenticate } from "../../../utils/authenticate-middleware"
import { maybeApplyLinkFilter } from "../../utils/maybe-apply-link-filter"
import { validateAndTransformBody } from "../../utils/validate-body"
import { validateAndTransformQuery } from "../../utils/validate-query"
import * as QueryConfig from "./query-config"
import {
AdminCreateStockLocation,
AdminCreateStockLocationFulfillmentSet,
AdminGetStockLocationsLocationParams,
AdminGetStockLocationParams,
AdminGetStockLocationsParams,
AdminPostStockLocationsLocationParams,
AdminPostStockLocationsLocationReq,
AdminPostStockLocationsParams,
AdminPostStockLocationsReq,
AdminStockLocationsLocationSalesChannelBatchReq,
AdminStockLocationsSalesChannel,
AdminUpdateStockLocation,
} from "./validators"
export const adminStockLocationRoutesMiddlewares: MiddlewareRoute[] = [
@@ -25,9 +23,9 @@ export const adminStockLocationRoutesMiddlewares: MiddlewareRoute[] = [
method: ["POST"],
matcher: "/admin/stock-locations",
middlewares: [
transformBody(AdminPostStockLocationsReq),
transformQuery(
AdminPostStockLocationsParams,
validateAndTransformBody(AdminCreateStockLocation),
validateAndTransformQuery(
AdminGetStockLocationParams,
QueryConfig.retrieveTransformQueryConfig
),
],
@@ -36,7 +34,7 @@ export const adminStockLocationRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/admin/stock-locations",
middlewares: [
transformQuery(
validateAndTransformQuery(
AdminGetStockLocationsParams,
QueryConfig.listTransformQueryConfig
),
@@ -51,20 +49,9 @@ export const adminStockLocationRoutesMiddlewares: MiddlewareRoute[] = [
method: ["POST"],
matcher: "/admin/stock-locations/:id",
middlewares: [
transformBody(AdminPostStockLocationsLocationReq),
transformQuery(
AdminPostStockLocationsLocationParams,
QueryConfig.retrieveTransformQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/admin/stock-locations/:id/sales-channels/batch*",
middlewares: [
transformBody(AdminStockLocationsLocationSalesChannelBatchReq),
transformQuery(
AdminPostStockLocationsLocationParams,
validateAndTransformBody(AdminUpdateStockLocation),
validateAndTransformQuery(
AdminGetStockLocationParams,
QueryConfig.retrieveTransformQueryConfig
),
],
@@ -73,8 +60,8 @@ export const adminStockLocationRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/admin/stock-locations/:id",
middlewares: [
transformQuery(
AdminGetStockLocationsLocationParams,
validateAndTransformQuery(
AdminGetStockLocationParams,
QueryConfig.retrieveTransformQueryConfig
),
],
@@ -84,8 +71,19 @@ export const adminStockLocationRoutesMiddlewares: MiddlewareRoute[] = [
matcher: "/admin/stock-locations/:id/fulfillment-sets",
middlewares: [
validateAndTransformBody(AdminCreateStockLocationFulfillmentSet),
transformQuery(
AdminPostStockLocationsParams,
validateAndTransformQuery(
AdminGetStockLocationParams,
QueryConfig.retrieveTransformQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/admin/stock-locations/:id/sales-channels/batch*",
middlewares: [
validateAndTransformBody(AdminStockLocationsSalesChannel),
validateAndTransformQuery(
AdminGetStockLocationParams,
QueryConfig.retrieveTransformQueryConfig
),
],

View File

@@ -5,33 +5,34 @@ import {
import { MedusaRequest, MedusaResponse } from "../../../types/routing"
import { createStockLocationsWorkflow } from "@medusajs/core-flows"
import { AdminPostStockLocationsReq } from "./validators"
import {
AdminCreateStockLocationType,
AdminGetStockLocationsParamsType,
} from "./validators"
import { refetchStockLocation } from "./helpers"
// Create stock location
export const POST = async (
req: MedusaRequest<AdminPostStockLocationsReq>,
req: MedusaRequest<AdminCreateStockLocationType>,
res: MedusaResponse
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const { result } = await createStockLocationsWorkflow(req.scope).run({
input: { locations: [req.validatedBody] },
})
const [stock_location] = await remoteQuery(
remoteQueryObjectFromString({
entryPoint: "stock_locations",
variables: {
id: result[0].id,
},
fields: req.remoteQueryConfig.fields,
})
const stockLocation = await refetchStockLocation(
result[0].id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ stock_location })
res.status(200).json({ stock_location: stockLocation })
}
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
export const GET = async (
req: MedusaRequest<AdminGetStockLocationsParamsType>,
res: MedusaResponse
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const { rows: stock_locations, metadata } = await remoteQuery(

View File

@@ -1,293 +1,75 @@
import { Transform, Type } from "class-transformer"
import {
IsNotEmpty,
IsObject,
IsOptional,
IsString,
ValidateNested,
} from "class-validator"
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
import { z } from "zod"
import { IsType } from "../../../utils"
import { createFindParams, createSelectParams } from "../../utils/validators"
/**
* @schema AdminPostStockLocationsReqAddress
* type: object
* required:
* - address_1
* - country_code
* properties:
* address_1:
* type: string
* description: Stock location address
* example: 35, Jhon Doe Ave
* address_2:
* type: string
* description: Stock location address' complement
* example: apartment 4432
* company:
* type: string
* description: Stock location address' company
* city:
* type: string
* description: Stock location address' city
* example: Mexico city
* country_code:
* description: "The two character ISO code for the country."
* type: string
* externalDocs:
* url: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements
* description: See a list of codes.
* phone:
* type: string
* description: Stock location address' phone number
* example: +1 555 61646
* postal_code:
* type: string
* description: Stock location address' postal code
* example: HD3-1G8
* province:
* type: string
* description: Stock location address' province
* example: Sinaloa
*/
class StockLocationCreateAddress {
@IsString()
address_1: string
export type AdminGetStockLocationParamsType = z.infer<
typeof AdminGetStockLocationParams
>
export const AdminGetStockLocationParams = createSelectParams()
@IsOptional()
@IsString()
address_2?: string
@IsOptional()
@IsString()
company?: string
@IsOptional()
@IsString()
city?: string
@IsString()
country_code: string
@IsOptional()
@IsString()
phone?: string
@IsOptional()
@IsString()
postal_code?: string
@IsOptional()
@IsString()
province?: string
}
/**
* @schema AdminPostStockLocationsReq
* type: object
* description: "The details of the stock location to create."
* required:
* - name
* properties:
* name:
* description: the name of the stock location
* type: string
* address_id:
* description: the ID of an existing stock location address to associate with the stock location. Only required if `address` is not provided.
* type: string
* metadata:
* type: object
* description: An optional key-value map with additional details
* example: {car: "white"}
* externalDocs:
* description: "Learn about the metadata attribute, and how to delete and update it."
* url: "https://docs.medusajs.com/development/entities/overview#metadata-attribute"
* address:
* description: A new stock location address to create and associate with the stock location. Only required if `address_id` is not provided.
* $ref: "#/components/schemas/StockLocationAddressInput"
*/
export class AdminPostStockLocationsReq {
@IsString()
@IsNotEmpty()
@Transform(({ value }: { value: string }) => value?.trim())
name: string
@IsOptional()
@ValidateNested()
@Type(() => StockLocationCreateAddress)
address?: StockLocationCreateAddress
@IsOptional()
@IsString()
address_id?: string
@IsObject()
@IsOptional()
metadata?: Record<string, unknown>
}
export class AdminPostStockLocationsParams extends FindParams {}
/**
* Parameters used to filter and configure the pagination of the retrieved stock locations.
*/
export class AdminGetStockLocationsParams extends extendedFindParamsMixin({
export type AdminGetStockLocationsParamsType = z.infer<
typeof AdminGetStockLocationsParams
>
export const AdminGetStockLocationsParams = createFindParams({
limit: 20,
offset: 0,
}) {
/**
* Search term to search stock location names.
*/
@IsString()
@IsOptional()
q?: string
}).merge(
z.object({
q: z.string().optional(),
id: z.union([z.string(), z.array(z.string())]).optional(),
name: z.union([z.string(), z.array(z.string())]).optional(),
address_id: z.union([z.string(), z.array(z.string())]).optional(),
sales_channel_id: z.union([z.string(), z.array(z.string())]).optional(),
$and: z.lazy(() => AdminGetStockLocationsParams.array()).optional(),
$or: z.lazy(() => AdminGetStockLocationsParams.array()).optional(),
})
)
/**
* IDs to filter stock locations by.
*/
@IsOptional()
@IsType([String, [String]])
id?: string | string[]
export type AdminUpsertStockLocationAddressType = z.infer<
typeof AdminUpsertStockLocationAddress
>
export const AdminUpsertStockLocationAddress = z.object({
address_1: z.string(),
address_2: z.string().optional(),
company: z.string().optional(),
city: z.string().optional(),
country_code: z.string(),
phone: z.string().optional(),
postal_code: z.string().optional(),
province: z.string().optional(),
})
/**
* Names to filter stock locations by.
*/
@IsOptional()
@IsType([String, [String]])
name?: string | string[]
export type AdminCreateStockLocationType = z.infer<
typeof AdminCreateStockLocation
>
export const AdminCreateStockLocation = z.object({
name: z.preprocess((val: any) => val.trim(), z.string()),
address: AdminUpsertStockLocationAddress.optional(),
address_id: z.string().optional(),
metadata: z.record(z.unknown()).optional(),
})
/**
* Filter stock locations by the ID of their associated addresses.
*/
@IsOptional()
@IsType([String, [String]])
address_id?: string | string[]
export type AdminUpdateStockLocationType = z.infer<
typeof AdminUpdateStockLocation
>
export const AdminUpdateStockLocation = z.object({
name: z
.preprocess((val: any) => val.trim(), z.string().optional())
.optional(),
address: AdminUpsertStockLocationAddress.optional(),
address_id: z.string().optional(),
metadata: z.record(z.unknown()).optional(),
})
/**
* Filter stock locations by the ID of their associated sales channels.
*/
@IsOptional()
@IsType([String, [String]])
sales_channel_id?: string | string[]
/**
* The field to sort the data by. By default, the sort order is ascending. To change the order to descending, prefix the field name with `-`.
*/
@IsString()
@IsOptional()
order?: string
}
/**
* The attributes of a stock location address to create or update.
*/
class StockLocationUpdateAddress {
/**
* First line address.
*/
@IsString()
address_1: string
/**
* Second line address.
*/
@IsOptional()
@IsString()
address_2?: string
/**
* Company.
*/
@IsOptional()
@IsString()
company?: string
/**
* City.
*/
@IsOptional()
@IsString()
city?: string
/**
* Country code.
*/
@IsString()
country_code: string
/**
* Phone.
*/
@IsOptional()
@IsString()
phone?: string
/**
* Postal code.
*/
@IsOptional()
@IsString()
postal_code?: string
/**
* Province.
*/
@IsOptional()
@IsString()
province?: string
}
/**
* @schema AdminPostStockLocationsLocationReq
* type: object
* description: "The details to update of the stock location."
* properties:
* name:
* description: the name of the stock location
* type: string
* address_id:
* description: the stock location address ID
* type: string
* metadata:
* type: object
* description: An optional key-value map with additional details
* example: {car: "white"}
* externalDocs:
* description: "Learn about the metadata attribute, and how to delete and update it."
* url: "https://docs.medusajs.com/development/entities/overview#metadata-attribute"
* address:
* description: The data of an associated address to create or update.
* $ref: "#/components/schemas/StockLocationAddressInput"
*/
export class AdminPostStockLocationsLocationReq {
@IsOptional()
@IsString()
name?: string
@IsOptional()
@ValidateNested()
@Type(() => StockLocationUpdateAddress)
address?: StockLocationUpdateAddress
@IsOptional()
@IsString()
address_id?: string
@IsObject()
@IsOptional()
metadata?: Record<string, unknown>
}
export class AdminPostStockLocationsLocationParams extends FindParams {}
export class AdminGetStockLocationsLocationParams extends FindParams {}
export class AdminStockLocationsLocationSalesChannelBatchReq {
@IsString({ each: true })
sales_channel_ids: string[]
}
export type AdminStockLocationsSalesChannelType = z.infer<
typeof AdminStockLocationsSalesChannel
>
export const AdminStockLocationsSalesChannel = z.object({
sales_channel_ids: z.array(z.string()),
})
export type AdminCreateStockLocationFulfillmentSetType = z.infer<
typeof AdminCreateStockLocationFulfillmentSet
>
export const AdminCreateStockLocationFulfillmentSet = z
.object({
name: z.string(),

View File

@@ -47,6 +47,7 @@ export function validateAndTransformQuery<TEntity extends BaseEntity>(
return async (req: MedusaRequest, _: MedusaResponse, next: NextFunction) => {
try {
const query = normalizeQuery(req)
const validated = await zodValidator(zodSchema, query, config)
const cnf = queryConfig.isList
? prepareListQuery(validated, queryConfig)

View File

@@ -61,21 +61,38 @@ export const createFindParams = ({
)
}
export const createOperatorMap = (type?: z.ZodType) => {
export const createOperatorMap = (
type?: z.ZodType,
valueParser?: (val: any) => any
) => {
if (!type) {
type = z.string()
}
let unionType: any = z.union([type, z.array(type)]).optional()
let arrayType: any = z.array(type).optional()
let simpleType: any = type.optional()
if (valueParser) {
unionType = z
.preprocess(valueParser, z.union([type, z.array(type)]))
.optional()
arrayType = z.preprocess(valueParser, z.array(type)).optional()
simpleType = z.preprocess(valueParser, type).optional()
}
return z.object({
$eq: z.union([type, z.array(type)]).optional(),
$ne: z.union([type, z.array(type)]).optional(),
$in: z.array(type).optional(),
$nin: z.array(type).optional(),
$like: type.optional(),
$re: type.optional(),
$contains: type.optional(),
$gt: type.optional(),
$gte: type.optional(),
$lt: type.optional(),
$lte: type.optional(),
$eq: unionType,
$ne: unionType,
$in: arrayType,
$nin: arrayType,
$like: simpleType,
$ilike: simpleType,
$re: simpleType,
$contains: simpleType,
$gt: simpleType,
$gte: simpleType,
$lt: simpleType,
$lte: simpleType,
})
}