fix(core-flows): complete cart improvements (#12646)
* fix(core-flows): use cartId as transactionId and acquire lock to complete cart * fix cart update compensation
This commit is contained in:
1 parent
820965e21a
commit
490bd7647f
14 files changed
+354
-20
No files matched your search
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/workflows-sdk": patch
|
||||||
|
"@medusajs/core-flows": patch
|
||||||
|
"@medusajs/utils": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix(core-flows): use cart_id to complete cart on payment webhook and lock cart before completion
|
||||||
@@ -747,6 +747,135 @@ medusaIntegrationTestRunner({
|
|||||||
paymentSession.id
|
paymentSession.id
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
it("should complete cart when payment webhook and storefront are called in simultaneously", async () => {
|
||||||
|
const salesChannel = await scModuleService.createSalesChannels({
|
||||||
|
name: "Webshop",
|
||||||
|
})
|
||||||
|
|
||||||
|
const location = await stockLocationModule.createStockLocations({
|
||||||
|
name: "Warehouse",
|
||||||
|
})
|
||||||
|
|
||||||
|
const [product] = await productModule.createProducts([
|
||||||
|
{
|
||||||
|
title: "Test product",
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
title: "Test variant",
|
||||||
|
manage_inventory: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const priceSet = await pricingModule.createPriceSets({
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await pricingModule.createPricePreferences({
|
||||||
|
attribute: "currency_code",
|
||||||
|
value: "usd",
|
||||||
|
has_tax_inclusive: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
await remoteLink.create([
|
||||||
|
{
|
||||||
|
[Modules.PRODUCT]: {
|
||||||
|
variant_id: product.variants[0].id,
|
||||||
|
},
|
||||||
|
[Modules.PRICING]: {
|
||||||
|
price_set_id: priceSet.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[Modules.SALES_CHANNEL]: {
|
||||||
|
sales_channel_id: salesChannel.id,
|
||||||
|
},
|
||||||
|
[Modules.STOCK_LOCATION]: {
|
||||||
|
stock_location_id: location.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
// create cart
|
||||||
|
const cart = await cartModuleService.createCarts({
|
||||||
|
currency_code: "usd",
|
||||||
|
sales_channel_id: salesChannel.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
await addToCartWorkflow(appContainer).run({
|
||||||
|
input: {
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
variant_id: product.variants[0].id,
|
||||||
|
quantity: 1,
|
||||||
|
requires_shipping: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
cart_id: cart.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await createPaymentCollectionForCartWorkflow(appContainer).run({
|
||||||
|
input: {
|
||||||
|
cart_id: cart.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const [payCol] = await remoteQuery(
|
||||||
|
remoteQueryObjectFromString({
|
||||||
|
entryPoint: "cart_payment_collection",
|
||||||
|
variables: { filters: { cart_id: cart.id } },
|
||||||
|
fields: ["payment_collection_id"],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const { result: paymentSession } =
|
||||||
|
await createPaymentSessionsWorkflow(appContainer).run({
|
||||||
|
input: {
|
||||||
|
payment_collection_id: payCol.payment_collection_id,
|
||||||
|
provider_id: "pp_system_default",
|
||||||
|
context: {},
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const [{ result: order }] = await Promise.all([
|
||||||
|
completeCartWorkflow(appContainer).run({
|
||||||
|
input: {
|
||||||
|
id: cart.id,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
processPaymentWorkflow(appContainer).run({
|
||||||
|
input: {
|
||||||
|
action: "captured",
|
||||||
|
data: {
|
||||||
|
session_id: paymentSession.id,
|
||||||
|
amount: 3000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
const { result: fullOrder } = await getOrderDetailWorkflow(
|
||||||
|
appContainer
|
||||||
|
).run({
|
||||||
|
input: {
|
||||||
|
fields: ["*"],
|
||||||
|
order_id: order.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(fullOrder.payment_status).toBe("captured")
|
||||||
|
expect(fullOrder.payment_collections[0].authorized_amount).toBe(3000)
|
||||||
|
expect(fullOrder.payment_collections[0].captured_amount).toBe(3000)
|
||||||
|
expect(fullOrder.payment_collections[0].status).toBe("completed")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -29,7 +29,18 @@ export const updateCartsStep = createStep(
|
|||||||
async (data: UpdateCartsStepInput, { container }) => {
|
async (data: UpdateCartsStepInput, { container }) => {
|
||||||
const cartModule = container.resolve<ICartModuleService>(Modules.CART)
|
const cartModule = container.resolve<ICartModuleService>(Modules.CART)
|
||||||
|
|
||||||
const { selects, relations } = getSelectsAndRelationsFromObjectArray(data)
|
const { selects, relations } = getSelectsAndRelationsFromObjectArray(data, {
|
||||||
|
requiredFields: [
|
||||||
|
"id",
|
||||||
|
"region_id",
|
||||||
|
"customer_id",
|
||||||
|
"sales_channel_id",
|
||||||
|
"email",
|
||||||
|
"currency_code",
|
||||||
|
"metadata",
|
||||||
|
"completed_at",
|
||||||
|
],
|
||||||
|
})
|
||||||
const cartsBeforeUpdate = await cartModule.listCarts(
|
const cartsBeforeUpdate = await cartModule.listCarts(
|
||||||
{ id: data.map((d) => d.id) },
|
{ id: data.map((d) => d.id) },
|
||||||
{ select: selects, relations }
|
{ select: selects, relations }
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import {
|
|||||||
useQueryGraphStep,
|
useQueryGraphStep,
|
||||||
useRemoteQueryStep,
|
useRemoteQueryStep,
|
||||||
} from "../../common"
|
} from "../../common"
|
||||||
|
import { acquireLockStep } from "../../locking/acquire-lock"
|
||||||
|
import { releaseLockStep } from "../../locking/release-lock"
|
||||||
import { addOrderTransactionStep } from "../../order/steps/add-order-transaction"
|
import { addOrderTransactionStep } from "../../order/steps/add-order-transaction"
|
||||||
import { createOrdersStep } from "../../order/steps/create-orders"
|
import { createOrdersStep } from "../../order/steps/create-orders"
|
||||||
import { authorizePaymentSessionStep } from "../../payment/steps/authorize-payment-session"
|
import { authorizePaymentSessionStep } from "../../payment/steps/authorize-payment-session"
|
||||||
@@ -60,7 +62,9 @@ export type CompleteCartWorkflowOutput = {
|
|||||||
id: string
|
id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const THREE_DAYS = 60 * 60 * 24 * 3
|
const THREE_DAYS = 60 * 60 * 24 * 3
|
||||||
|
const THIRTY_SECONDS = 30
|
||||||
|
const TWO_MINUTES = 60 * 2
|
||||||
|
|
||||||
export const completeCartWorkflowId = "complete-cart"
|
export const completeCartWorkflowId = "complete-cart"
|
||||||
/**
|
/**
|
||||||
@@ -93,6 +97,12 @@ export const completeCartWorkflow = createWorkflow(
|
|||||||
retentionTime: THREE_DAYS,
|
retentionTime: THREE_DAYS,
|
||||||
},
|
},
|
||||||
(input: WorkflowData<CompleteCartWorkflowInput>) => {
|
(input: WorkflowData<CompleteCartWorkflowInput>) => {
|
||||||
|
acquireLockStep({
|
||||||
|
key: input.id,
|
||||||
|
timeout: THIRTY_SECONDS,
|
||||||
|
ttl: TWO_MINUTES,
|
||||||
|
})
|
||||||
|
|
||||||
const orderCart = useQueryGraphStep({
|
const orderCart = useQueryGraphStep({
|
||||||
entity: "order_cart",
|
entity: "order_cart",
|
||||||
fields: ["cart_id", "order_id"],
|
fields: ["cart_id", "order_id"],
|
||||||
@@ -381,6 +391,10 @@ export const completeCartWorkflow = createWorkflow(
|
|||||||
return createdOrder
|
return createdOrder
|
||||||
})
|
})
|
||||||
|
|
||||||
|
releaseLockStep({
|
||||||
|
key: input.id,
|
||||||
|
})
|
||||||
|
|
||||||
const result = transform({ order, orderId }, ({ order, orderId }) => {
|
const result = transform({ order, orderId }, ({ order, orderId }) => {
|
||||||
return { id: order?.id ?? orderId } as CompleteCartWorkflowOutput
|
return { id: order?.id ?? orderId } as CompleteCartWorkflowOutput
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export * from "./fulfillment"
|
|||||||
export * from "./inventory"
|
export * from "./inventory"
|
||||||
export * from "./invite"
|
export * from "./invite"
|
||||||
export * from "./line-item"
|
export * from "./line-item"
|
||||||
|
export * from "./locking"
|
||||||
export * from "./notification"
|
export * from "./notification"
|
||||||
export * from "./order"
|
export * from "./order"
|
||||||
export * from "./payment"
|
export * from "./payment"
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import { isDefined, Modules } from "@medusajs/framework/utils"
|
||||||
|
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
|
||||||
|
import { setTimeout } from "timers/promises"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The keys to be locked
|
||||||
|
*/
|
||||||
|
export interface AcquireLockStepInput {
|
||||||
|
key: string | string[]
|
||||||
|
timeout?: number // in seconds. Defaults to 0
|
||||||
|
retryInterval?: number // in seconds. Defaults to 0.3
|
||||||
|
ttl?: number // in seconds
|
||||||
|
ownerId?: string
|
||||||
|
provider?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const acquireLockStepId = "acquire-lock-step"
|
||||||
|
/**
|
||||||
|
* This step acquires a lock for a given key.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const data = acquireLockStep({
|
||||||
|
* "key": "my-lock-key",
|
||||||
|
* "ttl": 60
|
||||||
|
* })
|
||||||
|
*/
|
||||||
|
export const acquireLockStep = createStep(
|
||||||
|
acquireLockStepId,
|
||||||
|
async (data: AcquireLockStepInput, { container }) => {
|
||||||
|
const keys = Array.isArray(data.key)
|
||||||
|
? data.key
|
||||||
|
: isDefined(data.key)
|
||||||
|
? [data.key]
|
||||||
|
: []
|
||||||
|
|
||||||
|
if (!keys.length) {
|
||||||
|
return new StepResponse(void 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const locking = container.resolve(Modules.LOCKING)
|
||||||
|
|
||||||
|
const retryInterval = data.retryInterval ?? 0.3
|
||||||
|
const tryUntil = Date.now() + (data.timeout ?? 0) * 1000
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
await locking.acquire(data.key, {
|
||||||
|
expire: data.ttl,
|
||||||
|
ownerId: data.ownerId,
|
||||||
|
provider: data.provider,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
} catch (e) {
|
||||||
|
if (Date.now() >= tryUntil) {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await setTimeout(retryInterval * 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StepResponse(void 0, {
|
||||||
|
keys,
|
||||||
|
ownerId: data.ownerId,
|
||||||
|
provider: data.provider,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async (data, { container }) => {
|
||||||
|
if (!data?.keys?.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const locking = container.resolve(Modules.LOCKING)
|
||||||
|
|
||||||
|
await locking.release(data.keys, {
|
||||||
|
ownerId: data.ownerId,
|
||||||
|
provider: data.provider,
|
||||||
|
})
|
||||||
|
|
||||||
|
return new StepResponse()
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./acquire-lock"
|
||||||
|
export * from "./release-lock"
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { isDefined, Modules } from "@medusajs/framework/utils"
|
||||||
|
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The locked keys to be released
|
||||||
|
*/
|
||||||
|
export interface ReleaseLockStepInput {
|
||||||
|
key: string | string[]
|
||||||
|
ownerId?: string
|
||||||
|
provider?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const releaseLockStepId = "release-lock-step"
|
||||||
|
/**
|
||||||
|
* This step releases a lock for a given key.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const data = releaseLockStep({
|
||||||
|
* "key": "my-lock-key"
|
||||||
|
* })
|
||||||
|
*/
|
||||||
|
export const releaseLockStep = createStep(
|
||||||
|
releaseLockStepId,
|
||||||
|
async (data: ReleaseLockStepInput, { container }) => {
|
||||||
|
const keys = Array.isArray(data.key)
|
||||||
|
? data.key
|
||||||
|
: isDefined(data.key)
|
||||||
|
? [data.key]
|
||||||
|
: []
|
||||||
|
|
||||||
|
if (!keys.length) {
|
||||||
|
return new StepResponse(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const locking = container.resolve(Modules.LOCKING)
|
||||||
|
const released = await locking.release(keys, {
|
||||||
|
ownerId: data.ownerId,
|
||||||
|
provider: data.provider,
|
||||||
|
})
|
||||||
|
|
||||||
|
return new StepResponse(released)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
import { createStep } from "@medusajs/framework/workflows-sdk"
|
||||||
|
import { completeCartWorkflowId } from "../../cart/workflows/complete-cart"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data to complete a cart after a payment is captured.
|
||||||
|
*/
|
||||||
|
export type CompleteCartAfterPaymentStepInput = {
|
||||||
|
/**
|
||||||
|
* The ID of the cart to complete.
|
||||||
|
*/
|
||||||
|
cart_id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const completeCartAfterPaymentStepId = "complete-cart-after-payment-step"
|
||||||
|
/**
|
||||||
|
* This step completes a cart after a payment is captured.
|
||||||
|
*/
|
||||||
|
export const completeCartAfterPaymentStep = createStep(
|
||||||
|
completeCartAfterPaymentStepId,
|
||||||
|
async (input: CompleteCartAfterPaymentStepInput, { container }) => {
|
||||||
|
const workflowEngine = container.resolve(Modules.WORKFLOW_ENGINE)
|
||||||
|
|
||||||
|
await workflowEngine.run(completeCartWorkflowId, {
|
||||||
|
input: {
|
||||||
|
id: input.cart_id,
|
||||||
|
},
|
||||||
|
transactionId: input.cart_id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import { WebhookActionResult } from "@medusajs/types"
|
import { WebhookActionResult } from "@medusajs/types"
|
||||||
import { PaymentActions } from "@medusajs/utils"
|
import { PaymentActions } from "@medusajs/utils"
|
||||||
import { createWorkflow, when } from "@medusajs/workflows-sdk"
|
import { createWorkflow, when } from "@medusajs/workflows-sdk"
|
||||||
import { completeCartWorkflow } from "../../cart/workflows/complete-cart"
|
|
||||||
import { useQueryGraphStep } from "../../common"
|
import { useQueryGraphStep } from "../../common"
|
||||||
import { authorizePaymentSessionStep } from "../steps"
|
import { authorizePaymentSessionStep } from "../steps"
|
||||||
|
import { completeCartAfterPaymentStep } from "../steps/complete-cart-after-payment"
|
||||||
import { capturePaymentWorkflow } from "./capture-payment"
|
import { capturePaymentWorkflow } from "./capture-payment"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -131,11 +131,9 @@ export const processPaymentWorkflow = createWorkflow(
|
|||||||
when({ cartPaymentCollection }, ({ cartPaymentCollection }) => {
|
when({ cartPaymentCollection }, ({ cartPaymentCollection }) => {
|
||||||
return !!cartPaymentCollection.data.length
|
return !!cartPaymentCollection.data.length
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
completeCartWorkflow
|
completeCartAfterPaymentStep({
|
||||||
.runAsStep({
|
cart_id: cartPaymentCollection.data[0].cart_id,
|
||||||
input: { id: cartPaymentCollection.data[0].cart_id },
|
}).config({
|
||||||
})
|
|
||||||
.config({
|
|
||||||
continueOnPermanentFailure: true, // Continue payment processing even if cart completion fails
|
continueOnPermanentFailure: true, // Continue payment processing even if cart completion fails
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
+8
-5
@@ -40,7 +40,7 @@ describe("getSelectsAndRelationsFromObjectArray", function () {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
output: {
|
output: {
|
||||||
selects: [
|
selects: expect.arrayContaining([
|
||||||
"attr_string",
|
"attr_string",
|
||||||
"attr_boolean",
|
"attr_boolean",
|
||||||
"attr_null",
|
"attr_null",
|
||||||
@@ -55,16 +55,19 @@ describe("getSelectsAndRelationsFromObjectArray", function () {
|
|||||||
"attr_array.attr_object.attr_boolean",
|
"attr_array.attr_object.attr_boolean",
|
||||||
"attr_array.attr_object.attr_null",
|
"attr_array.attr_object.attr_null",
|
||||||
"attr_array.attr_object.attr_undefined",
|
"attr_array.attr_object.attr_undefined",
|
||||||
],
|
"required_attr",
|
||||||
|
]),
|
||||||
relations: ["attr_object", "attr_array", "attr_array.attr_object"],
|
relations: ["attr_object", "attr_array", "attr_array.attr_object"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
expectations.forEach((expectation) => {
|
expectations.forEach((expectation) => {
|
||||||
expect(getSelectsAndRelationsFromObjectArray(expectation.input)).toEqual(
|
expect(
|
||||||
expectation.output
|
getSelectsAndRelationsFromObjectArray(expectation.input, {
|
||||||
)
|
requiredFields: ["required_attr"],
|
||||||
|
})
|
||||||
|
).toEqual(expectation.output)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -5,8 +5,9 @@ const KEYS_THAT_ARE_NOT_RELATIONS = ["metadata"]
|
|||||||
|
|
||||||
export function getSelectsAndRelationsFromObjectArray(
|
export function getSelectsAndRelationsFromObjectArray(
|
||||||
dataArray: object[],
|
dataArray: object[],
|
||||||
options: { objectFields: string[] } = {
|
options?: {
|
||||||
objectFields: [],
|
objectFields?: string[]
|
||||||
|
requiredFields?: string[]
|
||||||
},
|
},
|
||||||
prefix?: string
|
prefix?: string
|
||||||
): {
|
): {
|
||||||
@@ -15,10 +16,11 @@ export function getSelectsAndRelationsFromObjectArray(
|
|||||||
} {
|
} {
|
||||||
const selects: string[] = []
|
const selects: string[] = []
|
||||||
const relations: string[] = []
|
const relations: string[] = []
|
||||||
|
const { objectFields, requiredFields } = options ?? {}
|
||||||
|
|
||||||
for (const data of dataArray) {
|
for (const data of dataArray) {
|
||||||
for (const [key, value] of Object.entries(data)) {
|
for (const [key, value] of Object.entries(data)) {
|
||||||
if (isObject(value) && !options.objectFields.includes(key)) {
|
if (isObject(value) && !objectFields?.includes(key)) {
|
||||||
const res = getSelectsAndRelationsFromObjectArray(
|
const res = getSelectsAndRelationsFromObjectArray(
|
||||||
[value],
|
[value],
|
||||||
options,
|
options,
|
||||||
@@ -48,7 +50,10 @@ export function getSelectsAndRelationsFromObjectArray(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const uniqueSelects: string[] = deduplicate(selects)
|
const uniqueSelects: string[] = deduplicate([
|
||||||
|
...selects,
|
||||||
|
...(requiredFields ?? []),
|
||||||
|
])
|
||||||
const uniqueRelations: string[] = deduplicate(relations)
|
const uniqueRelations: string[] = deduplicate(relations)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ export function createHook<Name extends string, TInvokeInput, TInvokeOutput>(
|
|||||||
stepName: name,
|
stepName: name,
|
||||||
input: hookInput,
|
input: hookInput,
|
||||||
invokeFn,
|
invokeFn,
|
||||||
compensateFn,
|
compensateFn: compensateFn ?? (() => void 0),
|
||||||
})
|
})
|
||||||
|
|
||||||
if (this.hooks_.registered.includes(name)) {
|
if (this.hooks_.registered.includes(name)) {
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import { deepCopy, OrchestrationUtils, promiseAll } from "@medusajs/utils"
|
import {
|
||||||
|
deepCopy,
|
||||||
|
isDefined,
|
||||||
|
OrchestrationUtils,
|
||||||
|
promiseAll,
|
||||||
|
} from "@medusajs/utils"
|
||||||
|
|
||||||
async function resolveProperty(property, transactionContext) {
|
async function resolveProperty(property, transactionContext) {
|
||||||
const { invoke: invokeRes } = transactionContext
|
const { invoke: invokeRes } = transactionContext
|
||||||
@@ -78,5 +83,8 @@ export async function resolveValue(input, transactionContext) {
|
|||||||
? await resolveProperty(copiedInput, transactionContext)
|
? await resolveProperty(copiedInput, transactionContext)
|
||||||
: await unwrapInput(copiedInput, {})
|
: await unwrapInput(copiedInput, {})
|
||||||
|
|
||||||
return result && JSON.parse(JSON.stringify(result))
|
const strResult = JSON.stringify(result) // Symbols return undefined
|
||||||
|
if (isDefined(strResult)) {
|
||||||
|
return JSON.parse(strResult)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user