feat: move create inventory to @medusajs/workflows (#5301)
**Why** - We have some workflow-like flows in @medusajs/medusa. These should be moved over to the workflows package. - Inventory Items <> Variant currently assume a 1-1 mapping. There should be support for a many-to-many mapping. **What** - PR introduces a feature flag for supporting many-to-many mappings for inventory and variants. - Deletes legacy transaction handler in @medusajs/medusa. - Adjusts existing createInventoryItems handler to remove dependency on variant data. **Unkowns** ~~1. Couldn't find an existing test for the CreateProduct workflow. It should be tested that this still works as expected.~~ 2. Have removed transaction managers as we should move to handling consistency through orchestration tooling. Are we ready for that?
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
const path = require("path")
|
||||
const { ProductVariantInventoryService } = require("@medusajs/medusa")
|
||||
|
||||
const {
|
||||
bootstrapApp,
|
||||
} = require("../../../../environment-helpers/bootstrap-app")
|
||||
const { initDb, useDb } = require("../../../../environment-helpers/use-db")
|
||||
const { setPort, useApi } = require("../../../../environment-helpers/use-api")
|
||||
|
||||
const adminSeeder = require("../../../../helpers/admin-seeder")
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
const {
|
||||
simpleProductFactory,
|
||||
simpleOrderFactory,
|
||||
} = require("../../../../factories")
|
||||
const adminHeaders = { headers: { "x-medusa-access-token": "test_token" } }
|
||||
|
||||
describe("Inventory Items endpoints", () => {
|
||||
let appContainer
|
||||
let dbConnection
|
||||
let express
|
||||
|
||||
let variantId
|
||||
let inventoryItems
|
||||
let locationId
|
||||
let location2Id
|
||||
let location3Id
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
|
||||
const { container, app, port } = await bootstrapApp({ cwd, verbose: true })
|
||||
appContainer = container
|
||||
|
||||
// Set feature flag
|
||||
const flagRouter = appContainer.resolve("featureFlagRouter")
|
||||
flagRouter.setFlag("many_to_many_inventory", true)
|
||||
|
||||
setPort(port)
|
||||
express = app.listen(port, (err) => {
|
||||
process.send(port)
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const flagRouter = appContainer.resolve("featureFlagRouter")
|
||||
flagRouter.setFlag("many_to_many_inventory", false)
|
||||
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
express.close()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
const db = useDb()
|
||||
return await db.teardown()
|
||||
})
|
||||
|
||||
describe("Inventory Items", () => {
|
||||
it("should create inventory item without variant id", async () => {
|
||||
const api = useApi()
|
||||
|
||||
await api.post(
|
||||
`/admin/inventory-items`,
|
||||
{
|
||||
sku: "TABLE_LEG",
|
||||
description: "Table Leg",
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
/** @type {ProductVariantInventoryService} */
|
||||
const productVariantInventoryService = appContainer.resolve(
|
||||
"productVariantInventoryService"
|
||||
)
|
||||
|
||||
const inventoryService = appContainer.resolve("inventoryService")
|
||||
const inventoryItems = await inventoryService.list()
|
||||
|
||||
expect(inventoryItems.length).toEqual(1)
|
||||
|
||||
const variants = await productVariantInventoryService.listByItem([
|
||||
inventoryItems[0].id,
|
||||
])
|
||||
expect(variants.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -345,7 +345,7 @@ describe("Inventory Items endpoints", () => {
|
||||
|
||||
const inventoryItemCreateRes = await api.post(
|
||||
`/admin/inventory-items`,
|
||||
{ variant_id: variantId },
|
||||
{ variant_id: variantId, sku: "attach_this_to_variant" },
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import path from "path"
|
||||
import { initDb, useDb } from "../../../../environment-helpers/use-db"
|
||||
import { bootstrapApp } from "../../../../environment-helpers/bootstrap-app"
|
||||
import {
|
||||
createInventoryItems,
|
||||
CreateInventoryItemActions,
|
||||
pipe,
|
||||
} from "@medusajs/workflows"
|
||||
import { IInventoryService, WorkflowTypes } from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
describe("CreateInventoryItem workflow", function () {
|
||||
let medusaProcess
|
||||
let medusaContainer
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
|
||||
await initDb({ cwd } as any)
|
||||
const { container } = await bootstrapApp({ cwd })
|
||||
medusaContainer = container
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
|
||||
medusaProcess.kill()
|
||||
})
|
||||
|
||||
it("should compensate all the invoke if something fails", async () => {
|
||||
const workflow = createInventoryItems(medusaContainer)
|
||||
|
||||
workflow.appendAction(
|
||||
"fail_step",
|
||||
CreateInventoryItemActions.createInventoryItems,
|
||||
{
|
||||
invoke: pipe({}, async function failStep() {
|
||||
throw new Error(`Failed`)
|
||||
}),
|
||||
},
|
||||
{
|
||||
noCompensation: true,
|
||||
}
|
||||
)
|
||||
|
||||
const input: WorkflowTypes.InventoryWorkflow.CreateInventoryItemsWorkflowInputDTO =
|
||||
{
|
||||
inventoryItems: [
|
||||
{
|
||||
sku: "TABLE_LEG",
|
||||
description: "Table Leg",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const { result, errors, transaction } = await workflow.run({
|
||||
input,
|
||||
context: {},
|
||||
throwOnError: false,
|
||||
})
|
||||
|
||||
expect(errors).toEqual([
|
||||
{
|
||||
action: "fail_step",
|
||||
handlerType: "invoke",
|
||||
error: new Error(`Failed`),
|
||||
},
|
||||
])
|
||||
|
||||
expect(transaction.getState()).toEqual("reverted")
|
||||
|
||||
expect(result).toHaveLength(1)
|
||||
expect(result[0].inventoryItem).toEqual(
|
||||
expect.objectContaining({ id: expect.any(String) })
|
||||
)
|
||||
|
||||
const inventoryService: IInventoryService = medusaContainer.resolve(
|
||||
ModuleRegistrationName.INVENTORY
|
||||
)
|
||||
|
||||
const [inventoryItems] = await inventoryService.listInventoryItems(
|
||||
{ id: result[0].inventoryItem.id },
|
||||
{ withDeleted: true }
|
||||
)
|
||||
|
||||
expect(inventoryItems[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
id: result[0].inventoryItem.id,
|
||||
deleted_at: expect.any(Date),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@medusajs/cache-inmemory": "workspace:*",
|
||||
"@medusajs/event-bus-local": "workspace:*",
|
||||
"@medusajs/inventory": "workspace:^",
|
||||
"@medusajs/medusa": "workspace:*",
|
||||
"@medusajs/product": "workspace:^",
|
||||
"faker": "^5.5.3",
|
||||
|
||||
Reference in New Issue
Block a user