chore(product): Improve product normalization and fix http router with tracing (#11724)
**What** - Improve product normalization and prevent over fetching data - Fix HTTP router wrap handler with tracing enabled
This commit is contained in:
1 parent
e81deb49f8
commit
cc1309d370
15 files changed
+467
-121
No files matched your search
@@ -60,6 +60,7 @@
|
||||
"@types/multer": "^1.4.7",
|
||||
"jest": "^29.7.0",
|
||||
"rimraf": "^5.0.1",
|
||||
"supertest": "^4.0.2",
|
||||
"typescript": "^5.6.2",
|
||||
"yalc": "1.0.0-pre.53"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { ConfigModule } from "@medusajs/types"
|
||||
|
||||
export const customersGlobalMiddlewareMock = jest.fn()
|
||||
export const customersCreateMiddlewareMock = jest.fn()
|
||||
export const storeGlobalMiddlewareMock = jest.fn()
|
||||
|
||||
export const config = {
|
||||
projectConfig: {
|
||||
databaseLogging: false,
|
||||
http: {
|
||||
authCors: "http://localhost:9000",
|
||||
storeCors: "http://localhost:8000",
|
||||
adminCors: "http://localhost:7001",
|
||||
jwtSecret: "supersecret",
|
||||
cookieSecret: "superSecret",
|
||||
},
|
||||
},
|
||||
featureFlags: {},
|
||||
plugins: [],
|
||||
} satisfies Partial<ConfigModule>
|
||||
@@ -0,0 +1,6 @@
|
||||
import { MedusaError } from "@medusajs/framework/utils"
|
||||
import { Request, Response } from "express"
|
||||
|
||||
export function GET(req: Request, res: Response) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, "Failed")
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { defineMiddlewares } from "@medusajs/framework"
|
||||
|
||||
export const errorHandlerMock = jest
|
||||
.fn()
|
||||
.mockImplementation((err, req, res, next) => {
|
||||
console.log("errorHandlerMock", err)
|
||||
return res.status(400).json({
|
||||
type: err.code.toLowerCase(),
|
||||
message: err.message,
|
||||
})
|
||||
})
|
||||
|
||||
export default defineMiddlewares({
|
||||
errorHandler: (err, req, res, next) => errorHandlerMock(err, req, res, next),
|
||||
})
|
||||
@@ -0,0 +1,183 @@
|
||||
import {
|
||||
moduleLoader,
|
||||
ModulesDefinition,
|
||||
registerMedusaModule,
|
||||
} from "@medusajs/modules-sdk"
|
||||
import { ContainerRegistrationKeys, generateJwtToken } from "@medusajs/utils"
|
||||
import { asValue } from "awilix"
|
||||
import express from "express"
|
||||
import querystring from "querystring"
|
||||
import supertest from "supertest"
|
||||
|
||||
import { config } from "../mocks"
|
||||
import { ConfigModule, MedusaContainer } from "@medusajs/types"
|
||||
import { configManager } from "@medusajs/framework/config"
|
||||
import {
|
||||
ApiLoader,
|
||||
container,
|
||||
featureFlagsLoader,
|
||||
logger,
|
||||
MedusaRequest,
|
||||
} from "@medusajs/framework"
|
||||
|
||||
function asArray(resolvers) {
|
||||
return {
|
||||
resolve: (container) =>
|
||||
resolvers.map((resolver) => container.build(resolver)),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a test server that injects API Routes using the RoutesLoader
|
||||
*
|
||||
* @param {String} rootDir - The root directory of the project
|
||||
*/
|
||||
export const createServer = async (rootDir) => {
|
||||
const app = express()
|
||||
|
||||
const moduleResolutions = {}
|
||||
Object.entries(ModulesDefinition).forEach(([moduleKey, module]) => {
|
||||
moduleResolutions[moduleKey] = registerMedusaModule(
|
||||
moduleKey,
|
||||
module.defaultModuleDeclaration,
|
||||
undefined,
|
||||
module
|
||||
)[moduleKey]
|
||||
})
|
||||
|
||||
configManager.loadConfig({
|
||||
projectConfig: config as unknown as ConfigModule,
|
||||
baseDir: rootDir,
|
||||
})
|
||||
|
||||
container.registerAdd = function (this: MedusaContainer, name, registration) {
|
||||
const storeKey = name + "_STORE"
|
||||
|
||||
if (this.registrations[storeKey] === undefined) {
|
||||
this.register(storeKey, asValue([]))
|
||||
}
|
||||
const store = this.resolve(storeKey) as Array<any>
|
||||
|
||||
if (this.registrations[name] === undefined) {
|
||||
this.register(name, asArray(store))
|
||||
}
|
||||
store.unshift(registration)
|
||||
|
||||
return this
|
||||
}.bind(container)
|
||||
|
||||
container.register(ContainerRegistrationKeys.PG_CONNECTION, asValue({}))
|
||||
container.register("configModule", asValue(config))
|
||||
container.register({
|
||||
logger: asValue({
|
||||
error: () => {},
|
||||
}),
|
||||
manager: asValue({}),
|
||||
})
|
||||
|
||||
app.set("trust proxy", 1)
|
||||
app.use((req, _res, next) => {
|
||||
req["session"] = {}
|
||||
const data = req.get("Cookie")
|
||||
if (data) {
|
||||
req["session"] = {
|
||||
...req["session"],
|
||||
...JSON.parse(data),
|
||||
}
|
||||
}
|
||||
next()
|
||||
})
|
||||
|
||||
await featureFlagsLoader()
|
||||
await moduleLoader({ container, moduleResolutions, logger })
|
||||
|
||||
app.use((req, res, next) => {
|
||||
;(req as MedusaRequest).scope = container.createScope() as MedusaContainer
|
||||
next()
|
||||
})
|
||||
|
||||
await new ApiLoader({
|
||||
app,
|
||||
sourceDir: rootDir,
|
||||
}).load()
|
||||
|
||||
const superRequest = supertest(app)
|
||||
|
||||
return {
|
||||
request: async (method, url, opts: any = {}) => {
|
||||
const { payload, query, headers = {} } = opts
|
||||
|
||||
const queryParams = query && querystring.stringify(query)
|
||||
const req = superRequest[method.toLowerCase()](
|
||||
`${url}${queryParams ? "?" + queryParams : ""}`
|
||||
)
|
||||
headers.Cookie = headers.Cookie || ""
|
||||
if (opts.adminSession) {
|
||||
const token = generateJwtToken(
|
||||
{
|
||||
actor_id: opts.adminSession.userId || opts.adminSession.jwt?.userId,
|
||||
actor_type: "user",
|
||||
app_metadata: {
|
||||
user_id:
|
||||
opts.adminSession.userId || opts.adminSession.jwt?.userId,
|
||||
},
|
||||
},
|
||||
{
|
||||
secret: config.projectConfig.http.jwtSecret!,
|
||||
expiresIn: "1d",
|
||||
}
|
||||
)
|
||||
|
||||
headers.Authorization = `Bearer ${token}`
|
||||
}
|
||||
|
||||
if (opts.clientSession) {
|
||||
const token = generateJwtToken(
|
||||
{
|
||||
actor_id:
|
||||
opts.clientSession.customer_id ||
|
||||
opts.clientSession.jwt?.customer_id,
|
||||
actor_type: "customer",
|
||||
app_metadata: {
|
||||
customer_id:
|
||||
opts.clientSession.customer_id ||
|
||||
opts.clientSession.jwt?.customer_id,
|
||||
},
|
||||
},
|
||||
{ secret: config.projectConfig.http.jwtSecret!, expiresIn: "1d" }
|
||||
)
|
||||
|
||||
headers.Authorization = `Bearer ${token}`
|
||||
}
|
||||
|
||||
for (const name in headers) {
|
||||
if ({}.hasOwnProperty.call(headers, name)) {
|
||||
req.set(name, headers[name])
|
||||
}
|
||||
}
|
||||
|
||||
if (payload && !req.get("content-type")) {
|
||||
req.set("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
if (!req.get("accept")) {
|
||||
req.set("Accept", "application/json")
|
||||
}
|
||||
|
||||
req.set("Host", "localhost")
|
||||
|
||||
let res
|
||||
try {
|
||||
res = await req.send(JSON.stringify(payload))
|
||||
} catch (e) {
|
||||
if (e.response) {
|
||||
res = e.response
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { resolve } from "path"
|
||||
import { errorHandlerMock } from "../__fixtures__/routers/middlewares"
|
||||
import { createServer } from "../__fixtures__/server"
|
||||
import { instrumentHttpLayer } from "../index"
|
||||
import { MedusaError } from "@medusajs/framework/utils"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
jest.mock("../../commands/start", () => {
|
||||
return {}
|
||||
})
|
||||
|
||||
describe("HTTP Instrumentation", () => {
|
||||
let request
|
||||
|
||||
afterEach(function () {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
beforeAll(async function () {
|
||||
instrumentHttpLayer()
|
||||
|
||||
const rootDir = resolve(__dirname, "../__fixtures__/routers")
|
||||
|
||||
const { request: request_ } = await createServer(rootDir)
|
||||
|
||||
request = request_
|
||||
})
|
||||
|
||||
describe("traceRoute", () => {
|
||||
it("should be handled by the error handler when a route fails", async () => {
|
||||
const res = await request("GET", "/admin/fail", {
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: "admin_user",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
expect(res.status).toBe(400)
|
||||
expect(errorHandlerMock).toHaveBeenCalled()
|
||||
expect(errorHandlerMock).toHaveBeenCalledWith(
|
||||
new MedusaError(MedusaError.Types.INVALID_DATA, "Failed"),
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.anything()
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -110,26 +110,18 @@ export function instrumentHttpLayer() {
|
||||
}`
|
||||
|
||||
await HTTPTracer.trace(traceName, async (span) => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const _next = (error?: any) => {
|
||||
if (error) {
|
||||
span.setStatus({
|
||||
code: SpanStatusCode.ERROR,
|
||||
message: error.message || "Failed",
|
||||
})
|
||||
span.end()
|
||||
reject(error)
|
||||
} else {
|
||||
span.end()
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
|
||||
handler(req, res, _next)
|
||||
})
|
||||
try {
|
||||
await handler(req, res, next)
|
||||
} catch (error) {
|
||||
span.setStatus({
|
||||
code: SpanStatusCode.ERROR,
|
||||
message: error.message || "Failed",
|
||||
})
|
||||
throw error
|
||||
} finally {
|
||||
span.end()
|
||||
}
|
||||
})
|
||||
.catch(next)
|
||||
.then(next)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user