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:
committed by
GitHub
parent
a2bf6756ac
commit
45134e4d11
15
.changeset/loud-kiwis-promise.md
Normal file
15
.changeset/loud-kiwis-promise.md
Normal 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
|
||||
@@ -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")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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_,
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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]) {
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ declare global {
|
||||
allowedProperties: string[]
|
||||
includes?: Record<string, boolean>
|
||||
errors: string[]
|
||||
requestId?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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_(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
export * from "./context-parameter"
|
||||
export * from "./inject-entity-manager"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./context-parameter"
|
||||
export * from "./inject-manager"
|
||||
export * from "./inject-shared-context"
|
||||
export * from "./inject-transaction-manager"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
|
||||
@@ -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 },
|
||||
|
||||
Reference in New Issue
Block a user