diff --git a/packages/core/framework/src/config/types.ts b/packages/core/framework/src/config/types.ts index 872030ee6e..15278a3bcc 100644 --- a/packages/core/framework/src/config/types.ts +++ b/packages/core/framework/src/config/types.ts @@ -730,6 +730,27 @@ export type ProjectConfigOptions = { * ``` */ authMethodsPerActor?: Record + + /** + * Specifies the fields that can't be selected in the response unless specified in the allowed query config. + * This is useful to restrict sensitive fields from being exposed in the API. + * + * @example + * + * ```js title="medusa-config.js" + * module.exports = defineConfig({ + * projectConfig: { + * http: { + * restrictedFields: { + * store: ["order", "orders"], + * } + * } + * ``` + */ + restrictedFields?: { + store?: string[] + /*admin?: string[]*/ + } } } diff --git a/packages/core/framework/src/http/__tests__/validate-query.spec.ts b/packages/core/framework/src/http/__tests__/validate-query.spec.ts index 1add3c87a6..b65e59727a 100644 --- a/packages/core/framework/src/http/__tests__/validate-query.spec.ts +++ b/packages/core/framework/src/http/__tests__/validate-query.spec.ts @@ -2,6 +2,8 @@ import z from "zod" import { MedusaError } from "@medusajs/utils" import { validateAndTransformQuery } from "../utils/validate-query" import { MedusaNextFunction, MedusaRequest, MedusaResponse } from "../types" +import { RestrictedFields } from "../utils/restricted-fields" +import { QueryConfig } from "@medusajs/types" export const createSelectParams = () => { return z.object({ @@ -60,6 +62,7 @@ describe("validateAndTransformQuery", () => { it("should transform the input query", async () => { let mockRequest = { + restrictedFields: new RestrictedFields(), query: {}, } as MedusaRequest const mockResponse = {} as MedusaResponse @@ -148,6 +151,7 @@ describe("validateAndTransformQuery", () => { }) mockRequest = { + ...mockRequest, query: { limit: "10", offset: "5", @@ -167,6 +171,7 @@ describe("validateAndTransformQuery", () => { }) mockRequest = { + ...mockRequest, query: { limit: "10", offset: "5", @@ -202,6 +207,7 @@ describe("validateAndTransformQuery", () => { it("should transform the input query taking into account the fields symbols (+,- or no symbol)", async () => { let mockRequest = { + restrictedFields: new RestrictedFields(), query: { fields: "id", }, @@ -234,6 +240,7 @@ describe("validateAndTransformQuery", () => { ) mockRequest = { + ...mockRequest, query: { fields: "+test_prop,-prop-test-something", }, @@ -275,6 +282,7 @@ describe("validateAndTransformQuery", () => { ) mockRequest = { + ...mockRequest, query: { fields: "+test_prop,-updated_at", }, @@ -315,11 +323,16 @@ describe("validateAndTransformQuery", () => { }) it(`should transform the input and manage the allowed fields and relations properly without error`, async () => { + const restrictedFields = new RestrictedFields() let mockRequest = { + restrictedFields, query: { - fields: "*product.variants,+product.id", + fields: "product.*, *product.variants,+product.id", }, } as unknown as MedusaRequest + + restrictedFields.add(["product"]) + const mockResponse = {} as MedusaResponse const nextFunction: MedusaNextFunction = jest.fn() @@ -385,12 +398,14 @@ describe("validateAndTransformQuery", () => { "metadata.children.id", "metadata.product.id", "product.id", + "product.*", "product.variants.*", ], }) ) mockRequest = { + ...mockRequest, query: { fields: "store.name", }, @@ -441,6 +456,7 @@ describe("validateAndTransformQuery", () => { it("should throw when attempting to transform the input if disallowed fields are requested", async () => { let mockRequest = { + restrictedFields: new RestrictedFields(), query: { fields: "+test_prop", }, @@ -459,12 +475,6 @@ describe("validateAndTransformQuery", () => { "metadata.children.id", "metadata.product.id", ], - defaultRelations: [ - "metadata", - "metadata.parent", - "metadata.children", - "metadata.product", - ], allowed: [ "id", "created_at", @@ -490,6 +500,7 @@ describe("validateAndTransformQuery", () => { ) mockRequest = { + ...mockRequest, query: { fields: "product", }, @@ -506,12 +517,6 @@ describe("validateAndTransformQuery", () => { "metadata.children.id", "metadata.product.id", ], - defaultRelations: [ - "metadata", - "metadata.parent", - "metadata.children", - "metadata.product", - ], allowed: [ "id", "created_at", @@ -537,6 +542,7 @@ describe("validateAndTransformQuery", () => { ) mockRequest = { + ...mockRequest, query: { fields: "store", }, @@ -580,6 +586,7 @@ describe("validateAndTransformQuery", () => { ) mockRequest = { + ...mockRequest, query: { fields: "*product", }, @@ -621,6 +628,7 @@ describe("validateAndTransformQuery", () => { ) mockRequest = { + ...mockRequest, query: { fields: "*product.variants", }, @@ -663,6 +671,7 @@ describe("validateAndTransformQuery", () => { ) mockRequest = { + ...mockRequest, query: { fields: "*product", }, @@ -704,4 +713,47 @@ describe("validateAndTransformQuery", () => { ) ) }) + + it("should throw when attempting to transform the input if restricted fields are requested", async () => { + const restrictedFields = new RestrictedFields() + let mockRequest = { + restrictedFields, + query: { + fields: "*product", + }, + } as unknown as MedusaRequest + + restrictedFields.add(["product"]) + + const mockResponse = {} as MedusaResponse + const nextFunction: MedusaNextFunction = jest.fn() + + const queryConfig: QueryConfig = { + defaults: [ + "id", + "created_at", + "updated_at", + "deleted_at", + "metadata.id", + "metadata.parent.id", + "metadata.children.id", + "metadata.product.id", + ], + isList: true, + } + + const middleware = validateAndTransformQuery( + createFindParams(), + queryConfig + ) + + await middleware(mockRequest, mockResponse, nextFunction) + + expect(nextFunction).toHaveBeenLastCalledWith( + new MedusaError( + MedusaError.Types.INVALID_DATA, + `Requested fields [metadata.product.id, product] are not valid` + ) + ) + }) }) diff --git a/packages/core/framework/src/http/index.ts b/packages/core/framework/src/http/index.ts index fb9843d6dd..8ddfc76e20 100644 --- a/packages/core/framework/src/http/index.ts +++ b/packages/core/framework/src/http/index.ts @@ -12,3 +12,4 @@ export * from "./utils/define-middlewares" export * from "./utils/maybe-apply-link-filter" export * from "./utils/refetch-entities" export * from "./utils/unless-path" +export * from "./utils/restricted-fields" diff --git a/packages/core/framework/src/http/router.ts b/packages/core/framework/src/http/router.ts index 7328241df5..5d1634c939 100644 --- a/packages/core/framework/src/http/router.ts +++ b/packages/core/framework/src/http/router.ts @@ -23,6 +23,7 @@ import { ensurePublishableApiKeyMiddleware } from "./middlewares/ensure-publisha import { GlobalMiddlewareDescriptor, HTTP_METHODS, + MedusaNextFunction, MedusaRequest, MedusaResponse, MiddlewareFunction, @@ -35,6 +36,7 @@ import { RouteHandler, RouteVerb, } from "./types" +import { RestrictedFields } from "./utils/restricted-fields" const log = ({ activityId, @@ -244,7 +246,7 @@ export class ApiRoutesLoader { static traceMiddleware?: ( handler: RequestHandler | MiddlewareFunction, route: { route: string; method?: string } - ) => RequestHandler + ) => RequestHandler | MiddlewareFunction constructor({ app, @@ -589,14 +591,15 @@ export class ApiRoutesLoader { * Applies middleware that checks if a valid publishable key is set on store request */ applyStorePublishableKeyMiddleware(route: string) { - let middleware = - ensurePublishableApiKeyMiddleware as unknown as RequestHandler + let middleware = ensurePublishableApiKeyMiddleware as unknown as + | RequestHandler + | MiddlewareFunction if (ApiRoutesLoader.traceMiddleware) { middleware = ApiRoutesLoader.traceMiddleware(middleware, { route: route }) } - this.#router.use(route, middleware) + this.#router.use(route, middleware as RequestHandler) } /** @@ -609,7 +612,9 @@ export class ApiRoutesLoader { authType: AuthType | AuthType[], options?: { allowUnauthenticated?: boolean; allowUnregistered?: boolean } ) { - let authenticateMiddleware = authenticate(actorType, authType, options) + let authenticateMiddleware: RequestHandler | MiddlewareFunction = + authenticate(actorType, authType, options) + if (ApiRoutesLoader.traceMiddleware) { authenticateMiddleware = ApiRoutesLoader.traceMiddleware( authenticateMiddleware, @@ -617,7 +622,7 @@ export class ApiRoutesLoader { ) } - this.#router.use(route, authenticateMiddleware) + this.#router.use(route, authenticateMiddleware as RequestHandler) } /** @@ -933,14 +938,39 @@ export class RoutesLoader { app, activityId, sourceDir, + baseRestrictedFields = [], }: { app: Express activityId?: string sourceDir: string | string[] + baseRestrictedFields?: string[] }) { this.#app = app this.#activityId = activityId this.#sourceDir = sourceDir + + this.#assignRestrictedFields(baseRestrictedFields) + } + + #assignRestrictedFields(baseRestrictedFields: string[]) { + this.#app.use("/store", (( + req: MedusaRequest, + _: MedusaResponse, + next: MedusaNextFunction + ) => { + req.restrictedFields = new RestrictedFields() + req.restrictedFields.add(baseRestrictedFields) + next() + }) as unknown as RequestHandler) + + this.#app.use("/admin", (( + req: MedusaRequest, + _: MedusaResponse, + next: MedusaNextFunction + ) => { + req.restrictedFields = new RestrictedFields() + next() + }) as unknown as RequestHandler) } async load() { diff --git a/packages/core/framework/src/http/types.ts b/packages/core/framework/src/http/types.ts index 197f9a928c..b193b0a2c7 100644 --- a/packages/core/framework/src/http/types.ts +++ b/packages/core/framework/src/http/types.ts @@ -7,6 +7,7 @@ import { RequestQueryFields, } from "@medusajs/types" import { MedusaContainer } from "../container" +import { RestrictedFields } from "./utils/restricted-fields" /** * List of all the supported HTTP methods @@ -135,6 +136,9 @@ export interface MedusaRequest session?: any rawBody?: any requestId?: string + + restrictedFields?: RestrictedFields + /** * An object that carries the context that is used to calculate prices for variants */ diff --git a/packages/core/framework/src/http/utils/get-query-config.ts b/packages/core/framework/src/http/utils/get-query-config.ts index 27244ca47e..5f690c12a8 100644 --- a/packages/core/framework/src/http/utils/get-query-config.ts +++ b/packages/core/framework/src/http/utils/get-query-config.ts @@ -1,5 +1,5 @@ import { pick } from "lodash" -import { RequestQueryFields, FindConfig, QueryConfig } from "@medusajs/types" +import { FindConfig, QueryConfig, RequestQueryFields } from "@medusajs/types" import { isDefined, isPresent, @@ -23,11 +23,83 @@ export function pickByConfig( return obj } +function checkRestrictedFields({ + fields, + restricted, +}: { + fields: string[] + restricted: string[] +}): string[] { + const notAllowedFields: string[] = [] + + fields.forEach((field) => { + const fieldSegments = field.split(".") + const hasRestrictedField = restricted.some((restrictedField) => + fieldSegments.includes(restrictedField) + ) + if (hasRestrictedField) { + notAllowedFields.push(field) + return + } + + return + }) + + return notAllowedFields +} + +function checkAllowedFields({ + fields, + allowed, + starFields, +}: { + fields: string[] + starFields: Set + allowed: string[] +}): string[] { + const notAllowedFields: string[] = [] + + fields.forEach((field) => { + const hasAllowedField = allowed.includes(field) + + if (hasAllowedField) { + return + } + + // Select full relation in that case it must match an allowed field fully + // e.g product.variants in that case we must have a product.variants in the allowedFields + if (starFields.has(field)) { + if (hasAllowedField) { + return + } + notAllowedFields.push(field) + return + } + + const fieldStartsWithAllowedField = allowed.some((allowedField) => + field.startsWith(allowedField) + ) + + if (!fieldStartsWithAllowedField) { + notAllowedFields.push(field) + return + } + }) + + return notAllowedFields +} + export function prepareListQuery( validated: T, - queryConfig: QueryConfig = {} + queryConfig: QueryConfig & { restricted?: string[] } = {} ) { - let { allowed = [], defaults = [], defaultLimit = 50, isList } = queryConfig + let { + allowed = [], + restricted = [], + defaults = [], + defaultLimit = 50, + isList, + } = queryConfig const { order, fields, limit = defaultLimit, offset = 0 } = validated // e.g *product.variants meaning that we want all fields from the product.variants @@ -46,7 +118,8 @@ export function prepareListQuery( field.startsWith("-") || field.startsWith("+") || field.startsWith(" ") || - field.startsWith("*") + field.startsWith("*") || + field.endsWith(".*") ) }) @@ -68,44 +141,32 @@ export function prepareListQuery( } allFields.forEach((field) => { - if (field.startsWith("*")) { - starFields.add(field.replace(/^\*/, "")) + if (field.startsWith("*") || field.endsWith(".*")) { + starFields.add(field.replace(/(^\*|\.\*$)/, "")) allFields.delete(field) } }) - const notAllowedFields: string[] = [] + let notAllowedFields: string[] = [] - if (allowed.length) { - ;[...allFields, ...Array.from(starFields)].forEach((field) => { - const hasAllowedField = allowed.includes(field) + if (allowed.length || restricted.length) { + const fieldsToCheck = [...allFields, ...Array.from(starFields)] - if (hasAllowedField) { - return - } - - // Select full relation in that case it must match an allowed field fully - // e.g product.variants in that case we must have a product.variants in the allowedFields - if (starFields.has(field)) { - if (hasAllowedField) { - return - } - notAllowedFields.push(field) - return - } - - const fieldStartsWithAllowedField = allowed.some((allowedField) => - field.startsWith(allowedField) - ) - - if (!fieldStartsWithAllowedField) { - notAllowedFields.push(field) - return - } - }) + if (allowed.length) { + notAllowedFields = checkAllowedFields({ + fields: fieldsToCheck, + starFields, + allowed, + }) + } else if (restricted.length) { + notAllowedFields = checkRestrictedFields({ + fields: fieldsToCheck, + restricted, + }) + } } - if (allFields.size && notAllowedFields.length) { + if (notAllowedFields.length) { throw new MedusaError( MedusaError.Types.INVALID_DATA, `Requested fields [${Array.from(notAllowedFields).join( @@ -170,7 +231,7 @@ export function prepareListQuery( export function prepareRetrieveQuery( validated: T, - queryConfig?: QueryConfig + queryConfig?: QueryConfig & { restricted?: string[] } ) { const { listConfig, remoteQueryConfig } = prepareListQuery( validated, diff --git a/packages/core/framework/src/http/utils/restricted-fields.ts b/packages/core/framework/src/http/utils/restricted-fields.ts new file mode 100644 index 0000000000..7470ffcce2 --- /dev/null +++ b/packages/core/framework/src/http/utils/restricted-fields.ts @@ -0,0 +1,18 @@ +export class RestrictedFields { + /** + * Fields that are restricted from being selected in the response. + * Those fields can be allowed if specified in the allowed configuration of the query config of an end point. + * + * @type {string[]} + * @private + */ + #restrictedFields: Set = new Set() + + list() { + return Array.from(this.#restrictedFields) + } + + add(fields: string[]) { + fields.map((field) => this.#restrictedFields.add(field)) + } +} diff --git a/packages/core/framework/src/http/utils/validate-query.ts b/packages/core/framework/src/http/utils/validate-query.ts index a450f157ac..a7ea3a3096 100644 --- a/packages/core/framework/src/http/utils/validate-query.ts +++ b/packages/core/framework/src/http/utils/validate-query.ts @@ -68,14 +68,25 @@ export function validateAndTransformQuery( next: NextFunction ) { try { - const allowed = (req.allowed ?? queryConfig.allowed ?? []) as string[] + const restricted = req.restrictedFields?.list() + const allowed = queryConfig.allowed ?? [] + + // If any custom allowed fields are set, we add them to the allowed list along side the one configured in the query config if any + if (req.allowed?.length) { + allowed.push(...req.allowed) + } + delete req.allowed const query = normalizeQuery(req) const validated = await zodValidator(zodSchema, query) const cnf = queryConfig.isList - ? prepareListQuery(validated, { ...queryConfig, allowed }) - : prepareRetrieveQuery(validated, { ...queryConfig, allowed }) + ? prepareListQuery(validated, { ...queryConfig, allowed, restricted }) + : prepareRetrieveQuery(validated, { + ...queryConfig, + allowed, + restricted, + }) req.validatedQuery = validated req.filterableFields = getFilterableFields(req.validatedQuery) diff --git a/packages/core/types/src/common/common.ts b/packages/core/types/src/common/common.ts index d22e19a728..a30cfbd816 100644 --- a/packages/core/types/src/common/common.ts +++ b/packages/core/types/src/common/common.ts @@ -300,14 +300,22 @@ export type RawRounding = { */ export type QueryConfig = { /** - * Default fields and relations to return + * Default fields and relations to return. + * use `*` or `.*` to select all fields from a relations (e.g '*products' or 'products.*' will select all products properties) */ defaults?: (keyof TEntity | string)[] /** - * Fields and relations that are allowed to be requested + * Fields and relations that are allowed to be requested. + * Symbol such as `*`, `+` and `-` should be removed as they dont make sense for + * the authorization search. */ allowed?: string[] defaultLimit?: number + /** + * If the route that will use that configuration is supposed to return a list of entities. This + * will change the configuration that will be created on req.listConfig and req.remoteQueryConfig (among + * other things it will include pagination and sorting) + */ isList?: boolean } diff --git a/packages/core/types/src/common/config-module.ts b/packages/core/types/src/common/config-module.ts index fcfc03a01c..c5c7c963c0 100644 --- a/packages/core/types/src/common/config-module.ts +++ b/packages/core/types/src/common/config-module.ts @@ -724,6 +724,27 @@ export type ProjectConfigOptions = { * ``` */ authMethodsPerActor?: Record + + /** + * Specifies the fields that can't be selected in the response unless specified in the allowed query config. + * This is useful to restrict sensitive fields from being exposed in the API. + * + * @example + * + * ```js title="medusa-config.js" + * module.exports = defineConfig({ + * projectConfig: { + * http: { + * restrictedFields: { + * store: ["order", "orders"], + * } + * } + * ``` + */ + restrictedFields?: { + store?: string[] + /*admin?: string[]*/ + } } } diff --git a/packages/core/utils/src/common/__tests__/define-config.spec.ts b/packages/core/utils/src/common/__tests__/define-config.spec.ts index fba6873b15..0742378b52 100644 --- a/packages/core/utils/src/common/__tests__/define-config.spec.ts +++ b/packages/core/utils/src/common/__tests__/define-config.spec.ts @@ -1,5 +1,5 @@ import { Modules } from "../../modules-sdk" -import { defineConfig } from "../define-config" +import { DEFAULT_STORE_RESTRICTED_FIELDS, defineConfig } from "../define-config" describe("defineConfig", function () { it("should merge empty config with the defaults", function () { @@ -133,6 +133,13 @@ describe("defineConfig", function () { "authCors": "http://localhost:7000,http://localhost:7001,http://localhost:5173", "cookieSecret": "supersecret", "jwtSecret": "supersecret", + "restrictedFields": { + "store": [ + ${DEFAULT_STORE_RESTRICTED_FIELDS.map((v) => `"${v}"`).join( + ",\n " + )}, + ], + }, "storeCors": "http://localhost:8000", }, }, @@ -282,6 +289,13 @@ describe("defineConfig", function () { "authCors": "http://localhost:7000,http://localhost:7001,http://localhost:5173", "cookieSecret": "supersecret", "jwtSecret": "supersecret", + "restrictedFields": { + "store": [ + ${DEFAULT_STORE_RESTRICTED_FIELDS.map((v) => `"${v}"`).join( + ",\n " + )}, + ], + }, "storeCors": "http://localhost:8000", }, }, @@ -439,6 +453,13 @@ describe("defineConfig", function () { "authCors": "http://localhost:7000,http://localhost:7001,http://localhost:5173", "cookieSecret": "supersecret", "jwtSecret": "supersecret", + "restrictedFields": { + "store": [ + ${DEFAULT_STORE_RESTRICTED_FIELDS.map((v) => `"${v}"`).join( + ",\n " + )}, + ], + }, "storeCors": "http://localhost:8000", }, }, @@ -597,6 +618,13 @@ describe("defineConfig", function () { "authCors": "http://localhost:7000,http://localhost:7001,http://localhost:5173", "cookieSecret": "supersecret", "jwtSecret": "supersecret", + "restrictedFields": { + "store": [ + ${DEFAULT_STORE_RESTRICTED_FIELDS.map((v) => `"${v}"`).join( + ",\n " + )}, + ], + }, "storeCors": "http://localhost:8000", }, }, @@ -743,6 +771,13 @@ describe("defineConfig", function () { "authCors": "http://localhost:7000,http://localhost:7001,http://localhost:5173", "cookieSecret": "supersecret", "jwtSecret": "supersecret", + "restrictedFields": { + "store": [ + ${DEFAULT_STORE_RESTRICTED_FIELDS.map((v) => `"${v}"`).join( + ",\n " + )}, + ], + }, "storeCors": "http://localhost:8000", }, }, @@ -889,6 +924,13 @@ describe("defineConfig", function () { "authCors": "http://localhost:7000,http://localhost:7001,http://localhost:5173", "cookieSecret": "supersecret", "jwtSecret": "supersecret", + "restrictedFields": { + "store": [ + ${DEFAULT_STORE_RESTRICTED_FIELDS.map((v) => `"${v}"`).join( + ",\n " + )}, + ], + }, "storeCors": "http://localhost:8000", }, }, diff --git a/packages/core/utils/src/common/define-config.ts b/packages/core/utils/src/common/define-config.ts index e9ee521052..1e3065ea4d 100644 --- a/packages/core/utils/src/common/define-config.ts +++ b/packages/core/utils/src/common/define-config.ts @@ -20,6 +20,15 @@ const DEFAULT_DATABASE_URL = "postgres://localhost/medusa-starter-default" const DEFAULT_ADMIN_CORS = "http://localhost:7000,http://localhost:7001,http://localhost:5173" +export const DEFAULT_STORE_RESTRICTED_FIELDS = [ + "order", + "orders", + /*"customer", + "customers", + "payment_collection", + "payment_collections"*/ +] + type InternalModuleDeclarationOverride = InternalModuleDeclaration & { /** * Optional key to be used to identify the module, if not provided, it will be inferred from the module joiner config service name. @@ -79,6 +88,9 @@ export function defineConfig(config: Config = {}): ConfigModule { authCors: process.env.AUTH_CORS || DEFAULT_ADMIN_CORS, jwtSecret: process.env.JWT_SECRET || DEFAULT_SECRET, cookieSecret: process.env.COOKIE_SECRET || DEFAULT_SECRET, + restrictedFields: { + store: DEFAULT_STORE_RESTRICTED_FIELDS, + }, ...http, }, ...restOfProjectConfig, diff --git a/packages/medusa/src/api/store/carts/query-config.ts b/packages/medusa/src/api/store/carts/query-config.ts index 56b0edfa35..48fc1bb2bb 100644 --- a/packages/medusa/src/api/store/carts/query-config.ts +++ b/packages/medusa/src/api/store/carts/query-config.ts @@ -1,3 +1,4 @@ +// TODO: Global todo, review all default fields to prevent over fetching by default export const defaultStoreCartFields = [ "id", "currency_code", @@ -33,8 +34,13 @@ export const defaultStoreCartFields = [ "promotions.application_method.value", "promotions.application_method.type", "promotions.application_method.currency_code", + "items", + "items.thumbnail", + "region", "items.id", + "items.product", "items.product.id", + "items.variant", "items.variant_id", "items.product_id", "items.product.categories.id", diff --git a/packages/medusa/src/api/store/customers/query-config.ts b/packages/medusa/src/api/store/customers/query-config.ts index b6715b4424..91d0e1a04e 100644 --- a/packages/medusa/src/api/store/customers/query-config.ts +++ b/packages/medusa/src/api/store/customers/query-config.ts @@ -15,6 +15,10 @@ const defaultStoreCustomersFields = [ export const retrieveTransformQueryConfig = { defaults: defaultStoreCustomersFields, + allowed: [ + ...defaultStoreCustomersFields.map((f) => f.replace("*", "")), + "orders", + ], isList: false, } diff --git a/packages/medusa/src/api/store/return-reasons/query-config.ts b/packages/medusa/src/api/store/return-reasons/query-config.ts index f3c6e1bcdc..70c53a02c0 100644 --- a/packages/medusa/src/api/store/return-reasons/query-config.ts +++ b/packages/medusa/src/api/store/return-reasons/query-config.ts @@ -7,8 +7,8 @@ export const defaultStoreRetrieveReturnReasonFields = [ "created_at", "updated_at", "deleted_at", - "*.parent_return_reason", - "*.return_reason_children", + "*parent_return_reason", + "*return_reason_children", ] export const retrieveTransformQueryConfig = { diff --git a/packages/medusa/src/instrumentation/index.ts b/packages/medusa/src/instrumentation/index.ts index 2ab46362e8..b9ef9e1348 100644 --- a/packages/medusa/src/instrumentation/index.ts +++ b/packages/medusa/src/instrumentation/index.ts @@ -1,5 +1,10 @@ import { snakeCase } from "lodash" -import { Query } from "@medusajs/framework" +import { + MedusaNextFunction, + MedusaRequest, + MedusaResponse, + Query, +} from "@medusajs/framework" import { ApiRoutesLoader } from "@medusajs/framework/http" import { Tracer } from "@medusajs/framework/telemetry" import type { SpanExporter } from "@opentelemetry/sdk-trace-node" @@ -84,7 +89,11 @@ export function instrumentHttpLayer() { * OpenTelemetry */ ApiRoutesLoader.traceMiddleware = (handler) => { - return async (req, res, next) => { + return async ( + req: MedusaRequest, + res: MedusaResponse, + next: MedusaNextFunction + ) => { if (shouldExcludeResource(req.originalUrl)) { return handler(req, res, next) } diff --git a/packages/medusa/src/loaders/api.ts b/packages/medusa/src/loaders/api.ts index 4286fca604..8be4c0468f 100644 --- a/packages/medusa/src/loaders/api.ts +++ b/packages/medusa/src/loaders/api.ts @@ -3,6 +3,7 @@ import { join } from "path" import qs from "qs" import { RoutesLoader } from "@medusajs/framework/http" import { MedusaContainer, PluginDetails } from "@medusajs/framework/types" +import { ConfigModule } from "@medusajs/framework/config" type Options = { app: Express @@ -47,6 +48,12 @@ export default async ({ app, container, plugins }: Options) => { join(__dirname, "../api") ) + const { + projectConfig: { + http: { restrictedFields }, + }, + } = container.resolve("configModule") + // TODO: Figure out why this is causing issues with test when placed inside ./api.ts // Adding this here temporarily // Test: (packages/medusa/src/api/routes/admin/currencies/update-currency.ts) @@ -54,6 +61,7 @@ export default async ({ app, container, plugins }: Options) => { await new RoutesLoader({ app: app, sourceDir: sourcePaths, + baseRestrictedFields: restrictedFields?.store, }).load() } catch (err) { throw Error(