feat(fulfillment): List shipping options filtered by context anmd rules (#6507)
**What** Should be able to list the shipping options with or without a context, when a context is provided all the rules of the shipping options must be valid for the shipping options to be returned. FIXES CORE-1765
This commit is contained in:
committed by
GitHub
parent
b13c669528
commit
ac829fc67f
5
.changeset/loud-mirrors-perform.md
Normal file
5
.changeset/loud-mirrors-perform.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@medusajs/types": patch
|
||||
---
|
||||
|
||||
feat(fulfillment): List shipping options filtered by context and rules
|
||||
39
packages/fulfillment/integration-tests/__fixtures__/index.ts
Normal file
39
packages/fulfillment/integration-tests/__fixtures__/index.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { CreateShippingOptionDTO } from "@medusajs/types"
|
||||
|
||||
export function generateCreateShippingOptionsData({
|
||||
name,
|
||||
service_zone_id,
|
||||
shipping_profile_id,
|
||||
service_provider_id,
|
||||
price_type,
|
||||
rules,
|
||||
type,
|
||||
data,
|
||||
}: Omit<CreateShippingOptionDTO, "name" | "price_type" | "type"> & {
|
||||
price_type?: CreateShippingOptionDTO["price_type"]
|
||||
name?: string
|
||||
type?: CreateShippingOptionDTO["type"]
|
||||
}): Required<CreateShippingOptionDTO> {
|
||||
return {
|
||||
service_zone_id: service_zone_id,
|
||||
shipping_profile_id: shipping_profile_id,
|
||||
service_provider_id: service_provider_id,
|
||||
type: type ?? {
|
||||
code: "test-type",
|
||||
description: "test-description",
|
||||
label: "test-label",
|
||||
},
|
||||
data: data ?? {
|
||||
amount: 1000,
|
||||
},
|
||||
name: name ?? Math.random().toString(36).substring(7),
|
||||
price_type: price_type ?? "flat",
|
||||
rules: rules ?? [
|
||||
{
|
||||
attribute: "weight",
|
||||
operator: "eq",
|
||||
value: "test",
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import {Modules} from "@medusajs/modules-sdk"
|
||||
import {
|
||||
CreateFulfillmentSetDTO,
|
||||
CreateGeoZoneDTO,
|
||||
@@ -12,11 +12,21 @@ import {
|
||||
UpdateGeoZoneDTO,
|
||||
UpdateServiceZoneDTO,
|
||||
} from "@medusajs/types"
|
||||
import { GeoZoneType } from "@medusajs/utils"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
import {GeoZoneType} from "@medusajs/utils"
|
||||
import {moduleIntegrationTestRunner, SuiteOptions} from "medusa-test-utils"
|
||||
import {generateCreateShippingOptionsData} from "../__fixtures__"
|
||||
|
||||
jest.setTimeout(100000)
|
||||
|
||||
// TODO: Temporary until the providers are sorted out
|
||||
const createProvider = async (MikroOrmWrapper, providerId: string) => {
|
||||
const [{ id }] = await MikroOrmWrapper.forkManager().execute(
|
||||
`insert into service_provider (id) values ('${providerId}') returning id`
|
||||
)
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.FULFILLMENT,
|
||||
testSuite: ({
|
||||
@@ -249,6 +259,177 @@ moduleIntegrationTestRunner({
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("shipping options", () => {
|
||||
it("should list shipping options with a filter", async function () {
|
||||
const fulfillmentSet = await service.create({
|
||||
name: "test",
|
||||
type: "test-type",
|
||||
service_zones: [
|
||||
{
|
||||
name: "test",
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const shippingProfile = await service.createShippingProfiles({
|
||||
name: "test",
|
||||
type: "default",
|
||||
})
|
||||
|
||||
const providerId = await createProvider(
|
||||
MikroOrmWrapper,
|
||||
"sp_jdafwfleiwuonl"
|
||||
)
|
||||
|
||||
const [shippingOption1] = await service.createShippingOptions([
|
||||
generateCreateShippingOptionsData({
|
||||
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: providerId,
|
||||
rules: [
|
||||
{
|
||||
attribute: "test-attribute",
|
||||
operator: "in",
|
||||
value: ["test"],
|
||||
},
|
||||
],
|
||||
}),
|
||||
generateCreateShippingOptionsData({
|
||||
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: providerId,
|
||||
rules: [
|
||||
{
|
||||
attribute: "test-attribute",
|
||||
operator: "eq",
|
||||
value: "test",
|
||||
},
|
||||
{
|
||||
attribute: "test-attribute2.options",
|
||||
operator: "in",
|
||||
value: ["test", "test2"],
|
||||
},
|
||||
],
|
||||
}),
|
||||
])
|
||||
|
||||
const listedOptions = await service.listShippingOptions({
|
||||
name: shippingOption1.name,
|
||||
})
|
||||
|
||||
expect(listedOptions).toHaveLength(1)
|
||||
expect(listedOptions[0].id).toEqual(shippingOption1.id)
|
||||
})
|
||||
|
||||
it("should list shipping options with a context", async function () {
|
||||
const fulfillmentSet = await service.create({
|
||||
name: "test",
|
||||
type: "test-type",
|
||||
service_zones: [
|
||||
{
|
||||
name: "test",
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const shippingProfile = await service.createShippingProfiles({
|
||||
name: "test",
|
||||
type: "default",
|
||||
})
|
||||
|
||||
const providerId = await createProvider(
|
||||
MikroOrmWrapper,
|
||||
"sp_jdafwfleiwuonl"
|
||||
)
|
||||
|
||||
const [shippingOption1, , shippingOption3] =
|
||||
await service.createShippingOptions([
|
||||
generateCreateShippingOptionsData({
|
||||
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: providerId,
|
||||
rules: [
|
||||
{
|
||||
attribute: "test-attribute",
|
||||
operator: "in",
|
||||
value: ["test"],
|
||||
},
|
||||
],
|
||||
}),
|
||||
generateCreateShippingOptionsData({
|
||||
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: providerId,
|
||||
rules: [
|
||||
{
|
||||
attribute: "test-attribute",
|
||||
operator: "in",
|
||||
value: ["test-test"],
|
||||
},
|
||||
],
|
||||
}),
|
||||
generateCreateShippingOptionsData({
|
||||
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: providerId,
|
||||
rules: [
|
||||
{
|
||||
attribute: "test-attribute",
|
||||
operator: "eq",
|
||||
value: "test",
|
||||
},
|
||||
{
|
||||
attribute: "test-attribute2.options",
|
||||
operator: "in",
|
||||
value: ["test", "test2"],
|
||||
},
|
||||
],
|
||||
}),
|
||||
])
|
||||
|
||||
let listedOptions = await service.listShippingOptions({
|
||||
context: {
|
||||
"test-attribute": "test",
|
||||
"test-attribute2": {
|
||||
options: "test2",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
expect(listedOptions).toHaveLength(2)
|
||||
expect(listedOptions).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ id: shippingOption1.id }),
|
||||
expect.objectContaining({ id: shippingOption3.id }),
|
||||
])
|
||||
)
|
||||
|
||||
listedOptions = await service.listShippingOptions({
|
||||
fulfillment_set_id: { $ne: fulfillmentSet.id },
|
||||
context: {
|
||||
"test-attribute": "test",
|
||||
"test-attribute2": {
|
||||
options: "test2",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
expect(listedOptions).toHaveLength(0)
|
||||
|
||||
listedOptions = await service.listShippingOptions({
|
||||
fulfillment_set_type: "non-existing-type",
|
||||
context: {
|
||||
"test-attribute": "test",
|
||||
"test-attribute2": {
|
||||
options: "test2",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
expect(listedOptions).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("mutations", () => {
|
||||
@@ -788,34 +969,16 @@ moduleIntegrationTestRunner({
|
||||
fulfillment_set_id: fulfillmentSet.id,
|
||||
})
|
||||
|
||||
// TODO: change that for a real provider instead of fake data manual inserted data
|
||||
const [{ id: providerId }] =
|
||||
await MikroOrmWrapper.forkManager().execute(
|
||||
"insert into service_provider (id) values ('sp_jdafwfleiwuonl') returning id"
|
||||
)
|
||||
const providerId = await createProvider(
|
||||
MikroOrmWrapper,
|
||||
"sp_jdafwfleiwuonl"
|
||||
)
|
||||
|
||||
const createData: CreateShippingOptionDTO = {
|
||||
name: "test-option",
|
||||
price_type: "flat",
|
||||
const createData: CreateShippingOptionDTO = generateCreateShippingOptionsData({
|
||||
service_zone_id: serviceZone.id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: providerId,
|
||||
type: {
|
||||
code: "test-type",
|
||||
description: "test-description",
|
||||
label: "test-label",
|
||||
},
|
||||
data: {
|
||||
amount: 1000,
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
attribute: "test-attribute",
|
||||
operator: "in",
|
||||
value: ["test-value"],
|
||||
},
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
const createdShippingOption = await service.createShippingOptions(
|
||||
createData
|
||||
@@ -863,57 +1026,22 @@ moduleIntegrationTestRunner({
|
||||
fulfillment_set_id: fulfillmentSet.id,
|
||||
})
|
||||
|
||||
// TODO: change that for a real provider instead of fake data manual inserted data
|
||||
const [{ id: providerId }] =
|
||||
await MikroOrmWrapper.forkManager().execute(
|
||||
"insert into service_provider (id) values ('sp_jdafwfleiwuonl') returning id"
|
||||
)
|
||||
const providerId = await createProvider(
|
||||
MikroOrmWrapper,
|
||||
"sp_jdafwfleiwuonl"
|
||||
)
|
||||
|
||||
const createData: CreateShippingOptionDTO[] = [
|
||||
{
|
||||
name: "test-option",
|
||||
price_type: "flat",
|
||||
generateCreateShippingOptionsData({
|
||||
service_zone_id: serviceZone.id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: providerId,
|
||||
type: {
|
||||
code: "test-type",
|
||||
description: "test-description",
|
||||
label: "test-label",
|
||||
},
|
||||
data: {
|
||||
amount: 1000,
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
attribute: "test-attribute",
|
||||
operator: "eq",
|
||||
value: "test-value",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "test-option-2",
|
||||
price_type: "calculated",
|
||||
}),
|
||||
generateCreateShippingOptionsData({
|
||||
service_zone_id: serviceZone.id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: providerId,
|
||||
type: {
|
||||
code: "test-type",
|
||||
description: "test-description",
|
||||
label: "test-label",
|
||||
},
|
||||
data: {
|
||||
amount: 1000,
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
attribute: "test-attribute",
|
||||
operator: "eq",
|
||||
value: "test-value",
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
]
|
||||
|
||||
const createdShippingOptions = await service.createShippingOptions(
|
||||
@@ -968,34 +1096,23 @@ moduleIntegrationTestRunner({
|
||||
fulfillment_set_id: fulfillmentSet.id,
|
||||
})
|
||||
|
||||
// TODO: change that for a real provider instead of fake data manual inserted data
|
||||
const [{ id: providerId }] =
|
||||
await MikroOrmWrapper.forkManager().execute(
|
||||
"insert into service_provider (id) values ('sp_jdafwfleiwuonl') returning id"
|
||||
)
|
||||
const providerId = await createProvider(
|
||||
MikroOrmWrapper,
|
||||
"sp_jdafwfleiwuonl"
|
||||
)
|
||||
|
||||
const createData: CreateShippingOptionDTO = {
|
||||
name: "test-option",
|
||||
price_type: "flat",
|
||||
const createData: CreateShippingOptionDTO = generateCreateShippingOptionsData({
|
||||
service_zone_id: serviceZone.id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: providerId,
|
||||
type: {
|
||||
code: "test-type",
|
||||
description: "test-description",
|
||||
label: "test-label",
|
||||
},
|
||||
data: {
|
||||
amount: 1000,
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
attribute: "test-attribute",
|
||||
operator: "invalid",
|
||||
operator: "invalid" as any,
|
||||
value: "test-value",
|
||||
},
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
const err = await service
|
||||
.createShippingOptions(createData)
|
||||
@@ -1029,28 +1146,13 @@ moduleIntegrationTestRunner({
|
||||
"insert into service_provider (id) values ('sp_jdafwfleiwuonl') returning id"
|
||||
)
|
||||
|
||||
const shippingOption = await service.createShippingOptions({
|
||||
name: "test-option",
|
||||
price_type: "flat",
|
||||
service_zone_id: serviceZone.id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: providerId,
|
||||
type: {
|
||||
code: "test-type",
|
||||
description: "test-description",
|
||||
label: "test-label",
|
||||
},
|
||||
data: {
|
||||
amount: 1000,
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
attribute: "test-attribute",
|
||||
operator: "eq",
|
||||
value: "test-value",
|
||||
},
|
||||
],
|
||||
})
|
||||
const shippingOption = await service.createShippingOptions(
|
||||
generateCreateShippingOptionsData({
|
||||
service_zone_id: serviceZone.id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: providerId,
|
||||
})
|
||||
)
|
||||
|
||||
const ruleData = {
|
||||
attribute: "test-attribute",
|
||||
@@ -1835,33 +1937,16 @@ moduleIntegrationTestRunner({
|
||||
type: "default",
|
||||
})
|
||||
|
||||
const [serviceProvider] =
|
||||
await MikroOrmWrapper.forkManager().execute(
|
||||
"insert into service_provider (id) values ('sp_jdafwfleiwuonl') returning id"
|
||||
)
|
||||
const providerId = await createProvider(
|
||||
MikroOrmWrapper,
|
||||
"sp_jdafwfleiwuonl"
|
||||
)
|
||||
|
||||
const shippingOptionData = {
|
||||
name: "test",
|
||||
price_type: "flat",
|
||||
const shippingOptionData = generateCreateShippingOptionsData({
|
||||
service_zone_id: serviceZone.id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: serviceProvider.id,
|
||||
type: {
|
||||
code: "test",
|
||||
description: "test",
|
||||
label: "test",
|
||||
},
|
||||
data: {
|
||||
amount: 1000,
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
attribute: "test",
|
||||
operator: "eq",
|
||||
value: "test",
|
||||
},
|
||||
],
|
||||
}
|
||||
service_provider_id: providerId,
|
||||
})
|
||||
|
||||
const shippingOption = await service.createShippingOptions(
|
||||
shippingOptionData
|
||||
@@ -1873,7 +1958,7 @@ moduleIntegrationTestRunner({
|
||||
price_type: "calculated",
|
||||
service_zone_id: serviceZone.id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: serviceProvider.id,
|
||||
service_provider_id: providerId,
|
||||
type: {
|
||||
code: "updated-test",
|
||||
description: "updated-test",
|
||||
@@ -1955,33 +2040,16 @@ moduleIntegrationTestRunner({
|
||||
type: "default",
|
||||
})
|
||||
|
||||
const [serviceProvider] =
|
||||
await MikroOrmWrapper.forkManager().execute(
|
||||
"insert into service_provider (id) values ('sp_jdafwfleiwuonl') returning id"
|
||||
)
|
||||
const providerId = await createProvider(
|
||||
MikroOrmWrapper,
|
||||
"sp_jdafwfleiwuonl"
|
||||
)
|
||||
|
||||
const shippingOptionData = {
|
||||
name: "test",
|
||||
price_type: "flat",
|
||||
const shippingOptionData = generateCreateShippingOptionsData({
|
||||
service_zone_id: serviceZone.id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: serviceProvider.id,
|
||||
type: {
|
||||
code: "test",
|
||||
description: "test",
|
||||
label: "test",
|
||||
},
|
||||
data: {
|
||||
amount: 1000,
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
attribute: "test",
|
||||
operator: "eq",
|
||||
value: "test",
|
||||
},
|
||||
],
|
||||
}
|
||||
service_provider_id: providerId,
|
||||
})
|
||||
|
||||
const shippingOption = await service.createShippingOptions(
|
||||
shippingOptionData
|
||||
@@ -1993,7 +2061,7 @@ moduleIntegrationTestRunner({
|
||||
price_type: "calculated",
|
||||
service_zone_id: serviceZone.id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: serviceProvider.id,
|
||||
service_provider_id: providerId,
|
||||
data: {
|
||||
amount: 2000,
|
||||
},
|
||||
@@ -2068,56 +2136,22 @@ moduleIntegrationTestRunner({
|
||||
type: "default",
|
||||
})
|
||||
|
||||
const [serviceProvider] =
|
||||
await MikroOrmWrapper.forkManager().execute(
|
||||
"insert into service_provider (id) values ('sp_jdafwfleiwuonl') returning id"
|
||||
)
|
||||
const providerId = await createProvider(
|
||||
MikroOrmWrapper,
|
||||
"sp_jdafwfleiwuonl"
|
||||
)
|
||||
|
||||
const shippingOptionData = [
|
||||
{
|
||||
name: "test",
|
||||
price_type: "flat",
|
||||
generateCreateShippingOptionsData({
|
||||
service_zone_id: serviceZone.id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: serviceProvider.id,
|
||||
type: {
|
||||
code: "test",
|
||||
description: "test",
|
||||
label: "test",
|
||||
},
|
||||
data: {
|
||||
amount: 1000,
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
attribute: "test",
|
||||
operator: "eq",
|
||||
value: "test",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "test2",
|
||||
price_type: "calculated",
|
||||
service_provider_id: providerId,
|
||||
}),
|
||||
generateCreateShippingOptionsData({
|
||||
service_zone_id: serviceZone.id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: serviceProvider.id,
|
||||
type: {
|
||||
code: "test",
|
||||
description: "test",
|
||||
label: "test",
|
||||
},
|
||||
data: {
|
||||
amount: 1000,
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
attribute: "test",
|
||||
operator: "eq",
|
||||
value: "test",
|
||||
},
|
||||
],
|
||||
},
|
||||
service_provider_id: providerId,
|
||||
})
|
||||
]
|
||||
|
||||
const shippingOptions = await service.createShippingOptions(
|
||||
@@ -2131,7 +2165,7 @@ moduleIntegrationTestRunner({
|
||||
price_type: "calculated",
|
||||
service_zone_id: serviceZone.id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: serviceProvider.id,
|
||||
service_provider_id: providerId,
|
||||
type: {
|
||||
code: "updated-test",
|
||||
description: "updated-test",
|
||||
@@ -2154,7 +2188,7 @@ moduleIntegrationTestRunner({
|
||||
price_type: "calculated",
|
||||
service_zone_id: serviceZone.id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: serviceProvider.id,
|
||||
service_provider_id: providerId,
|
||||
type: {
|
||||
code: "updated-test",
|
||||
description: "updated-test",
|
||||
@@ -2307,33 +2341,16 @@ moduleIntegrationTestRunner({
|
||||
type: "default",
|
||||
})
|
||||
|
||||
const [serviceProvider] =
|
||||
await MikroOrmWrapper.forkManager().execute(
|
||||
"insert into service_provider (id) values ('sp_jdafwfleiwuonl') returning id"
|
||||
)
|
||||
const providerId = await createProvider(
|
||||
MikroOrmWrapper,
|
||||
"sp_jdafwfleiwuonl"
|
||||
)
|
||||
|
||||
const shippingOptionData = {
|
||||
name: "test",
|
||||
price_type: "flat",
|
||||
const shippingOptionData = generateCreateShippingOptionsData({
|
||||
service_zone_id: serviceZone.id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: serviceProvider.id,
|
||||
type: {
|
||||
code: "test",
|
||||
description: "test",
|
||||
label: "test",
|
||||
},
|
||||
data: {
|
||||
amount: 1000,
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
attribute: "test",
|
||||
operator: "eq",
|
||||
value: "test",
|
||||
},
|
||||
],
|
||||
}
|
||||
service_provider_id: providerId,
|
||||
})
|
||||
|
||||
const shippingOption = await service.createShippingOptions(
|
||||
shippingOptionData
|
||||
@@ -2374,33 +2391,16 @@ moduleIntegrationTestRunner({
|
||||
type: "default",
|
||||
})
|
||||
|
||||
const [serviceProvider] =
|
||||
await MikroOrmWrapper.forkManager().execute(
|
||||
"insert into service_provider (id) values ('sp_jdafwfleiwuonl') returning id"
|
||||
)
|
||||
const providerId = await createProvider(
|
||||
MikroOrmWrapper,
|
||||
"sp_jdafwfleiwuonl"
|
||||
)
|
||||
|
||||
const shippingOptionData = {
|
||||
name: "test",
|
||||
price_type: "flat",
|
||||
const shippingOptionData = generateCreateShippingOptionsData({
|
||||
service_zone_id: serviceZone.id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: serviceProvider.id,
|
||||
type: {
|
||||
code: "test",
|
||||
description: "test",
|
||||
label: "test",
|
||||
},
|
||||
data: {
|
||||
amount: 1000,
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
attribute: "test",
|
||||
operator: "eq",
|
||||
value: "test",
|
||||
},
|
||||
],
|
||||
}
|
||||
service_provider_id: providerId,
|
||||
})
|
||||
|
||||
const shippingOption = await service.createShippingOptions(
|
||||
shippingOptionData
|
||||
@@ -2444,33 +2444,18 @@ moduleIntegrationTestRunner({
|
||||
name: "test",
|
||||
fulfillment_set_id: fulfillmentSet.id,
|
||||
})
|
||||
const [serviceProvider] =
|
||||
await MikroOrmWrapper.forkManager().execute(
|
||||
"insert into service_provider (id) values ('sp_jdafwfleiwuonl') returning id"
|
||||
)
|
||||
const providerId = await createProvider(
|
||||
MikroOrmWrapper,
|
||||
"sp_jdafwfleiwuonl"
|
||||
)
|
||||
|
||||
const shippingOption = await service.createShippingOptions({
|
||||
name: "test",
|
||||
price_type: "flat",
|
||||
service_zone_id: serviceZone.id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: serviceProvider.id,
|
||||
type: {
|
||||
code: "test",
|
||||
description: "test",
|
||||
label: "test",
|
||||
},
|
||||
data: {
|
||||
amount: 1000,
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
attribute: "test",
|
||||
operator: "eq",
|
||||
value: "test",
|
||||
},
|
||||
],
|
||||
})
|
||||
const shippingOption = await service.createShippingOptions(
|
||||
generateCreateShippingOptionsData({
|
||||
service_zone_id: serviceZone.id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
service_provider_id: providerId,
|
||||
})
|
||||
)
|
||||
|
||||
const updateData = {
|
||||
id: shippingOption.rules[0].id,
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import {
|
||||
Context,
|
||||
DAL,
|
||||
FilterableShippingOptionProps,
|
||||
FilterQuery,
|
||||
FindConfig,
|
||||
FulfillmentTypes,
|
||||
IFulfillmentModuleService,
|
||||
InternalModuleDeclaration,
|
||||
ModuleJoinerConfig,
|
||||
ModulesSdkTypes,
|
||||
ShippingOptionDTO,
|
||||
UpdateFulfillmentSetDTO,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
@@ -29,7 +33,7 @@ import {
|
||||
ShippingOptionType,
|
||||
ShippingProfile,
|
||||
} from "@models"
|
||||
import { validateRules } from "@utils"
|
||||
import { isContextValid, validateRules } from "@utils"
|
||||
|
||||
const generateMethodForModels = [
|
||||
ServiceZone,
|
||||
@@ -113,6 +117,100 @@ export default class FulfillmentModuleService<
|
||||
return joinerConfig
|
||||
}
|
||||
|
||||
protected static normalizeShippingOptionsListParams(
|
||||
filters: FilterableShippingOptionProps = {},
|
||||
config: FindConfig<ShippingOptionDTO> = {}
|
||||
) {
|
||||
let { fulfillment_set_id, fulfillment_set_type, context, ...where } =
|
||||
filters
|
||||
|
||||
const normalizedConfig = { ...config }
|
||||
normalizedConfig.relations = [
|
||||
"rules",
|
||||
"type",
|
||||
"shipping_profile",
|
||||
"service_provider",
|
||||
...(normalizedConfig.relations ?? []),
|
||||
]
|
||||
// The assumption is that there won't be an infinite amount of shipping options. So if a context filtering needs to be applied we can retrieve them all.
|
||||
normalizedConfig.take =
|
||||
normalizedConfig.take ?? (context ? null : undefined)
|
||||
|
||||
let normalizedFilters = { ...where } as FilterQuery
|
||||
|
||||
if (fulfillment_set_id || fulfillment_set_type) {
|
||||
const fulfillmentSetConstraints = {}
|
||||
|
||||
if (fulfillment_set_id) {
|
||||
fulfillmentSetConstraints["id"] = fulfillment_set_id
|
||||
}
|
||||
|
||||
if (fulfillment_set_type) {
|
||||
fulfillmentSetConstraints["type"] = fulfillment_set_type
|
||||
}
|
||||
|
||||
normalizedFilters = {
|
||||
...normalizedFilters,
|
||||
service_zone: {
|
||||
fulfillment_set: fulfillmentSetConstraints,
|
||||
},
|
||||
}
|
||||
|
||||
normalizedConfig.relations.push("service_zone.fulfillment_set")
|
||||
}
|
||||
|
||||
normalizedConfig.relations = Array.from(new Set(normalizedConfig.relations))
|
||||
|
||||
return {
|
||||
filters: normalizedFilters,
|
||||
config: normalizedConfig,
|
||||
context,
|
||||
}
|
||||
}
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
// @ts-ignore
|
||||
async listShippingOptions(
|
||||
filters: FilterableShippingOptionProps = {},
|
||||
config: FindConfig<ShippingOptionDTO> = {},
|
||||
sharedContext?: Context
|
||||
): Promise<FulfillmentTypes.ShippingOptionDTO[]> {
|
||||
const {
|
||||
filters: normalizedFilters,
|
||||
config: normalizedConfig,
|
||||
context,
|
||||
} = FulfillmentModuleService.normalizeShippingOptionsListParams(
|
||||
filters,
|
||||
config
|
||||
)
|
||||
|
||||
let shippingOptions = await this.shippingOptionService_.list(
|
||||
normalizedFilters,
|
||||
normalizedConfig,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
// Apply rules context filtering
|
||||
if (context) {
|
||||
shippingOptions = shippingOptions.filter((shippingOption) => {
|
||||
if (!shippingOption.rules?.length) {
|
||||
return true
|
||||
}
|
||||
|
||||
return isContextValid(
|
||||
context,
|
||||
shippingOption.rules.map((r) => r)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
return await this.baseRepository_.serialize<
|
||||
FulfillmentTypes.ShippingOptionDTO[]
|
||||
>(shippingOptions, {
|
||||
populate: true,
|
||||
})
|
||||
}
|
||||
|
||||
create(
|
||||
data: FulfillmentTypes.CreateFulfillmentSetDTO[],
|
||||
sharedContext?: Context
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { isContextValidForRules, RuleOperator } from "../utils"
|
||||
import { isContextValid, RuleOperator } from "../utils"
|
||||
|
||||
describe("isContextValidForRules", () => {
|
||||
const context = {
|
||||
@@ -19,37 +19,37 @@ describe("isContextValidForRules", () => {
|
||||
value: "wrongValue",
|
||||
}
|
||||
|
||||
it("returns true when all rules are valid and atLeastOneValidRule is false", () => {
|
||||
it("returns true when all rules are valid", () => {
|
||||
const rules = [validRule, validRule]
|
||||
expect(isContextValidForRules(context, rules)).toBe(true)
|
||||
expect(isContextValid(context, rules)).toBe(true)
|
||||
})
|
||||
|
||||
it("returns true when all rules are valid and atLeastOneValidRule is true", () => {
|
||||
it("returns true when some rules are valid", () => {
|
||||
const rules = [validRule, validRule]
|
||||
const options = { atLeastOneValidRule: true }
|
||||
expect(isContextValidForRules(context, rules, options)).toBe(true)
|
||||
const options = { someAreValid: true }
|
||||
expect(isContextValid(context, rules, options)).toBe(true)
|
||||
})
|
||||
|
||||
it("returns true when some rules are valid and atLeastOneValidRule is true", () => {
|
||||
it("returns true when some rules are valid and someAreValid is true", () => {
|
||||
const rules = [validRule, invalidRule]
|
||||
const options = { atLeastOneValidRule: true }
|
||||
expect(isContextValidForRules(context, rules, options)).toBe(true)
|
||||
const options = { someAreValid: true }
|
||||
expect(isContextValid(context, rules, options)).toBe(true)
|
||||
})
|
||||
|
||||
it("returns false when some rules are valid and atLeastOneValidRule is false", () => {
|
||||
it("returns false when some rules are valid", () => {
|
||||
const rules = [validRule, invalidRule]
|
||||
expect(isContextValidForRules(context, rules)).toBe(false)
|
||||
expect(isContextValid(context, rules)).toBe(false)
|
||||
})
|
||||
|
||||
it("returns false when no rules are valid and atLeastOneValidRule is true", () => {
|
||||
it("returns false when no rules are valid and someAreValid is true", () => {
|
||||
const rules = [invalidRule, invalidRule]
|
||||
const options = { atLeastOneValidRule: true }
|
||||
expect(isContextValidForRules(context, rules, options)).toBe(false)
|
||||
const options = { someAreValid: true }
|
||||
expect(isContextValid(context, rules, options)).toBe(false)
|
||||
})
|
||||
|
||||
it("returns false when no rules are valid and atLeastOneValidRule is false", () => {
|
||||
it("returns false when no rules are valid", () => {
|
||||
const rules = [invalidRule, invalidRule]
|
||||
expect(isContextValidForRules(context, rules)).toBe(false)
|
||||
expect(isContextValid(context, rules)).toBe(false)
|
||||
})
|
||||
|
||||
it("returns true when the 'gt' operator is valid", () => {
|
||||
@@ -61,7 +61,7 @@ describe("isContextValidForRules", () => {
|
||||
},
|
||||
]
|
||||
const context = { attribute1: "2" }
|
||||
expect(isContextValidForRules(context, rules)).toBe(true)
|
||||
expect(isContextValid(context, rules)).toBe(true)
|
||||
})
|
||||
|
||||
it("returns false when the 'gt' operator is invalid", () => {
|
||||
@@ -73,7 +73,7 @@ describe("isContextValidForRules", () => {
|
||||
},
|
||||
]
|
||||
const context = { attribute1: "0" }
|
||||
expect(isContextValidForRules(context, rules)).toBe(false)
|
||||
expect(isContextValid(context, rules)).toBe(false)
|
||||
})
|
||||
|
||||
it("returns true when the 'gte' operator is valid", () => {
|
||||
@@ -85,7 +85,7 @@ describe("isContextValidForRules", () => {
|
||||
},
|
||||
]
|
||||
const context = { attribute1: "2" }
|
||||
expect(isContextValidForRules(context, rules)).toBe(true)
|
||||
expect(isContextValid(context, rules)).toBe(true)
|
||||
})
|
||||
|
||||
it("returns false when the 'gte' operator is invalid", () => {
|
||||
@@ -97,7 +97,7 @@ describe("isContextValidForRules", () => {
|
||||
},
|
||||
]
|
||||
const context = { attribute1: "2" }
|
||||
expect(isContextValidForRules(context, rules)).toBe(false)
|
||||
expect(isContextValid(context, rules)).toBe(false)
|
||||
})
|
||||
|
||||
it("returns true when the 'lt' operator is valid", () => {
|
||||
@@ -109,7 +109,7 @@ describe("isContextValidForRules", () => {
|
||||
},
|
||||
]
|
||||
const context = { attribute1: "2" }
|
||||
expect(isContextValidForRules(context, rules)).toBe(true)
|
||||
expect(isContextValid(context, rules)).toBe(true)
|
||||
})
|
||||
|
||||
it("returns false when the 'lt' operator is invalid", () => {
|
||||
@@ -121,7 +121,7 @@ describe("isContextValidForRules", () => {
|
||||
},
|
||||
]
|
||||
const context = { attribute1: "2" }
|
||||
expect(isContextValidForRules(context, rules)).toBe(false)
|
||||
expect(isContextValid(context, rules)).toBe(false)
|
||||
})
|
||||
|
||||
it("returns true when the 'lte' operator is valid", () => {
|
||||
@@ -133,7 +133,7 @@ describe("isContextValidForRules", () => {
|
||||
},
|
||||
]
|
||||
const context = { attribute1: "2" }
|
||||
expect(isContextValidForRules(context, rules)).toBe(true)
|
||||
expect(isContextValid(context, rules)).toBe(true)
|
||||
})
|
||||
|
||||
// ... existing tests ...
|
||||
@@ -147,7 +147,7 @@ describe("isContextValidForRules", () => {
|
||||
},
|
||||
]
|
||||
const context = { attribute1: "2" }
|
||||
expect(isContextValidForRules(context, rules)).toBe(false)
|
||||
expect(isContextValid(context, rules)).toBe(false)
|
||||
})
|
||||
|
||||
it("returns true when the 'in' operator is valid", () => {
|
||||
@@ -159,7 +159,7 @@ describe("isContextValidForRules", () => {
|
||||
},
|
||||
]
|
||||
const context = { attribute1: "2" }
|
||||
expect(isContextValidForRules(context, rules)).toBe(true)
|
||||
expect(isContextValid(context, rules)).toBe(true)
|
||||
})
|
||||
|
||||
it("returns false when the 'in' operator is invalid", () => {
|
||||
@@ -171,7 +171,7 @@ describe("isContextValidForRules", () => {
|
||||
},
|
||||
]
|
||||
const context = { attribute1: "2" }
|
||||
expect(isContextValidForRules(context, rules)).toBe(false)
|
||||
expect(isContextValid(context, rules)).toBe(false)
|
||||
})
|
||||
|
||||
it("returns true when the 'nin' operator is valid", () => {
|
||||
@@ -183,7 +183,7 @@ describe("isContextValidForRules", () => {
|
||||
},
|
||||
]
|
||||
const context = { attribute1: "2" }
|
||||
expect(isContextValidForRules(context, rules)).toBe(true)
|
||||
expect(isContextValid(context, rules)).toBe(true)
|
||||
})
|
||||
|
||||
it("returns false when the 'nin' operator is invalid", () => {
|
||||
@@ -195,7 +195,7 @@ describe("isContextValidForRules", () => {
|
||||
},
|
||||
]
|
||||
const context = { attribute1: "2" }
|
||||
expect(isContextValidForRules(context, rules)).toBe(false)
|
||||
expect(isContextValid(context, rules)).toBe(false)
|
||||
})
|
||||
|
||||
it("returns true when the 'ne' operator is valid", () => {
|
||||
@@ -207,7 +207,7 @@ describe("isContextValidForRules", () => {
|
||||
},
|
||||
]
|
||||
const context = { attribute1: "2" }
|
||||
expect(isContextValidForRules(context, rules)).toBe(true)
|
||||
expect(isContextValid(context, rules)).toBe(true)
|
||||
})
|
||||
|
||||
it("returns false when the 'ne' operator is invalid", () => {
|
||||
@@ -219,7 +219,7 @@ describe("isContextValidForRules", () => {
|
||||
},
|
||||
]
|
||||
const context = { attribute1: "2" }
|
||||
expect(isContextValidForRules(context, rules)).toBe(false)
|
||||
expect(isContextValid(context, rules)).toBe(false)
|
||||
})
|
||||
|
||||
it("returns true when the 'eq' operator is valid", () => {
|
||||
@@ -231,7 +231,7 @@ describe("isContextValidForRules", () => {
|
||||
},
|
||||
]
|
||||
const context = { attribute1: "2" }
|
||||
expect(isContextValidForRules(context, rules)).toBe(true)
|
||||
expect(isContextValid(context, rules)).toBe(true)
|
||||
})
|
||||
|
||||
it("returns false when the 'eq' operator is invalid", () => {
|
||||
@@ -243,6 +243,6 @@ describe("isContextValidForRules", () => {
|
||||
},
|
||||
]
|
||||
const context = { attribute1: "2" }
|
||||
expect(isContextValidForRules(context, rules)).toBe(false)
|
||||
expect(isContextValid(context, rules)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -10,8 +10,8 @@ import { isString, MedusaError, pickValueFromObject } from "@medusajs/utils"
|
||||
|
||||
export type Rule = {
|
||||
attribute: string
|
||||
operator: RuleOperator
|
||||
value: string | string[]
|
||||
operator: Lowercase<keyof typeof RuleOperator>
|
||||
value: string | string[] | null
|
||||
}
|
||||
|
||||
export enum RuleOperator {
|
||||
@@ -71,18 +71,18 @@ const operatorsPredicate = {
|
||||
* @param rules
|
||||
* @param options
|
||||
*/
|
||||
export function isContextValidForRules(
|
||||
export function isContextValid(
|
||||
context: Record<string, any>,
|
||||
rules: Rule[],
|
||||
options: {
|
||||
atLeastOneValidRule: boolean
|
||||
someAreValid: boolean
|
||||
} = {
|
||||
atLeastOneValidRule: false,
|
||||
someAreValid: false,
|
||||
}
|
||||
) {
|
||||
const { atLeastOneValidRule } = options
|
||||
const { someAreValid } = options
|
||||
|
||||
const loopComparator = atLeastOneValidRule ? rules.some : rules.every
|
||||
const loopComparator = someAreValid ? rules.some : rules.every
|
||||
const predicate = (rule) => {
|
||||
const { attribute, operator, value } = rule
|
||||
const contextValue = pickValueFromObject(attribute, context)
|
||||
@@ -137,6 +137,13 @@ export function validateRule(rule: Record<string, unknown>): boolean {
|
||||
"Rule value must be an array for in/nin operators"
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (!isString(rule.value)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Rule value must be a string for the selected operator ${rule.operator}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
@@ -37,6 +37,8 @@ export interface FilterableShippingOptionProps
|
||||
extends BaseFilterable<FilterableShippingOptionProps> {
|
||||
id?: string | string[] | OperatorMap<string | string[]>
|
||||
name?: string | string[] | OperatorMap<string | string[]>
|
||||
fulfillment_set_id?: string | string[] | OperatorMap<string | string[]>
|
||||
fulfillment_set_type?: string | string[] | OperatorMap<string | string[]>
|
||||
price_type?:
|
||||
| ShippingOptionPriceType
|
||||
| ShippingOptionPriceType[]
|
||||
@@ -44,4 +46,5 @@ export interface FilterableShippingOptionProps
|
||||
service_zone?: FilterableServiceZoneProps
|
||||
shipping_option_type?: FilterableShippingOptionTypeProps
|
||||
rules?: FilterableShippingOptionRuleProps
|
||||
context?: Record<string, unknown>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user