feat: Create shipping options workflow (#6962)

**What**

- Create main workflow
- Update create pricing rule types step to be idempotent
- update remote joiner to support granular isList confguration on field aliases
- Add full workflow integration tests
This commit is contained in:
Adrien de Peretti
2024-04-06 11:51:48 +00:00
committed by GitHub
parent 5e30b8cce6
commit 65794f4bb5
20 changed files with 804 additions and 20 deletions
@@ -0,0 +1,270 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import {
FulfillmentSetDTO,
FulfillmentWorkflow,
IFulfillmentModuleService,
IRegionModuleService,
ServiceZoneDTO,
ShippingProfileDTO,
} from "@medusajs/types"
import { medusaIntegrationTestRunner } from "medusa-test-utils/dist"
import { 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"
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 shipping options and prices", async () => {
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",
},
],
}
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.*",
],
})
const [createdShippingOption] = await remoteQuery(remoteQueryObject)
const prices = createdShippingOption.prices
delete createdShippingOption.prices
expect(createdShippingOption).toEqual(
expect.objectContaining({
id: result[0].id,
name: shippingOptionData.name,
price_type: shippingOptionData.price_type,
service_zone_id: serviceZone.id,
shipping_profile_id: shippingProfile.id,
provider_id: provider_id,
data: null,
metadata: null,
type: expect.objectContaining({
id: expect.any(String),
code: shippingOptionData.type.code,
label: shippingOptionData.type.label,
description: shippingOptionData.type.description,
}),
shipping_option_type_id: expect.any(String),
})
)
expect(prices).toHaveLength(2)
expect(prices).toContainEqual(
expect.objectContaining({
currency_code: "usd",
amount: 10,
})
)
expect(prices).toContainEqual(
expect.objectContaining({
currency_code: "eur",
amount: 100,
rules_count: 1,
})
)
})
it("should revert the shipping options and prices", async () => {
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",
},
],
}
const workflow = createShippingOptionsWorkflow(container)
workflow.addAction(
"throw",
{
invoke: async function failStep() {
throw new Error(`Failed to create shipping options`)
},
},
{
noCompensation: true,
}
)
const { errors } = await workflow.run({
input: [shippingOptionData],
throwOnError: false,
})
expect(errors).toHaveLength(1)
expect(errors[0].error.message).toEqual(
`Failed to create shipping options`
)
const remoteQuery = container.resolve(
ContainerRegistrationKeys.REMOTE_QUERY
)
const remoteQueryObject = remoteQueryObjectFromString({
entryPoint: "shipping_option",
fields: ["id"],
})
const createdShippingOptions = await remoteQuery(remoteQueryObject)
expect(createdShippingOptions).toHaveLength(0)
const priceSetsRemoteQueryObject = remoteQueryObjectFromString({
entryPoint: "price_sets",
fields: ["id"],
})
const createdPriceSets = await remoteQuery(priceSetsRemoteQueryObject)
expect(createdPriceSets).toHaveLength(0)
})
})
},
})
@@ -88,7 +88,7 @@ medusaIntegrationTestRunner({
const link = await remoteQuery({
shipping_option: {
fields: ["id"],
price: {
price_set_link: {
fields: ["id", "price_set_id", "shipping_option_id"],
},
},
@@ -99,7 +99,7 @@ medusaIntegrationTestRunner({
expect.arrayContaining([
expect.objectContaining({
id: shippingOption.id,
price: expect.objectContaining({
price_set_link: expect.objectContaining({
price_set_id: priceSet.id,
shipping_option_id: shippingOption.id,
}),
+10 -10
View File
@@ -21,6 +21,15 @@ const customPaymentProvider = {
},
}
const customFulfillmentProvider = {
resolve: "@medusajs/fulfillment-manual",
options: {
config: {
"test-provider": {},
},
},
}
module.exports = {
plugins: [],
projectConfig: {
@@ -91,16 +100,7 @@ module.exports = {
[Modules.FULFILLMENT]: {
/** @type {import('@medusajs/fulfillment').FulfillmentModuleOptions} */
options: {
providers: [
{
resolve: "@medusajs/fulfillment-manual",
options: {
config: {
"test-provider": {},
},
},
},
],
providers: [customFulfillmentProvider],
},
},
},
+2
View File
@@ -15,6 +15,8 @@
"@medusajs/cache-inmemory": "workspace:*",
"@medusajs/customer": "workspace:^",
"@medusajs/event-bus-local": "workspace:*",
"@medusajs/fulfillment": "workspace:^",
"@medusajs/fulfillment-manual": "workspace:^",
"@medusajs/inventory-next": "workspace:^",
"@medusajs/medusa": "workspace:*",
"@medusajs/modules-sdk": "workspace:^",