feat(medusa, core-flows, types): Allow to update the rules from a shipping options (#7175)
**What** Add support for the following operations - update rules from the update shipping options end point - update rules from the batch update end point Also added some improvements, that can be revisited later - Add a rule value normalizer, jsonb will transform the input value to a [primitive](https://www.postgresql.org/docs/current/datatype-json.html#JSON-TYPE-MAPPING-TABLE) when possible meaning that passing `"true"` will result in storing `true` and not the string. The normalizer takes care of that
This commit is contained in:
1 parent
3affcc2525
commit
e26cda4b6a
11 files changed
+470
-41
No files matched your search
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
"@medusajs/core-flows": patch
|
||||||
|
"@medusajs/fulfillment": patch
|
||||||
|
"@medusajs/types": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(medusa, core-flows, types): Allow to update the rules from a shipping options
|
||||||
@@ -254,6 +254,14 @@ medusaIntegrationTestRunner({
|
|||||||
amount: 10000,
|
amount: 10000,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
rules: [
|
||||||
|
shippingOptionRule,
|
||||||
|
{
|
||||||
|
operator: RuleOperator.EQ,
|
||||||
|
attribute: "new_attr",
|
||||||
|
value: "true",
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateResponse = await api.post(
|
const updateResponse = await api.post(
|
||||||
@@ -264,6 +272,7 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
expect(updateResponse.status).toEqual(200)
|
expect(updateResponse.status).toEqual(200)
|
||||||
expect(updateResponse.data.shipping_option.prices).toHaveLength(2)
|
expect(updateResponse.data.shipping_option.prices).toHaveLength(2)
|
||||||
|
expect(updateResponse.data.shipping_option.rules).toHaveLength(2)
|
||||||
expect(updateResponse.data.shipping_option).toEqual(
|
expect(updateResponse.data.shipping_option).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
@@ -301,6 +310,12 @@ medusaIntegrationTestRunner({
|
|||||||
attribute: "old_attr",
|
attribute: "old_attr",
|
||||||
value: "old value",
|
value: "old value",
|
||||||
}),
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
operator: "eq",
|
||||||
|
attribute: "new_attr",
|
||||||
|
value: "true",
|
||||||
|
}),
|
||||||
]),
|
]),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|||||||
+326
@@ -0,0 +1,326 @@
|
|||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import {
|
||||||
|
BatchWorkflowInput,
|
||||||
|
CreateShippingOptionRuleDTO,
|
||||||
|
FulfillmentSetDTO,
|
||||||
|
FulfillmentWorkflow,
|
||||||
|
IFulfillmentModuleService,
|
||||||
|
IRegionModuleService,
|
||||||
|
ServiceZoneDTO,
|
||||||
|
ShippingProfileDTO,
|
||||||
|
UpdateShippingOptionRuleDTO,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { medusaIntegrationTestRunner } from "medusa-test-utils/dist"
|
||||||
|
import {
|
||||||
|
batchShippingOptionRulesWorkflow,
|
||||||
|
createShippingOptionsWorkflow,
|
||||||
|
} from "@medusajs/core-flows"
|
||||||
|
import {
|
||||||
|
ContainerRegistrationKeys,
|
||||||
|
remoteQueryObjectFromString,
|
||||||
|
RuleOperator,
|
||||||
|
} from "@medusajs/utils"
|
||||||
|
|
||||||
|
jest.setTimeout(100000)
|
||||||
|
|
||||||
|
const env = { MEDUSA_FF_MEDUSA_V2: true }
|
||||||
|
const provider_id = "manual_test-provider"
|
||||||
|
|
||||||
|
async function createShippingOptionFixture({
|
||||||
|
container,
|
||||||
|
serviceZone,
|
||||||
|
shippingProfile,
|
||||||
|
}) {
|
||||||
|
const regionService = container.resolve(
|
||||||
|
ModuleRegistrationName.REGION
|
||||||
|
) as IRegionModuleService
|
||||||
|
|
||||||
|
const [region] = await regionService.create([
|
||||||
|
{
|
||||||
|
name: "Test region",
|
||||||
|
currency_code: "eur",
|
||||||
|
countries: ["fr"],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const shippingOptionData: FulfillmentWorkflow.CreateShippingOptionsWorkflowInput =
|
||||||
|
{
|
||||||
|
name: "Test shipping option",
|
||||||
|
price_type: "flat",
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id,
|
||||||
|
type: {
|
||||||
|
code: "manual-type",
|
||||||
|
label: "Manual Type",
|
||||||
|
description: "Manual Type Description",
|
||||||
|
},
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
region_id: region.id,
|
||||||
|
amount: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "total",
|
||||||
|
operator: RuleOperator.EQ,
|
||||||
|
value: "100",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attribute: "is_store",
|
||||||
|
operator: RuleOperator.EQ,
|
||||||
|
value: "true",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const { result } = await createShippingOptionsWorkflow(container).run({
|
||||||
|
input: [shippingOptionData],
|
||||||
|
})
|
||||||
|
|
||||||
|
const remoteQuery = container.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||||
|
|
||||||
|
const remoteQueryObject = remoteQueryObjectFromString({
|
||||||
|
entryPoint: "shipping_option",
|
||||||
|
variables: {
|
||||||
|
id: result[0].id,
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"price_type",
|
||||||
|
"service_zone_id",
|
||||||
|
"shipping_profile_id",
|
||||||
|
"provider_id",
|
||||||
|
"data",
|
||||||
|
"metadata",
|
||||||
|
"type.*",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
"deleted_at",
|
||||||
|
"shipping_option_type_id",
|
||||||
|
"prices.*",
|
||||||
|
"rules.*",
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const [createdShippingOption] = await remoteQuery(remoteQueryObject)
|
||||||
|
|
||||||
|
return createdShippingOption
|
||||||
|
}
|
||||||
|
|
||||||
|
medusaIntegrationTestRunner({
|
||||||
|
env,
|
||||||
|
testSuite: ({ getContainer }) => {
|
||||||
|
let service: IFulfillmentModuleService
|
||||||
|
let container
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
container = getContainer()
|
||||||
|
service = container.resolve(ModuleRegistrationName.FULFILLMENT)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Fulfillment workflows", () => {
|
||||||
|
let fulfillmentSet: FulfillmentSetDTO
|
||||||
|
let serviceZone: ServiceZoneDTO
|
||||||
|
let shippingProfile: ShippingProfileDTO
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
fulfillmentSet = await service.create({
|
||||||
|
name: "Test fulfillment set",
|
||||||
|
type: "manual_test",
|
||||||
|
})
|
||||||
|
|
||||||
|
serviceZone = await service.createServiceZones({
|
||||||
|
name: "Test service zone",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: "country",
|
||||||
|
country_code: "US",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create, update and delete rules in batch", async () => {
|
||||||
|
const shippingOption = await createShippingOptionFixture({
|
||||||
|
container,
|
||||||
|
serviceZone,
|
||||||
|
shippingProfile,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(shippingOption.rules).toHaveLength(2)
|
||||||
|
|
||||||
|
const ruleToUpdate = shippingOption.rules.find((rule) => {
|
||||||
|
return rule.attribute === "is_store"
|
||||||
|
})
|
||||||
|
const ruleToDelete = shippingOption.rules.find((rule) => {
|
||||||
|
return rule.attribute === "total"
|
||||||
|
})
|
||||||
|
|
||||||
|
const workflowInput: BatchWorkflowInput<
|
||||||
|
CreateShippingOptionRuleDTO,
|
||||||
|
UpdateShippingOptionRuleDTO
|
||||||
|
> = {
|
||||||
|
create: [
|
||||||
|
{
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
attribute: "new_attribute",
|
||||||
|
operator: RuleOperator.EQ,
|
||||||
|
value: "100",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
update: [
|
||||||
|
{
|
||||||
|
...ruleToUpdate,
|
||||||
|
value: "false",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
delete: [ruleToDelete.id],
|
||||||
|
}
|
||||||
|
|
||||||
|
await batchShippingOptionRulesWorkflow(container).run({
|
||||||
|
input: workflowInput,
|
||||||
|
})
|
||||||
|
|
||||||
|
const remoteQuery = container.resolve(
|
||||||
|
ContainerRegistrationKeys.REMOTE_QUERY
|
||||||
|
)
|
||||||
|
|
||||||
|
const remoteQueryObject = remoteQueryObjectFromString({
|
||||||
|
entryPoint: "shipping_option",
|
||||||
|
variables: {
|
||||||
|
id: shippingOption.id,
|
||||||
|
},
|
||||||
|
fields: ["id", "rules.*"],
|
||||||
|
})
|
||||||
|
|
||||||
|
const [updatedShippingOption] = await remoteQuery(remoteQueryObject)
|
||||||
|
|
||||||
|
const newAttrRule = updatedShippingOption.rules.find((rule) => {
|
||||||
|
return rule.attribute === "new_attribute"
|
||||||
|
})
|
||||||
|
const updatedRule = updatedShippingOption.rules.find((rule) => {
|
||||||
|
return rule.attribute === "is_store"
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(updatedShippingOption.rules).toHaveLength(2)
|
||||||
|
expect(newAttrRule).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
attribute: "new_attribute",
|
||||||
|
operator: "eq",
|
||||||
|
value: 100,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(updatedRule).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
value: "false",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should revert the shipping options rules batch actions", async () => {
|
||||||
|
const shippingOption = await createShippingOptionFixture({
|
||||||
|
container,
|
||||||
|
serviceZone,
|
||||||
|
shippingProfile,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(shippingOption.rules).toHaveLength(2)
|
||||||
|
|
||||||
|
const ruleToUpdate = shippingOption.rules.find((rule) => {
|
||||||
|
return rule.attribute === "is_store"
|
||||||
|
})
|
||||||
|
const ruleToDelete = shippingOption.rules.find((rule) => {
|
||||||
|
return rule.attribute === "total"
|
||||||
|
})
|
||||||
|
|
||||||
|
const workflowInput: BatchWorkflowInput<
|
||||||
|
CreateShippingOptionRuleDTO,
|
||||||
|
UpdateShippingOptionRuleDTO
|
||||||
|
> = {
|
||||||
|
create: [
|
||||||
|
{
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
attribute: "new_attribute",
|
||||||
|
operator: RuleOperator.EQ,
|
||||||
|
value: "100",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
update: [
|
||||||
|
{
|
||||||
|
...ruleToUpdate,
|
||||||
|
value: "false",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
delete: [ruleToDelete.id],
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflow = batchShippingOptionRulesWorkflow(container)
|
||||||
|
|
||||||
|
workflow.addAction(
|
||||||
|
"throw",
|
||||||
|
{
|
||||||
|
invoke: async function failStep() {
|
||||||
|
throw new Error(`Failed to update shipping option rules`)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
noCompensation: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const { errors } = await workflow.run({
|
||||||
|
input: workflowInput,
|
||||||
|
throwOnError: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(errors).toHaveLength(1)
|
||||||
|
expect(errors[0].error.message).toEqual(
|
||||||
|
`Failed to update shipping option rules`
|
||||||
|
)
|
||||||
|
|
||||||
|
const remoteQuery = container.resolve(
|
||||||
|
ContainerRegistrationKeys.REMOTE_QUERY
|
||||||
|
)
|
||||||
|
|
||||||
|
const remoteQueryObject = remoteQueryObjectFromString({
|
||||||
|
entryPoint: "shipping_option",
|
||||||
|
variables: {
|
||||||
|
id: shippingOption.id,
|
||||||
|
},
|
||||||
|
fields: ["id", "rules.*"],
|
||||||
|
})
|
||||||
|
|
||||||
|
const [updatedShippingOption] = await remoteQuery(remoteQueryObject)
|
||||||
|
|
||||||
|
expect(updatedShippingOption.rules).toHaveLength(2)
|
||||||
|
expect(updatedShippingOption.rules).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
attribute: "is_store",
|
||||||
|
operator: "eq",
|
||||||
|
value: "true",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
attribute: "total",
|
||||||
|
operator: "eq",
|
||||||
|
value: 100,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
AddFulfillmentShippingOptionRulesWorkflowDTO,
|
AddFulfillmentShippingOptionRulesWorkflowDTO,
|
||||||
IFulfillmentModuleService,
|
IFulfillmentModuleService,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||||
|
|
||||||
export const createShippingOptionRulesStepId = "create-shipping-option-rules"
|
export const createShippingOptionRulesStepId = "create-shipping-option-rules"
|
||||||
export const createShippingOptionRulesStep = createStep(
|
export const createShippingOptionRulesStep = createStep(
|
||||||
@@ -18,12 +18,12 @@ export const createShippingOptionRulesStep = createStep(
|
|||||||
ModuleRegistrationName.FULFILLMENT
|
ModuleRegistrationName.FULFILLMENT
|
||||||
)
|
)
|
||||||
|
|
||||||
const createdPromotionRules =
|
const createdShippingOptionRules =
|
||||||
await fulfillmentModule.createShippingOptionRules(data)
|
await fulfillmentModule.createShippingOptionRules(data)
|
||||||
|
|
||||||
return new StepResponse(
|
return new StepResponse(
|
||||||
createdPromotionRules,
|
createdShippingOptionRules,
|
||||||
createdPromotionRules.map((pr) => pr.id)
|
createdShippingOptionRules.map((pr) => pr.id)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
async (ruleIds, { container }) => {
|
async (ruleIds, { container }) => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
RemoveFulfillmentShippingOptionRulesWorkflowDTO,
|
RemoveFulfillmentShippingOptionRulesWorkflowDTO,
|
||||||
RuleOperatorType,
|
RuleOperatorType,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||||
|
|
||||||
export const deleteShippingOptionRulesStepId = "delete-shipping-option-rules"
|
export const deleteShippingOptionRulesStepId = "delete-shipping-option-rules"
|
||||||
export const deleteShippingOptionRulesStep = createStep(
|
export const deleteShippingOptionRulesStep = createStep(
|
||||||
@@ -13,6 +13,10 @@ export const deleteShippingOptionRulesStep = createStep(
|
|||||||
input: RemoveFulfillmentShippingOptionRulesWorkflowDTO,
|
input: RemoveFulfillmentShippingOptionRulesWorkflowDTO,
|
||||||
{ container }
|
{ container }
|
||||||
) => {
|
) => {
|
||||||
|
if (!input.ids?.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const { ids } = input
|
const { ids } = input
|
||||||
|
|
||||||
const fulfillmentModule = container.resolve<IFulfillmentModuleService>(
|
const fulfillmentModule = container.resolve<IFulfillmentModuleService>(
|
||||||
@@ -39,6 +43,7 @@ export const deleteShippingOptionRulesStep = createStep(
|
|||||||
|
|
||||||
await fulfillmentModule.createShippingOptionRules(
|
await fulfillmentModule.createShippingOptionRules(
|
||||||
shippingOptionRules.map((rule) => ({
|
shippingOptionRules.map((rule) => ({
|
||||||
|
id: rule.id,
|
||||||
attribute: rule.attribute,
|
attribute: rule.attribute,
|
||||||
operator: rule.operator as RuleOperatorType,
|
operator: rule.operator as RuleOperatorType,
|
||||||
value: rule.value as unknown as string | string[],
|
value: rule.value as unknown as string | string[],
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import {
|
||||||
|
IFulfillmentModuleService,
|
||||||
|
UpdateFulfillmentShippingOptionRulesWorkflowDTO,
|
||||||
|
UpdateShippingOptionRuleDTO,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||||
|
|
||||||
|
export const updateShippingOptionRulesStepId = "update-shipping-option-rules"
|
||||||
|
export const updateShippingOptionRulesStep = createStep(
|
||||||
|
updateShippingOptionRulesStepId,
|
||||||
|
async (
|
||||||
|
input: UpdateFulfillmentShippingOptionRulesWorkflowDTO,
|
||||||
|
{ container }
|
||||||
|
) => {
|
||||||
|
if (!input.data?.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = input
|
||||||
|
|
||||||
|
const fulfillmentModule = container.resolve<IFulfillmentModuleService>(
|
||||||
|
ModuleRegistrationName.FULFILLMENT
|
||||||
|
)
|
||||||
|
|
||||||
|
const ids = data.map((d) => d.id)
|
||||||
|
const shippingOptionRules = await fulfillmentModule.listShippingOptionRules(
|
||||||
|
{ id: ids },
|
||||||
|
{ select: ["id", "attribute", "operator", "value", "shipping_option_id"] }
|
||||||
|
)
|
||||||
|
|
||||||
|
const updatedPromotionRules =
|
||||||
|
await fulfillmentModule.updateShippingOptionRules(data)
|
||||||
|
|
||||||
|
return new StepResponse(
|
||||||
|
updatedPromotionRules,
|
||||||
|
shippingOptionRules as unknown as UpdateShippingOptionRuleDTO[]
|
||||||
|
)
|
||||||
|
},
|
||||||
|
async (previousRulesData, { container }) => {
|
||||||
|
if (!previousRulesData?.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const fulfillmentModule = container.resolve<IFulfillmentModuleService>(
|
||||||
|
ModuleRegistrationName.FULFILLMENT
|
||||||
|
)
|
||||||
|
|
||||||
|
await fulfillmentModule.updateShippingOptionRules(previousRulesData)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -6,15 +6,16 @@ import {
|
|||||||
UpdateShippingOptionRuleDTO,
|
UpdateShippingOptionRuleDTO,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
WorkflowData,
|
|
||||||
createWorkflow,
|
createWorkflow,
|
||||||
parallelize,
|
parallelize,
|
||||||
transform,
|
transform,
|
||||||
|
WorkflowData,
|
||||||
} from "@medusajs/workflows-sdk"
|
} from "@medusajs/workflows-sdk"
|
||||||
import {
|
import {
|
||||||
createShippingOptionRulesStep,
|
createShippingOptionRulesStep,
|
||||||
deleteShippingOptionRulesStep,
|
deleteShippingOptionRulesStep,
|
||||||
} from "../steps"
|
} from "../steps"
|
||||||
|
import { updateShippingOptionRulesStep } from "../steps/update-shipping-option-rules"
|
||||||
|
|
||||||
export const batchShippingOptionRulesWorkflowId = "batch-shipping-option-rules"
|
export const batchShippingOptionRulesWorkflowId = "batch-shipping-option-rules"
|
||||||
export const batchShippingOptionRulesWorkflow = createWorkflow(
|
export const batchShippingOptionRulesWorkflow = createWorkflow(
|
||||||
@@ -27,25 +28,21 @@ export const batchShippingOptionRulesWorkflow = createWorkflow(
|
|||||||
>
|
>
|
||||||
>
|
>
|
||||||
): WorkflowData<BatchWorkflowOutput<ShippingOptionRuleDTO>> => {
|
): WorkflowData<BatchWorkflowOutput<ShippingOptionRuleDTO>> => {
|
||||||
const createInput = transform({ input }, (data) => ({
|
const actionInputs = transform({ input }, (data) => {
|
||||||
data: data.input.create ?? [],
|
const { create, update, delete: del } = data.input
|
||||||
}))
|
return {
|
||||||
|
createInput: { data: create ?? [] },
|
||||||
|
updateInput: { data: update ?? [] },
|
||||||
|
deleteInput: { ids: del ?? [] },
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const updateInput = transform({ input }, (data) => ({
|
const [created, updated, deleted] = parallelize(
|
||||||
data: data.input.update ?? [],
|
createShippingOptionRulesStep(actionInputs.createInput),
|
||||||
}))
|
updateShippingOptionRulesStep(actionInputs.updateInput),
|
||||||
|
deleteShippingOptionRulesStep(actionInputs.deleteInput)
|
||||||
const deleteInput = transform({ input }, (data) => ({
|
|
||||||
ids: data.input.delete ?? [],
|
|
||||||
}))
|
|
||||||
|
|
||||||
// TODO: Currently we don't support edits, add support for this.
|
|
||||||
// We just call the steps directly here since there are no independent workflows, switch to CRUD workflows if they get added.
|
|
||||||
const [created, deleted] = parallelize(
|
|
||||||
createShippingOptionRulesStep(createInput),
|
|
||||||
deleteShippingOptionRulesStep(deleteInput)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return transform({ created, deleted }, (data) => ({ ...data, updated: [] }))
|
return transform({ created, deleted, updated }, (data) => data)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -14,17 +14,17 @@ import {
|
|||||||
UpdateServiceZoneDTO,
|
UpdateServiceZoneDTO,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
|
arrayDifference,
|
||||||
EmitEvents,
|
EmitEvents,
|
||||||
FulfillmentUtils,
|
FulfillmentUtils,
|
||||||
|
getSetDifference,
|
||||||
InjectManager,
|
InjectManager,
|
||||||
InjectTransactionManager,
|
InjectTransactionManager,
|
||||||
|
isString,
|
||||||
MedusaContext,
|
MedusaContext,
|
||||||
MedusaError,
|
MedusaError,
|
||||||
Modules,
|
Modules,
|
||||||
ModulesSdkUtils,
|
ModulesSdkUtils,
|
||||||
arrayDifference,
|
|
||||||
getSetDifference,
|
|
||||||
isString,
|
|
||||||
promiseAll,
|
promiseAll,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
@@ -38,9 +38,9 @@ import {
|
|||||||
ShippingOptionType,
|
ShippingOptionType,
|
||||||
ShippingProfile,
|
ShippingProfile,
|
||||||
} from "@models"
|
} from "@models"
|
||||||
import { isContextValid, validateRules } from "@utils"
|
import {isContextValid, validateAndNormalizeRules} from "@utils"
|
||||||
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
|
import {entityNameToLinkableKeysMap, joinerConfig} from "../joiner-config"
|
||||||
import { UpdateShippingOptionsInput } from "../types/service"
|
import {UpdateShippingOptionsInput} from "../types/service"
|
||||||
import FulfillmentProviderService from "./fulfillment-provider"
|
import FulfillmentProviderService from "./fulfillment-provider"
|
||||||
|
|
||||||
const generateMethodForModels = [
|
const generateMethodForModels = [
|
||||||
@@ -343,7 +343,7 @@ export default class FulfillmentModuleService<
|
|||||||
| FulfillmentTypes.CreateServiceZoneDTO,
|
| FulfillmentTypes.CreateServiceZoneDTO,
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<TServiceZoneEntity | TServiceZoneEntity[]> {
|
): Promise<TServiceZoneEntity | TServiceZoneEntity[]> {
|
||||||
let data_ = Array.isArray(data) ? data : [data]
|
const data_ = Array.isArray(data) ? data : [data]
|
||||||
|
|
||||||
if (!data_.length) {
|
if (!data_.length) {
|
||||||
return []
|
return []
|
||||||
@@ -402,7 +402,7 @@ export default class FulfillmentModuleService<
|
|||||||
| FulfillmentTypes.CreateShippingOptionDTO,
|
| FulfillmentTypes.CreateShippingOptionDTO,
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<TShippingOptionEntity | TShippingOptionEntity[]> {
|
): Promise<TShippingOptionEntity | TShippingOptionEntity[]> {
|
||||||
let data_ = Array.isArray(data) ? data : [data]
|
const data_ = Array.isArray(data) ? data : [data]
|
||||||
|
|
||||||
if (!data_.length) {
|
if (!data_.length) {
|
||||||
return []
|
return []
|
||||||
@@ -410,7 +410,7 @@ export default class FulfillmentModuleService<
|
|||||||
|
|
||||||
const rules = data_.flatMap((d) => d.rules).filter(Boolean)
|
const rules = data_.flatMap((d) => d.rules).filter(Boolean)
|
||||||
if (rules.length) {
|
if (rules.length) {
|
||||||
validateRules(rules as Record<string, unknown>[])
|
validateAndNormalizeRules(rules as Record<string, unknown>[])
|
||||||
}
|
}
|
||||||
|
|
||||||
const createdShippingOptions = await this.shippingOptionService_.create(
|
const createdShippingOptions = await this.shippingOptionService_.create(
|
||||||
@@ -555,7 +555,7 @@ export default class FulfillmentModuleService<
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
validateRules(data_ as unknown as Record<string, unknown>[])
|
validateAndNormalizeRules(data_ as unknown as Record<string, unknown>[])
|
||||||
|
|
||||||
const createdShippingOptionRules =
|
const createdShippingOptionRules =
|
||||||
await this.shippingOptionRuleService_.create(data_, sharedContext)
|
await this.shippingOptionRuleService_.create(data_, sharedContext)
|
||||||
@@ -1175,7 +1175,7 @@ export default class FulfillmentModuleService<
|
|||||||
})
|
})
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
|
|
||||||
validateRules(newRules as Record<string, unknown>[])
|
validateAndNormalizeRules(newRules as Record<string, unknown>[])
|
||||||
|
|
||||||
shippingOption.rules = shippingOption.rules.map((rule) => {
|
shippingOption.rules = shippingOption.rules.map((rule) => {
|
||||||
if (!("id" in rule)) {
|
if (!("id" in rule)) {
|
||||||
@@ -1382,7 +1382,7 @@ export default class FulfillmentModuleService<
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
validateRules(data_ as unknown as Record<string, unknown>[])
|
validateAndNormalizeRules(data_ as unknown as Record<string, unknown>[])
|
||||||
|
|
||||||
const updatedShippingOptionRules =
|
const updatedShippingOptionRules =
|
||||||
await this.shippingOptionRuleService_.update(data_, sharedContext)
|
await this.shippingOptionRuleService_.update(data_, sharedContext)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
isObject,
|
||||||
isString,
|
isString,
|
||||||
MedusaError,
|
MedusaError,
|
||||||
pickValueFromObject,
|
pickValueFromObject,
|
||||||
@@ -97,21 +98,21 @@ export function validateRule(rule: Record<string, unknown>): boolean {
|
|||||||
if (!rule.attribute || !rule.operator || !rule.value) {
|
if (!rule.attribute || !rule.operator || !rule.value) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_DATA,
|
MedusaError.Types.INVALID_DATA,
|
||||||
"Rule must have an attribute, an operator and contextValue value"
|
"Rule must have an attribute, an operator and a value"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isString(rule.attribute)) {
|
if (!isString(rule.attribute)) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_DATA,
|
MedusaError.Types.INVALID_DATA,
|
||||||
"Rule attribute must be contextValue string"
|
"Rule attribute must be a string"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isString(rule.operator)) {
|
if (!isString(rule.operator)) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_DATA,
|
MedusaError.Types.INVALID_DATA,
|
||||||
"Rule operator must be contextValue string"
|
"Rule operator must be a string"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,10 +133,10 @@ export function validateRule(rule: Record<string, unknown>): boolean {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!isString(rule.value)) {
|
if (Array.isArray(rule.value) || isObject(rule.value)) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_DATA,
|
MedusaError.Types.INVALID_DATA,
|
||||||
`Rule value must be a string for the selected operator ${rule.operator}`
|
`Rule value must be a string, bool, number value for the selected operator ${rule.operator}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,6 +144,24 @@ export function validateRule(rule: Record<string, unknown>): boolean {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizeRulesValue<T extends Partial<Rule>>(rules: T[]): void {
|
||||||
|
rules.forEach((rule) => {
|
||||||
|
/**
|
||||||
|
* If a string is provided, then we don't want jsonb to convert to the primitive value based on the RFC
|
||||||
|
*/
|
||||||
|
if (rule.value === "true" || rule.value === "false") {
|
||||||
|
rule.value = rule.value === "true" ? '"true"' : '"false"'
|
||||||
|
}
|
||||||
|
|
||||||
|
return rule
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validateAndNormalizeRules<T extends Partial<Rule>>(rules: T[]) {
|
||||||
|
rules.forEach(validateRule)
|
||||||
|
normalizeRulesValue(rules)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate contextValue set of rules
|
* Validate contextValue set of rules
|
||||||
* @param rules
|
* @param rules
|
||||||
|
|||||||
@@ -126,5 +126,6 @@ export const AdminUpdateShippingOption = z
|
|||||||
)
|
)
|
||||||
.array()
|
.array()
|
||||||
.optional(),
|
.optional(),
|
||||||
|
rules: AdminUpdateShippingOptionRule.or(AdminCreateShippingOptionRule).array().optional(),
|
||||||
})
|
})
|
||||||
.strict()
|
.strict()
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
import { CreateShippingOptionRuleDTO } from "./mutations"
|
import {
|
||||||
|
CreateShippingOptionRuleDTO,
|
||||||
|
UpdateShippingOptionRuleDTO,
|
||||||
|
} from "./mutations"
|
||||||
|
|
||||||
export type AddFulfillmentShippingOptionRulesWorkflowDTO = {
|
export type AddFulfillmentShippingOptionRulesWorkflowDTO = {
|
||||||
data: CreateShippingOptionRuleDTO[]
|
data: CreateShippingOptionRuleDTO[]
|
||||||
@@ -7,3 +10,7 @@ export type AddFulfillmentShippingOptionRulesWorkflowDTO = {
|
|||||||
export type RemoveFulfillmentShippingOptionRulesWorkflowDTO = {
|
export type RemoveFulfillmentShippingOptionRulesWorkflowDTO = {
|
||||||
ids: string[]
|
ids: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type UpdateFulfillmentShippingOptionRulesWorkflowDTO = {
|
||||||
|
data: UpdateShippingOptionRuleDTO[]
|
||||||
|
}
|
||||||
Reference in new issue
Block a user