feat: Clean up shipping options management (#6977)
**What** - cleanup existing route on some aspects but not all - add new create shipping options end point and types - add set of integration tests - cleanup existing fulfillment routes - align existing integration tests - transform old type to zod types and validators - fix stock location route
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
"@medusajs/types": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Feat/shipping options api 3
|
||||||
@@ -63,11 +63,11 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("POST /admin/fulfillment/shipping-options/:id/rules/batch/add", () => {
|
describe("POST /admin/shipping-options/:id/rules/batch/add", () => {
|
||||||
it("should throw error when required params are missing", async () => {
|
it("should throw error when required params are missing", async () => {
|
||||||
const { response } = await api
|
const { response } = await api
|
||||||
.post(
|
.post(
|
||||||
`/admin/fulfillment/shipping-options/${shippingOption.id}/rules/batch/add`,
|
`/admin/shipping-options/${shippingOption.id}/rules/batch/add`,
|
||||||
{
|
{
|
||||||
rules: [{ operator: RuleOperator.EQ, value: "new_value" }],
|
rules: [{ operator: RuleOperator.EQ, value: "new_value" }],
|
||||||
},
|
},
|
||||||
@@ -86,7 +86,7 @@ medusaIntegrationTestRunner({
|
|||||||
it.only("should throw error when shipping option does not exist", async () => {
|
it.only("should throw error when shipping option does not exist", async () => {
|
||||||
const { response } = await api
|
const { response } = await api
|
||||||
.post(
|
.post(
|
||||||
`/admin/fulfillment/shipping-options/does-not-exist/rules/batch/add`,
|
`/admin/shipping-options/does-not-exist/rules/batch/add`,
|
||||||
{
|
{
|
||||||
rules: [
|
rules: [
|
||||||
{ attribute: "new_attr", operator: "eq", value: "new value" },
|
{ attribute: "new_attr", operator: "eq", value: "new value" },
|
||||||
@@ -106,7 +106,7 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
it("should add rules to a shipping option successfully", async () => {
|
it("should add rules to a shipping option successfully", async () => {
|
||||||
const response = await api.post(
|
const response = await api.post(
|
||||||
`/admin/fulfillment/shipping-options/${shippingOption.id}/rules/batch/add`,
|
`/admin/shipping-options/${shippingOption.id}/rules/batch/add`,
|
||||||
{
|
{
|
||||||
rules: [
|
rules: [
|
||||||
{ operator: "eq", attribute: "new_attr", value: "new value" },
|
{ operator: "eq", attribute: "new_attr", value: "new value" },
|
||||||
@@ -140,11 +140,11 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("POST /admin/fulfillment/shipping-options/:id/rules/batch/remove", () => {
|
describe("POST /admin/shipping-options/:id/rules/batch/remove", () => {
|
||||||
it("should throw error when required params are missing", async () => {
|
it("should throw error when required params are missing", async () => {
|
||||||
const { response } = await api
|
const { response } = await api
|
||||||
.post(
|
.post(
|
||||||
`/admin/fulfillment/shipping-options/${shippingOption.id}/rules/batch/remove`,
|
`/admin/shipping-options/${shippingOption.id}/rules/batch/remove`,
|
||||||
{},
|
{},
|
||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
@@ -161,7 +161,7 @@ medusaIntegrationTestRunner({
|
|||||||
it("should throw error when shipping option does not exist", async () => {
|
it("should throw error when shipping option does not exist", async () => {
|
||||||
const { response } = await api
|
const { response } = await api
|
||||||
.post(
|
.post(
|
||||||
`/admin/fulfillment/shipping-options/does-not-exist/rules/batch/remove`,
|
`/admin/shipping-options/does-not-exist/rules/batch/remove`,
|
||||||
{ rule_ids: ["test"] },
|
{ rule_ids: ["test"] },
|
||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
@@ -176,7 +176,7 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
it("should add rules to a shipping option successfully", async () => {
|
it("should add rules to a shipping option successfully", async () => {
|
||||||
const response = await api.post(
|
const response = await api.post(
|
||||||
`/admin/fulfillment/shipping-options/${shippingOption.id}/rules/batch/remove`,
|
`/admin/shipping-options/${shippingOption.id}/rules/batch/remove`,
|
||||||
{
|
{
|
||||||
rule_ids: [shippingOption.rules[0].id],
|
rule_ids: [shippingOption.rules[0].id],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,207 @@
|
|||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import {
|
||||||
|
IFulfillmentModuleService,
|
||||||
|
IRegionModuleService,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { RuleOperator } from "@medusajs/utils"
|
||||||
|
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||||
|
import { createAdminUser } from "../../../../helpers/create-admin-user"
|
||||||
|
|
||||||
|
jest.setTimeout(50000)
|
||||||
|
|
||||||
|
const env = { MEDUSA_FF_MEDUSA_V2: true }
|
||||||
|
const adminHeaders = { headers: { "x-medusa-access-token": "test_token" } }
|
||||||
|
|
||||||
|
medusaIntegrationTestRunner({
|
||||||
|
env,
|
||||||
|
testSuite: ({ dbConnection, getContainer, api }) => {
|
||||||
|
describe("Admin: Shipping Option API", () => {
|
||||||
|
let appContainer
|
||||||
|
let fulfillmentModule: IFulfillmentModuleService
|
||||||
|
let regionService: IRegionModuleService
|
||||||
|
|
||||||
|
let shippingProfile
|
||||||
|
let fulfillmentSet
|
||||||
|
let region
|
||||||
|
|
||||||
|
const shippingOptionRule = {
|
||||||
|
operator: RuleOperator.EQ,
|
||||||
|
attribute: "old_attr",
|
||||||
|
value: "old value",
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
appContainer = getContainer()
|
||||||
|
fulfillmentModule = appContainer.resolve(
|
||||||
|
ModuleRegistrationName.FULFILLMENT
|
||||||
|
)
|
||||||
|
regionService = appContainer.resolve(ModuleRegistrationName.REGION)
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await createAdminUser(dbConnection, adminHeaders, appContainer)
|
||||||
|
|
||||||
|
shippingProfile = await fulfillmentModule.createShippingProfiles({
|
||||||
|
name: "Test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
fulfillmentSet = await fulfillmentModule.create({
|
||||||
|
name: "Test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "Test",
|
||||||
|
geo_zones: [{ type: "country", country_code: "us" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
region = await regionService.create({
|
||||||
|
name: "Test region",
|
||||||
|
countries: ["FR"],
|
||||||
|
currency_code: "eur",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("POST /admin/shipping-options", () => {
|
||||||
|
it("should throw error when required params are missing", async () => {
|
||||||
|
const shippingOptionPayload = {
|
||||||
|
name: "Test shipping option",
|
||||||
|
}
|
||||||
|
|
||||||
|
let err = await api
|
||||||
|
.post(
|
||||||
|
`/admin/shipping-options`,
|
||||||
|
shippingOptionPayload,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
.catch((e) => e.response)
|
||||||
|
|
||||||
|
const errorsFields = [
|
||||||
|
{
|
||||||
|
code: "invalid_type",
|
||||||
|
expected: "string",
|
||||||
|
received: "undefined",
|
||||||
|
path: ["service_zone_id"],
|
||||||
|
message: "Required",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: "invalid_type",
|
||||||
|
expected: "string",
|
||||||
|
received: "undefined",
|
||||||
|
path: ["shipping_profile_id"],
|
||||||
|
message: "Required",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expected: "'calculated' | 'flat'",
|
||||||
|
received: "undefined",
|
||||||
|
code: "invalid_type",
|
||||||
|
path: ["price_type"],
|
||||||
|
message: "Required",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: "invalid_type",
|
||||||
|
expected: "string",
|
||||||
|
received: "undefined",
|
||||||
|
path: ["provider_id"],
|
||||||
|
message: "Required",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: "invalid_type",
|
||||||
|
expected: "object",
|
||||||
|
received: "undefined",
|
||||||
|
path: ["type"],
|
||||||
|
message: "Required",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: "invalid_type",
|
||||||
|
expected: "array",
|
||||||
|
received: "undefined",
|
||||||
|
path: ["prices"],
|
||||||
|
message: "Required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
expect(err.status).toEqual(400)
|
||||||
|
expect(err.data).toEqual({
|
||||||
|
type: "invalid_data",
|
||||||
|
message: `Invalid request body: ${JSON.stringify(errorsFields)}`,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create a shipping option successfully", async () => {
|
||||||
|
const shippingOptionPayload = {
|
||||||
|
name: "Test shipping option",
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: "manual_test-provider",
|
||||||
|
price_type: "flat",
|
||||||
|
type: {
|
||||||
|
label: "Test type",
|
||||||
|
description: "Test description",
|
||||||
|
code: "test-code",
|
||||||
|
},
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
region_id: region.id,
|
||||||
|
amount: 1000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rules: [shippingOptionRule],
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await api.post(
|
||||||
|
`/admin/shipping-options`,
|
||||||
|
shippingOptionPayload,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.shipping_option).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: shippingOptionPayload.name,
|
||||||
|
provider: expect.objectContaining({
|
||||||
|
id: shippingOptionPayload.provider_id,
|
||||||
|
}),
|
||||||
|
price_type: shippingOptionPayload.price_type,
|
||||||
|
type: expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
label: shippingOptionPayload.type.label,
|
||||||
|
description: shippingOptionPayload.type.description,
|
||||||
|
code: shippingOptionPayload.type.code,
|
||||||
|
}),
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
prices: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 1000,
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
currency_code: "eur",
|
||||||
|
amount: 1000,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
rules: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
operator: "eq",
|
||||||
|
attribute: "old_attr",
|
||||||
|
value: "old value",
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { transformBody } from "../../../api/middlewares"
|
|
||||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
|
||||||
import { authenticate } from "../../../utils/authenticate-middleware"
|
|
||||||
import {
|
|
||||||
AdminPostFulfillmentShippingOptionsRulesBatchAddReq,
|
|
||||||
AdminPostFulfillmentShippingOptionsRulesBatchRemoveReq,
|
|
||||||
} from "./validators"
|
|
||||||
|
|
||||||
export const adminFulfillmentRoutesMiddlewares: MiddlewareRoute[] = [
|
|
||||||
{
|
|
||||||
matcher: "/admin/fulfillment*",
|
|
||||||
middlewares: [authenticate("admin", ["bearer", "session"])],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: ["POST"],
|
|
||||||
matcher: "/admin/fulfillment/shipping-options/:id/rules/batch/add",
|
|
||||||
middlewares: [
|
|
||||||
transformBody(AdminPostFulfillmentShippingOptionsRulesBatchAddReq),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
method: ["POST"],
|
|
||||||
matcher: "/admin/fulfillment/shipping-options/:id/rules/batch/remove",
|
|
||||||
middlewares: [
|
|
||||||
transformBody(AdminPostFulfillmentShippingOptionsRulesBatchRemoveReq),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
export const defaultAdminShippingOptionRelations = ["rules"]
|
|
||||||
export const allowedAdminShippingOptionRelations = [
|
|
||||||
...defaultAdminShippingOptionRelations,
|
|
||||||
]
|
|
||||||
export const defaultAdminShippingOptionFields = [
|
|
||||||
"id",
|
|
||||||
"name",
|
|
||||||
"price_type",
|
|
||||||
"data",
|
|
||||||
"metadata",
|
|
||||||
"created_at",
|
|
||||||
"updated_at",
|
|
||||||
"rules.id",
|
|
||||||
"rules.attribute",
|
|
||||||
"rules.operator",
|
|
||||||
"rules.value",
|
|
||||||
]
|
|
||||||
|
|
||||||
export const retrieveTransformQueryConfig = {
|
|
||||||
defaultFields: defaultAdminShippingOptionFields,
|
|
||||||
defaultRelations: defaultAdminShippingOptionRelations,
|
|
||||||
allowedRelations: allowedAdminShippingOptionRelations,
|
|
||||||
isList: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
export const listTransformQueryConfig = {
|
|
||||||
...retrieveTransformQueryConfig,
|
|
||||||
isList: true,
|
|
||||||
}
|
|
||||||
-45
@@ -1,45 +0,0 @@
|
|||||||
import { addRulesToFulfillmentShippingOptionWorkflow } from "@medusajs/core-flows"
|
|
||||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
|
||||||
import { IFulfillmentModuleService } from "@medusajs/types"
|
|
||||||
import {
|
|
||||||
AuthenticatedMedusaRequest,
|
|
||||||
MedusaResponse,
|
|
||||||
} from "../../../../../../../../types/routing"
|
|
||||||
import {
|
|
||||||
defaultAdminShippingOptionFields,
|
|
||||||
defaultAdminShippingOptionRelations,
|
|
||||||
} from "../../../../../query-config"
|
|
||||||
import { AdminPostFulfillmentShippingOptionsRulesBatchAddReq } from "../../../../../validators"
|
|
||||||
|
|
||||||
export const POST = async (
|
|
||||||
req: AuthenticatedMedusaRequest<AdminPostFulfillmentShippingOptionsRulesBatchAddReq>,
|
|
||||||
res: MedusaResponse
|
|
||||||
) => {
|
|
||||||
const id = req.params.id
|
|
||||||
const workflow = addRulesToFulfillmentShippingOptionWorkflow(req.scope)
|
|
||||||
|
|
||||||
const { errors } = await workflow.run({
|
|
||||||
input: {
|
|
||||||
data: req.validatedBody.rules.map((rule) => ({
|
|
||||||
...rule,
|
|
||||||
shipping_option_id: id,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
throwOnError: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (Array.isArray(errors) && errors[0]) {
|
|
||||||
throw errors[0].error
|
|
||||||
}
|
|
||||||
|
|
||||||
const fulfillmentService: IFulfillmentModuleService = req.scope.resolve(
|
|
||||||
ModuleRegistrationName.FULFILLMENT
|
|
||||||
)
|
|
||||||
|
|
||||||
const shippingOption = await fulfillmentService.retrieveShippingOption(id, {
|
|
||||||
select: defaultAdminShippingOptionFields,
|
|
||||||
relations: defaultAdminShippingOptionRelations,
|
|
||||||
})
|
|
||||||
|
|
||||||
res.status(200).json({ shipping_option: shippingOption })
|
|
||||||
}
|
|
||||||
-40
@@ -1,40 +0,0 @@
|
|||||||
import { removeRulesFromFulfillmentShippingOptionWorkflow } from "@medusajs/core-flows"
|
|
||||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
|
||||||
import { IFulfillmentModuleService } from "@medusajs/types"
|
|
||||||
import {
|
|
||||||
AuthenticatedMedusaRequest,
|
|
||||||
MedusaResponse,
|
|
||||||
} from "../../../../../../../../types/routing"
|
|
||||||
import {
|
|
||||||
defaultAdminShippingOptionFields,
|
|
||||||
defaultAdminShippingOptionRelations,
|
|
||||||
} from "../../../../../query-config"
|
|
||||||
import { AdminPostFulfillmentShippingOptionsRulesBatchRemoveReq } from "../../../../../validators"
|
|
||||||
|
|
||||||
export const POST = async (
|
|
||||||
req: AuthenticatedMedusaRequest<AdminPostFulfillmentShippingOptionsRulesBatchRemoveReq>,
|
|
||||||
res: MedusaResponse
|
|
||||||
) => {
|
|
||||||
const id = req.params.id
|
|
||||||
const workflow = removeRulesFromFulfillmentShippingOptionWorkflow(req.scope)
|
|
||||||
|
|
||||||
const { errors } = await workflow.run({
|
|
||||||
input: { ids: req.validatedBody.rule_ids },
|
|
||||||
throwOnError: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (Array.isArray(errors) && errors[0]) {
|
|
||||||
throw errors[0].error
|
|
||||||
}
|
|
||||||
|
|
||||||
const fulfillmentService: IFulfillmentModuleService = req.scope.resolve(
|
|
||||||
ModuleRegistrationName.FULFILLMENT
|
|
||||||
)
|
|
||||||
|
|
||||||
const shippingOption = await fulfillmentService.retrieveShippingOption(id, {
|
|
||||||
select: defaultAdminShippingOptionFields,
|
|
||||||
relations: defaultAdminShippingOptionRelations,
|
|
||||||
})
|
|
||||||
|
|
||||||
res.status(200).json({ shipping_option: shippingOption })
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import { RuleOperator } from "@medusajs/utils"
|
|
||||||
import { Type } from "class-transformer"
|
|
||||||
import {
|
|
||||||
ArrayNotEmpty,
|
|
||||||
IsArray,
|
|
||||||
IsEnum,
|
|
||||||
IsNotEmpty,
|
|
||||||
IsString,
|
|
||||||
ValidateNested,
|
|
||||||
} from "class-validator"
|
|
||||||
import { IsType } from "../../../utils"
|
|
||||||
|
|
||||||
export class AdminPostFulfillmentShippingOptionsRulesBatchAddReq {
|
|
||||||
@IsArray()
|
|
||||||
@ValidateNested({ each: true })
|
|
||||||
@Type(() => FulfillmentRuleCreate)
|
|
||||||
rules: FulfillmentRuleCreate[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AdminPostFulfillmentShippingOptionsRulesBatchRemoveReq {
|
|
||||||
@ArrayNotEmpty()
|
|
||||||
@IsString({ each: true })
|
|
||||||
rule_ids: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FulfillmentRuleCreate {
|
|
||||||
@IsEnum(RuleOperator)
|
|
||||||
operator: RuleOperator
|
|
||||||
|
|
||||||
@IsNotEmpty()
|
|
||||||
@IsString()
|
|
||||||
attribute: string
|
|
||||||
|
|
||||||
@IsType([String, [String]])
|
|
||||||
value: string | string[]
|
|
||||||
}
|
|
||||||
@@ -27,7 +27,7 @@ export const POST = async (
|
|||||||
variables: {
|
variables: {
|
||||||
id: result[0].id,
|
id: result[0].id,
|
||||||
},
|
},
|
||||||
fields: req.retrieveConfig.select as string[],
|
fields: req.remoteQueryConfig.fields,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -45,11 +45,9 @@ export const GET = async (
|
|||||||
entryPoint: "inventory_items",
|
entryPoint: "inventory_items",
|
||||||
variables: {
|
variables: {
|
||||||
filters: req.filterableFields,
|
filters: req.filterableFields,
|
||||||
order: req.listConfig.order,
|
...req.remoteQueryConfig.pagination,
|
||||||
skip: req.listConfig.skip,
|
|
||||||
take: req.listConfig.take,
|
|
||||||
},
|
},
|
||||||
fields: [...(req.listConfig.select as string[])],
|
fields: req.remoteQueryConfig.fields,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { rows: inventory_items, metadata } = await remoteQuery({
|
const { rows: inventory_items, metadata } = await remoteQuery({
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
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"
|
||||||
|
|
||||||
|
export const POST = async (
|
||||||
|
req: AuthenticatedMedusaRequest<
|
||||||
|
AdminShippingOptionRulesBatchAddType
|
||||||
|
>,
|
||||||
|
res: MedusaResponse<AdminShippingOptionRetrieveResponse>
|
||||||
|
) => {
|
||||||
|
const id = req.params.id
|
||||||
|
const workflow = addRulesToFulfillmentShippingOptionWorkflow(req.scope)
|
||||||
|
|
||||||
|
const { errors } = await workflow.run({
|
||||||
|
input: {
|
||||||
|
data: req.validatedBody.rules.map((rule) => ({
|
||||||
|
...rule,
|
||||||
|
shipping_option_id: id,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
throwOnError: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (Array.isArray(errors) && errors[0]) {
|
||||||
|
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)
|
||||||
|
|
||||||
|
res.status(200).json({ shipping_option: shippingOption })
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
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"
|
||||||
|
|
||||||
|
export const POST = async (
|
||||||
|
req: AuthenticatedMedusaRequest<AdminShippingOptionRulesBatchRemoveType>,
|
||||||
|
res: MedusaResponse<AdminShippingOptionRetrieveResponse>
|
||||||
|
) => {
|
||||||
|
const workflow = removeRulesFromFulfillmentShippingOptionWorkflow(req.scope)
|
||||||
|
|
||||||
|
const { errors } = await workflow.run({
|
||||||
|
input: { ids: req.validatedBody.rule_ids },
|
||||||
|
throwOnError: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (Array.isArray(errors) && errors[0]) {
|
||||||
|
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)
|
||||||
|
|
||||||
|
res.status(200).json({ shipping_option: shippingOption })
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
||||||
|
import { authenticate } from "../../../utils/authenticate-middleware"
|
||||||
|
import {
|
||||||
|
AdminCreateShippingOption,
|
||||||
|
AdminGetShippingOptionParams,
|
||||||
|
AdminShippingOptionRulesBatchAdd,
|
||||||
|
AdminShippingOptionRulesBatchRemove,
|
||||||
|
} from "./validators"
|
||||||
|
import { retrieveTransformQueryConfig } from "./query-config"
|
||||||
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
|
|
||||||
|
export const adminShippingOptionRoutesMiddlewares: MiddlewareRoute[] = [
|
||||||
|
{
|
||||||
|
matcher: "/admin/shipping-options*",
|
||||||
|
middlewares: [authenticate("admin", ["bearer", "session"])],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
method: ["POST"],
|
||||||
|
matcher: "/admin/shipping-options",
|
||||||
|
middlewares: [
|
||||||
|
validateAndTransformQuery(
|
||||||
|
AdminGetShippingOptionParams,
|
||||||
|
retrieveTransformQueryConfig
|
||||||
|
),
|
||||||
|
validateAndTransformBody(AdminCreateShippingOption),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
method: ["POST"],
|
||||||
|
matcher: "/admin/shipping-options/:id/rules/batch/add",
|
||||||
|
middlewares: [
|
||||||
|
validateAndTransformQuery(
|
||||||
|
AdminGetShippingOptionParams,
|
||||||
|
retrieveTransformQueryConfig
|
||||||
|
),
|
||||||
|
validateAndTransformBody(AdminShippingOptionRulesBatchAdd),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
method: ["POST"],
|
||||||
|
matcher: "/admin/shipping-options/:id/rules/batch/remove",
|
||||||
|
middlewares: [
|
||||||
|
validateAndTransformQuery(
|
||||||
|
AdminGetShippingOptionParams,
|
||||||
|
retrieveTransformQueryConfig
|
||||||
|
),
|
||||||
|
validateAndTransformBody(AdminShippingOptionRulesBatchRemove),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
export const defaultAdminShippingOptionFields = [
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"price_type",
|
||||||
|
"data",
|
||||||
|
"metadata",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
"*rules",
|
||||||
|
"*type",
|
||||||
|
"*prices",
|
||||||
|
"*service_zone",
|
||||||
|
"*shipping_profile",
|
||||||
|
"*provider",
|
||||||
|
]
|
||||||
|
|
||||||
|
export const retrieveTransformQueryConfig = {
|
||||||
|
defaults: defaultAdminShippingOptionFields,
|
||||||
|
isList: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const listTransformQueryConfig = {
|
||||||
|
...retrieveTransformQueryConfig,
|
||||||
|
isList: true,
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { createShippingOptionsWorkflow } from "@medusajs/core-flows"
|
||||||
|
import {
|
||||||
|
ContainerRegistrationKeys,
|
||||||
|
remoteQueryObjectFromString,
|
||||||
|
} from "@medusajs/utils"
|
||||||
|
import { AdminShippingOptionRetrieveResponse } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
AuthenticatedMedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "../../../types/routing"
|
||||||
|
import { AdminCreateShippingOptionType } from "./validators"
|
||||||
|
|
||||||
|
export const POST = async (
|
||||||
|
req: AuthenticatedMedusaRequest<AdminCreateShippingOptionType>,
|
||||||
|
res: MedusaResponse<AdminShippingOptionRetrieveResponse>
|
||||||
|
) => {
|
||||||
|
const shippingOptionPayload = req.validatedBody
|
||||||
|
|
||||||
|
const workflow = createShippingOptionsWorkflow(req.scope)
|
||||||
|
|
||||||
|
const { result, errors } = await workflow.run({
|
||||||
|
input: [shippingOptionPayload],
|
||||||
|
throwOnError: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (Array.isArray(errors) && errors[0]) {
|
||||||
|
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)
|
||||||
|
|
||||||
|
res.status(200).json({ shipping_option: shippingOption })
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import {
|
||||||
|
RuleOperator,
|
||||||
|
ShippingOptionPriceType as ShippingOptionPriceTypeEnum,
|
||||||
|
} from "@medusajs/utils"
|
||||||
|
import { z } from "zod"
|
||||||
|
import { createSelectParams } from "../../utils/validators"
|
||||||
|
|
||||||
|
export const AdminGetShippingOptionParams = createSelectParams()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHIPPING OPTIONS RULES
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const AdminCreateShippingOptionRule = z
|
||||||
|
.object({
|
||||||
|
operator: z.nativeEnum(RuleOperator),
|
||||||
|
attribute: z.string(),
|
||||||
|
value: z.string().or(z.array(z.string())),
|
||||||
|
})
|
||||||
|
.strict()
|
||||||
|
|
||||||
|
export const AdminShippingOptionRulesBatchAdd = z
|
||||||
|
.object({
|
||||||
|
rules: AdminCreateShippingOptionRule.array(),
|
||||||
|
})
|
||||||
|
.strict()
|
||||||
|
|
||||||
|
export type AdminShippingOptionRulesBatchAddType = z.infer<
|
||||||
|
typeof AdminShippingOptionRulesBatchAdd
|
||||||
|
>
|
||||||
|
|
||||||
|
export const AdminShippingOptionRulesBatchRemove = z
|
||||||
|
.object({
|
||||||
|
rule_ids: z.array(z.string()),
|
||||||
|
})
|
||||||
|
.strict()
|
||||||
|
|
||||||
|
export type AdminShippingOptionRulesBatchRemoveType = z.infer<
|
||||||
|
typeof AdminShippingOptionRulesBatchRemove
|
||||||
|
>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHIPPING OPTIONS
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const AdminCreateShippingOptionType = z
|
||||||
|
.object({
|
||||||
|
label: z.string(),
|
||||||
|
description: z.string(),
|
||||||
|
code: z.string(),
|
||||||
|
})
|
||||||
|
.strict()
|
||||||
|
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
export const AdminCreateShippingOptionPriceWithCurrency = z
|
||||||
|
.object({
|
||||||
|
currency_code: z.string(),
|
||||||
|
amount: z.number(),
|
||||||
|
})
|
||||||
|
.strict()
|
||||||
|
|
||||||
|
export const AdminCreateShippingOptionPriceWithRegion = z
|
||||||
|
.object({
|
||||||
|
region_id: z.string(),
|
||||||
|
amount: z.number(),
|
||||||
|
})
|
||||||
|
.strict()
|
||||||
|
|
||||||
|
export const AdminCreateShippingOption = z
|
||||||
|
.object({
|
||||||
|
name: z.string(),
|
||||||
|
service_zone_id: z.string(),
|
||||||
|
shipping_profile_id: z.string(),
|
||||||
|
data: z.record(z.unknown()).optional(),
|
||||||
|
price_type: z.nativeEnum(ShippingOptionPriceTypeEnum),
|
||||||
|
provider_id: z.string(),
|
||||||
|
type: AdminCreateShippingOptionType,
|
||||||
|
prices: AdminCreateShippingOptionPriceWithCurrency.or(
|
||||||
|
AdminCreateShippingOptionPriceWithRegion
|
||||||
|
).array(),
|
||||||
|
rules: AdminCreateShippingOptionRule.array().optional(),
|
||||||
|
})
|
||||||
|
.strict()
|
||||||
|
|
||||||
|
export type AdminCreateShippingOptionType = z.infer<
|
||||||
|
typeof AdminCreateShippingOption
|
||||||
|
>
|
||||||
@@ -5,8 +5,8 @@ import { adminCollectionRoutesMiddlewares } from "./admin/collections/middleware
|
|||||||
import { adminCurrencyRoutesMiddlewares } from "./admin/currencies/middlewares"
|
import { adminCurrencyRoutesMiddlewares } from "./admin/currencies/middlewares"
|
||||||
import { adminCustomerGroupRoutesMiddlewares } from "./admin/customer-groups/middlewares"
|
import { adminCustomerGroupRoutesMiddlewares } from "./admin/customer-groups/middlewares"
|
||||||
import { adminCustomerRoutesMiddlewares } from "./admin/customers/middlewares"
|
import { adminCustomerRoutesMiddlewares } from "./admin/customers/middlewares"
|
||||||
|
import { adminShippingOptionRoutesMiddlewares } from "./admin/shipping-options/middlewares"
|
||||||
import { adminDraftOrderRoutesMiddlewares } from "./admin/draft-orders/middlewares"
|
import { adminDraftOrderRoutesMiddlewares } from "./admin/draft-orders/middlewares"
|
||||||
import { adminFulfillmentRoutesMiddlewares } from "./admin/fulfillment/middlewares"
|
|
||||||
import { adminInventoryRoutesMiddlewares } from "./admin/inventory-items/middlewares"
|
import { adminInventoryRoutesMiddlewares } from "./admin/inventory-items/middlewares"
|
||||||
import { adminInviteRoutesMiddlewares } from "./admin/invites/middlewares"
|
import { adminInviteRoutesMiddlewares } from "./admin/invites/middlewares"
|
||||||
import { adminPaymentRoutesMiddlewares } from "./admin/payments/middlewares"
|
import { adminPaymentRoutesMiddlewares } from "./admin/payments/middlewares"
|
||||||
@@ -59,7 +59,7 @@ export const config: MiddlewaresConfig = {
|
|||||||
...adminInventoryRoutesMiddlewares,
|
...adminInventoryRoutesMiddlewares,
|
||||||
...adminCollectionRoutesMiddlewares,
|
...adminCollectionRoutesMiddlewares,
|
||||||
...adminPricingRoutesMiddlewares,
|
...adminPricingRoutesMiddlewares,
|
||||||
...adminFulfillmentRoutesMiddlewares,
|
...adminShippingOptionRoutesMiddlewares,
|
||||||
...adminDraftOrderRoutesMiddlewares,
|
...adminDraftOrderRoutesMiddlewares,
|
||||||
...adminSalesChannelRoutesMiddlewares,
|
...adminSalesChannelRoutesMiddlewares,
|
||||||
...adminStockLocationRoutesMiddlewares,
|
...adminStockLocationRoutesMiddlewares,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export const POST = async (
|
|||||||
res: MedusaResponse
|
res: MedusaResponse
|
||||||
) => {
|
) => {
|
||||||
const { id } = req.params
|
const { id } = req.params
|
||||||
const { context, provider_id, data } = req.body
|
const { context = {}, provider_id, data } = req.body
|
||||||
|
|
||||||
// If the customer is logged in, we auto-assign them to the payment collection
|
// If the customer is logged in, we auto-assign them to the payment collection
|
||||||
if (req.auth?.actor_id) {
|
if (req.auth?.actor_id) {
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ import type { Customer, User } from "../models"
|
|||||||
|
|
||||||
import { MedusaContainer, RequestQueryFields } from "@medusajs/types"
|
import { MedusaContainer, RequestQueryFields } from "@medusajs/types"
|
||||||
import { FindConfig } from "./common"
|
import { FindConfig } from "./common"
|
||||||
|
import * as core from "express-serve-static-core"
|
||||||
|
|
||||||
export interface MedusaRequest<Body = unknown> extends Request {
|
export interface MedusaRequest<Body = unknown>
|
||||||
|
extends Request<core.ParamsDictionary, any, Body> {
|
||||||
validatedBody: Body
|
validatedBody: Body
|
||||||
validatedQuery: RequestQueryFields & Record<string, unknown>
|
validatedQuery: RequestQueryFields & Record<string, unknown>
|
||||||
/**
|
/**
|
||||||
@@ -22,7 +24,10 @@ export interface MedusaRequest<Body = unknown> extends Request {
|
|||||||
/**
|
/**
|
||||||
* An object containing fields and variables to be used with the remoteQuery
|
* An object containing fields and variables to be used with the remoteQuery
|
||||||
*/
|
*/
|
||||||
remoteQueryConfig: { fields: string[]; pagination: { order?: Record<string, string>, skip?: number, take?: number } }
|
remoteQueryConfig: {
|
||||||
|
fields: string[]
|
||||||
|
pagination: { order?: Record<string, string>; skip?: number; take?: number }
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* An object containing the fields that are filterable e.g `{ id: Any<String> }`
|
* An object containing the fields that are filterable e.g `{ id: Any<String> }`
|
||||||
*/
|
*/
|
||||||
@@ -58,12 +63,12 @@ export interface AuthenticatedMedusaRequest<Body = never>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MedusaResponse = Response
|
export type MedusaResponse<Body = unknown> = Response<Body>
|
||||||
|
|
||||||
export type MedusaNextFunction = NextFunction
|
export type MedusaNextFunction = NextFunction
|
||||||
|
|
||||||
export type MedusaRequestHandler = (
|
export type MedusaRequestHandler<Body = unknown, Res = unknown> = (
|
||||||
req: MedusaRequest<unknown>,
|
req: MedusaRequest<Body>,
|
||||||
res: MedusaResponse,
|
res: MedusaResponse<Res>,
|
||||||
next: MedusaNextFunction
|
next: MedusaNextFunction
|
||||||
) => Promise<void> | void
|
) => Promise<void> | void
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import { AdminShippingOptionTypeResponse } from "./shipping-option-type"
|
|||||||
import { AdminShippingOptionRuleResponse } from "./shipping-option-rule"
|
import { AdminShippingOptionRuleResponse } from "./shipping-option-rule"
|
||||||
import { AdminShippingProfileResponse } from "./shipping-profile"
|
import { AdminShippingProfileResponse } from "./shipping-profile"
|
||||||
import { AdminFulfillmentProviderResponse } from "./fulfillment-provider"
|
import { AdminFulfillmentProviderResponse } from "./fulfillment-provider"
|
||||||
import { AdminFulfillmentResponse } from "./fulfillment"
|
import { AdminPriceSetPriceResponse } from "../../pricing"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
export interface AdminShippingOptionResponse {
|
interface AdminShippingOptionResponse {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
price_type: ShippingOptionPriceType
|
price_type: ShippingOptionPriceType
|
||||||
@@ -21,11 +21,18 @@ export interface AdminShippingOptionResponse {
|
|||||||
metadata: Record<string, unknown> | null
|
metadata: Record<string, unknown> | null
|
||||||
service_zone: AdminServiceZoneResponse
|
service_zone: AdminServiceZoneResponse
|
||||||
shipping_profile: AdminShippingProfileResponse
|
shipping_profile: AdminShippingProfileResponse
|
||||||
fulfillment_provider: AdminFulfillmentProviderResponse
|
provider: AdminFulfillmentProviderResponse
|
||||||
type: AdminShippingOptionTypeResponse
|
type: AdminShippingOptionTypeResponse
|
||||||
rules: AdminShippingOptionRuleResponse[]
|
rules: AdminShippingOptionRuleResponse[]
|
||||||
fulfillments: AdminFulfillmentResponse[]
|
prices: AdminPriceSetPriceResponse[]
|
||||||
created_at: Date
|
created_at: Date
|
||||||
updated_at: Date
|
updated_at: Date
|
||||||
deleted_at: Date | null
|
deleted_at: Date | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
export interface AdminShippingOptionRetrieveResponse {
|
||||||
|
shipping_option: AdminShippingOptionResponse[]
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export * from "./api-key"
|
export * from "./api-key"
|
||||||
export * from "./fulfillment"
|
export * from "./fulfillment"
|
||||||
|
export * from "./pricing"
|
||||||
export * from "./sales-channel"
|
export * from "./sales-channel"
|
||||||
export * from "./stock-locations"
|
export * from "./stock-locations"
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./price"
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export interface AdminPriceSetPriceResponse {
|
||||||
|
id: string
|
||||||
|
amount: number
|
||||||
|
currency_code: string
|
||||||
|
created_at: string
|
||||||
|
updated_at: string
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user