feat: CartRegion link, definition + workflow (#6392)

This commit is contained in:
Oli Juhl authored and GitHub committed 2024-02-16 14:06:26 +00:00
1 parent 7df4947ecf
commit 24fb102a56
17 files changed
+495 -218

No files matched your search

@@ -0,0 +1,27 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { IRegionModuleService } from "@medusajs/types"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
export const findOneOrAnyRegionStepId = "find-one-or-any-region"
export const findOneOrAnyRegionStep = createStep(
findOneOrAnyRegionStepId,
async (data: { regionId?: string }, { container }) => {
const service = container.resolve<IRegionModuleService>(
ModuleRegistrationName.REGION
)
if (!data.regionId) {
const regions = await service.list({})
if (!regions?.length) {
throw Error("No regions found")
}
return new StepResponse(regions[0])
}
const region = await service.retrieve(data.regionId)
return new StepResponse(region)
}
)
@@ -1,3 +1,4 @@
export * from "./create-carts"
export * from "./find-one-or-any-region"
export * from "./update-carts"
@@ -1,13 +1,31 @@
import { CartDTO, CreateCartDTO } from "@medusajs/types"
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
import { createCartsStep } from "../steps"
import { CartDTO, CreateCartWorkflowInputDTO } from "@medusajs/types"
import {
WorkflowData,
createWorkflow,
transform,
} from "@medusajs/workflows-sdk"
import { createCartsStep, findOneOrAnyRegionStep } from "../steps"
type WorkflowInput = { cartData: CreateCartDTO[] }
type WorkflowInput = CreateCartWorkflowInputDTO
export const createCartsWorkflowId = "create-carts"
export const createCartsWorkflow = createWorkflow(
createCartsWorkflowId,
export const createCartWorkflowId = "create-cart"
export const createCartWorkflow = createWorkflow(
createCartWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<CartDTO[]> => {
return createCartsStep(input.cartData)
const region = findOneOrAnyRegionStep({
regionId: input.region_id,
})
const cartInput = transform({ input, region }, (data) => {
return {
...data.input,
currency_code: data?.input.currency_code || data.region.currency_code,
region_id: data.region.id,
}
})
const cart = createCartsStep([cartInput])
return cart
}
)
@@ -0,0 +1,28 @@
import { Modules } from "@medusajs/modules-sdk"
import { ModuleJoinerConfig } from "@medusajs/types"
export const CartRegion: ModuleJoinerConfig = {
isLink: true,
isReadOnlyLink: true,
extends: [
{
serviceName: Modules.CART,
relationship: {
serviceName: Modules.REGION,
primaryKey: "id",
foreignKey: "region_id",
alias: "region",
},
},
{
serviceName: Modules.REGION,
relationship: {
serviceName: Modules.CART,
primaryKey: "region_id",
foreignKey: "id",
alias: "carts",
isList: true,
},
},
],
}
@@ -1,8 +1,10 @@
export * from "./cart-region"
export * from "./cart-sales-channel"
export * from "./inventory-level-stock-location"
export * from "./order-sales-channel"
export * from "./product-sales-channel"
export * from "./product-shipping-profile"
export * from "./product-variant-inventory-item"
export * from "./product-variant-price-set"
export * from "./product-shipping-profile"
export * from "./product-sales-channel"
export * from "./cart-sales-channel"
export * from "./order-sales-channel"
export * from "./publishable-api-key-sales-channel"
@@ -11,12 +11,11 @@ export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
const query = {
cart: {
__args: variables,
...defaultStoreCartRemoteQueryObject,
},
}
const [cart] = await remoteQuery(query)
const [cart] = await remoteQuery(query, { cart: variables })
res.json({ cart })
}
@@ -9,6 +9,7 @@ export const defaultStoreCartFields = [
export const defaultStoreCartRelations = [
"items",
"region",
"shipping_address",
"billing_address",
"shipping_methods",
@@ -61,4 +62,7 @@ export const defaultStoreCartRemoteQueryObject = {
"phone",
],
},
region: {
fields: ["id", "name", "currency_code"],
},
}
+18 -10
View File
@@ -1,17 +1,13 @@
import { createCartsWorkflow } from "@medusajs/core-flows"
import { createCartWorkflow } from "@medusajs/core-flows"
import { CreateCartDTO } from "@medusajs/types"
import { MedusaRequest, MedusaResponse } from "../../../types/routing"
import { defaultStoreCartRemoteQueryObject } from "../carts/query-config"
export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
const createCartWorkflow = createCartsWorkflow(req.scope)
const cartData = [
{
...(req.validatedBody as CreateCartDTO),
},
]
const workflow = createCartWorkflow(req.scope)
const { result, errors } = await createCartWorkflow.run({
input: { cartData },
const { result, errors } = await workflow.run({
input: req.validatedBody as CreateCartDTO,
throwOnError: false,
})
@@ -19,5 +15,17 @@ export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
throw errors[0].error
}
res.status(200).json({ cart: result[0] })
const remoteQuery = req.scope.resolve("remoteQuery")
const variables = { id: result[0].id }
const query = {
cart: {
...defaultStoreCartRemoteQueryObject,
},
}
const [cart] = await remoteQuery(query, { cart: variables })
res.status(200).json({ cart })
}
@@ -1,7 +1,3 @@
import {
createCart as createCartWorkflow,
Workflows,
} from "@medusajs/core-flows"
import {
IsArray,
IsInt,
@@ -10,7 +6,7 @@ import {
IsString,
ValidateNested,
} from "class-validator"
import { isDefined, MedusaError } from "medusa-core-utils"
import { MedusaError, isDefined } from "medusa-core-utils"
import { defaultStoreCartFields, defaultStoreCartRelations } from "."
import {
CartService,
@@ -19,7 +15,6 @@ import {
RegionService,
} from "../../../../services"
import { MedusaContainer } from "@medusajs/modules-sdk"
import { FlagRouter } from "@medusajs/utils"
import { Type } from "class-transformer"
import reqIp from "request-ip"
@@ -122,130 +117,96 @@ export default async (req, res) => {
user_agent: req.get("user-agent"),
}
const isWorkflowEnabled = featureFlagRouter.isFeatureEnabled({
workflows: Workflows.CreateCart,
})
const lineItemService: LineItemService = req.scope.resolve("lineItemService")
const regionService: RegionService = req.scope.resolve("regionService")
let cart
if (isWorkflowEnabled) {
const cartWorkflow = createCartWorkflow(req.scope as MedusaContainer)
const input = {
...validated,
publishableApiKeyScopes: req.publishableApiKeyScopes,
context: {
...reqContext,
...validated.context,
},
}
const { result, errors } = await cartWorkflow.run({
input,
context: {
manager: entityManager,
},
throwOnError: false,
})
if (Array.isArray(errors)) {
if (isDefined(errors[0])) {
throw errors[0].error
}
}
cart = result
let regionId!: string
if (isDefined(validated.region_id)) {
regionId = validated.region_id as string
} else {
const lineItemService: LineItemService =
req.scope.resolve("lineItemService")
const regionService: RegionService = req.scope.resolve("regionService")
const regions = await regionService.list({})
let regionId!: string
if (isDefined(validated.region_id)) {
regionId = validated.region_id as string
} else {
const regions = await regionService.list({})
if (!regions?.length) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`A region is required to create a cart`
)
}
regionId = regions[0].id
if (!regions?.length) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`A region is required to create a cart`
)
}
const toCreate: Partial<CartCreateProps> = {
region_id: regionId,
sales_channel_id: validated.sales_channel_id,
context: {
...reqContext,
...validated.context,
},
}
if (req.user && req.user.customer_id) {
const customerService = req.scope.resolve("customerService")
const customer = await customerService.retrieve(req.user.customer_id)
toCreate["customer_id"] = customer.id
toCreate["email"] = customer.email
}
if (validated.country_code) {
toCreate["shipping_address"] = {
country_code: validated.country_code.toLowerCase(),
}
}
if (
!toCreate.sales_channel_id &&
req.publishableApiKeyScopes?.sales_channel_ids.length
) {
if (req.publishableApiKeyScopes.sales_channel_ids.length > 1) {
throw new MedusaError(
MedusaError.Types.UNEXPECTED_STATE,
"The PublishableApiKey provided in the request header has multiple associated sales channels."
)
}
toCreate.sales_channel_id =
req.publishableApiKeyScopes.sales_channel_ids[0]
}
cart = await entityManager.transaction(async (manager) => {
const cartServiceTx = cartService.withTransaction(manager)
const lineItemServiceTx = lineItemService.withTransaction(manager)
const createdCart = await cartServiceTx.create(toCreate)
if (validated.items?.length) {
const generateInputData = validated.items.map((item) => {
return {
variantId: item.variant_id,
quantity: item.quantity,
}
})
const generatedLineItems: LineItem[] = await lineItemServiceTx.generate(
generateInputData,
{
region_id: regionId,
customer_id: req.user?.customer_id,
}
)
await cartServiceTx.addOrUpdateLineItems(
createdCart.id,
generatedLineItems,
{
validateSalesChannels:
featureFlagRouter.isFeatureEnabled("sales_channels"),
}
)
}
return createdCart
})
regionId = regions[0].id
}
const toCreate: Partial<CartCreateProps> = {
region_id: regionId,
sales_channel_id: validated.sales_channel_id,
context: {
...reqContext,
...validated.context,
},
}
if (req.user && req.user.customer_id) {
const customerService = req.scope.resolve("customerService")
const customer = await customerService.retrieve(req.user.customer_id)
toCreate["customer_id"] = customer.id
toCreate["email"] = customer.email
}
if (validated.country_code) {
toCreate["shipping_address"] = {
country_code: validated.country_code.toLowerCase(),
}
}
if (
!toCreate.sales_channel_id &&
req.publishableApiKeyScopes?.sales_channel_ids.length
) {
if (req.publishableApiKeyScopes.sales_channel_ids.length > 1) {
throw new MedusaError(
MedusaError.Types.UNEXPECTED_STATE,
"The PublishableApiKey provided in the request header has multiple associated sales channels."
)
}
toCreate.sales_channel_id = req.publishableApiKeyScopes.sales_channel_ids[0]
}
let cart = await entityManager.transaction(async (manager) => {
const cartServiceTx = cartService.withTransaction(manager)
const lineItemServiceTx = lineItemService.withTransaction(manager)
const createdCart = await cartServiceTx.create(toCreate)
if (validated.items?.length) {
const generateInputData = validated.items.map((item) => {
return {
variantId: item.variant_id,
quantity: item.quantity,
}
})
const generatedLineItems: LineItem[] = await lineItemServiceTx.generate(
generateInputData,
{
region_id: regionId,
customer_id: req.user?.customer_id,
}
)
await cartServiceTx.addOrUpdateLineItems(
createdCart.id,
generatedLineItems,
{
validateSalesChannels:
featureFlagRouter.isFeatureEnabled("sales_channels"),
}
)
}
return createdCart
})
// }
cart = await cartService.retrieveWithTotals(cart!.id, {
select: defaultStoreCartFields,
relations: defaultStoreCartRelations,
+7 -10
View File
@@ -293,16 +293,13 @@ async function MedusaApp_({
allowUnregistered: true,
})
const {
remoteLink,
linkResolution,
runMigrations: linkModuleMigration,
} = await initializeLinks({
config: linkModuleOptions,
linkModules,
injectedDependencies,
moduleExports: isMedusaModule(linkModule) ? linkModule : undefined,
})
const { remoteLink, runMigrations: linkModuleMigration } =
await initializeLinks({
config: linkModuleOptions,
linkModules,
injectedDependencies,
moduleExports: isMedusaModule(linkModule) ? linkModule : undefined,
})
const loadedSchema = getLoadedSchema()
const { schema, notFound } = cleanAndMergeSchema(loadedSchema)
+1 -1
View File
@@ -1,5 +1,5 @@
import { ModuleExports } from "@medusajs/types"
import { RegionModuleService } from "@services"
import { RegionModuleService } from "./services"
import { Modules } from "@medusajs/modules-sdk"
import { ModulesSdkUtils } from "@medusajs/utils"
+1 -1
View File
@@ -1,4 +1,4 @@
export * from "./common"
export * from "./mutations"
export * from "./service"
export * from "./workflows"
+20
View File
@@ -0,0 +1,20 @@
import {
CreateAddressDTO,
CreateLineItemDTO,
UpdateAddressDTO,
} from "./mutations"
export interface CreateCartWorkflowInputDTO {
region_id?: string
customer_id?: string
sales_channel_id?: string
email?: string
currency_code: string
shipping_address_id?: string
billing_address_id?: string
shipping_address?: CreateAddressDTO | UpdateAddressDTO
billing_address?: CreateAddressDTO | UpdateAddressDTO
metadata?: Record<string, unknown>
items?: CreateLineItemDTO[]
}