Skip to content

Commit

Permalink
feat: move create inventory to @medusajs/workflows (#5301)
Browse files Browse the repository at this point in the history
**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?
  • Loading branch information
srindom authored Oct 11, 2023
1 parent bbd9dd4 commit 66413d0
Show file tree
Hide file tree
Showing 25 changed files with 482 additions and 283 deletions.
8 changes: 8 additions & 0 deletions .changeset/tame-queens-cough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@medusajs/medusa": patch
"@medusajs/inventory": patch
"@medusajs/types": patch
"@medusajs/utils": patch
"@medusajs/workflows": patch
---
fix: move create inventory workflow to @medusajs/workflows
Original file line number Diff line number Diff line change
@@ -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)
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

Expand Down
Original file line number Diff line number Diff line change
@@ -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),
})
)
})
})
1 change: 1 addition & 0 deletions integration-tests/plugins/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packages/inventory/src/utils/build-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function buildQuery<TWhereKeys extends object, TEntity = unknown>(
where: buildWhere<TWhereKeys, TEntity>(selector),
}

if ("deleted_at" in selector) {
if ("deleted_at" in selector || config.withDeleted) {
query.withDeleted = true
}

Expand Down
4 changes: 4 additions & 0 deletions packages/inventory/src/utils/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ export function getListQuery(
queryBuilder.select(legacySelect.map((s) => "inv_item." + s))
}

if (query.withDeleted) {
queryBuilder.withDeleted()
}

if (query.order) {
const toSelect: string[] = []
const parsed = Object.entries(query.order).reduce((acc, [k, v]) => {
Expand Down
Loading

0 comments on commit 66413d0

Please sign in to comment.