Skip to content

Commit

Permalink
feat(medusa, link-modules): sales channel <> cart link (#5459)
Browse files Browse the repository at this point in the history
* feat: sales channel joiner config

* feat: product sales channel link config, SC list method

* feat: migration

* fix: refactor list SC

* refactor: SC repo api

* chore: changeset

* feat: add dedicated FF

* wip: cart<>sc link and migration

* chore: changeset

* fix: update migration with the cart table constraints

* feat: populate the pivot table

* chore: remove relation from joiner config

* fix: constraint name

* fix: filter out link relations when calling internal services

* feat: product<> sc join entity

* fix: update case

* fix: add FF on in the repository, fix tests

* fix: assign id when FF is on

* fix: target table

* feat: product service - fetch SC with RQ

* feat: admin list products & SC with isolated product domain

* feat: get admin product

* feat: store endpoints

* fix: remove duplicate import

* fix: remove "name" prop

* feat: typeorm entity changes

* feat: pivot table, entity, on cart create changes

* feat: update carts' SC

* feat: cart - getValidatedSalesChannel with RQ

* feat: refactor

* wip: changes to create cart workflow

* fix: remove join table entity due to migrations failing

* fix: product seeder if FF is on

* feat: attach SC handler and test

* fix: env

* feat: workflow compensation, cart service retrieve with RQ

* fix: remote joiner implode map

* chore: update changesets

* fix: remove methods from SC service/repo

* feat: use remote link in handlers

* fix: remove SC service calls

* fix: link params

* fix: migration add constraint to make link upsert pass

* refactor: workflow product handlers to handle remote links

* fix: condition

* fix: use correct method

* fix: build

* wip: update FF

* fix: update FF in the handlers

* chore: migrate to medusav2 FF

* chore: uncomment test

* fix: product factory

* fix: unlinking SC and product

* fix: use module name variable

* refactor: cleanup query definitions

* fix: add constraint

* wip: migrate FF

* fix: comments

* feat: cart entity callbacks, fix tests

* fix: only create SC in test

* wip: services updates, changes to models

* chore: rename prop

* fix: add hook

* fix: address comments

* fix: temp sc filtering

* fix: use RQ to filter by SC

* fix: relations on retrieve

* feat: migration sync data, remove FF

* fix: revert order of queries

* fix: alter migration, relations in service

* fix: revert id

* fix: migrations

* fix: make expand work

* fix: remote link method call

* fix: try making tests work without id in the pivot table

* test: use remote link

* test: relations changes

* fix: preserve channel id column

* fix: seeder and factory

* fix: remove sales_channels from response

* feat: support feature flag arrays

* fix: cover everything with correct FF

* fix: remove verbose

* fix: unit and plugin tests

* chore: comments

* fix: reenable workflow handler, add comments, split cart create workflow tests

* chore: reenable link in the create mehod, update changesets

* fix: address feedback

* fix: revert migration

* fix: change the migration to follow link module

* fix: migration syntax

* fix: merge conflicts

* fix: typo

* feat: remove store sales channel foreign key

* fix: merge migrations

* fix: FF keys

* refactor: cart service

* refactor: FF missing key

* fix: comments

* fix: address PR comments

* fix: new changesets

* fix: revert flag router changes

* chore: refactor `isFeatureEnabled`

---------

Co-authored-by: Carlos R. L. Rodrigues <rodrigolr@gmail.com>
Co-authored-by: Riqwan Thamir <rmthamir@gmail.com>
  • Loading branch information
3 people authored Dec 22, 2023
1 parent b5a07cf commit 76332ca
Show file tree
Hide file tree
Showing 28 changed files with 662 additions and 89 deletions.
9 changes: 9 additions & 0 deletions .changeset/two-chefs-complain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@medusajs/orchestration": patch
"@medusajs/link-modules": patch
"@medusajs/core-flows": patch
"@medusajs/medusa": patch
"@medusajs/utils": patch
---

feat: SalesChannel <> Cart joiner config
18 changes: 16 additions & 2 deletions integration-tests/factories/simple-cart-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
ShippingMethodFactoryData,
simpleShippingMethodFactory,
} from "./simple-shipping-method-factory"
import { generateEntityId } from "@medusajs/utils"

