@@ -168,8 +168,8 @@ medusaIntegrationTestRunner({
|
|||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
|
|
||||||
const workflowId = batchJobRes.data.workflow_id
|
const transactionId = batchJobRes.data.transaction_id
|
||||||
expect(workflowId).toBeTruthy()
|
expect(transactionId).toBeTruthy()
|
||||||
|
|
||||||
await subscriberExecution
|
await subscriberExecution
|
||||||
const notifications = (
|
const notifications = (
|
||||||
@@ -214,8 +214,8 @@ medusaIntegrationTestRunner({
|
|||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
|
|
||||||
const workflowId = batchJobRes.data.workflow_id
|
const transactionId = batchJobRes.data.transaction_id
|
||||||
expect(workflowId).toBeTruthy()
|
expect(transactionId).toBeTruthy()
|
||||||
|
|
||||||
await subscriberExecution
|
await subscriberExecution
|
||||||
const notifications = (
|
const notifications = (
|
||||||
|
|||||||
@@ -101,8 +101,18 @@ medusaIntegrationTestRunner({
|
|||||||
// BREAKING: The batch endpoints moved to the domain routes (admin/batch-jobs -> /admin/products/import). The payload and response changed as well.
|
// BREAKING: The batch endpoints moved to the domain routes (admin/batch-jobs -> /admin/products/import). The payload and response changed as well.
|
||||||
const batchJobRes = await api.post("/admin/products/import", form, meta)
|
const batchJobRes = await api.post("/admin/products/import", form, meta)
|
||||||
|
|
||||||
const workflowId = batchJobRes.data.workflow_id
|
const transactionId = batchJobRes.data.transaction_id
|
||||||
expect(workflowId).toBeTruthy()
|
expect(transactionId).toBeTruthy()
|
||||||
|
expect(batchJobRes.data.summary).toEqual({
|
||||||
|
toCreate: 1,
|
||||||
|
toUpdate: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
`/admin/products/import/${transactionId}/confirm`,
|
||||||
|
{},
|
||||||
|
meta
|
||||||
|
)
|
||||||
|
|
||||||
await subscriberExecution
|
await subscriberExecution
|
||||||
const notifications = (
|
const notifications = (
|
||||||
|
|||||||
@@ -23,3 +23,4 @@ export * from "./delete-product-tags"
|
|||||||
export * from "./generate-product-csv"
|
export * from "./generate-product-csv"
|
||||||
export * from "./parse-product-csv"
|
export * from "./parse-product-csv"
|
||||||
export * from "./group-products-for-batch"
|
export * from "./group-products-for-batch"
|
||||||
|
export * from "./wait-confirmation-product-import"
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { createStep } from "@medusajs/workflows-sdk"
|
||||||
|
|
||||||
|
export const waitConfirmationProductImportStepId =
|
||||||
|
"wait-confirmation-product-import"
|
||||||
|
export const waitConfirmationProductImportStep = createStep(
|
||||||
|
{
|
||||||
|
name: waitConfirmationProductImportStepId,
|
||||||
|
async: true,
|
||||||
|
// After an hour we want to timeout and cancel the import so we don't have orphaned workflows
|
||||||
|
timeout: 60 * 60 * 1 * 1000,
|
||||||
|
},
|
||||||
|
async () => {}
|
||||||
|
)
|
||||||
@@ -5,7 +5,11 @@ import {
|
|||||||
} from "@medusajs/workflows-sdk"
|
} from "@medusajs/workflows-sdk"
|
||||||
import { WorkflowTypes } from "@medusajs/types"
|
import { WorkflowTypes } from "@medusajs/types"
|
||||||
import { sendNotificationsStep } from "../../notification"
|
import { sendNotificationsStep } from "../../notification"
|
||||||
import { groupProductsForBatchStep, parseProductCsvStep } from "../steps"
|
import {
|
||||||
|
waitConfirmationProductImportStep,
|
||||||
|
groupProductsForBatchStep,
|
||||||
|
parseProductCsvStep,
|
||||||
|
} from "../steps"
|
||||||
import { batchProductsWorkflow } from "./batch-products"
|
import { batchProductsWorkflow } from "./batch-products"
|
||||||
|
|
||||||
export const importProductsWorkflowId = "import-products"
|
export const importProductsWorkflowId = "import-products"
|
||||||
@@ -13,11 +17,18 @@ export const importProductsWorkflow = createWorkflow(
|
|||||||
importProductsWorkflowId,
|
importProductsWorkflowId,
|
||||||
(
|
(
|
||||||
input: WorkflowData<WorkflowTypes.ProductWorkflow.ImportProductsDTO>
|
input: WorkflowData<WorkflowTypes.ProductWorkflow.ImportProductsDTO>
|
||||||
): WorkflowData<void> => {
|
): WorkflowData<WorkflowTypes.ProductWorkflow.ImportProductsSummary> => {
|
||||||
const products = parseProductCsvStep(input.fileContent)
|
const products = parseProductCsvStep(input.fileContent)
|
||||||
const batchRequest = groupProductsForBatchStep(products)
|
const batchRequest = groupProductsForBatchStep(products)
|
||||||
|
|
||||||
// TODO: Add async confirmation step here
|
const summary = transform({ batchRequest }, (data) => {
|
||||||
|
return {
|
||||||
|
toCreate: data.batchRequest.create.length,
|
||||||
|
toUpdate: data.batchRequest.update.length,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
waitConfirmationProductImportStep()
|
||||||
|
|
||||||
batchProductsWorkflow.runAsStep({ input: batchRequest })
|
batchProductsWorkflow.runAsStep({ input: batchRequest })
|
||||||
|
|
||||||
@@ -37,5 +48,6 @@ export const importProductsWorkflow = createWorkflow(
|
|||||||
})
|
})
|
||||||
|
|
||||||
sendNotificationsStep(notifications)
|
sendNotificationsStep(notifications)
|
||||||
|
return summary
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -32,11 +32,15 @@ export interface AdminProductVariantDeleteResponse
|
|||||||
extends DeleteResponse<"variant", AdminProduct> {}
|
extends DeleteResponse<"variant", AdminProduct> {}
|
||||||
|
|
||||||
export interface AdminExportProductResponse {
|
export interface AdminExportProductResponse {
|
||||||
workflow_id: string
|
transaction_id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdminImportProductResponse {
|
export interface AdminImportProductResponse {
|
||||||
workflow_id: string
|
transaction_id: string
|
||||||
|
summary: {
|
||||||
|
toCreate: number
|
||||||
|
toUpdate: number
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdminBatchProductVariantResponse
|
export interface AdminBatchProductVariantResponse
|
||||||
|
|||||||
@@ -2,3 +2,8 @@ export interface ImportProductsDTO {
|
|||||||
fileContent: string
|
fileContent: string
|
||||||
filename: string
|
filename: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ImportProductsSummary {
|
||||||
|
toCreate: number
|
||||||
|
toUpdate: number
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,5 +17,5 @@ export const POST = async (
|
|||||||
input,
|
input,
|
||||||
})
|
})
|
||||||
|
|
||||||
res.status(202).json({ workflow_id: transaction.transactionId })
|
res.status(202).json({ transaction_id: transaction.transactionId })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import {
|
||||||
|
AuthenticatedMedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "../../../../../../types/routing"
|
||||||
|
|
||||||
|
import { IWorkflowEngineService } from "@medusajs/types"
|
||||||
|
import { ModuleRegistrationName, TransactionHandlerType } from "@medusajs/utils"
|
||||||
|
import {
|
||||||
|
importProductsWorkflowId,
|
||||||
|
waitConfirmationProductImportStepId,
|
||||||
|
} from "@medusajs/core-flows"
|
||||||
|
import { StepResponse } from "@medusajs/workflows-sdk"
|
||||||
|
|
||||||
|
export const POST = async (
|
||||||
|
req: AuthenticatedMedusaRequest,
|
||||||
|
res: MedusaResponse
|
||||||
|
) => {
|
||||||
|
const workflowEngineService: IWorkflowEngineService = req.scope.resolve(
|
||||||
|
ModuleRegistrationName.WORKFLOW_ENGINE
|
||||||
|
)
|
||||||
|
const transactionId = req.params.transaction_id
|
||||||
|
|
||||||
|
await workflowEngineService.setStepSuccess({
|
||||||
|
idempotencyKey: {
|
||||||
|
action: TransactionHandlerType.INVOKE,
|
||||||
|
transactionId,
|
||||||
|
stepId: waitConfirmationProductImportStepId,
|
||||||
|
workflowId: importProductsWorkflowId,
|
||||||
|
},
|
||||||
|
stepResponse: new StepResponse(true),
|
||||||
|
})
|
||||||
|
|
||||||
|
res.status(202).json({})
|
||||||
|
}
|
||||||
@@ -19,12 +19,14 @@ export const POST = async (
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { transaction } = await importProductsWorkflow(req.scope).run({
|
const { result, transaction } = await importProductsWorkflow(req.scope).run({
|
||||||
input: {
|
input: {
|
||||||
filename: input.originalname,
|
filename: input.originalname,
|
||||||
fileContent: input.buffer.toString("utf-8"),
|
fileContent: input.buffer.toString("utf-8"),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
res.status(202).json({ workflow_id: transaction.transactionId })
|
res
|
||||||
|
.status(202)
|
||||||
|
.json({ transaction_id: transaction.transactionId, summary: result })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,6 +92,11 @@ export const adminProductRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
matcher: "/admin/products/import",
|
matcher: "/admin/products/import",
|
||||||
middlewares: [upload.single("file")],
|
middlewares: [upload.single("file")],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
method: ["POST"],
|
||||||
|
matcher: "/admin/products/import/:transaction_id/confirm",
|
||||||
|
middlewares: [],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
method: ["GET"],
|
method: ["GET"],
|
||||||
matcher: "/admin/products/:id",
|
matcher: "/admin/products/:id",
|
||||||
|
|||||||
Reference in New Issue
Block a user