diff --git a/integration-tests/modules/__tests__/product/workflows/update-product-variants.spec.ts b/integration-tests/modules/__tests__/product/workflows/update-product-variants.spec.ts new file mode 100644 index 0000000000..6bc9b8e798 --- /dev/null +++ b/integration-tests/modules/__tests__/product/workflows/update-product-variants.spec.ts @@ -0,0 +1,190 @@ +import { updateProductVariantsWorkflow } from "@medusajs/core-flows" +import { medusaIntegrationTestRunner } from "@medusajs/test-utils" +import { IProductModuleService } from "@medusajs/types" +import { MedusaError, Modules } from "@medusajs/utils" + +jest.setTimeout(50000) + +medusaIntegrationTestRunner({ + env: {}, + testSuite: ({ getContainer }) => { + describe("Workflows: Update product variants", () => { + let appContainer + let service: IProductModuleService + + beforeAll(async () => { + appContainer = getContainer() + service = appContainer.resolve(Modules.PRODUCT) + }) + + describe("updateProductVariantsWorkflow", () => { + beforeAll(() => { + updateProductVariantsWorkflow.hooks.productVariantsUpdated(() => { + throw new MedusaError( + MedusaError.Types.NOT_ALLOWED, + "product variants updated hook failed" + ) + }) + }) + + describe("compensation", () => { + it("should revert the updated variants using product_variants if the hook fails", async () => { + const workflow = updateProductVariantsWorkflow(appContainer) + + const [product1, product2] = await service.createProducts([ + { + title: "test1", + variants: [ + { + title: "variant1", + sku: "variant1", + }, + ], + }, + { + title: "test2", + variants: [ + { + title: "variant2", + sku: "variant2", + }, + ], + }, + ]) + + const { errors } = await workflow.run({ + input: { + product_variants: [ + { + id: product1.variants[0].id, + title: "variant1-updated", + }, + ], + }, + throwOnError: false, + }) + + expect(errors).toEqual([ + { + action: "productVariantsUpdated", + handlerType: "invoke", + error: expect.objectContaining({ + message: `product variants updated hook failed`, + }), + }, + ]) + + const products = await service.listProducts( + {}, + { + relations: ["variants"], + } + ) + + expect(products).toHaveLength(2) + expect(products).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + title: product1.title, + variants: [ + expect.objectContaining({ + title: product1.variants[0].title, + sku: product1.variants[0].sku, + }), + ], + }), + expect.objectContaining({ + title: product2.title, + variants: [ + expect.objectContaining({ + title: product2.variants[0].title, + sku: product2.variants[0].sku, + }), + ], + }), + ]) + ) + }) + + it("should revert the updated variants using selector/update if the hook fails", async () => { + const workflow = updateProductVariantsWorkflow(appContainer) + + const [product1, product2] = await service.createProducts([ + { + title: "test1", + variants: [ + { + title: "variant1", + sku: "variant1", + }, + ], + }, + { + title: "test2", + variants: [ + { + title: "variant2", + sku: "variant2", + }, + ], + }, + ]) + + const { errors } = await workflow.run({ + input: { + selector: { + id: product1.variants[0].id, + }, + update: { + title: "variant1-updated", + }, + }, + throwOnError: false, + }) + + expect(errors).toEqual([ + { + action: "productVariantsUpdated", + handlerType: "invoke", + error: expect.objectContaining({ + message: `product variants updated hook failed`, + }), + }, + ]) + + const products = await service.listProducts( + {}, + { + relations: ["variants"], + } + ) + + expect(products).toHaveLength(2) + expect(products).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + title: product1.title, + variants: [ + expect.objectContaining({ + title: product1.variants[0].title, + sku: product1.variants[0].sku, + }), + ], + }), + expect.objectContaining({ + title: product2.title, + variants: [ + expect.objectContaining({ + title: product2.variants[0].title, + sku: product2.variants[0].sku, + }), + ], + }), + ]) + ) + }) + }) + }) + }) + }, +}) diff --git a/packages/core/workflows-sdk/src/utils/_playground.ts b/packages/core/workflows-sdk/src/utils/_playground.ts index 70ee2ecca9..d751719ab9 100644 --- a/packages/core/workflows-sdk/src/utils/_playground.ts +++ b/packages/core/workflows-sdk/src/utils/_playground.ts @@ -1,117 +1,54 @@ -import { z } from "zod" -import { - createStep, - createWorkflow, - StepResponse, - WorkflowData, -} from "./composer" +import { createStep, createWorkflow, StepResponse, transform } from "./composer" import { createHook } from "./composer/create-hook" import { WorkflowResponse } from "./composer/helpers/workflow-response" -const step1 = createStep("step1", async (input: {}, context) => { - return new StepResponse({ step1: "step1" }) -}) - -type Step2Input = { filters: { id: string[] } } | { filters: { id: string } } -const step2 = createStep("step2", async (input: Step2Input, context) => { - return new StepResponse({ step2: input }) -}) - -const step3 = createStep("step3", async function (_, context) { - return new StepResponse({ step3: "step3" }) -}) - -const step4 = createStep("step4", async function (_, context) { - return new StepResponse({ id: 1 }) -}) - -const step5 = createStep("step5", async function (_, context) { - return new StepResponse(void 0) -}) - -const workflow = createWorkflow( - "sub-workflow", - function (input: WorkflowData<{ outsideWorkflowData: string }>) { - step1() - step2({ filters: { id: [] } }) - - let somethingHook = createHook( - "something", - { id: "1" }, - { - resultValidator: z.object({ - id: z.number(), - }), - } - ) - - const step4Result = step4().config({ name: "foo" }) - step4Result - - const step5Result = step5().config({ name: "foo" }) - step5Result - - return new WorkflowResponse( - { r: somethingHook.getResult(), step3: step3() }, - { hooks: [somethingHook] } - ) +const step1 = createStep( + "step1", + () => { + return new StepResponse("step1") + }, + () => { + console.log("compensate step1") } ) -workflow.hooks.something((input, context) => { - console.log("input>", input) - console.log("context>", context) - return new StepResponse({ id: 2, foo: "bar" }) -}) +const step2 = createStep( + "step2", + (input: any) => { + return new StepResponse(input) + }, + (input) => { + console.log("compensate step2", input) + } +) -workflow.run().then((res) => { - console.log("res", res) -}) +const workflow = createWorkflow("workflow", () => { + const step1Result = step1() -/*const workflow2 = createWorkflow("workflow", function () { - const step1Res = step1() - - const step3Res = when({ value: true }, ({ value }) => { - return value - }).then(() => { - return step3() + const step2Input = transform({ step1Result }, (input) => { + return input }) - transform({ step3Res }, ({ step3Res }) => { - console.log(step3Res) + const step2Result = step2(step2Input) + + const hook = createHook("hook", { + step2Result, }) - const workflowRes = workflow.asStep({ outsideWorkflowData: step1Res.step1 }) - - return workflowRes -})*/ - -// workflow() -// .run({}) -// .then((res) => { -// console.log(res.result) -// }) - -/*const step1 = createStep("step1", async (input: {}, context) => { - return new StepResponse({ step1: ["step1"] }) + return new WorkflowResponse(void 0, { + hooks: [hook], + }) }) -const step2 = createStep("step2", async (input: string[], context) => { - return new StepResponse({ step2: input }) -}) - -const step3 = createStep("step3", async () => { - return new StepResponse({ step3: "step3" }) -}) - -const workflow = createWorkflow("workflow", function () { - const step1Res = step1() - step3() - return step2(step1Res.step1) +workflow.hooks.hook(() => { + throw new Error("hook failed") }) workflow() - .run({}) + .run() .then((res) => { - console.log(res.result) - })*/ + console.log(res) + }) + .catch((e) => { + console.log(e) + })