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:
Adrien de Peretti
2024-04-30 18:45:17 +02:00
committed by GitHub
parent 3affcc2525
commit e26cda4b6a
11 changed files with 470 additions and 41 deletions

View File

@@ -254,6 +254,14 @@ medusaIntegrationTestRunner({
amount: 10000,
},
],
rules: [
shippingOptionRule,
{
operator: RuleOperator.EQ,
attribute: "new_attr",
value: "true",
},
],
}
const updateResponse = await api.post(
@@ -264,6 +272,7 @@ medusaIntegrationTestRunner({
expect(updateResponse.status).toEqual(200)
expect(updateResponse.data.shipping_option.prices).toHaveLength(2)
expect(updateResponse.data.shipping_option.rules).toHaveLength(2)
expect(updateResponse.data.shipping_option).toEqual(
expect.objectContaining({
id: expect.any(String),
@@ -301,6 +310,12 @@ medusaIntegrationTestRunner({
attribute: "old_attr",
value: "old value",
}),
expect.objectContaining({
id: expect.any(String),
operator: "eq",
attribute: "new_attr",
value: "true",
}),
]),
})
)

View File

@@ -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,
}),
])
)
})
})
},
})