chore: local workflow proxying methods to pass context (#6263)

What:
- When calling a module's method inside a Local Workflow the MedusaContext is passed as the last argument to the method if not provided
- Add `requestId` to req
- A couple of fixes on Remote Joiner and the data fetcher for internal services

Why:
- The context used to initialize the workflow has to be shared with all modules. properties like transactionId will be used to emit events and requestId to trace logs for example.
This commit is contained in:
Carlos R. L. Rodrigues
2024-02-01 10:37:26 -03:00
committed by GitHub
parent a2bf6756ac
commit 45134e4d11
40 changed files with 576 additions and 170 deletions

View File

@@ -0,0 +1,15 @@
---
"@medusajs/stock-location": patch
"@medusajs/orchestration": patch
"@medusajs/workflows-sdk": patch
"@medusajs/link-modules": patch
"@medusajs/modules-sdk": patch
"@medusajs/inventory": patch
"@medusajs/pricing": patch
"@medusajs/product": patch
"@medusajs/medusa": patch
"@medusajs/types": patch
"@medusajs/utils": patch
---
Workflows passing MedusaContext as argument

View File

@@ -100,4 +100,98 @@ describe("POST /admin/campaigns", () => {
})
)
})
it("should create 3 campaigns in parallel and have the context passed as argument when calling createCampaigns with different transactionId", async () => {
const parallelPromotion = await promotionModuleService.create({
code: "PARALLEL",
type: "standard",
})
const spyCreateCampaigns = jest.spyOn(
promotionModuleService.constructor.prototype,
"createCampaigns"
)
const api = useApi() as any
const a = async () => {
return await api.post(
`/admin/campaigns`,
{
name: "camp_1",
campaign_identifier: "camp_1",
starts_at: new Date("01/01/2024").toISOString(),
ends_at: new Date("01/02/2024").toISOString(),
promotions: [{ id: parallelPromotion.id }],
budget: {
limit: 1000,
type: "usage",
},
},
adminHeaders
)
}
const b = async () => {
return await api.post(
`/admin/campaigns`,
{
name: "camp_2",
campaign_identifier: "camp_2",
starts_at: new Date("01/02/2024").toISOString(),
ends_at: new Date("01/03/2029").toISOString(),
promotions: [{ id: parallelPromotion.id }],
budget: {
limit: 500,
type: "usage",
},
},
adminHeaders
)
}
const c = async () => {
return await api.post(
`/admin/campaigns`,
{
name: "camp_3",
campaign_identifier: "camp_3",
starts_at: new Date("01/03/2024").toISOString(),
ends_at: new Date("01/04/2029").toISOString(),
promotions: [{ id: parallelPromotion.id }],
budget: {
limit: 250,
type: "usage",
},
},
{
headers: {
...adminHeaders.headers,
"x-request-id": "my-custom-request-id",
},
}
)
}
await Promise.all([a(), b(), c()])
expect(spyCreateCampaigns).toHaveBeenCalledTimes(3)
expect(spyCreateCampaigns.mock.calls[0][1].__type).toBe("MedusaContext")
const distinctTransactionId = [
...new Set(
spyCreateCampaigns.mock.calls.map((call) => call[1].transactionId)
),
]
expect(distinctTransactionId).toHaveLength(3)
const distinctRequestId = [
...new Set(
spyCreateCampaigns.mock.calls.map((call) => call[1].requestId)
),
]
expect(distinctRequestId).toHaveLength(3)
expect(distinctRequestId).toContain("my-custom-request-id")
})
})

View File

