feat(translation): Add support for locale to the graph query (#14454)

* feat(translation): Add support for locale to the graph query

* feat(translation): Add support for locale to the graph query

* feat(translation): Add support for locale to the graph query

* feat(translation): fix import

* fix

* cleanup

* fix context in product routes

* Create dull-onions-punch.md
This commit is contained in:
Adrien de Peretti
2026-01-06 12:29:27 +01:00
committed by GitHub
parent 0490a1c67f
commit 0ffd790109
20 changed files with 531 additions and 236 deletions

View File

@@ -0,0 +1,8 @@
---
"@medusajs/medusa": patch
"@medusajs/core-flows": patch
"@medusajs/modules-sdk": patch
"@medusajs/types": patch
---
feat(translation): Add support for locale to the graph query

View File

@@ -5,7 +5,6 @@ import {
RemoteQueryFunction, RemoteQueryFunction,
} from "@medusajs/framework/types" } from "@medusajs/framework/types"
import { import {
applyTranslations,
ContainerRegistrationKeys, ContainerRegistrationKeys,
deduplicate, deduplicate,
FeatureFlag, FeatureFlag,
@@ -122,17 +121,16 @@ export const updateCartItemsTranslationsStep = createStep(
}) })
} }
const { data: variants } = await query.graph({ const { data: variants } = await query.graph(
entity: "variants", {
filters: { id: variantIds }, entity: "variants",
fields: productVariantsFields, filters: { id: variantIds },
}) fields: productVariantsFields,
},
await applyTranslations({ {
localeCode: data.locale, locale: data.locale,
objects: variants as Record<string, any>[], }
container, )
})
const translatedItems = applyTranslationsToItems( const translatedItems = applyTranslationsToItems(
items as { variant_id?: string; [key: string]: any }[], items as { variant_id?: string; [key: string]: any }[],

View File

@@ -43,6 +43,5 @@ const step = createStep(
*/ */
export const getTranslatedLineItemsStep = <T>( export const getTranslatedLineItemsStep = <T>(
data: GetTranslatedLineItemsStepInput<T> data: GetTranslatedLineItemsStepInput<T>
): ReturnType<StepFunction<any, T[]>> => step(data) as unknown as ReturnType< ): ReturnType<StepFunction<any, T[]>> =>
StepFunction<any, T[]> step(data) as unknown as ReturnType<StepFunction<any, T[]>>
>

View File

@@ -5,7 +5,6 @@ import {
RemoteQueryFunction, RemoteQueryFunction,
} from "@medusajs/framework/types" } from "@medusajs/framework/types"
import { import {
applyTranslations,
ContainerRegistrationKeys, ContainerRegistrationKeys,
deduplicate, deduplicate,
FeatureFlag, FeatureFlag,
@@ -137,17 +136,16 @@ export const updateOrderItemsTranslationsStep = createStep(
}) })
} }
const { data: variants } = await query.graph({ const { data: variants } = await query.graph(
entity: "variants", {
filters: { id: variantIds }, entity: "variants",
fields: productVariantsFields, filters: { id: variantIds },
}) fields: productVariantsFields,
},
await applyTranslations({ {
localeCode: data.locale, locale: data.locale,
objects: variants as Record<string, any>[], }
container, )
})
const translatedItems = applyTranslationsToItems( const translatedItems = applyTranslationsToItems(
items as { variant_id?: string; [key: string]: any }[], items as { variant_id?: string; [key: string]: any }[],

View File

@@ -1,5 +1,4 @@
import { import {
applyTranslations,
ContainerRegistrationKeys, ContainerRegistrationKeys,
FeatureFlag, FeatureFlag,
Modules, Modules,
@@ -30,19 +29,18 @@ export const updateOrderShippingMethodsTranslationsStep = createStep(
const query = container.resolve(ContainerRegistrationKeys.QUERY) const query = container.resolve(ContainerRegistrationKeys.QUERY)
const orderModuleService = container.resolve(Modules.ORDER) const orderModuleService = container.resolve(Modules.ORDER)
const { data: translatedShippingOptions } = await query.graph({ const { data: translatedShippingOptions } = await query.graph(
entity: "shipping_option", {
fields: ["id", "name"], entity: "shipping_option",
filters: { fields: ["id", "name"],
id: data.shippingMethods.map((sm) => sm.shipping_option_id), filters: {
id: data.shippingMethods.map((sm) => sm.shipping_option_id),
},
}, },
}) {
locale: data.locale,
await applyTranslations({ }
localeCode: data.locale, )
objects: translatedShippingOptions,
container,
})
const shippingOptionTranslationMap = new Map<string, string>( const shippingOptionTranslationMap = new Map<string, string>(
translatedShippingOptions.map((tos) => [tos.id, tos.name]) translatedShippingOptions.map((tos) => [tos.id, tos.name])

View File

@@ -0,0 +1,301 @@
import { MedusaContainer } from "@medusajs/types"
import * as utils from "@medusajs/utils"
import { Query } from "../query"
jest.mock("@medusajs/utils", () => ({
...jest.requireActual("@medusajs/utils"),
applyTranslations: jest.fn(),
}))
const mockApplyTranslations = utils.applyTranslations as jest.Mock
function createMockRemoteQuery(queryResult: any = []) {
return {
query: jest.fn().mockResolvedValue(queryResult),
getEntitiesMap: jest.fn().mockReturnValue(new Map()),
}
}
function createMockContainer(): MedusaContainer {
return {
resolve: jest.fn(),
} as unknown as MedusaContainer
}
describe("Query.graph locale integration", () => {
beforeEach(() => {
jest.clearAllMocks()
})
describe("when locale option is provided", () => {
it("should call applyTranslations with the correct locale code", async () => {
const mockRemoteQuery = createMockRemoteQuery([
{ id: "prod_1", title: "Test" },
])
const mockContainer = createMockContainer()
const query = new Query({
remoteQuery: mockRemoteQuery as any,
indexModule: null as any,
container: mockContainer,
})
await query.graph(
{ entity: "product", fields: ["id", "title"] },
{ locale: "fr-FR" }
)
expect(mockApplyTranslations).toHaveBeenCalledTimes(1)
expect(mockApplyTranslations).toHaveBeenCalledWith({
localeCode: "fr-FR",
objects: expect.any(Array),
container: mockContainer,
})
})
it("should call applyTranslations with the result data array", async () => {
const resultData = [
{ id: "prod_1", title: "Product 1" },
{ id: "prod_2", title: "Product 2" },
]
const mockRemoteQuery = createMockRemoteQuery(resultData)
const mockContainer = createMockContainer()
const query = new Query({
remoteQuery: mockRemoteQuery as any,
indexModule: null as any,
container: mockContainer,
})
await query.graph(
{ entity: "product", fields: ["id", "title"] },
{ locale: "en-US" }
)
expect(mockApplyTranslations).toHaveBeenCalledWith(
expect.objectContaining({
objects: resultData,
})
)
})
it("should call applyTranslations with the container instance", async () => {
const mockRemoteQuery = createMockRemoteQuery([])
const mockContainer = createMockContainer()
const query = new Query({
remoteQuery: mockRemoteQuery as any,
indexModule: null as any,
container: mockContainer,
})
await query.graph(
{ entity: "product", fields: ["id"] },
{ locale: "de-DE" }
)
expect(mockApplyTranslations).toHaveBeenCalledWith(
expect.objectContaining({
container: mockContainer,
})
)
})
it("should call applyTranslations for paginated results", async () => {
const paginatedResult = {
rows: [{ id: "prod_1" }, { id: "prod_2" }],
metadata: { skip: 0, take: 10, count: 2 },
}
const mockRemoteQuery = createMockRemoteQuery(paginatedResult)
const mockContainer = createMockContainer()
const query = new Query({
remoteQuery: mockRemoteQuery as any,
indexModule: null as any,
container: mockContainer,
})
await query.graph(
{
entity: "product",
fields: ["id"],
pagination: { skip: 0, take: 10 },
},
{ locale: "es-ES" }
)
expect(mockApplyTranslations).toHaveBeenCalledWith(
expect.objectContaining({
objects: paginatedResult.rows,
})
)
})
})
describe("when locale option is NOT provided", () => {
it("should NOT call applyTranslations when options is undefined", async () => {
const mockRemoteQuery = createMockRemoteQuery([{ id: "prod_1" }])
const mockContainer = createMockContainer()
const query = new Query({
remoteQuery: mockRemoteQuery as any,
indexModule: null as any,
container: mockContainer,
})
await query.graph({ entity: "product", fields: ["id"] })
expect(mockApplyTranslations).not.toHaveBeenCalled()
})
it("should NOT call applyTranslations when options is an empty object", async () => {
const mockRemoteQuery = createMockRemoteQuery([{ id: "prod_1" }])
const mockContainer = createMockContainer()
const query = new Query({
remoteQuery: mockRemoteQuery as any,
indexModule: null as any,
container: mockContainer,
})
await query.graph({ entity: "product", fields: ["id"] }, {})
expect(mockApplyTranslations).not.toHaveBeenCalled()
})
it("should NOT call applyTranslations when locale is explicitly undefined", async () => {
const mockRemoteQuery = createMockRemoteQuery([{ id: "prod_1" }])
const mockContainer = createMockContainer()
const query = new Query({
remoteQuery: mockRemoteQuery as any,
indexModule: null as any,
container: mockContainer,
})
await query.graph(
{ entity: "product", fields: ["id"] },
{ locale: undefined }
)
expect(mockApplyTranslations).not.toHaveBeenCalled()
})
it("should NOT call applyTranslations when other options are provided but locale is missing", async () => {
const mockRemoteQuery = createMockRemoteQuery([{ id: "prod_1" }])
const mockContainer = createMockContainer()
const query = new Query({
remoteQuery: mockRemoteQuery as any,
indexModule: null as any,
container: mockContainer,
})
await query.graph(
{ entity: "product", fields: ["id"] },
{ throwIfKeyNotFound: true }
)
expect(mockApplyTranslations).not.toHaveBeenCalled()
})
})
describe("applyTranslations parameter validation", () => {
it("should pass empty array to applyTranslations when query returns empty array", async () => {
const mockRemoteQuery = createMockRemoteQuery([])
const mockContainer = createMockContainer()
const query = new Query({
remoteQuery: mockRemoteQuery as any,
indexModule: null as any,
container: mockContainer,
})
await query.graph(
{ entity: "product", fields: ["id"] },
{ locale: "fr-FR" }
)
expect(mockApplyTranslations).toHaveBeenCalledWith({
localeCode: "fr-FR",
objects: [],
container: mockContainer,
})
})
it("should preserve all three parameters correctly", async () => {
const resultData = [{ id: "test_1" }]
const mockRemoteQuery = createMockRemoteQuery(resultData)
const mockContainer = createMockContainer()
const query = new Query({
remoteQuery: mockRemoteQuery as any,
indexModule: null as any,
container: mockContainer,
})
await query.graph(
{ entity: "product", fields: ["id"] },
{ locale: "pt-BR" }
)
const callArgs = mockApplyTranslations.mock.calls[0][0]
expect(callArgs).toHaveProperty("localeCode", "pt-BR")
expect(callArgs).toHaveProperty("objects", resultData)
expect(callArgs).toHaveProperty("container", mockContainer)
})
it("should work with different locale formats", async () => {
const mockRemoteQuery = createMockRemoteQuery([{ id: "prod_1" }])
const mockContainer = createMockContainer()
const query = new Query({
remoteQuery: mockRemoteQuery as any,
indexModule: null as any,
container: mockContainer,
})
const locales = ["en", "en-US", "zh-Hans-CN", "pt-BR"]
for (const locale of locales) {
jest.clearAllMocks()
await query.graph({ entity: "product", fields: ["id"] }, { locale })
expect(mockApplyTranslations).toHaveBeenCalledWith(
expect.objectContaining({ localeCode: locale })
)
}
})
})
describe("return value behavior with locale", () => {
it("should return the result after applyTranslations is called", async () => {
const resultData = [{ id: "prod_1", title: "Original" }]
const mockRemoteQuery = createMockRemoteQuery(resultData)
const mockContainer = createMockContainer()
const query = new Query({
remoteQuery: mockRemoteQuery as any,
indexModule: null as any,
container: mockContainer,
})
const result = await query.graph(
{ entity: "product", fields: ["id", "title"] },
{ locale: "fr-FR" }
)
expect(result).toEqual({
data: resultData,
metadata: undefined,
})
})
it("should return the same reference that was passed to applyTranslations", async () => {
const resultData = [{ id: "prod_1" }]
const mockRemoteQuery = createMockRemoteQuery(resultData)
const mockContainer = createMockContainer()
const query = new Query({
remoteQuery: mockRemoteQuery as any,
indexModule: null as any,
container: mockContainer,
})
const result = await query.graph(
{ entity: "product", fields: ["id"] },
{ locale: "de-DE" }
)
const passedObjects = mockApplyTranslations.mock.calls[0][0].objects
expect(result.data).toBe(passedObjects)
})
})
})

View File

@@ -14,6 +14,7 @@ import {
import { import {
Cached, Cached,
MedusaError, MedusaError,
applyTranslations,
isObject, isObject,
remoteQueryObjectFromString, remoteQueryObjectFromString,
unflattenObjectKeys, unflattenObjectKeys,
@@ -238,7 +239,17 @@ export class Query {
) )
} }
return this.#unwrapRemoteQueryResponse(response) const result = this.#unwrapRemoteQueryResponse(response)
if (options?.locale) {
await applyTranslations({
localeCode: options.locale,
objects: result.data,
container: this.container,
})
}
return result
} }
/** /**
@@ -309,6 +320,14 @@ export class Query {
}) })
} }
if (options?.locale) {
await applyTranslations({
localeCode: options.locale,
objects: finalResultset.data,
container: this.container,
})
}
return { return {
data: finalResultset.data, data: finalResultset.data,
metadata: indexResponse.metadata as RemoteQueryFunctionReturnPagination, metadata: indexResponse.metadata as RemoteQueryFunctionReturnPagination,

View File

@@ -94,6 +94,11 @@ export interface RemoteJoinerOptions {
throwIfRelationNotFound?: boolean | string[] throwIfRelationNotFound?: boolean | string[]
initialData?: object | object[] initialData?: object | object[]
initialDataOnly?: boolean initialDataOnly?: boolean
/**
* The locale to use for the query.
* Translation will be applied to the query result based on the locale.
*/
locale?: string
cache?: { cache?: {
/** /**
* Whether to enable the cache. This is only useful if you want to enable without providing any * Whether to enable the cache. This is only useful if you want to enable without providing any

View File

@@ -1,26 +1,37 @@
import { HttpTypes } from "@medusajs/framework/types"
import { import {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
MedusaResponse, MedusaResponse,
} from "@medusajs/framework/http" } from "@medusajs/framework/http"
import { refetchCollection } from "../helpers" import { HttpTypes } from "@medusajs/framework/types"
import { applyTranslations } from "@medusajs/framework/utils" import {
ContainerRegistrationKeys,
MedusaError,
} from "@medusajs/framework/utils"
export const GET = async ( export const GET = async (
req: AuthenticatedMedusaRequest<HttpTypes.SelectParams>, req: AuthenticatedMedusaRequest<HttpTypes.SelectParams>,
res: MedusaResponse<HttpTypes.StoreCollectionResponse> res: MedusaResponse<HttpTypes.StoreCollectionResponse>
) => { ) => {
const collection = await refetchCollection( const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
req.params.id,
req.scope, const { data: collections } = await query.graph(
req.queryConfig.fields {
entity: "product_collection",
filters: { id: req.params.id },
fields: req.queryConfig.fields,
},
{
locale: req.locale,
}
) )
await applyTranslations({ const collection = collections[0]
localeCode: req.locale, if (!collection) {
objects: [collection], throw new MedusaError(
container: req.scope, MedusaError.Types.NOT_FOUND,
}) `Collection with id: ${req.params.id} was not found`
)
}
res.status(200).json({ collection }) res.status(200).json({ collection: collection })
} }

View File

@@ -1,42 +1,33 @@
import { HttpTypes } from "@medusajs/framework/types"
import { import {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
MedusaResponse, MedusaResponse,
} from "@medusajs/framework/http" } from "@medusajs/framework/http"
import { HttpTypes } from "@medusajs/framework/types"
import { import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
applyTranslations,
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/framework/utils"
export const GET = async ( export const GET = async (
req: AuthenticatedMedusaRequest<HttpTypes.StoreCollectionListParams>, req: AuthenticatedMedusaRequest<HttpTypes.StoreCollectionListParams>,
res: MedusaResponse<HttpTypes.StoreCollectionListResponse> res: MedusaResponse<HttpTypes.StoreCollectionListResponse>
) => { ) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
const query = remoteQueryObjectFromString({ const { data: collections, metadata } = await query.graph(
entryPoint: "product_collection", {
variables: { entity: "product_collection",
filters: req.filterableFields, filters: req.filterableFields,
...req.queryConfig.pagination, pagination: req.queryConfig.pagination,
fields: req.queryConfig.fields,
}, },
fields: req.queryConfig.fields, {
}) locale: req.locale,
}
const { rows: collections, metadata } = await remoteQuery(query) )
await applyTranslations({
localeCode: req.locale,
objects: collections,
container: req.scope,
})
res.json({ res.json({
collections, collections,
count: metadata.count, count: metadata!.count,
offset: metadata.skip, offset: metadata!.skip,
limit: metadata.take, limit: metadata!.take,
}) })
} }

View File

@@ -1,22 +1,30 @@
import { StoreProductCategoryResponse } from "@medusajs/framework/types"
import { applyTranslations, MedusaError } from "@medusajs/framework/utils"
import { import {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
MedusaResponse, MedusaResponse,
refetchEntity,
} from "@medusajs/framework/http" } from "@medusajs/framework/http"
import { StoreProductCategoryResponse } from "@medusajs/framework/types"
import {
ContainerRegistrationKeys,
MedusaError,
} from "@medusajs/framework/utils"
import { StoreProductCategoryParamsType } from "../validators" import { StoreProductCategoryParamsType } from "../validators"
export const GET = async ( export const GET = async (
req: AuthenticatedMedusaRequest<StoreProductCategoryParamsType>, req: AuthenticatedMedusaRequest<StoreProductCategoryParamsType>,
res: MedusaResponse<StoreProductCategoryResponse> res: MedusaResponse<StoreProductCategoryResponse>
) => { ) => {
const category = await refetchEntity({ const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
entity: "product_category",
idOrFilter: { id: req.params.id, ...req.filterableFields }, const { data: category } = await query.graph(
scope: req.scope, {
fields: req.queryConfig.fields, entity: "product_category",
}) filters: { id: req.params.id, ...req.filterableFields },
fields: req.queryConfig.fields,
},
{
locale: req.locale,
}
)
if (!category) { if (!category) {
throw new MedusaError( throw new MedusaError(
@@ -25,11 +33,5 @@ export const GET = async (
) )
} }
await applyTranslations({ res.json({ product_category: category[0] })
localeCode: req.locale,
objects: [category],
container: req.scope,
})
res.json({ product_category: category })
} }

View File

@@ -6,10 +6,7 @@ import {
StoreProductCategoryListParams, StoreProductCategoryListParams,
StoreProductCategoryListResponse, StoreProductCategoryListResponse,
} from "@medusajs/framework/types" } from "@medusajs/framework/types"
import { import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
applyTranslations,
ContainerRegistrationKeys,
} from "@medusajs/framework/utils"
export const GET = async ( export const GET = async (
req: AuthenticatedMedusaRequest<StoreProductCategoryListParams>, req: AuthenticatedMedusaRequest<StoreProductCategoryListParams>,
@@ -17,18 +14,17 @@ export const GET = async (
) => { ) => {
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY) const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
const { data: product_categories, metadata } = await query.graph({ const { data: product_categories, metadata } = await query.graph(
entity: "product_category", {
fields: req.queryConfig.fields, entity: "product_category",
filters: req.filterableFields, fields: req.queryConfig.fields,
pagination: req.queryConfig.pagination, filters: req.filterableFields,
}) pagination: req.queryConfig.pagination,
},
await applyTranslations({ {
localeCode: req.locale, locale: req.locale,
objects: product_categories, }
container: req.scope, )
})
res.json({ res.json({
product_categories, product_categories,

View File

@@ -1,13 +1,12 @@
import { StoreProductTagResponse } from "@medusajs/framework/types"
import {
applyTranslations,
ContainerRegistrationKeys,
MedusaError,
} from "@medusajs/framework/utils"
import { import {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
MedusaResponse, MedusaResponse,
} from "@medusajs/framework/http" } from "@medusajs/framework/http"
import { StoreProductTagResponse } from "@medusajs/framework/types"
import {
ContainerRegistrationKeys,
MedusaError,
} from "@medusajs/framework/utils"
import { StoreProductTagParamsType } from "../validators" import { StoreProductTagParamsType } from "../validators"
@@ -17,13 +16,18 @@ export const GET = async (
) => { ) => {
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY) const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
const { data } = await query.graph({ const { data } = await query.graph(
entity: "product_tag", {
filters: { entity: "product_tag",
id: req.params.id, filters: {
id: req.params.id,
},
fields: req.queryConfig.fields,
}, },
fields: req.queryConfig.fields, {
}) locale: req.locale,
}
)
if (!data.length) { if (!data.length) {
throw new MedusaError( throw new MedusaError(
@@ -34,11 +38,5 @@ export const GET = async (
const productTag = data[0] const productTag = data[0]
await applyTranslations({
localeCode: req.locale,
objects: [productTag],
container: req.scope,
})
res.json({ product_tag: productTag }) res.json({ product_tag: productTag })
} }

View File

@@ -1,12 +1,9 @@
import { HttpTypes } from "@medusajs/framework/types"
import {
applyTranslations,
ContainerRegistrationKeys,
} from "@medusajs/framework/utils"
import { import {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
MedusaResponse, MedusaResponse,
} from "@medusajs/framework/http" } from "@medusajs/framework/http"
import { HttpTypes } from "@medusajs/framework/types"
import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
export const GET = async ( export const GET = async (
req: AuthenticatedMedusaRequest<HttpTypes.StoreProductTagListParams>, req: AuthenticatedMedusaRequest<HttpTypes.StoreProductTagListParams>,
@@ -14,18 +11,17 @@ export const GET = async (
) => { ) => {
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY) const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
const { data: product_tags, metadata } = await query.graph({ const { data: product_tags, metadata } = await query.graph(
entity: "product_tag", {
filters: req.filterableFields, entity: "product_tag",
pagination: req.queryConfig.pagination, filters: req.filterableFields,
fields: req.queryConfig.fields, pagination: req.queryConfig.pagination,
}) fields: req.queryConfig.fields,
},
await applyTranslations({ {
localeCode: req.locale, locale: req.locale,
objects: product_tags, }
container: req.scope, )
})
res.json({ res.json({
product_tags, product_tags,

View File

@@ -1,13 +1,12 @@
import { StoreProductTypeResponse } from "@medusajs/framework/types"
import {
applyTranslations,
ContainerRegistrationKeys,
MedusaError,
} from "@medusajs/framework/utils"
import { import {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
MedusaResponse, MedusaResponse,
} from "@medusajs/framework/http" } from "@medusajs/framework/http"
import { StoreProductTypeResponse } from "@medusajs/framework/types"
import {
ContainerRegistrationKeys,
MedusaError,
} from "@medusajs/framework/utils"
import { StoreProductTypeParamsType } from "../validators" import { StoreProductTypeParamsType } from "../validators"
@@ -17,13 +16,18 @@ export const GET = async (
) => { ) => {
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY) const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
const { data } = await query.graph({ const { data } = await query.graph(
entity: "product_type", {
filters: { entity: "product_type",
id: req.params.id, filters: {
id: req.params.id,
},
fields: req.queryConfig.fields,
}, },
fields: req.queryConfig.fields, {
}) locale: req.locale,
}
)
if (!data.length) { if (!data.length) {
throw new MedusaError( throw new MedusaError(
@@ -34,11 +38,5 @@ export const GET = async (
const productType = data[0] const productType = data[0]
await applyTranslations({
localeCode: req.locale,
objects: [productType],
container: req.scope,
})
res.json({ product_type: productType }) res.json({ product_type: productType })
} }

View File

@@ -1,12 +1,9 @@
import { HttpTypes } from "@medusajs/framework/types"
import {
applyTranslations,
ContainerRegistrationKeys,
} from "@medusajs/framework/utils"
import { import {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
MedusaResponse, MedusaResponse,
} from "@medusajs/framework/http" } from "@medusajs/framework/http"
import { HttpTypes } from "@medusajs/framework/types"
import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
export const GET = async ( export const GET = async (
req: AuthenticatedMedusaRequest<HttpTypes.StoreProductTypeListParams>, req: AuthenticatedMedusaRequest<HttpTypes.StoreProductTypeListParams>,
@@ -14,18 +11,17 @@ export const GET = async (
) => { ) => {
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY) const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
const { data: product_types, metadata } = await query.graph({ const { data: product_types, metadata } = await query.graph(
entity: "product_type", {
filters: req.filterableFields, entity: "product_type",
pagination: req.queryConfig.pagination, filters: req.filterableFields,
fields: req.queryConfig.fields, pagination: req.queryConfig.pagination,
}) fields: req.queryConfig.fields,
},
await applyTranslations({ {
localeCode: req.locale, locale: req.locale,
objects: product_types, }
container: req.scope, )
})
res.json({ res.json({
product_types, product_types,

View File

@@ -4,7 +4,6 @@ import {
} from "@medusajs/framework/http" } from "@medusajs/framework/http"
import { HttpTypes, QueryContextType } from "@medusajs/framework/types" import { HttpTypes, QueryContextType } from "@medusajs/framework/types"
import { import {
applyTranslations,
ContainerRegistrationKeys, ContainerRegistrationKeys,
MedusaError, MedusaError,
QueryContext, QueryContext,
@@ -42,15 +41,20 @@ export const GET = async (
context["calculated_price"] = QueryContext(req.pricingContext) context["calculated_price"] = QueryContext(req.pricingContext)
} }
const { data: variants = [] } = await query.graph({ const { data: variants = [] } = await query.graph(
entity: "variant", {
filters: { entity: "variant",
...req.filterableFields, filters: {
id: req.params.id, ...req.filterableFields,
id: req.params.id,
},
fields: req.queryConfig.fields,
context,
}, },
fields: req.queryConfig.fields, {
context, locale: req.locale,
}) }
)
const variant = variants[0] const variant = variants[0]
@@ -61,12 +65,6 @@ export const GET = async (
) )
} }
await applyTranslations({
localeCode: req.locale,
objects: [variant],
container: req.scope,
})
if (withInventoryQuantity) { if (withInventoryQuantity) {
await wrapVariantsWithInventoryQuantityForSalesChannel(req, [variant]) await wrapVariantsWithInventoryQuantityForSalesChannel(req, [variant])
} }

View File

@@ -4,7 +4,6 @@ import {
} from "@medusajs/framework/http" } from "@medusajs/framework/http"
import { HttpTypes, QueryContextType } from "@medusajs/framework/types" import { HttpTypes, QueryContextType } from "@medusajs/framework/types"
import { import {
applyTranslations,
ContainerRegistrationKeys, ContainerRegistrationKeys,
QueryContext, QueryContext,
} from "@medusajs/framework/utils" } from "@medusajs/framework/utils"
@@ -13,8 +12,7 @@ import { StoreRequestWithContext } from "../types"
import { wrapVariantsWithTaxPrices } from "./helpers" import { wrapVariantsWithTaxPrices } from "./helpers"
type StoreVariantListRequest<T = HttpTypes.StoreProductVariantParams> = type StoreVariantListRequest<T = HttpTypes.StoreProductVariantParams> =
StoreRequestWithContext<T> & StoreRequestWithContext<T> & AuthenticatedMedusaRequest<T>
AuthenticatedMedusaRequest<T>
/** /**
* @since 2.11.2 * @since 2.11.2
@@ -52,15 +50,10 @@ export const GET = async (
cache: { cache: {
enable: true, enable: true,
}, },
locale: req.locale,
} }
) )
await applyTranslations({
localeCode: req.locale,
objects: variants,
container: req.scope,
})
if (withInventoryQuantity) { if (withInventoryQuantity) {
await wrapVariantsWithInventoryQuantityForSalesChannel(req, variants) await wrapVariantsWithInventoryQuantityForSalesChannel(req, variants)
} }

View File

@@ -1,15 +1,13 @@
import { MedusaResponse } from "@medusajs/framework/http" import { MedusaResponse } from "@medusajs/framework/http"
import { HttpTypes } from "@medusajs/framework/types" import { HttpTypes, QueryContextType } from "@medusajs/framework/types"
import { import {
applyTranslations, ContainerRegistrationKeys,
isPresent,
MedusaError, MedusaError,
QueryContext, QueryContext,
} from "@medusajs/framework/utils" } from "@medusajs/framework/utils"
import { wrapVariantsWithInventoryQuantityForSalesChannel } from "../../../utils/middlewares" import { wrapVariantsWithInventoryQuantityForSalesChannel } from "../../../utils/middlewares"
import { import {
filterOutInternalProductCategories, filterOutInternalProductCategories,
refetchProduct,
RequestWithContext, RequestWithContext,
wrapProductsWithTaxPrices, wrapProductsWithTaxPrices,
} from "../helpers" } from "../helpers"
@@ -33,12 +31,11 @@ export const GET = async (
...req.filterableFields, ...req.filterableFields,
} }
if (isPresent(req.pricingContext)) { const context: QueryContextType = {}
filters["context"] ??= {}
filters["context"]["variants"] ??= {} if (req.pricingContext) {
filters["context"]["variants"]["calculated_price"] ??= QueryContext( context["variants"] ??= {}
req.pricingContext! context["variants"]["calculated_price"] ??= QueryContext(req.pricingContext)
)
} }
const includesCategoriesField = req.queryConfig.fields.some((field) => const includesCategoriesField = req.queryConfig.fields.some((field) =>
@@ -49,11 +46,20 @@ export const GET = async (
req.queryConfig.fields.push("categories.is_internal") req.queryConfig.fields.push("categories.is_internal")
} }
const product = await refetchProduct( const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
filters,
req.scope, const { data: products } = await query.graph(
req.queryConfig.fields {
entity: "product",
filters,
context,
fields: req.queryConfig.fields,
},
{
locale: req.locale,
}
) )
const product = products[0]
if (!product) { if (!product) {
throw new MedusaError( throw new MedusaError(
@@ -74,10 +80,5 @@ export const GET = async (
} }
await wrapProductsWithTaxPrices(req, [product]) await wrapProductsWithTaxPrices(req, [product])
await applyTranslations({
localeCode: req.locale,
objects: [product],
container: req.scope,
})
res.json({ product }) res.json({ product })
} }

View File

@@ -1,7 +1,6 @@
import { MedusaResponse } from "@medusajs/framework/http" import { MedusaResponse } from "@medusajs/framework/http"
import { HttpTypes, QueryContextType } from "@medusajs/framework/types" import { HttpTypes, QueryContextType } from "@medusajs/framework/types"
import { import {
applyTranslations,
ContainerRegistrationKeys, ContainerRegistrationKeys,
FeatureFlag, FeatureFlag,
isPresent, isPresent,
@@ -76,6 +75,7 @@ async function getProductsWithIndexEngine(
cache: { cache: {
enable: true, enable: true,
}, },
locale: req.locale,
} }
) )
@@ -88,12 +88,6 @@ async function getProductsWithIndexEngine(
await wrapProductsWithTaxPrices(req, products) await wrapProductsWithTaxPrices(req, products)
await applyTranslations({
localeCode: req.locale,
objects: products,
container: req.scope,
})
res.json({ res.json({
products, products,
count: metadata!.estimate_count, count: metadata!.estimate_count,
@@ -138,6 +132,7 @@ async function getProducts(
cache: { cache: {
enable: true, enable: true,
}, },
locale: req.locale,
} }
) )
@@ -150,12 +145,6 @@ async function getProducts(
await wrapProductsWithTaxPrices(req, products) await wrapProductsWithTaxPrices(req, products)
await applyTranslations({
localeCode: req.locale,
objects: products,
container: req.scope,
})
res.json({ res.json({
products, products,
count: metadata!.count, count: metadata!.count,