diff --git a/.changeset/clever-countries-guess.md b/.changeset/clever-countries-guess.md new file mode 100644 index 0000000000..32af17ee24 --- /dev/null +++ b/.changeset/clever-countries-guess.md @@ -0,0 +1,5 @@ +--- +"@medusajs/orchestration": patch +--- + +Serialize orchestrator error diff --git a/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts b/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts index 483c42136e..8ca136f7b1 100644 --- a/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts +++ b/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts @@ -326,7 +326,7 @@ medusaIntegrationTestRunner({ { action: "find-one-or-any-region", handlerType: "invoke", - error: new Error("No regions found"), + error: expect.objectContaining({ message: "No regions found" }), }, ]) }) @@ -417,9 +417,9 @@ medusaIntegrationTestRunner({ { action: "confirm-inventory-step", handlerType: "invoke", - error: new Error( - "Some variant does not have the required inventory" - ), + error: expect.objectContaining({ + message: "Some variant does not have the required inventory", + }), }, ]) }) @@ -441,9 +441,9 @@ medusaIntegrationTestRunner({ { action: "find-sales-channel", handlerType: "invoke", - error: new Error( - `Unable to assign cart to disabled Sales Channel: Webshop` - ), + error: expect.objectContaining({ + message: `Unable to assign cart to disabled Sales Channel: Webshop`, + }), }, ]) }) @@ -471,7 +471,9 @@ medusaIntegrationTestRunner({ { action: "throw", handlerType: "invoke", - error: new Error(`Failed to create cart`), + error: expect.objectContaining({ + message: `Failed to create cart`, + }), }, ]) @@ -508,7 +510,9 @@ medusaIntegrationTestRunner({ { action: "throw", handlerType: "invoke", - error: new Error(`Failed to create cart`), + error: expect.objectContaining({ + message: `Failed to create cart`, + }), }, ]) @@ -705,9 +709,9 @@ medusaIntegrationTestRunner({ { action: "get-variant-price-sets", handlerType: "invoke", - error: new Error( - `Variants with IDs ${product.variants[0].id} do not have a price` - ), + error: expect.objectContaining({ + message: `Variants with IDs ${product.variants[0].id} do not have a price`, + }), }, ]) }) @@ -734,7 +738,9 @@ medusaIntegrationTestRunner({ { action: "validate-variants-exist", handlerType: "invoke", - error: new Error(`Variants with IDs prva_foo do not exist`), + error: expect.objectContaining({ + message: `Variants with IDs prva_foo do not exist`, + }), }, ]) }) @@ -975,7 +981,9 @@ medusaIntegrationTestRunner({ { action: "throw", handlerType: "invoke", - error: new Error(`Failed to update something after line items`), + error: expect.objectContaining({ + message: `Failed to update something after line items`, + }), }, ]) @@ -1064,9 +1072,9 @@ medusaIntegrationTestRunner({ { action: "throw", handlerType: "invoke", - error: new Error( - `Failed to do something after deleting line items` - ), + error: expect.objectContaining({ + message: `Failed to do something after deleting line items`, + }), }, ]) @@ -1178,9 +1186,9 @@ medusaIntegrationTestRunner({ { action: "throw", handlerType: "invoke", - error: new Error( - `Failed to do something after linking cart and payment collection` - ), + error: expect.objectContaining({ + message: `Failed to do something after linking cart and payment collection`, + }), }, ]) @@ -1353,9 +1361,9 @@ medusaIntegrationTestRunner({ { action: "throw", handlerType: "invoke", - error: new Error( - `Failed to do something after updating payment collections` - ), + error: expect.objectContaining({ + message: `Failed to do something after updating payment collections`, + }), }, ]) @@ -1807,9 +1815,9 @@ medusaIntegrationTestRunner({ expect(errors).toEqual([ { action: "get-shipping-option-price-sets", - error: new Error( - `Shipping options with IDs ${shippingOption.id} do not have a price` - ), + error: expect.objectContaining({ + message: `Shipping options with IDs ${shippingOption.id} do not have a price`, + }), handlerType: "invoke", }, ]) diff --git a/integration-tests/modules/__tests__/payment/payment-session.workflows.spec.ts b/integration-tests/modules/__tests__/payment/payment-session.workflows.spec.ts index d05a8ceb9e..36823c6a51 100644 --- a/integration-tests/modules/__tests__/payment/payment-session.workflows.spec.ts +++ b/integration-tests/modules/__tests__/payment/payment-session.workflows.spec.ts @@ -110,9 +110,9 @@ medusaIntegrationTestRunner({ { action: "throw", handlerType: "invoke", - error: new Error( - `Failed to do something after creating payment sessions` - ), + error: expect.objectContaining({ + message: `Failed to do something after creating payment sessions`, + }), }, ]) diff --git a/integration-tests/plugins/__tests__/workflows/inventory/create-inventory-items.ts b/integration-tests/plugins/__tests__/workflows/inventory/create-inventory-items.ts index 293fa75ea3..fdc720d1a6 100644 --- a/integration-tests/plugins/__tests__/workflows/inventory/create-inventory-items.ts +++ b/integration-tests/plugins/__tests__/workflows/inventory/create-inventory-items.ts @@ -66,7 +66,7 @@ describe("CreateInventoryItem workflow", function () { { action: "fail_step", handlerType: "invoke", - error: new Error(`Failed`), + error: expect.objectContaining({ message: `Failed` }), }, ]) diff --git a/integration-tests/plugins/__tests__/workflows/product/create-product.ts b/integration-tests/plugins/__tests__/workflows/product/create-product.ts index 8f230e2f74..b888e5692c 100644 --- a/integration-tests/plugins/__tests__/workflows/product/create-product.ts +++ b/integration-tests/plugins/__tests__/workflows/product/create-product.ts @@ -89,7 +89,9 @@ describe.skip("CreateProduct workflow", function () { { action: "fail_step", handlerType: "invoke", - error: new Error(`Failed to create products`), + error: expect.objectContaining({ + message: `Failed to create products`, + }), }, ]) diff --git a/integration-tests/plugins/__tests__/workflows/product/update-product.ts b/integration-tests/plugins/__tests__/workflows/product/update-product.ts index c4b0013f69..c795e09a24 100644 --- a/integration-tests/plugins/__tests__/workflows/product/update-product.ts +++ b/integration-tests/plugins/__tests__/workflows/product/update-product.ts @@ -1,9 +1,9 @@ -import { WorkflowTypes } from "@medusajs/types" import { Handlers, updateProducts, UpdateProductsActions, } from "@medusajs/core-flows" +import { WorkflowTypes } from "@medusajs/types" import { pipe } from "@medusajs/workflows-sdk" import path from "path" @@ -86,7 +86,9 @@ describe.skip("UpdateProduct workflow", function () { { action: "fail_step", handlerType: "invoke", - error: new Error(`Failed to update products`), + error: expect.objectContaining({ + message: `Failed to update products`, + }), }, ]) diff --git a/packages/orchestration/src/transaction/errors.ts b/packages/orchestration/src/transaction/errors.ts index 2bbda62dc2..615188d9a8 100644 --- a/packages/orchestration/src/transaction/errors.ts +++ b/packages/orchestration/src/transaction/errors.ts @@ -45,3 +45,30 @@ export class TransactionTimeoutError extends Error { this.name = "TransactionTimeoutError" } } + +export function serializeError(error) { + const serialized = { + message: error.message, + name: error.name, + stack: error.stack, + } + + Object.getOwnPropertyNames(error).forEach((key) => { + // eslint-disable-next-line no-prototype-builtins + if (!serialized.hasOwnProperty(key)) { + serialized[key] = error[key] + } + }) + + return serialized +} + +export function isErrorLike(value) { + return ( + !!value && + typeof value === "object" && + "name" in value && + "message" in value && + "stack" in value + ) +} diff --git a/packages/orchestration/src/transaction/transaction-orchestrator.ts b/packages/orchestration/src/transaction/transaction-orchestrator.ts index d706b2a8ba..0c31b17c37 100644 --- a/packages/orchestration/src/transaction/transaction-orchestrator.ts +++ b/packages/orchestration/src/transaction/transaction-orchestrator.ts @@ -16,7 +16,9 @@ import { import { MedusaError, promiseAll, TransactionStepState } from "@medusajs/utils" import { EventEmitter } from "events" import { + isErrorLike, PermanentStepFailureError, + serializeError, TransactionStepTimeoutError, TransactionTimeoutError, } from "./errors" @@ -479,6 +481,10 @@ export class TransactionOrchestrator extends EventEmitter { ): Promise { step.failures++ + if (isErrorLike(error)) { + error = serializeError(error) + } + if ( !isTimeout && step.getStates().status !== TransactionStepStatus.PERMANENT_FAILURE diff --git a/packages/workflows-sdk/src/utils/composer/__tests__/index.spec.ts b/packages/workflows-sdk/src/utils/composer/__tests__/index.spec.ts index 1be1407ea9..4888ce5cb2 100644 --- a/packages/workflows-sdk/src/utils/composer/__tests__/index.spec.ts +++ b/packages/workflows-sdk/src/utils/composer/__tests__/index.spec.ts @@ -1,6 +1,6 @@ import { createStep } from "../create-step" -import { StepResponse } from "../helpers" import { createWorkflow } from "../create-workflow" +import { StepResponse } from "../helpers" import { transform } from "../transform" describe("Workflow composer", () => { @@ -31,7 +31,9 @@ describe("Workflow composer", () => { { action: "step2", handlerType: "invoke", - error: new Error("step2 failed"), + error: expect.objectContaining({ + message: "step2 failed", + }), }, expect.objectContaining({ message: "Cannot read properties of undefined (reading 'result')",