@@ -1,15 +1,15 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { IProductModuleService, WorkflowTypes } from "@medusajs/types"
import {
createProducts,
CreateProductsActions,
Handlers,
createProducts,
} from "@medusajs/core-flows"
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { IProductModuleService, WorkflowTypes } from "@medusajs/types"
import { pipe } from "@medusajs/workflows-sdk"
import path from "path"
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
import { getContainer } from "../../../../environment-helpers/use-container"
import { initDb, useDb } from "../../../../environment-helpers/use-db"
import { pipe } from "@medusajs/workflows-sdk"
jest.setTimeout(30000)
@@ -25,8 +25,6 @@ describe("CreateProduct workflow", function () {
})
afterAll(async () => {
console.log("GLOABL GC()", typeof global)
const db = useDb()
await db.shutdown()
await shutdownServer()

View File

@@ -1,23 +1,24 @@
import jwt from "jsonwebtoken"
import {
AuthProviderDTO,
AuthTypes,
AuthUserDTO,
AuthenticationInput,
AuthenticationResponse,
AuthTypes,
Context,
CreateAuthProviderDTO,
CreateAuthUserDTO,
DAL,
FilterableAuthProviderProps,
FilterableAuthUserProps,
FindConfig,
InternalModuleDeclaration,
JWTGenerationOptions,
MedusaContainer,
ModuleJoinerConfig,
JWTGenerationOptions,
UpdateAuthUserDTO,
} from "@medusajs/types"
import { AuthProvider, AuthUser } from "@models"
import { joinerConfig } from "../joiner-config"
import { AuthProviderService, AuthUserService } from "@services"
import {
AbstractAuthModuleProvider,
InjectManager,
@@ -25,16 +26,10 @@ import {
MedusaContext,
MedusaError,
} from "@medusajs/utils"
import {
AuthProviderDTO,
AuthUserDTO,
CreateAuthProviderDTO,
CreateAuthUserDTO,
FilterableAuthProviderProps,
FilterableAuthUserProps,
UpdateAuthUserDTO,
} from "@medusajs/types"
import { AuthProvider, AuthUser } from "@models"
import { AuthProviderService, AuthUserService } from "@services"
import { ServiceTypes } from "@types"
import { joinerConfig } from "../joiner-config"
type AuthModuleOptions = {
jwt_secret: string
@@ -90,7 +85,7 @@ export default class AuthModuleService<
async retrieveAuthProvider(
provider: string,
config: FindConfig<AuthProviderDTO> = {},
sharedContext: Context = {}
@MedusaContext() sharedContext: Context = {}
): Promise<AuthProviderDTO> {
const authProvider = await this.authProviderService_.retrieve(
provider,
@@ -107,7 +102,7 @@ export default class AuthModuleService<
async listAuthProviders(
filters: FilterableAuthProviderProps = {},
config: FindConfig<AuthProviderDTO> = {},
sharedContext: Context = {}
@MedusaContext() sharedContext: Context = {}
): Promise<AuthProviderDTO[]> {
const authProviders = await this.authProviderService_.list(
filters,

View File

@@ -8,14 +8,14 @@ import {
} from "@medusajs/types"
import {
InjectEntityManager,
isDefined,
MedusaContext,
MedusaError,
isDefined,
} from "@medusajs/utils"
import { DeepPartial, EntityManager, FindManyOptions, In } from "typeorm"
import { InventoryItem } from "../models"
import { getListQuery } from "../utils/query"
import { buildQuery } from "../utils/build-query"
import { getListQuery } from "../utils/query"
type InjectedDependencies = {
eventBusService: IEventBusService
@@ -47,7 +47,7 @@ export default class InventoryItemService {
async list(
selector: FilterableInventoryItemProps = {},
config: FindConfig<InventoryItem> = { relations: [], skip: 0, take: 10 },
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<InventoryItemDTO[]> {
const queryBuilder = getListQuery(
context.transactionManager ?? this.manager_,
@@ -68,7 +68,7 @@ export default class InventoryItemService {
async retrieve(
inventoryItemId: string,
config: FindConfig<InventoryItem> = {},
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<InventoryItem> {
if (!isDefined(inventoryItemId)) {
throw new MedusaError(
@@ -102,7 +102,7 @@ export default class InventoryItemService {
async listAndCount(
selector: FilterableInventoryItemProps = {},
config: FindConfig<InventoryItem> = { relations: [], skip: 0, take: 10 },
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<[InventoryItemDTO[], number]> {
const queryBuilder = getListQuery(
context.transactionManager ?? this.manager_,

View File

@@ -7,9 +7,9 @@ import {
} from "@medusajs/types"
import {
InjectEntityManager,
isDefined,
MedusaContext,
MedusaError,
isDefined,
} from "@medusajs/utils"
import { DeepPartial, EntityManager, FindManyOptions, In } from "typeorm"
import { InventoryLevel } from "../models"
@@ -46,7 +46,7 @@ export default class InventoryLevelService {
async list(
selector: FilterableInventoryLevelProps = {},
config: FindConfig<InventoryLevel> = { relations: [], skip: 0, take: 10 },
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<InventoryLevel[]> {
const manager = context.transactionManager ?? this.manager_
const levelRepository = manager.getRepository(InventoryLevel)
@@ -65,7 +65,7 @@ export default class InventoryLevelService {
async listAndCount(
selector: FilterableInventoryLevelProps = {},
config: FindConfig<InventoryLevel> = { relations: [], skip: 0, take: 10 },
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<[InventoryLevel[], number]> {
const manager = context.transactionManager ?? this.manager_
const levelRepository = manager.getRepository(InventoryLevel)
@@ -85,7 +85,7 @@ export default class InventoryLevelService {
async retrieve(
inventoryLevelId: string,
config: FindConfig<InventoryLevel> = {},
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<InventoryLevel> {
if (!isDefined(inventoryLevelId)) {
throw new MedusaError(
@@ -319,7 +319,7 @@ export default class InventoryLevelService {
async getStockedQuantity(
inventoryItemId: string,
locationIds: string[] | string,
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<number> {
if (!Array.isArray(locationIds)) {
locationIds = [locationIds]
@@ -348,7 +348,7 @@ export default class InventoryLevelService {
async getAvailableQuantity(
inventoryItemId: string,
locationIds: string[] | string,
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<number> {
if (!Array.isArray(locationIds)) {
locationIds = [locationIds]
@@ -377,7 +377,7 @@ export default class InventoryLevelService {
async getReservedQuantity(
inventoryItemId: string,
locationIds: string[] | string,
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<number> {
if (!Array.isArray(locationIds)) {
locationIds = [locationIds]

View File

@@ -74,7 +74,7 @@ export default class InventoryService implements IInventoryService {
async listInventoryItems(
selector: FilterableInventoryItemProps,
config: FindConfig<InventoryItemDTO> = { relations: [], skip: 0, take: 10 },
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<[InventoryItemDTO[], number]> {
return await this.inventoryItemService_.listAndCount(
selector,
@@ -85,7 +85,7 @@ export default class InventoryService implements IInventoryService {
async list(
selector: FilterableInventoryItemProps,
config: FindConfig<InventoryItemDTO> = { relations: [], skip: 0, take: 10 },
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<InventoryItemDTO[]> {
return await this.inventoryItemService_.list(selector, config, context)
}
@@ -104,7 +104,7 @@ export default class InventoryService implements IInventoryService {
skip: 0,
take: 10,
},
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<[InventoryLevelDTO[], number]> {
return await this.inventoryLevelService_.listAndCount(
selector,
@@ -127,7 +127,7 @@ export default class InventoryService implements IInventoryService {
skip: 0,
take: 10,
},
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<[ReservationItemDTO[], number]> {
return await this.reservationItemService_.listAndCount(
selector,
@@ -146,7 +146,7 @@ export default class InventoryService implements IInventoryService {
async retrieveInventoryItem(
inventoryItemId: string,
config?: FindConfig<InventoryItemDTO>,
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<InventoryItemDTO> {
const inventoryItem = await this.inventoryItemService_.retrieve(
inventoryItemId,
@@ -166,7 +166,7 @@ export default class InventoryService implements IInventoryService {
async retrieveInventoryLevel(
inventoryItemId: string,
locationId: string,
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<InventoryLevelDTO> {
const [inventoryLevel] = await this.inventoryLevelService_.list(
{ inventory_item_id: inventoryItemId, location_id: locationId },
@@ -191,7 +191,7 @@ export default class InventoryService implements IInventoryService {
*/
async retrieveReservationItem(
reservationId: string,
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<ReservationItemDTO> {
return await this.reservationItemService_.retrieve(
reservationId,
@@ -202,7 +202,7 @@ export default class InventoryService implements IInventoryService {
private async ensureInventoryLevels(
data: { location_id: string; inventory_item_id: string }[],
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<InventoryLevelDTO[]> {
const inventoryLevels = await this.inventoryLevelService_.list(
{
@@ -629,7 +629,7 @@ export default class InventoryService implements IInventoryService {
async retrieveAvailableQuantity(
inventoryItemId: string,
locationIds: string[],
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<number> {
// Throws if item does not exist
await this.inventoryItemService_.retrieve(
@@ -665,7 +665,7 @@ export default class InventoryService implements IInventoryService {
async retrieveStockedQuantity(
inventoryItemId: string,
locationIds: string[],
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<number> {
// Throws if item does not exist
await this.inventoryItemService_.retrieve(
@@ -701,7 +701,7 @@ export default class InventoryService implements IInventoryService {
async retrieveReservedQuantity(
inventoryItemId: string,
locationIds: string[],
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<number> {
// Throws if item does not exist
await this.inventoryItemService_.retrieve(

View File

@@ -8,9 +8,9 @@ import {
} from "@medusajs/types"
import {
InjectEntityManager,
isDefined,
MedusaContext,
MedusaError,
isDefined,
promiseAll,
} from "@medusajs/utils"
import { EntityManager, FindManyOptions, In } from "typeorm"
@@ -55,7 +55,7 @@ export default class ReservationItemService {
async list(
selector: FilterableReservationItemProps = {},
config: FindConfig<ReservationItem> = { relations: [], skip: 0, take: 10 },
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<ReservationItem[]> {
const manager = context.transactionManager ?? this.manager_
const itemRepository = manager.getRepository(ReservationItem)
@@ -75,7 +75,7 @@ export default class ReservationItemService {
async listAndCount(
selector: FilterableReservationItemProps = {},
config: FindConfig<ReservationItem> = { relations: [], skip: 0, take: 10 },
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<[ReservationItem[], number]> {
const manager = context.transactionManager ?? this.manager_
const itemRepository = manager.getRepository(ReservationItem)
@@ -96,7 +96,7 @@ export default class ReservationItemService {
async retrieve(
reservationItemId: string,
config: FindConfig<ReservationItem> = {},
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<ReservationItem> {
if (!isDefined(reservationItemId)) {
throw new MedusaError(

View File

@@ -11,12 +11,12 @@ import {
import {
InjectManager,
InjectTransactionManager,
isDefined,
mapObjectTo,
MapToConfig,
MedusaContext,
MedusaError,
ModulesSdkUtils,
isDefined,
mapObjectTo,
} from "@medusajs/utils"
import { LinkService } from "@services"
import { shouldForceTransaction } from "../utils"
@@ -229,7 +229,7 @@ export default class LinkModuleService<TLink> implements ILinkModule {
async softDelete(
data: any,
{ returnLinkableKeys }: SoftDeleteReturn = {},
sharedContext: Context = {}
@MedusaContext() sharedContext: Context = {}
): Promise<Record<string, unknown[]> | void> {
this.validateFields(data)
@@ -270,7 +270,7 @@ export default class LinkModuleService<TLink> implements ILinkModule {
async restore(
data: any,
{ returnLinkableKeys }: RestoreReturn = {},
sharedContext: Context = {}
@MedusaContext() sharedContext: Context = {}
): Promise<Record<string, unknown[]> | void> {
this.validateFields(data)

View File

@@ -30,6 +30,9 @@ export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
const { result, errors } = await createCampaigns.run({
input: { campaignsData },
throwOnError: false,
context: {
requestId: req.requestId,
},
})
if (Array.isArray(errors) && errors[0]) {

View File

@@ -13,9 +13,9 @@ import { asValue } from "awilix"
import { createMedusaContainer } from "medusa-core-utils"
import { track } from "medusa-telemetry"
import { EOL } from "os"
import path from "path"
import requestIp from "request-ip"
import { Connection } from "typeorm"
import { v4 } from "uuid"
import { MedusaContainer } from "../types/global"
import apiLoader from "./api"
import loadConfig from "./config"
@@ -192,7 +192,8 @@ export default async ({
// Add the registered services to the request scope
expressApp.use((req: Request, res: Response, next: NextFunction) => {
container.register({ manager: asValue(dataSource.manager) })
;(req as any).scope = container.createScope()
req.scope = container.createScope() as MedusaContainer
req.requestId = (req.headers["x-request-id"] as string) ?? v4()
next()
})

View File

@@ -18,6 +18,7 @@ declare global {
allowedProperties: string[]
includes?: Record<string, boolean>
errors: string[]
requestId?: string
}
}
}

View File

@@ -6,6 +6,7 @@ import type { MedusaContainer } from "./global"
export interface MedusaRequest extends Request {
user?: (User | Customer) & { customer_id?: string; userId?: string }
scope: MedusaContainer
requestId?: string
auth_user?: { id: string; app_metadata: Record<string, any>; scope: string }
}

View File

@@ -1,6 +1,23 @@
import { MedusaModule, RemoteQuery } from "@medusajs/modules-sdk"
import { MedusaContainer } from "@medusajs/types"
function hasPagination(options: { [attr: string]: unknown }): boolean {
if (!options) {
return false
}
const attrs = ["skip"]
return Object.keys(options).some((key) => attrs.includes(key))
}
function buildPagination(options, count) {
return {
skip: options.skip,
take: options.take,
count,
}
}
export function remoteQueryFetchData(container: MedusaContainer) {
return async (expand, keyField, ids, relationship) => {
const serviceConfig = expand.serviceConfig
@@ -12,11 +29,35 @@ export function remoteQueryFetchData(container: MedusaContainer) {
return
}
const filters = {}
let filters = {}
const options = {
...RemoteQuery.getAllFieldsAndRelations(expand),
}
const availableOptions = [
"skip",
"take",
"limit",
"offset",
"order",
"sort",
"withDeleted",
]
const availableOptionsAlias = new Map([
["limit", "take"],
["offset", "skip"],
["sort", "order"],
])
for (const arg of expand.args || []) {
if (arg.name === "filters" && arg.value) {
filters = { ...arg.value }
} else if (availableOptions.includes(arg.name)) {
const argName = availableOptionsAlias.has(arg.name)
? availableOptionsAlias.get(arg.name)
: arg.name
options[argName] = arg.value
}
}
const expandRelations = Object.keys(expand.expands ?? {})
// filter out links from relations because TypeORM will throw if the relation doesn't exist
@@ -33,11 +74,9 @@ export function remoteQueryFetchData(container: MedusaContainer) {
filters[keyField] = ids
}
const hasPagination = Object.keys(options).some((key) =>
["skip"].includes(key)
)
const hasPagination_ = hasPagination(options)
let methodName = hasPagination ? "listAndCount" : "list"
let methodName = hasPagination_ ? "listAndCount" : "list"
if (relationship?.args?.methodSuffix) {
methodName += relationship.args.methodSuffix
@@ -47,12 +86,12 @@ export function remoteQueryFetchData(container: MedusaContainer) {
const result = await service[methodName](filters, options)
if (hasPagination) {
if (hasPagination_) {
const [data, count] = result
return {
data: {
rows: data,
metadata: {},
metadata: buildPagination(options, count),
},
path: "rows",
}

View File

@@ -9,6 +9,7 @@ import {
import {
ContainerRegistrationKeys,
createMedusaContainer,
MedusaModuleType,
} from "@medusajs/utils"
import { asFunction, asValue } from "awilix"
@@ -19,7 +20,7 @@ export async function loadInternalModule(
): Promise<{ error?: Error } | void> {
const registrationName = resolution.definition.registrationName
const { scope, resources } =
const { resources } =
resolution.moduleDeclaration as InternalModuleDeclaration
let loadedModule: ModuleExports
@@ -111,6 +112,7 @@ export async function loadInternalModule(
const moduleService = loadedModule.service
container.register({
[registrationName]: asFunction((cradle) => {
;(moduleService as any).__type = MedusaModuleType
return new moduleService(
localContainer.cradle,
resolution.options,

View File

@@ -1,3 +1,8 @@
import {
RemoteFetchDataCallback,
RemoteJoiner,
toRemoteJoinerQuery,
} from "@medusajs/orchestration"
import {
JoinerRelationship,
JoinerServiceConfig,
@@ -6,11 +11,6 @@ import {
RemoteExpandProperty,
RemoteJoinerQuery,
} from "@medusajs/types"
import {
RemoteFetchDataCallback,
RemoteJoiner,
toRemoteJoinerQuery,
} from "@medusajs/orchestration"
import { isString, toPascalCase } from "@medusajs/utils"
import { MedusaModule } from "./medusa-module"
@@ -30,7 +30,7 @@ export class RemoteQuery {
servicesConfig?: ModuleJoinerConfig[]
}) {
const servicesConfig_ = [...servicesConfig]
if (!modulesLoaded?.length) {
modulesLoaded = MedusaModule.getLoadedModules().map(
(mod) => Object.values(mod)[0]
@@ -164,6 +164,7 @@ export class RemoteQuery {
"offset",
"cursor",
"sort",
"withDeleted",
]
const availableOptionsAlias = new Map([
["limit", "take"],
@@ -228,7 +229,7 @@ export class RemoteQuery {
if (isString(query)) {
finalQuery = RemoteJoiner.parseQuery(query, variables)
} else if (!isString(finalQuery?.service) && !isString(finalQuery?.alias)) {
finalQuery = toRemoteJoinerQuery(query)
finalQuery = toRemoteJoinerQuery(query, variables)
}
return await this.remoteJoiner.query(finalQuery)

View File

@@ -144,4 +144,79 @@ describe("toRemoteJoinerQuery", () => {
],
})
})
it("should transform a nested object with arguments and directives to a Remote Joiner Query format only using variables", async () => {
const obj = {
product: {
fields: ["id", "title", "handle"],
variants: {
fields: ["sku"],
__directives: {
directiveName: "value",
},
shipping_profiles: {
profile: {
fields: ["id", "name"],
},
},
},
},
}
const rjQuery = toRemoteJoinerQuery(obj, {
product: {
limit: 10,
offset: 0,
},
"product.variants.shipping_profiles.profile": {
context: {
customer_group: "cg_123",
region_id: "US",
},
},
})
expect(rjQuery).toEqual({
alias: "product",
fields: ["id", "title", "handle"],
expands: [
{
property: "variants",
directives: [
{
name: "directiveName",
value: "value",
},
],
fields: ["sku"],
},
{
property: "variants.shipping_profiles",
},
{
property: "variants.shipping_profiles.profile",
args: [
{
name: "context",
value: {
customer_group: "cg_123",
region_id: "US",
},
},
],
fields: ["id", "name"],
},
],
args: [
{
name: "limit",
value: 10,
},
{
name: "offset",
value: 0,
},
],
})
})
})

View File

@@ -37,10 +37,11 @@ class GraphQLParser {
}
private parseValueNode(valueNode: ValueNode): unknown {
const obj = {}
switch (valueNode.kind) {
case Kind.VARIABLE:
const variableName = valueNode.name.value
return this.variables ? this.variables[variableName] : undefined
return this.variables ? this.variables[valueNode.name.value] : undefined
case Kind.INT:
return parseInt(valueNode.value, 10)
case Kind.FLOAT:
@@ -55,7 +56,6 @@ class GraphQLParser {
case Kind.LIST:
return valueNode.values.map((v) => this.parseValueNode(v))
case Kind.OBJECT:
let obj = {}
for (const field of valueNode.fields) {
obj[field.name.value] = this.parseValueNode(field.value)
}

View File

@@ -1,53 +1,81 @@
import { RemoteJoinerQuery } from "@medusajs/types"
export function toRemoteJoinerQuery(obj: any): RemoteJoinerQuery {
export function toRemoteJoinerQuery(
obj: any,
variables: Record<string, any> = {}
): RemoteJoinerQuery {
const remoteJoinerQuery: RemoteJoinerQuery = {
alias: "",
fields: [],
expands: [],
}
function extractRecursive(obj, parentName = "", isEntryPoint = true) {
for (const key in obj) {
let entryPoint = ""
function extractRecursive(obj: any, parentName = "", isEntryPoint = true) {
for (const key of Object.keys(obj ?? {})) {
const value = obj[key]
const canExpand =
typeof value === "object" &&
!["fields", "__args", "__directives"].includes(key)
if (canExpand) {
const entityName = parentName ? `${parentName}.${key}` : key
const expandObj: any = {
property: entityName,
}
const reference = isEntryPoint ? remoteJoinerQuery : expandObj
if (value.__args) {
reference.args = Object.entries(value.__args).map(
([name, value]) => ({
name,
value,
})
)
}
if (value.__directives) {
reference.directives = Object.entries(value.__directives).map(
([name, value]) => ({ name, value })
)
}
reference.fields = value.fields
if (isEntryPoint) {
remoteJoinerQuery.alias = key
} else {
remoteJoinerQuery.expands!.push(expandObj)
}
extractRecursive(value, isEntryPoint ? "" : entityName, false)
if (!canExpand) {
continue
}
const entityName = parentName ? `${parentName}.${key}` : key
const variablesPath = !isEntryPoint
? `${entryPoint}${parentName ? "." + parentName : parentName}.${key}`
: key
if (isEntryPoint) {
entryPoint = key
}
const currentVariables = variables[variablesPath]
const expandObj: any = {
property: entityName,
}
const reference = isEntryPoint ? remoteJoinerQuery : expandObj
if (currentVariables) {
reference.args = Object.entries(currentVariables).map(
([name, value]) => ({
name,
value,
})
)
}
if (value.__args) {
reference.args = [
...(reference.__args || []),
...Object.entries(value.__args).map(([name, value]) => ({
name,
value,
})),
]
}
if (value.__directives) {
reference.directives = Object.entries(value.__directives).map(
([name, value]) => ({ name, value })
)
}
if (value.fields) {
reference.fields = value.fields
}
if (isEntryPoint) {
remoteJoinerQuery.alias = key
} else {
remoteJoinerQuery.expands!.push(expandObj)
}
extractRecursive(value, isEntryPoint ? "" : entityName, false)
}
return remoteJoinerQuery

View File

@@ -53,7 +53,7 @@ export class RemoteJoiner {
}, {})
if (expands) {
for (const key in expands) {
for (const key of Object.keys(expands ?? {})) {
const expand = expands[key]
if (expand) {
if (Array.isArray(data[key])) {
@@ -146,12 +146,14 @@ export class RemoteJoiner {
const isReadOnlyDefinition =
service.serviceName === undefined || service.isReadOnlyLink
if (!isReadOnlyDefinition) {
if (!service.alias) {
service.alias = [{ name: service.serviceName!.toLowerCase() }]
} else if (!Array.isArray(service.alias)) {
service.alias ??= []
if (!Array.isArray(service.alias)) {
service.alias = [service.alias]
}
service.alias.push({ name: service.serviceName! })
// handle alias.name as array
for (let idx = 0; idx < service.alias.length; idx++) {
const alias = service.alias[idx]
@@ -173,6 +175,11 @@ export class RemoteJoiner {
for (const alias of service.alias) {
if (this.serviceConfigCache.has(`alias_${alias.name}}`)) {
const defined = this.serviceConfigCache.get(`alias_${alias.name}}`)
if (service.serviceName === defined?.serviceName) {
continue
}
throw new Error(
`Cannot add alias "${alias.name}" for "${service.serviceName}". It is already defined for Service "${defined?.serviceName}".`
)
@@ -223,7 +230,9 @@ export class RemoteJoiner {
(rel) => rel.isInternalService === true
)
if (isInternalServicePresent) continue
if (isInternalServicePresent) {
continue
}
throw new Error(`Service "${serviceName}" was not found`)
}
@@ -338,7 +347,9 @@ export class RemoteJoiner {
relationship
)
const isObj = isDefined(response.path)
const resData = isObj ? response.data[response.path!] : response.data
let resData = isObj ? response.data[response.path!] : response.data
resData = Array.isArray(resData) ? resData : [resData]
const filteredDataArray = resData.map((data: any) =>
RemoteJoiner.filterFields(data, expand.fields, expand.expands)

View File

@@ -1,5 +1,5 @@
import { Context, LoadedModule, MedusaContainer } from "@medusajs/types"
import { createContainerLike, createMedusaContainer } from "@medusajs/utils"
import { createMedusaContainer } from "@medusajs/utils"
import { asValue } from "awilix"
import {
@@ -25,7 +25,7 @@ export class GlobalWorkflow extends WorkflowManager {
if (!Array.isArray(modulesLoaded) && modulesLoaded) {
if (!("cradle" in modulesLoaded)) {
container = createContainerLike(modulesLoaded)
container = createMedusaContainer(modulesLoaded)
} else {
container = modulesLoaded
}

View File

@@ -1,5 +1,10 @@
import { Context, LoadedModule, MedusaContainer } from "@medusajs/types"
import { createContainerLike, createMedusaContainer } from "@medusajs/utils"
import {
MedusaContext,
MedusaContextType,
MedusaModuleType,
createMedusaContainer,
} from "@medusajs/utils"
import { asValue } from "awilix"
import {
DistributedTransaction,
@@ -28,6 +33,7 @@ export class LocalWorkflow {
protected customOptions: Partial<TransactionModelOptions> = {}
protected workflow: WorkflowDefinition
protected handlers: Map<string, StepHandler>
protected medusaContext?: Context
constructor(
workflowId: string,
@@ -47,9 +53,9 @@ export class LocalWorkflow {
if (!Array.isArray(modulesLoaded) && modulesLoaded) {
if (!("cradle" in modulesLoaded)) {
container = createContainerLike(modulesLoaded)
container = createMedusaContainer(modulesLoaded)
} else {
container = modulesLoaded
container = createMedusaContainer({}, modulesLoaded) // copy container
}
} else if (Array.isArray(modulesLoaded) && modulesLoaded.length) {
container = createMedusaContainer()
@@ -60,7 +66,49 @@ export class LocalWorkflow {
}
}
this.container = container
this.container = this.contextualizedMedusaModules(container)
}
private contextualizedMedusaModules(container) {
if (!container) {
return createMedusaContainer()
}
// eslint-disable-next-line
const this_ = this
const originalResolver = container.resolve
container.resolve = function (registrationName, opts) {
const resolved = originalResolver(registrationName, opts)
if (resolved?.constructor?.__type !== MedusaModuleType) {
return resolved
}
return new Proxy(resolved, {
get: function (target, prop) {
if (typeof target[prop] !== "function") {
return target[prop]
}
return async (...args) => {
const ctxIndex =
MedusaContext.getIndex(target, prop as string) ?? args.length - 1
const hasContext = args[ctxIndex]?.__type === MedusaContextType
if (!hasContext) {
const context = this_.medusaContext
if (context?.__type === MedusaContextType) {
delete context?.manager
delete context?.transactionManager
args[ctxIndex] = context
}
}
return await target[prop].apply(target, [...args])
}
},
})
}
return container
}
protected commit() {
@@ -265,7 +313,7 @@ export class LocalWorkflow {
if (this.flow.hasChanges) {
this.commit()
}
this.medusaContext = context
const { handler, orchestrator } = this.workflow
const transaction = await orchestrator.beginTransaction(
@@ -288,6 +336,7 @@ export class LocalWorkflow {
}
async getRunningTransaction(uniqueTransactionId: string, context?: Context) {
this.medusaContext = context
const { handler, orchestrator } = this.workflow
const transaction = await orchestrator.retrieveExistingTransaction(
@@ -303,6 +352,7 @@ export class LocalWorkflow {
context?: Context,
subscribe?: DistributedTransactionEvents
) {
this.medusaContext = context
const { orchestrator } = this.workflow
const transaction = await this.getRunningTransaction(
@@ -329,6 +379,7 @@ export class LocalWorkflow {
context?: Context,
subscribe?: DistributedTransactionEvents
): Promise<DistributedTransaction> {
this.medusaContext = context
const { handler, orchestrator } = this.workflow
const { cleanUpEventListeners } = this.registerEventCallbacks({
@@ -355,6 +406,7 @@ export class LocalWorkflow {
context?: Context,
subscribe?: DistributedTransactionEvents
): Promise<DistributedTransaction> {
this.medusaContext = context
const { handler, orchestrator } = this.workflow
const { cleanUpEventListeners } = this.registerEventCallbacks({

View File

@@ -81,7 +81,7 @@ export class WorkflowManager {
const finalFlow = flow instanceof OrchestratorBuilder ? flow.build() : flow
if (WorkflowManager.workflows.has(workflowId)) {
function excludeStepUuid(key, value) {
const excludeStepUuid = (key, value) => {
return key === "uuid" ? undefined : value
}

View File

@@ -7,7 +7,6 @@ import {
FindConfig,
InternalModuleDeclaration,
ModuleJoinerConfig,
MoneyAmountDTO,
PriceSetDTO,
PricingContext,
PricingFilters,
@@ -56,14 +55,14 @@ import {
PriceSetService,
RuleTypeService,
} from "@services"
import { ServiceTypes } from "@types"
import { validatePriceListDates } from "@utils"
import { CreatePriceListRuleValueDTO } from "src/types/services"
import {
LinkableKeys,
entityNameToLinkableKeysMap,
joinerConfig,
} from "../joiner-config"
import { validatePriceListDates } from "@utils"
import { ServiceTypes } from "@types"
import { CreatePriceListRuleValueDTO } from "src/types/services"
type InjectedDependencies = {
baseRepository: DAL.RepositoryService
pricingRepository: PricingRepositoryService
@@ -639,8 +638,8 @@ export default class PricingModuleService<
// Price set money amounts
let maCursor = 0
const priceSetMoneyAmountsBulkData: unknown[] =
input.flatMap(({ priceSetId, prices }) =>
const priceSetMoneyAmountsBulkData: unknown[] = input.flatMap(
({ priceSetId, prices }) =>
prices.map(() => {
const ma = createdMoneyAmounts[maCursor]
const numberOfRules = Object.entries(
@@ -654,7 +653,7 @@ export default class PricingModuleService<
rules_count: numberOfRules,
}
})
)
)
const createdPriceSetMoneyAmounts =
await this.priceSetMoneyAmountService_.create(
priceSetMoneyAmountsBulkData as ServiceTypes.CreatePriceSetMoneyAmountDTO[],
@@ -1638,7 +1637,6 @@ export default class PricingModuleService<
validatePriceListDates(updatePriceListData)
if (typeof rules === "object") {
updatePriceListData.rules_count = Object.keys(rules).length
}

View File

@@ -54,6 +54,11 @@ import {
MedusaError,
promiseAll,
} from "@medusajs/utils"
import { ProductEventData, ProductEvents } from "../types/services/product"
import {
ProductCategoryEventData,
ProductCategoryEvents,
} from "../types/services/product-category"
import {
CreateProductOptionValueDTO,
UpdateProductOptionValueDTO,
@@ -63,11 +68,6 @@ import {
joinerConfig,
LinkableKeys,
} from "./../joiner-config"
import {
ProductCategoryEventData,
ProductCategoryEvents,
} from "../types/services/product-category"
import { ProductEventData, ProductEvents } from "../types/services/product"
type InjectedDependencies = {
baseRepository: DAL.RepositoryService
@@ -897,7 +897,7 @@ export default class ProductModuleService<
@InjectManager("baseRepository_")
async create(
data: ProductTypes.CreateProductDTO[],
sharedContext?: Context
@MedusaContext() sharedContext: Context = {}
): Promise<ProductTypes.ProductDTO[]> {
const products = await this.create_(data, sharedContext)
const createdProducts = await this.baseRepository_.serialize<
@@ -1319,7 +1319,7 @@ export default class ProductModuleService<
>(
productIds: string[],
{ returnLinkableKeys }: SoftDeleteReturn<TReturnableLinkableKeys> = {},
sharedContext: Context = {}
@MedusaContext() sharedContext: Context = {}
): Promise<Record<Lowercase<keyof typeof LinkableKeys>, string[]> | void> {
const [products, cascadedEntitiesMap] = await this.softDelete_(
productIds,
@@ -1369,7 +1369,7 @@ export default class ProductModuleService<
>(
productIds: string[],
{ returnLinkableKeys }: RestoreReturn<TReturnableLinkableKeys> = {},
sharedContext: Context = {}
@MedusaContext() sharedContext: Context = {}
): Promise<Record<Lowercase<keyof typeof LinkableKeys>, string[]> | void> {
const [_, cascadedEntitiesMap] = await this.restore_(
productIds,

View File

@@ -939,7 +939,7 @@ export default class PromotionModuleService<
>(
ids: string | string[],
{ returnLinkableKeys }: SoftDeleteReturn<TReturnableLinkableKeys> = {},
sharedContext: Context = {}
@MedusaContext() sharedContext: Context = {}
): Promise<Record<Lowercase<keyof typeof LinkableKeys>, string[]> | void> {
const idsToDelete = Array.isArray(ids) ? ids : [ids]
let [_, cascadedEntitiesMap] = await this.softDelete_(
@@ -975,7 +975,7 @@ export default class PromotionModuleService<
>(
ids: string | string[],
{ returnLinkableKeys }: RestoreReturn<TReturnableLinkableKeys> = {},
sharedContext: Context = {}
@MedusaContext() sharedContext: Context = {}
): Promise<Record<Lowercase<keyof typeof LinkableKeys>, string[]> | void> {
const idsToRestore = Array.isArray(ids) ? ids : [ids]
const [_, cascadedEntitiesMap] = await this.restore_(
@@ -1376,7 +1376,7 @@ export default class PromotionModuleService<
async softDeleteCampaigns<TReturnableLinkableKeys extends string>(
ids: string | string[],
{ returnLinkableKeys }: SoftDeleteReturn<TReturnableLinkableKeys> = {},
sharedContext: Context = {}
@MedusaContext() sharedContext: Context = {}
): Promise<Record<Lowercase<keyof typeof LinkableKeys>, string[]> | void> {
const idsToDelete = Array.isArray(ids) ? ids : [ids]
let [_, cascadedEntitiesMap] = await this.softDeleteCampaigns_(
@@ -1412,7 +1412,7 @@ export default class PromotionModuleService<
>(
ids: string | string[],
{ returnLinkableKeys }: RestoreReturn<TReturnableLinkableKeys> = {},
sharedContext: Context = {}
@MedusaContext() sharedContext: Context = {}
): Promise<Record<Lowercase<keyof typeof LinkableKeys>, string[]> | void> {
const idsToRestore = Array.isArray(ids) ? ids : [ids]
const [_, cascadedEntitiesMap] = await this.restoreCampaigns_(

View File

@@ -64,7 +64,7 @@ export default class StockLocationService {
async list(
selector: FilterableStockLocationProps = {},
config: FindConfig<StockLocation> = { relations: [], skip: 0, take: 10 },
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<StockLocation[]> {
const [locations] = await this.listAndCount(selector, config, context)
return locations
@@ -80,7 +80,7 @@ export default class StockLocationService {
async listAndCount(
selector: FilterableStockLocationProps = {},
config: FindConfig<StockLocation> = { relations: [], skip: 0, take: 10 },
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<[StockLocation[], number]> {
const manager = context.transactionManager ?? this.manager_
const locationRepo = manager.getRepository(StockLocation)
@@ -119,7 +119,7 @@ export default class StockLocationService {
async retrieve(
stockLocationId: string,
config: FindConfig<StockLocation> = {},
context: SharedContext = {}
@MedusaContext() context: SharedContext = {}
): Promise<StockLocation> {
if (!isDefined(stockLocationId)) {
throw new MedusaError(

View File

@@ -22,6 +22,7 @@ export type SharedContext = {
* A context used to share resources, such as transaction manager, between the application and the module.
*/
export type Context<TManager = unknown> = {
__type?: "MedusaContext"
/**
* An instance of a transaction manager of type `TManager`, which is a typed parameter passed to the context to specify the type of the `transactionManager`.
*/
@@ -42,4 +43,9 @@ export type Context<TManager = unknown> = {
* A string indicating the ID of the current transaction.
*/
transactionId?: string
/**
* A string indicating the ID of the current request.
*/
requestId?: string
}

View File

@@ -18,8 +18,11 @@ import {
FilterQuery as MikroFilterQuery,
} from "@mikro-orm/core/typings"
import { MedusaError, isString } from "../../common"
import { MedusaContext } from "../../decorators"
import { InjectTransactionManager, buildQuery } from "../../modules-sdk"
import {
InjectTransactionManager,
MedusaContext,
buildQuery,
} from "../../modules-sdk"
import {
getSoftDeletedCascadedEntitiesIdsMappedBy,
transactionWrapper,

View File

@@ -1,5 +1,5 @@
import { Context, DAL, RepositoryTransformOptions } from "@medusajs/types"
import { MedusaContext } from "../decorators"
import { MedusaContext } from "../modules-sdk"
import { transactionWrapper } from "./utils"
class AbstractBase<T = any> {

View File

@@ -1,2 +1 @@
export * from "./context-parameter"
export * from "./inject-entity-manager"

View File

@@ -1,5 +1,7 @@
import { Context, SharedContext } from "@medusajs/types"
import { MedusaContextType } from "../modules-sdk/decorators"
// @deprecated Use InjectManager instead
export function InjectEntityManager(
shouldForceTransaction: (target: any) => boolean = () => false,
managerProperty: string | false = "manager_"
@@ -31,7 +33,7 @@ export function InjectEntityManager(
: this[managerProperty]
).transaction(
async (transactionManager) => {
args[argIndex] = args[argIndex] ?? {}
args[argIndex] = args[argIndex] ?? { __type: MedusaContextType }
args[argIndex].transactionManager = transactionManager
return await originalMethod.apply(this, args)

View File

@@ -4,13 +4,15 @@ export * from "./common"
export * from "./dal"
export * from "./decorators"
export * from "./event-bus"
export * from "./exceptions"
export * from "./feature-flags"
export * from "./modules-sdk"
export * from "./orchestration"
export * from "./payment"
export * from "./pricing"
export * from "./product"
export * from "./promotion"
export * from "./search"
export * from "./shipping"
export * from "./orchestration"
export * from "./exceptions"
export const MedusaModuleType = Symbol.for("MedusaModule")

View File

@@ -14,7 +14,7 @@ import {
shouldForceTransaction,
upperCaseFirst,
} from "../common"
import { MedusaContext } from "../decorators"
import { MedusaContext } from "../modules-sdk"
import { buildQuery } from "./build-query"
import { InjectManager, InjectTransactionManager } from "./decorators"

View File

@@ -8,3 +8,12 @@ export function MedusaContext() {
target.MedusaContextIndex_[propertyKey] = parameterIndex
}
}
MedusaContext.getIndex = function (
target: any,
propertyKey: string
): number | undefined {
return target.MedusaContextIndex_?.[propertyKey]
}
export const MedusaContextType = "MedusaContext"

View File

@@ -1,3 +1,4 @@
export * from "./context-parameter"
export * from "./inject-manager"
export * from "./inject-shared-context"
export * from "./inject-transaction-manager"

View File

@@ -1,4 +1,4 @@
import { Context, SharedContext } from "@medusajs/types"
import { Context } from "@medusajs/types"
export function InjectManager(managerProperty?: string): MethodDecorator {
return function (
@@ -16,13 +16,31 @@ export function InjectManager(managerProperty?: string): MethodDecorator {
const argIndex = target.MedusaContextIndex_[propertyKey]
descriptor.value = function (...args: any[]) {
const context: SharedContext | Context = { ...(args[argIndex] ?? {}) }
const originalContext = args[argIndex] ?? {}
const copiedContext = {} as Context
for (const key in originalContext) {
if (key === "manager" || key === "transactionManager") {
continue
}
Object.defineProperty(copiedContext, key, {
get: function () {
return originalContext[key]
},
set: function (value) {
originalContext[key] = value
},
})
}
const resourceWithManager = !managerProperty
? this
: this[managerProperty]
context.manager = context.manager ?? resourceWithManager.getFreshManager()
args[argIndex] = context
copiedContext.manager ??= resourceWithManager.getFreshManager()
copiedContext.transactionManager ??= originalContext?.transactionManager
args[argIndex] = copiedContext
return originalMethod.apply(this, args)
}

View File

@@ -1,4 +1,5 @@
import { Context, SharedContext } from "@medusajs/types"
import { MedusaContextType } from "./context-parameter"
export function InjectSharedContext(): MethodDecorator {
return function (
@@ -16,7 +17,9 @@ export function InjectSharedContext(): MethodDecorator {
const argIndex = target.MedusaContextIndex_[propertyKey]
descriptor.value = function (...args: any[]) {
const context: SharedContext | Context = { ...(args[argIndex] ?? {}) }
const context: SharedContext | Context = {
...(args[argIndex] ?? { __type: MedusaContextType }),
}
args[argIndex] = context
return originalMethod.apply(this, args)

View File

@@ -1,5 +1,6 @@
import { Context, SharedContext } from "@medusajs/types"
import { Context } from "@medusajs/types"
import { isString } from "../../common"
import { MedusaContextType } from "./context-parameter"
export function InjectTransactionManager(
shouldForceTransactionOrManagerProperty:
@@ -31,7 +32,8 @@ export function InjectTransactionManager(
const argIndex = target.MedusaContextIndex_[propertyKey]
descriptor.value = async function (...args: any[]) {
const shouldForceTransactionRes = shouldForceTransaction(target)
const context: SharedContext | Context = args[argIndex] ?? {}
const context: Context = args[argIndex] ?? {}
const originalContext = args[argIndex] ?? {}
if (!shouldForceTransactionRes && context?.transactionManager) {
return await originalMethod.apply(this, args)
@@ -42,8 +44,27 @@ export function InjectTransactionManager(
: this[managerProperty]
).transaction(
async (transactionManager) => {
args[argIndex] = { ...(args[argIndex] ?? {}) }
args[argIndex].transactionManager = transactionManager
const copiedContext = {} as Context
for (const key in originalContext) {
if (key === "manager" || key === "transactionManager") {
continue
}
Object.defineProperty(copiedContext, key, {
get: function () {
return originalContext[key]
},
set: function (value) {
originalContext[key] = value
},
})
}
copiedContext.transactionManager ??= transactionManager
copiedContext.manager ??= originalContext?.manager
copiedContext.__type ??= MedusaContextType
args[argIndex] = copiedContext
return await originalMethod.apply(this, args)
},

View File

@@ -9,6 +9,7 @@ import {
import { Context, LoadedModule, MedusaContainer } from "@medusajs/types"
import { MedusaModule } from "@medusajs/modules-sdk"
import { MedusaContextType } from "@medusajs/utils"
import { EOL } from "os"
import { ulid } from "ulid"
import { MedusaWorkflow } from "../medusa-workflow"
@@ -159,7 +160,7 @@ function createContextualWorkflowRunner<
)
}
const flow = new LocalWorkflow(workflowId, container)
const flow = new LocalWorkflow(workflowId, container!)
const originalRun = flow.run.bind(flow)
const originalRegisterStepSuccess = flow.registerStepSuccess.bind(flow)
@@ -197,7 +198,13 @@ function createContextualWorkflowRunner<
}
const newRun = async (
{ input, context, throwOnError, resultFrom, events }: FlowRunOptions = {
{
input,
context: outerContext,
throwOnError,
resultFrom,
events,
}: FlowRunOptions = {
throwOnError: true,
resultFrom: defaultResult,
}
@@ -205,6 +212,13 @@ function createContextualWorkflowRunner<
resultFrom ??= defaultResult
throwOnError ??= true
const context = {
...outerContext,
__type: MedusaContextType,
}
context.transactionId ??= ulid()
if (typeof dataPreparation === "function") {
try {
const copyInput = input ? JSON.parse(JSON.stringify(input)) : input
@@ -224,7 +238,7 @@ function createContextualWorkflowRunner<
return await originalExecution(
originalRun,
{ throwOnError, resultFrom },
context?.transactionId ?? ulid(),
context.transactionId,
input,
context,
events
@@ -236,7 +250,7 @@ function createContextualWorkflowRunner<
{
response,
idempotencyKey,
context,
context: outerContext,
throwOnError,
resultFrom,
events,
@@ -249,6 +263,13 @@ function createContextualWorkflowRunner<
resultFrom ??= defaultResult
throwOnError ??= true
const [, transactionId] = idempotencyKey.split(":")
const context = {
...outerContext,
transactionId,
__type: MedusaContextType,
}
return await originalExecution(
originalRegisterStepSuccess,
{ throwOnError, resultFrom },
@@ -264,7 +285,7 @@ function createContextualWorkflowRunner<
{
response,
idempotencyKey,
context,
context: outerContext,
throwOnError,
resultFrom,
events,
@@ -277,6 +298,13 @@ function createContextualWorkflowRunner<
resultFrom ??= defaultResult
throwOnError ??= true
const [, transactionId] = idempotencyKey.split(":")
const context = {
...outerContext,
transactionId,
__type: MedusaContextType,
}
return await originalExecution(
originalRegisterStepFailure,
{ throwOnError, resultFrom },