export type CartFactoryData = {
id?: string
Expand All @@ -32,6 +33,8 @@ export type CartFactoryData = {
sales_channel_id?: string
}

const isMedusaV2Enabled = process.env.MEDUSA_FF_MEDUSA_V2 == "true"

export const simpleCartFactory = async (
dataSource: DataSource,
data: CartFactoryData = {},
Expand Down Expand Up @@ -77,15 +80,26 @@ export const simpleCartFactory = async (
}

const id = data.id || `simple-cart-${Math.random() * 1000}`
const toSave = manager.create(Cart, {
let toSave = {
id,
email:
typeof data.email !== "undefined" ? data.email : faker.internet.email(),
region_id: regionId,
customer_id: customerId,
shipping_address_id: address.id,
sales_channel_id: sales_channel?.id ?? data.sales_channel_id ?? null,
})
}

if (isMedusaV2Enabled) {
await manager.query(
`INSERT INTO "cart_sales_channel" (id, cart_id, sales_channel_id)
VALUES ('${generateEntityId(undefined, "cartsc")}', '${toSave.id}', '${
sales_channel?.id ?? data.sales_channel_id
}');`
)
}

toSave = manager.create(Cart, toSave)

const cart = await manager.save(toSave)

Expand Down
96 changes: 96 additions & 0 deletions integration-tests/plugins/__tests__/cart/store/ff-medusa-v2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Region } from "@medusajs/medusa"
import path from "path"
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
import { useApi } from "../../../../environment-helpers/use-api"
import { initDb, useDb } from "../../../../environment-helpers/use-db"
import {
simpleProductFactory,
simpleSalesChannelFactory,
} from "../../../../factories"

jest.setTimeout(30000)

const env = {
MEDUSA_FF_MEDUSA_V2: true,
}

describe("/store/carts", () => {
let dbConnection
let shutdownServer

const doAfterEach = async () => {
const db = useDb()
return await db.teardown()
}

beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
dbConnection = await initDb({ cwd, env } as any)
shutdownServer = await startBootstrapApp({ cwd, env })
})

afterAll(async () => {
const db = useDb()
await db.shutdown()
await shutdownServer()
})

describe("POST /store/carts", () => {
let prod1
let prodSale

beforeEach(async () => {
const manager = dbConnection.manager
await manager.insert(Region, {
id: "region",
name: "Test Region",
currency_code: "usd",
tax_rate: 0,
})

await manager.query(
`UPDATE "country"
SET region_id='region'
WHERE iso_2 = 'us'`
)

prod1 = await simpleProductFactory(dbConnection, {
id: "test-product",
variants: [{ id: "test-variant_1" }],
})

prodSale = await simpleProductFactory(dbConnection, {
id: "test-product-sale",
variants: [
{
id: "test-variant-sale",
prices: [{ amount: 1000, currency: "usd" }],
},
],
})

await simpleSalesChannelFactory(dbConnection, {
id: "amazon-sc",
name: "Amazon store",
})
})

afterEach(async () => {
await doAfterEach()
})

it("should create a cart in a sales channel", async () => {
const api = useApi()

const response = await api.post("/store/carts", {
sales_channel_id: "amazon-sc",
})

expect(response.status).toEqual(200)

const getRes = await api.get(`/store/carts/${response.data.cart.id}`)
expect(getRes.status).toEqual(200)
expect(getRes.data.cart.sales_channel.id).toEqual("amazon-sc")
})
})
})
2 changes: 1 addition & 1 deletion integration-tests/plugins/__tests__/cart/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe("/store/carts", () => {

beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
dbConnection = await initDb({ cwd })
dbConnection = await initDb({ cwd } as any)
shutdownServer = await startBootstrapApp({ cwd })
})

Expand Down
6 changes: 1 addition & 5 deletions integration-tests/plugins/__tests__/inventory/order/order.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@ const {
startBootstrapApp,
} = require("../../../../environment-helpers/bootstrap-app")
const { initDb, useDb } = require("../../../../environment-helpers/use-db")
const {
setPort,
useApi,
useExpressServer,
} = require("../../../../environment-helpers/use-api")
const { useApi } = require("../../../../environment-helpers/use-api")

const adminSeeder = require("../../../../helpers/admin-seeder")
const {
Expand Down
48 changes: 44 additions & 4 deletions packages/core-flows/src/definition/cart/create-cart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { exportWorkflow, pipe } from "@medusajs/workflows-sdk"
enum CreateCartActions {
setContext = "setContext",
attachLineItems = "attachLineItems",
attachToSalesChannel = "attachToSalesChannel",
findRegion = "findRegion",
findSalesChannel = "findSalesChannel",
createCart = "createCart",
Expand Down Expand Up @@ -58,10 +59,13 @@ const workflowSteps: TransactionStepsDefinition = {
noCompensation: true,
next: {
action: CreateCartActions.createCart,
next: {
action: CreateCartActions.attachLineItems,
noCompensation: true,
},
next: [
{
action: CreateCartActions.attachLineItems,
noCompensation: true,
},
{ action: CreateCartActions.attachToSalesChannel },
],
},
},
},
Expand Down Expand Up @@ -134,6 +138,10 @@ const handlers = new Map([
invoke: pipe(
{
invoke: [
{
from: CreateCartActions.findSalesChannel,
alias: CartHandlers.createCart.aliases.SalesChannel,
},
{
from: CreateCartActions.findRegion,
alias: CartHandlers.createCart.aliases.Region,
Expand Down Expand Up @@ -186,6 +194,38 @@ const handlers = new Map([
),
},
],
[
CreateCartActions.attachToSalesChannel,
{
invoke: pipe(
{
invoke: [
{
from: CreateCartActions.createCart,
alias: CartHandlers.attachCartToSalesChannel.aliases.Cart,
},
{
from: CreateCartActions.findSalesChannel,
alias: CartHandlers.attachCartToSalesChannel.aliases.SalesChannel,
},
],
},
CartHandlers.attachCartToSalesChannel
),
compensate: pipe(
{
invoke: [
{
from: CreateCartActions.findSalesChannel,
alias:
CartHandlers.detachCartFromSalesChannel.aliases.SalesChannel,
},
],
},
CartHandlers.detachCartFromSalesChannel
),
},
],
])

WorkflowManager.register(Workflows.CreateCart, workflowSteps, handlers)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { MedusaV2Flag } from "@medusajs/utils"
import { WorkflowArguments } from "@medusajs/workflows-sdk"

type HandlerInputData = {
cart: {
id: string
}
sales_channel: {
sales_channel_id: string
}
}

enum Aliases {
Cart = "cart",
SalesChannel = "sales_channel",
}

export async function attachCartToSalesChannel({
container,
data,
}: WorkflowArguments<HandlerInputData>): Promise<void> {
const featureFlagRouter = container.resolve("featureFlagRouter")
const remoteLink = container.resolve("remoteLink")

if (!featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
return
}

const cart = data[Aliases.Cart]
const salesChannel = data[Aliases.SalesChannel]

await remoteLink.create({
cartService: {
cart_id: cart.id,
},
salesChannelService: {
sales_channel_id: salesChannel.sales_channel_id,
},
})
}

attachCartToSalesChannel.aliases = Aliases
4 changes: 1 addition & 3 deletions packages/core-flows/src/handlers/cart/create-cart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,13 @@ export async function createCart({
const cartService = container.resolve("cartService")
const cartServiceTx = cartService.withTransaction(manager)

const cart = await cartServiceTx.create({
return await cartServiceTx.create({
...data[Aliases.SalesChannel],
...data[Aliases.Addresses],
...data[Aliases.Customer],
...data[Aliases.Region],
...data[Aliases.Context],
})

return cart
}

createCart.aliases = Aliases
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { MedusaV2Flag } from "@medusajs/utils"
import { WorkflowArguments } from "@medusajs/workflows-sdk"

type HandlerInputData = {
cart: {
id: string
}
sales_channel: {
sales_channel_id: string
}
}

enum Aliases {
Cart = "cart",
SalesChannel = "sales_channel",
}

export async function detachCartFromSalesChannel({
container,
data,
}: WorkflowArguments<HandlerInputData>): Promise<void> {
const featureFlagRouter = container.resolve("featureFlagRouter")
const remoteLink = container.resolve("remoteLink")

if (!featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
return
}

const cart = data[Aliases.Cart]
const salesChannel = data[Aliases.SalesChannel]

await remoteLink.dismiss({
cartService: {
cart_id: cart.id,
},
salesChannelService: {
sales_channel_id: salesChannel.sales_channel_id,
},
})
}

detachCartFromSalesChannel.aliases = Aliases
2 changes: 2 additions & 0 deletions packages/core-flows/src/handlers/cart/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ export * from "./attach-line-items-to-cart"
export * from "./create-cart"
export * from "./remove-cart"
export * from "./retrieve-cart"
export * from "./attach-cart-to-sales-channel"
export * from "./detach-cart-from-sales-channel"
Loading

0 comments on commit 76332ca

Please sign in to comment.