feat: Replace existing router with the new implementation (#11646)
This commit is contained in:
6
.changeset/warm-spiders-smell.md
Normal file
6
.changeset/warm-spiders-smell.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
"@medusajs/framework": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat: Replace existing router with the new implementation
|
||||||
@@ -93,6 +93,7 @@
|
|||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"morgan": "^1.9.1",
|
"morgan": "^1.9.1",
|
||||||
|
"path-to-regexp": "^0.1.10",
|
||||||
"tsconfig-paths": "^4.2.0",
|
"tsconfig-paths": "^4.2.0",
|
||||||
"zod": "3.22.4"
|
"zod": "3.22.4"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { container } from "../../../container"
|
|||||||
import { featureFlagsLoader } from "../../../feature-flags"
|
import { featureFlagsLoader } from "../../../feature-flags"
|
||||||
import { logger } from "../../../logger"
|
import { logger } from "../../../logger"
|
||||||
import { MedusaRequest } from "../../types"
|
import { MedusaRequest } from "../../types"
|
||||||
import { RoutesLoader } from "../../router"
|
import { ApiLoader } from "../../router"
|
||||||
|
|
||||||
function asArray(resolvers) {
|
function asArray(resolvers) {
|
||||||
return {
|
return {
|
||||||
@@ -94,7 +94,7 @@ export const createServer = async (rootDir) => {
|
|||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
||||||
await new RoutesLoader({
|
await new ApiLoader({
|
||||||
app,
|
app,
|
||||||
sourceDir: rootDir,
|
sourceDir: rootDir,
|
||||||
}).load()
|
}).load()
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
storeGlobalMiddlewareMock,
|
storeGlobalMiddlewareMock,
|
||||||
} from "../__fixtures__/mocks"
|
} from "../__fixtures__/mocks"
|
||||||
import { createServer } from "../__fixtures__/server"
|
import { createServer } from "../__fixtures__/server"
|
||||||
import { MedusaNextFunction, RoutesLoader } from "../index"
|
import { MedusaNextFunction, ApiLoader } from "../index"
|
||||||
|
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
@@ -237,7 +237,7 @@ describe("RoutesLoader", function () {
|
|||||||
__dirname,
|
__dirname,
|
||||||
"../__fixtures__/routers-duplicate-parameter"
|
"../__fixtures__/routers-duplicate-parameter"
|
||||||
)
|
)
|
||||||
const err = await new RoutesLoader({
|
const err = await new ApiLoader({
|
||||||
app,
|
app,
|
||||||
sourceDir: rootDir,
|
sourceDir: rootDir,
|
||||||
})
|
})
|
||||||
@@ -246,7 +246,7 @@ describe("RoutesLoader", function () {
|
|||||||
|
|
||||||
expect(err).toBeDefined()
|
expect(err).toBeDefined()
|
||||||
expect(err.message).toBe(
|
expect(err.message).toBe(
|
||||||
"Duplicate parameters found in route /admin/customers/[id]/orders/[id] (id). Make sure that all parameters are unique."
|
"Duplicate parameters found in route /admin/customers/[id]/orders/[id]/route.ts (id). Make sure that all parameters are unique."
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { MiddlewareFileLoader } from "../middleware-file-loader"
|
|||||||
describe("Middleware file loader", () => {
|
describe("Middleware file loader", () => {
|
||||||
it("should load routes from the filesystem", async () => {
|
it("should load routes from the filesystem", async () => {
|
||||||
const BASE_DIR = resolve(__dirname, "../__fixtures__/routers-middleware")
|
const BASE_DIR = resolve(__dirname, "../__fixtures__/routers-middleware")
|
||||||
const loader = new MiddlewareFileLoader({})
|
const loader = new MiddlewareFileLoader()
|
||||||
await loader.scanDir(BASE_DIR)
|
await loader.scanDir(BASE_DIR)
|
||||||
|
|
||||||
expect(loader.getBodyParserConfigRoutes()).toMatchInlineSnapshot(`
|
expect(loader.getBodyParserConfigRoutes()).toMatchInlineSnapshot(`
|
||||||
@@ -14,12 +14,22 @@ describe("Middleware file loader", () => {
|
|||||||
"preserveRawBody": true,
|
"preserveRawBody": true,
|
||||||
},
|
},
|
||||||
"matcher": "/webhooks",
|
"matcher": "/webhooks",
|
||||||
"method": undefined,
|
"methods": [
|
||||||
|
"GET",
|
||||||
|
"POST",
|
||||||
|
"PUT",
|
||||||
|
"PATCH",
|
||||||
|
"DELETE",
|
||||||
|
"OPTIONS",
|
||||||
|
"HEAD",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"config": false,
|
"config": false,
|
||||||
"matcher": "/webhooks/*",
|
"matcher": "/webhooks/*",
|
||||||
"method": "POST",
|
"methods": [
|
||||||
|
"POST",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
`)
|
`)
|
||||||
@@ -28,22 +38,26 @@ describe("Middleware file loader", () => {
|
|||||||
{
|
{
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
"matcher": "/customers",
|
"matcher": "/customers",
|
||||||
"method": undefined,
|
"methods": undefined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
"matcher": "/customers",
|
"matcher": "/customers",
|
||||||
"method": "POST",
|
"methods": [
|
||||||
|
"POST",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
"matcher": "/store/*",
|
"matcher": "/store/*",
|
||||||
"method": undefined,
|
"methods": undefined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
"matcher": "/webhooks/*",
|
"matcher": "/webhooks/*",
|
||||||
"method": "POST",
|
"methods": [
|
||||||
|
"POST",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
`)
|
`)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { RoutesLoader } from "../routes-loader"
|
|||||||
describe("Routes loader", () => {
|
describe("Routes loader", () => {
|
||||||
it("should load routes from the filesystem", async () => {
|
it("should load routes from the filesystem", async () => {
|
||||||
const BASE_DIR = resolve(__dirname, "../__fixtures__/routers")
|
const BASE_DIR = resolve(__dirname, "../__fixtures__/routers")
|
||||||
const loader = new RoutesLoader({})
|
const loader = new RoutesLoader()
|
||||||
await loader.scanDir(BASE_DIR)
|
await loader.scanDir(BASE_DIR)
|
||||||
|
|
||||||
expect(loader.getRoutes()).toMatchInlineSnapshot(`
|
expect(loader.getRoutes()).toMatchInlineSnapshot(`
|
||||||
@@ -12,10 +12,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/orders/[id]/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/orders/[id]/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/orders/:id",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/orders/[id]/route.ts",
|
"relativePath": "/admin/orders/[id]/route.ts",
|
||||||
"route": "/admin/orders/:id",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -23,10 +24,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/orders/[id]/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/orders/[id]/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/orders/:id",
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/orders/[id]/route.ts",
|
"relativePath": "/admin/orders/[id]/route.ts",
|
||||||
"route": "/admin/orders/:id",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -34,10 +36,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/orders/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/orders/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/orders",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/orders/route.ts",
|
"relativePath": "/admin/orders/route.ts",
|
||||||
"route": "/admin/orders",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -45,10 +48,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/products/[id]/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/products/[id]/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/products/:id",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/products/[id]/route.ts",
|
"relativePath": "/admin/products/[id]/route.ts",
|
||||||
"route": "/admin/products/:id",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -56,10 +60,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/products",
|
||||||
"method": "DELETE",
|
"method": "DELETE",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/products/route.ts",
|
"relativePath": "/admin/products/route.ts",
|
||||||
"route": "/admin/products",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -67,10 +72,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/products",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/products/route.ts",
|
"relativePath": "/admin/products/route.ts",
|
||||||
"route": "/admin/products",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -78,10 +84,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/products",
|
||||||
"method": "HEAD",
|
"method": "HEAD",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/products/route.ts",
|
"relativePath": "/admin/products/route.ts",
|
||||||
"route": "/admin/products",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -89,10 +96,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/products",
|
||||||
"method": "OPTIONS",
|
"method": "OPTIONS",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/products/route.ts",
|
"relativePath": "/admin/products/route.ts",
|
||||||
"route": "/admin/products",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -100,10 +108,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/products",
|
||||||
"method": "PATCH",
|
"method": "PATCH",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/products/route.ts",
|
"relativePath": "/admin/products/route.ts",
|
||||||
"route": "/admin/products",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -111,10 +120,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/products",
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/products/route.ts",
|
"relativePath": "/admin/products/route.ts",
|
||||||
"route": "/admin/products",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -122,10 +132,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/products",
|
||||||
"method": "PUT",
|
"method": "PUT",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/products/route.ts",
|
"relativePath": "/admin/products/route.ts",
|
||||||
"route": "/admin/products",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -133,10 +144,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/route.ts",
|
"relativePath": "/admin/route.ts",
|
||||||
"route": "/admin",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -144,10 +156,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin",
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/route.ts",
|
"relativePath": "/admin/route.ts",
|
||||||
"route": "/admin",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -155,10 +168,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/customers/[customer_id]/orders/[order_id]/route.ts",
|
"absolutePath": "${BASE_DIR}/customers/[customer_id]/orders/[order_id]/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/customers/:customer_id/orders/:order_id",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/customers/[customer_id]/orders/[order_id]/route.ts",
|
"relativePath": "/customers/[customer_id]/orders/[order_id]/route.ts",
|
||||||
"route": "/customers/:customer_id/orders/:order_id",
|
|
||||||
"shouldAppendAdminCors": false,
|
"shouldAppendAdminCors": false,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -166,10 +180,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/customers/route.ts",
|
"absolutePath": "${BASE_DIR}/customers/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/customers",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/customers/route.ts",
|
"relativePath": "/customers/route.ts",
|
||||||
"route": "/customers",
|
|
||||||
"shouldAppendAdminCors": false,
|
"shouldAppendAdminCors": false,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -184,7 +199,7 @@ describe("Routes loader", () => {
|
|||||||
__dirname,
|
__dirname,
|
||||||
"../__fixtures__/routers-with-duplicates"
|
"../__fixtures__/routers-with-duplicates"
|
||||||
)
|
)
|
||||||
const loader = new RoutesLoader({})
|
const loader = new RoutesLoader()
|
||||||
await loader.scanDir(BASE_DIR)
|
await loader.scanDir(BASE_DIR)
|
||||||
await loader.scanDir(BASE_DIR_2)
|
await loader.scanDir(BASE_DIR_2)
|
||||||
|
|
||||||
@@ -193,10 +208,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/orders/[id]/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/orders/[id]/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/orders/:id",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/orders/[id]/route.ts",
|
"relativePath": "/admin/orders/[id]/route.ts",
|
||||||
"route": "/admin/orders/:id",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -204,10 +220,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/orders/[id]/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/orders/[id]/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/orders/:id",
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/orders/[id]/route.ts",
|
"relativePath": "/admin/orders/[id]/route.ts",
|
||||||
"route": "/admin/orders/:id",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -215,10 +232,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/orders/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/orders/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/orders",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/orders/route.ts",
|
"relativePath": "/admin/orders/route.ts",
|
||||||
"route": "/admin/orders",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -226,10 +244,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR_2}/admin/products/[id]/route.ts",
|
"absolutePath": "${BASE_DIR_2}/admin/products/[id]/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/products/:id",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"optedOutOfAuth": true,
|
"optedOutOfAuth": true,
|
||||||
"relativePath": "/admin/products/[id]/route.ts",
|
"relativePath": "/admin/products/[id]/route.ts",
|
||||||
"route": "/admin/products/:id",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -237,10 +256,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/products",
|
||||||
"method": "DELETE",
|
"method": "DELETE",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/products/route.ts",
|
"relativePath": "/admin/products/route.ts",
|
||||||
"route": "/admin/products",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -248,10 +268,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR_2}/admin/products/route.ts",
|
"absolutePath": "${BASE_DIR_2}/admin/products/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/products",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"optedOutOfAuth": true,
|
"optedOutOfAuth": true,
|
||||||
"relativePath": "/admin/products/route.ts",
|
"relativePath": "/admin/products/route.ts",
|
||||||
"route": "/admin/products",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -259,10 +280,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/products",
|
||||||
"method": "HEAD",
|
"method": "HEAD",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/products/route.ts",
|
"relativePath": "/admin/products/route.ts",
|
||||||
"route": "/admin/products",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -270,10 +292,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/products",
|
||||||
"method": "OPTIONS",
|
"method": "OPTIONS",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/products/route.ts",
|
"relativePath": "/admin/products/route.ts",
|
||||||
"route": "/admin/products",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -281,10 +304,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/products",
|
||||||
"method": "PATCH",
|
"method": "PATCH",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/products/route.ts",
|
"relativePath": "/admin/products/route.ts",
|
||||||
"route": "/admin/products",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -292,10 +316,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR_2}/admin/products/route.ts",
|
"absolutePath": "${BASE_DIR_2}/admin/products/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/products",
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"optedOutOfAuth": true,
|
"optedOutOfAuth": true,
|
||||||
"relativePath": "/admin/products/route.ts",
|
"relativePath": "/admin/products/route.ts",
|
||||||
"route": "/admin/products",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -303,10 +328,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/products/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin/products",
|
||||||
"method": "PUT",
|
"method": "PUT",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/products/route.ts",
|
"relativePath": "/admin/products/route.ts",
|
||||||
"route": "/admin/products",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -314,10 +340,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/route.ts",
|
"relativePath": "/admin/route.ts",
|
||||||
"route": "/admin",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -325,10 +352,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/admin/route.ts",
|
"absolutePath": "${BASE_DIR}/admin/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/admin",
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/admin/route.ts",
|
"relativePath": "/admin/route.ts",
|
||||||
"route": "/admin",
|
|
||||||
"shouldAppendAdminCors": true,
|
"shouldAppendAdminCors": true,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -336,10 +364,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/customers/[customer_id]/orders/[order_id]/route.ts",
|
"absolutePath": "${BASE_DIR}/customers/[customer_id]/orders/[order_id]/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/customers/:customer_id/orders/:order_id",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/customers/[customer_id]/orders/[order_id]/route.ts",
|
"relativePath": "/customers/[customer_id]/orders/[order_id]/route.ts",
|
||||||
"route": "/customers/:customer_id/orders/:order_id",
|
|
||||||
"shouldAppendAdminCors": false,
|
"shouldAppendAdminCors": false,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -347,10 +376,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR}/customers/route.ts",
|
"absolutePath": "${BASE_DIR}/customers/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/customers",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/customers/route.ts",
|
"relativePath": "/customers/route.ts",
|
||||||
"route": "/customers",
|
|
||||||
"shouldAppendAdminCors": false,
|
"shouldAppendAdminCors": false,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": false,
|
"shouldAppendStoreCors": false,
|
||||||
@@ -358,10 +388,11 @@ describe("Routes loader", () => {
|
|||||||
{
|
{
|
||||||
"absolutePath": "${BASE_DIR_2}/store/[customer_id]/orders/[order_id]/route.ts",
|
"absolutePath": "${BASE_DIR_2}/store/[customer_id]/orders/[order_id]/route.ts",
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isRoute": true,
|
||||||
|
"matcher": "/store/:customer_id/orders/:order_id",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"optedOutOfAuth": false,
|
"optedOutOfAuth": false,
|
||||||
"relativePath": "/store/[customer_id]/orders/[order_id]/route.ts",
|
"relativePath": "/store/[customer_id]/orders/[order_id]/route.ts",
|
||||||
"route": "/store/:customer_id/orders/:order_id",
|
|
||||||
"shouldAppendAdminCors": false,
|
"shouldAppendAdminCors": false,
|
||||||
"shouldAppendAuthCors": false,
|
"shouldAppendAuthCors": false,
|
||||||
"shouldAppendStoreCors": true,
|
"shouldAppendStoreCors": true,
|
||||||
@@ -376,7 +407,7 @@ describe("Routes loader", () => {
|
|||||||
"../__fixtures__/routers-duplicate-parameter"
|
"../__fixtures__/routers-duplicate-parameter"
|
||||||
)
|
)
|
||||||
|
|
||||||
const loader = new RoutesLoader({})
|
const loader = new RoutesLoader()
|
||||||
await expect(() => loader.scanDir(BASE_DIR)).rejects.toThrow(
|
await expect(() => loader.scanDir(BASE_DIR)).rejects.toThrow(
|
||||||
"Duplicate parameters found in route /admin/customers/[id]/orders/[id]/route.ts (id). Make sure that all parameters are unique."
|
"Duplicate parameters found in route /admin/customers/[id]/orders/[id]/route.ts (id). Make sure that all parameters are unique."
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,6 +19,14 @@ describe("Routes sorter", () => {
|
|||||||
isAppRoute: true,
|
isAppRoute: true,
|
||||||
handler: () => {},
|
handler: () => {},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
matcher: "/admin",
|
||||||
|
handler: () => {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
matcher: "/store",
|
||||||
|
handler: () => {},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
matcher: "/admin/products/export",
|
matcher: "/admin/products/export",
|
||||||
methods: ["GET"],
|
methods: ["GET"],
|
||||||
@@ -32,12 +40,20 @@ describe("Routes sorter", () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
matcher: "/admin/products/:id",
|
matcher: "/admin/products/:id",
|
||||||
|
isAppRoute: true,
|
||||||
methods: ["GET"],
|
methods: ["GET"],
|
||||||
handler: () => {},
|
handler: () => {},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
matcher: "/admin/products/:id",
|
||||||
|
isAppRoute: true,
|
||||||
|
methods: ["POST"],
|
||||||
|
handler: () => {},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
matcher: "/admin/products/batch",
|
matcher: "/admin/products/batch",
|
||||||
methods: ["GET"],
|
isAppRoute: true,
|
||||||
|
methods: ["POST"],
|
||||||
handler: () => {},
|
handler: () => {},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -57,6 +73,7 @@ describe("Routes sorter", () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
matcher: "/admin/:id/export",
|
matcher: "/admin/:id/export",
|
||||||
|
isAppRoute: true,
|
||||||
methods: ["GET"],
|
methods: ["GET"],
|
||||||
handler: () => {},
|
handler: () => {},
|
||||||
},
|
},
|
||||||
@@ -69,6 +86,14 @@ describe("Routes sorter", () => {
|
|||||||
|
|
||||||
expect(sorter.sort()).toMatchInlineSnapshot(`
|
expect(sorter.sort()).toMatchInlineSnapshot(`
|
||||||
[
|
[
|
||||||
|
{
|
||||||
|
"handler": [Function],
|
||||||
|
"matcher": "/admin",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handler": [Function],
|
||||||
|
"matcher": "/store",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
"matcher": "/v1",
|
"matcher": "/v1",
|
||||||
@@ -136,13 +161,15 @@ describe("Routes sorter", () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isAppRoute": true,
|
||||||
"matcher": "/admin/products/batch",
|
"matcher": "/admin/products/batch",
|
||||||
"methods": [
|
"methods": [
|
||||||
"GET",
|
"POST",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isAppRoute": true,
|
||||||
"matcher": "/admin/products/:id",
|
"matcher": "/admin/products/:id",
|
||||||
"methods": [
|
"methods": [
|
||||||
"GET",
|
"GET",
|
||||||
@@ -150,6 +177,15 @@ describe("Routes sorter", () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"handler": [Function],
|
"handler": [Function],
|
||||||
|
"isAppRoute": true,
|
||||||
|
"matcher": "/admin/products/:id",
|
||||||
|
"methods": [
|
||||||
|
"POST",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handler": [Function],
|
||||||
|
"isAppRoute": true,
|
||||||
"matcher": "/admin/:id/export",
|
"matcher": "/admin/:id/export",
|
||||||
"methods": [
|
"methods": [
|
||||||
"GET",
|
"GET",
|
||||||
@@ -286,4 +322,77 @@ describe("Routes sorter", () => {
|
|||||||
]
|
]
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should sort routes with multiple params and static values", () => {
|
||||||
|
const sorter = new RoutesSorter([
|
||||||
|
{
|
||||||
|
matcher: "/admin/promotions/:id/:rule_type",
|
||||||
|
methods: ["GET"],
|
||||||
|
handler: () => {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
matcher:
|
||||||
|
"/admin/promotions/rule-value-options/:rule_type/:rule_attribute_id",
|
||||||
|
methods: ["GET"],
|
||||||
|
handler: () => {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
matcher: "/admin/promotions/rule-attribute-options/:rule_type",
|
||||||
|
methods: ["GET"],
|
||||||
|
handler: () => {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
matcher: "/admin/promotions/:id/:rule_type",
|
||||||
|
methods: ["GET"],
|
||||||
|
handler: () => {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
matcher: "/admin/promotions/rule-attribute-options/:rule_type",
|
||||||
|
methods: ["GET"],
|
||||||
|
isAppRoute: true,
|
||||||
|
handler: () => {},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(sorter.sort()).toMatchInlineSnapshot(`
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"handler": [Function],
|
||||||
|
"matcher": "/admin/promotions/rule-value-options/:rule_type/:rule_attribute_id",
|
||||||
|
"methods": [
|
||||||
|
"GET",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handler": [Function],
|
||||||
|
"matcher": "/admin/promotions/rule-attribute-options/:rule_type",
|
||||||
|
"methods": [
|
||||||
|
"GET",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handler": [Function],
|
||||||
|
"isAppRoute": true,
|
||||||
|
"matcher": "/admin/promotions/rule-attribute-options/:rule_type",
|
||||||
|
"methods": [
|
||||||
|
"GET",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handler": [Function],
|
||||||
|
"matcher": "/admin/promotions/:id/:rule_type",
|
||||||
|
"methods": [
|
||||||
|
"GET",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handler": [Function],
|
||||||
|
"matcher": "/admin/promotions/:id/:rule_type",
|
||||||
|
"methods": [
|
||||||
|
"GET",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ import { join } from "path"
|
|||||||
import { dynamicImport, FileSystem } from "@medusajs/utils"
|
import { dynamicImport, FileSystem } from "@medusajs/utils"
|
||||||
|
|
||||||
import { logger } from "../logger"
|
import { logger } from "../logger"
|
||||||
import type {
|
import {
|
||||||
MiddlewaresConfig,
|
type MiddlewaresConfig,
|
||||||
BodyParserConfigRoute,
|
type BodyParserConfigRoute,
|
||||||
ScannedMiddlewareDescriptor,
|
type MiddlewareDescriptor,
|
||||||
|
type MedusaErrorHandlerFunction,
|
||||||
|
HTTP_METHODS,
|
||||||
} from "./types"
|
} from "./types"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,21 +15,6 @@ import type {
|
|||||||
*/
|
*/
|
||||||
const MIDDLEWARE_FILE_NAME = "middlewares"
|
const MIDDLEWARE_FILE_NAME = "middlewares"
|
||||||
|
|
||||||
const log = ({
|
|
||||||
activityId,
|
|
||||||
message,
|
|
||||||
}: {
|
|
||||||
activityId?: string
|
|
||||||
message: string
|
|
||||||
}) => {
|
|
||||||
if (activityId) {
|
|
||||||
logger.progress(activityId, message)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exposes the API to scan a directory and load the `middleware.ts` file. This file contains
|
* Exposes the API to scan a directory and load the `middleware.ts` file. This file contains
|
||||||
* the configuration for certain global middlewares and core routes validators. Also, it may
|
* the configuration for certain global middlewares and core routes validators. Also, it may
|
||||||
@@ -35,19 +22,19 @@ const log = ({
|
|||||||
*/
|
*/
|
||||||
export class MiddlewareFileLoader {
|
export class MiddlewareFileLoader {
|
||||||
/**
|
/**
|
||||||
* Middleware collected manually or by scanning directories
|
* Global error handler exported from the middleware file loader
|
||||||
*/
|
*/
|
||||||
#middleware: ScannedMiddlewareDescriptor[] = []
|
#errorHandler?: MedusaErrorHandlerFunction
|
||||||
#bodyParserConfigRoutes: BodyParserConfigRoute[] = []
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An eventual activity id for information tracking
|
* Middleware collected manually or by scanning directories
|
||||||
*/
|
*/
|
||||||
readonly #activityId?: string
|
#middleware: MiddlewareDescriptor[] = []
|
||||||
|
|
||||||
constructor({ activityId }: { activityId?: string }) {
|
/**
|
||||||
this.#activityId = activityId
|
* Route matchers on which a custom body parser config is used
|
||||||
}
|
*/
|
||||||
|
#bodyParserConfigRoutes: BodyParserConfigRoute[] = []
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes the middleware file and returns the middleware and the
|
* Processes the middleware file and returns the middleware and the
|
||||||
@@ -58,25 +45,23 @@ export class MiddlewareFileLoader {
|
|||||||
|
|
||||||
const middlewareConfig = middlewareExports.default
|
const middlewareConfig = middlewareExports.default
|
||||||
if (!middlewareConfig) {
|
if (!middlewareConfig) {
|
||||||
log({
|
logger.warn(
|
||||||
activityId: this.#activityId,
|
`No middleware configuration found in ${absolutePath}. Skipping middleware configuration.`
|
||||||
message: `No middleware configuration found in ${absolutePath}. Skipping middleware configuration.`,
|
)
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const routes = middlewareConfig.routes as MiddlewaresConfig["routes"]
|
const routes = middlewareConfig.routes as MiddlewaresConfig["routes"]
|
||||||
if (!routes || !Array.isArray(routes)) {
|
if (!routes || !Array.isArray(routes)) {
|
||||||
log({
|
logger.warn(
|
||||||
activityId: this.#activityId,
|
`Invalid default export found in ${absolutePath}. Make sure to use "defineMiddlewares" function and export its output.`
|
||||||
message: `Invalid default export found in ${absolutePath}. Make sure to use "defineMiddlewares" function and export its output.`,
|
)
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = routes.reduce<{
|
const result = routes.reduce<{
|
||||||
bodyParserConfigRoutes: BodyParserConfigRoute[]
|
bodyParserConfigRoutes: BodyParserConfigRoute[]
|
||||||
middleware: ScannedMiddlewareDescriptor[]
|
middleware: MiddlewareDescriptor[]
|
||||||
}>(
|
}>(
|
||||||
(result, route) => {
|
(result, route) => {
|
||||||
if (!route.matcher) {
|
if (!route.matcher) {
|
||||||
@@ -92,9 +77,15 @@ export class MiddlewareFileLoader {
|
|||||||
const matcher = String(route.matcher)
|
const matcher = String(route.matcher)
|
||||||
|
|
||||||
if ("bodyParser" in route && route.bodyParser !== undefined) {
|
if ("bodyParser" in route && route.bodyParser !== undefined) {
|
||||||
|
const methods = route.methods || [...HTTP_METHODS]
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
`using custom bodyparser config on matcher ${methods}:${route.matcher}`
|
||||||
|
)
|
||||||
|
|
||||||
result.bodyParserConfigRoutes.push({
|
result.bodyParserConfigRoutes.push({
|
||||||
matcher: matcher,
|
matcher: matcher,
|
||||||
method: route.method,
|
methods,
|
||||||
config: route.bodyParser,
|
config: route.bodyParser,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -104,7 +95,7 @@ export class MiddlewareFileLoader {
|
|||||||
result.middleware.push({
|
result.middleware.push({
|
||||||
handler: middleware,
|
handler: middleware,
|
||||||
matcher: matcher,
|
matcher: matcher,
|
||||||
method: route.method,
|
methods: route.methods,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -116,8 +107,16 @@ export class MiddlewareFileLoader {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
this.#middleware = result.middleware
|
const errorHandler =
|
||||||
this.#bodyParserConfigRoutes = result.bodyParserConfigRoutes
|
middlewareConfig.errorHandler as MiddlewaresConfig["errorHandler"]
|
||||||
|
|
||||||
|
if (errorHandler) {
|
||||||
|
this.#errorHandler = errorHandler
|
||||||
|
}
|
||||||
|
this.#middleware = this.#middleware.concat(result.middleware)
|
||||||
|
this.#bodyParserConfigRoutes = this.#bodyParserConfigRoutes.concat(
|
||||||
|
result.bodyParserConfigRoutes
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -133,11 +132,18 @@ export class MiddlewareFileLoader {
|
|||||||
)
|
)
|
||||||
} else if (await fs.exists(`${MIDDLEWARE_FILE_NAME}.js`)) {
|
} else if (await fs.exists(`${MIDDLEWARE_FILE_NAME}.js`)) {
|
||||||
await this.#processMiddlewareFile(
|
await this.#processMiddlewareFile(
|
||||||
join(sourceDir, `${MIDDLEWARE_FILE_NAME}.ts`)
|
join(sourceDir, `${MIDDLEWARE_FILE_NAME}.js`)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the globally registered error handler (if any)
|
||||||
|
*/
|
||||||
|
getErrorHandler() {
|
||||||
|
return this.#errorHandler
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a collection of registered middleware
|
* Returns a collection of registered middleware
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
import { isObject, isPresent } from "@medusajs/utils"
|
import { isObject, isPresent } from "@medusajs/utils"
|
||||||
import { MedusaNextFunction, MedusaRequest } from "../types"
|
import type {
|
||||||
|
MedusaNextFunction,
|
||||||
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "../types"
|
||||||
|
|
||||||
export function applyDefaultFilters<TFilter extends object>(
|
export function applyDefaultFilters<TFilter extends object>(
|
||||||
filtersToApply: TFilter
|
filtersToApply: TFilter
|
||||||
) {
|
) {
|
||||||
return async (req: MedusaRequest, _, next: MedusaNextFunction) => {
|
return async function defaultFiltersMiddleware(
|
||||||
|
req: MedusaRequest,
|
||||||
|
_: MedusaResponse,
|
||||||
|
next: MedusaNextFunction
|
||||||
|
) {
|
||||||
for (const [filter, filterValue] of Object.entries(filtersToApply)) {
|
for (const [filter, filterValue] of Object.entries(filtersToApply)) {
|
||||||
let valueToApply = filterValue
|
let valueToApply = filterValue
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
import { MedusaNextFunction, MedusaRequest } from "../types"
|
import type {
|
||||||
|
MedusaNextFunction,
|
||||||
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "../types"
|
||||||
|
|
||||||
export function applyParamsAsFilters(mappings: { [param: string]: string }) {
|
export function applyParamsAsFilters(mappings: { [param: string]: string }) {
|
||||||
return async (req: MedusaRequest, _, next: MedusaNextFunction) => {
|
return async function paramsAsFiltersMiddleware(
|
||||||
|
req: MedusaRequest,
|
||||||
|
_: MedusaResponse,
|
||||||
|
next: MedusaNextFunction
|
||||||
|
) {
|
||||||
for (const [param, paramValue] of Object.entries(req.params)) {
|
for (const [param, paramValue] of Object.entries(req.params)) {
|
||||||
if (mappings[param]) {
|
if (mappings[param]) {
|
||||||
req.filterableFields[mappings[param]] = paramValue
|
req.filterableFields[mappings[param]] = paramValue
|
||||||
|
|||||||
96
packages/core/framework/src/http/middlewares/bodyparser.ts
Normal file
96
packages/core/framework/src/http/middlewares/bodyparser.ts
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import { memoize } from "lodash"
|
||||||
|
import logger from "@medusajs/cli/dist/reporter"
|
||||||
|
import { json, NextFunction, RequestHandler, text, urlencoded } from "express"
|
||||||
|
|
||||||
|
import type {
|
||||||
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
MiddlewareVerb,
|
||||||
|
ParserConfigArgs,
|
||||||
|
MiddlewareFunction,
|
||||||
|
BodyParserConfigRoute,
|
||||||
|
} from "../types"
|
||||||
|
import type { RoutesFinder } from "../routes-finder"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parsers to use for parsing the HTTP request body
|
||||||
|
*/
|
||||||
|
const parsers = {
|
||||||
|
json: memoize(function jsonParserMiddleware(options?: ParserConfigArgs) {
|
||||||
|
return json({
|
||||||
|
limit: options?.sizeLimit,
|
||||||
|
verify: options?.preserveRawBody
|
||||||
|
? (req: MedusaRequest, res: MedusaResponse, buf: Buffer) => {
|
||||||
|
req.rawBody = buf
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
text: memoize(function textParser(options?: ParserConfigArgs) {
|
||||||
|
return text({
|
||||||
|
limit: options?.sizeLimit,
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
urlencoded: memoize(function urlencodedParserMiddleware(
|
||||||
|
options?: ParserConfigArgs
|
||||||
|
) {
|
||||||
|
return urlencoded({
|
||||||
|
limit: options?.sizeLimit,
|
||||||
|
extended: true,
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the bodyparser middlewares stack that creates custom bodyparsers
|
||||||
|
* during an HTTP request based upon the defined config. The bodyparser
|
||||||
|
* instances are cached for re-use.
|
||||||
|
*/
|
||||||
|
export function createBodyParserMiddlewaresStack(
|
||||||
|
route: string,
|
||||||
|
routesFinder: RoutesFinder<BodyParserConfigRoute>,
|
||||||
|
tracer?: (
|
||||||
|
handler: RequestHandler | MiddlewareFunction,
|
||||||
|
route: { route: string; method?: string }
|
||||||
|
) => RequestHandler | MiddlewareFunction
|
||||||
|
) {
|
||||||
|
return (["json", "text", "urlencoded"] as (keyof typeof parsers)[]).map(
|
||||||
|
(parser) => {
|
||||||
|
function bodyParser(
|
||||||
|
req: MedusaRequest,
|
||||||
|
res: MedusaResponse,
|
||||||
|
next: NextFunction
|
||||||
|
) {
|
||||||
|
const matchingRoute = routesFinder.find(
|
||||||
|
req.path,
|
||||||
|
req.method as MiddlewareVerb
|
||||||
|
)
|
||||||
|
const parserMiddleware = parsers[parser]
|
||||||
|
|
||||||
|
if (!matchingRoute) {
|
||||||
|
return parserMiddleware()(req, res, next)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchingRoute.config === false) {
|
||||||
|
logger.debug(
|
||||||
|
`skipping ${parser} bodyparser middleware ${req.method} ${req.path}`
|
||||||
|
)
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
`using custom ${parser} bodyparser config ${req.method} ${req.path}`
|
||||||
|
)
|
||||||
|
return parserMiddleware(matchingRoute.config)(req, res, next)
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(bodyParser, "name", {
|
||||||
|
value: `${parser}BodyParser`,
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
tracer ? tracer(bodyParser, { route }) : bodyParser
|
||||||
|
) as RequestHandler
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,7 +1,15 @@
|
|||||||
import { MedusaNextFunction, MedusaRequest } from "../types"
|
import type {
|
||||||
|
MedusaNextFunction,
|
||||||
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "../types"
|
||||||
|
|
||||||
export function clearFiltersByKey(keys: string[]) {
|
export function clearFiltersByKey(keys: string[]) {
|
||||||
return async (req: MedusaRequest, _, next: MedusaNextFunction) => {
|
return async function clearFiltersByKeyMiddleware(
|
||||||
|
req: MedusaRequest,
|
||||||
|
_: MedusaResponse,
|
||||||
|
next: MedusaNextFunction
|
||||||
|
) {
|
||||||
keys.forEach((key) => {
|
keys.forEach((key) => {
|
||||||
delete req.filterableFields[key]
|
delete req.filterableFields[key]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
MedusaError,
|
MedusaError,
|
||||||
PUBLISHABLE_KEY_HEADER,
|
PUBLISHABLE_KEY_HEADER,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import {
|
import type {
|
||||||
MedusaNextFunction,
|
MedusaNextFunction,
|
||||||
MedusaResponse,
|
MedusaResponse,
|
||||||
MedusaStoreRequest,
|
MedusaStoreRequest,
|
||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
|
|
||||||
export async function ensurePublishableApiKeyMiddleware(
|
export async function ensurePublishableApiKeyMiddleware(
|
||||||
req: MedusaStoreRequest,
|
req: MedusaStoreRequest,
|
||||||
_res: MedusaResponse,
|
_: MedusaResponse,
|
||||||
next: MedusaNextFunction
|
next: MedusaNextFunction
|
||||||
) {
|
) {
|
||||||
const publishableApiKey = req.get(PUBLISHABLE_KEY_HEADER)
|
const publishableApiKey = req.get(PUBLISHABLE_KEY_HEADER)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { NextFunction, Response } from "express"
|
import { NextFunction, ErrorRequestHandler, Response } from "express"
|
||||||
|
|
||||||
import { ContainerRegistrationKeys, MedusaError } from "@medusajs/utils"
|
import { ContainerRegistrationKeys, MedusaError } from "@medusajs/utils"
|
||||||
import { formatException } from "./exception-formatter"
|
import { formatException } from "./exception-formatter"
|
||||||
@@ -13,12 +13,12 @@ const INVALID_REQUEST_ERROR = "invalid_request_error"
|
|||||||
const INVALID_STATE_ERROR = "invalid_state_error"
|
const INVALID_STATE_ERROR = "invalid_state_error"
|
||||||
|
|
||||||
export function errorHandler() {
|
export function errorHandler() {
|
||||||
return (
|
return function coreErrorHandler(
|
||||||
err: MedusaError,
|
err: MedusaError,
|
||||||
req: MedusaRequest,
|
req: MedusaRequest,
|
||||||
res: Response,
|
res: Response,
|
||||||
next: NextFunction
|
_: NextFunction
|
||||||
) => {
|
) {
|
||||||
const logger = req.scope.resolve(ContainerRegistrationKeys.LOGGER)
|
const logger = req.scope.resolve(ContainerRegistrationKeys.LOGGER)
|
||||||
|
|
||||||
err = formatException(err)
|
err = formatException(err)
|
||||||
@@ -76,7 +76,7 @@ export function errorHandler() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res.status(statusCode).json(errObj)
|
res.status(statusCode).json(errObj)
|
||||||
}
|
} as unknown as ErrorRequestHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
64
packages/core/framework/src/http/routes-finder.ts
Normal file
64
packages/core/framework/src/http/routes-finder.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import pathToRegexp from "path-to-regexp"
|
||||||
|
import type { MiddlewareVerb, RouteVerb } from "./types"
|
||||||
|
|
||||||
|
export class RoutesFinder<
|
||||||
|
T extends
|
||||||
|
| { matcher: string; methods: MiddlewareVerb | MiddlewareVerb[] }
|
||||||
|
| { matcher: string; method: RouteVerb }
|
||||||
|
> {
|
||||||
|
/**
|
||||||
|
* Cache of existing matches to avoid regex tests on every
|
||||||
|
* single HTTP request
|
||||||
|
*/
|
||||||
|
#existingMatches: Map<
|
||||||
|
string,
|
||||||
|
| (T & {
|
||||||
|
matchRegex: RegExp
|
||||||
|
})
|
||||||
|
| null
|
||||||
|
> = new Map()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection of registered routes
|
||||||
|
*/
|
||||||
|
#routes: (T & {
|
||||||
|
matchRegex: RegExp
|
||||||
|
})[] = []
|
||||||
|
|
||||||
|
constructor(routes?: T[]) {
|
||||||
|
if (routes) {
|
||||||
|
routes.forEach((route) => this.add(route))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register route for lookup
|
||||||
|
*/
|
||||||
|
add(route: T) {
|
||||||
|
this.#routes.push({
|
||||||
|
...route,
|
||||||
|
matchRegex: pathToRegexp(route.matcher),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the matching route for a given HTTP method and URL
|
||||||
|
*/
|
||||||
|
find(url: string, method: MiddlewareVerb) {
|
||||||
|
const key = `${method}:${url}`
|
||||||
|
if (this.#existingMatches.has(key)) {
|
||||||
|
return this.#existingMatches.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
const result =
|
||||||
|
this.#routes.find((route) => {
|
||||||
|
if ("methods" in route) {
|
||||||
|
return route.methods.includes(method) && route.matchRegex.test(url)
|
||||||
|
}
|
||||||
|
return route.method === method && route.matchRegex.test(url)
|
||||||
|
}) ?? null
|
||||||
|
|
||||||
|
this.#existingMatches.set(key, result)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,7 @@
|
|||||||
import { join, parse, sep } from "path"
|
import { join, parse, sep } from "path"
|
||||||
import { dynamicImport, readDirRecursive } from "@medusajs/utils"
|
import { dynamicImport, readDirRecursive } from "@medusajs/utils"
|
||||||
import { logger } from "../logger"
|
import { logger } from "../logger"
|
||||||
import {
|
import { type RouteVerb, HTTP_METHODS, type RouteDescriptor } from "./types"
|
||||||
type RouteVerb,
|
|
||||||
HTTP_METHODS,
|
|
||||||
type ScannedRouteDescriptor,
|
|
||||||
type FileSystemRouteDescriptor,
|
|
||||||
} from "./types"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File name that is used to indicate that the file is a route file
|
* File name that is used to indicate that the file is a route file
|
||||||
@@ -43,21 +38,6 @@ const ADMIN_ROUTE_MATCH = /(\/admin$|\/admin\/)/
|
|||||||
const STORE_ROUTE_MATCH = /(\/store$|\/store\/)/
|
const STORE_ROUTE_MATCH = /(\/store$|\/store\/)/
|
||||||
const AUTH_ROUTE_MATCH = /(\/auth$|\/auth\/)/
|
const AUTH_ROUTE_MATCH = /(\/auth$|\/auth\/)/
|
||||||
|
|
||||||
const log = ({
|
|
||||||
activityId,
|
|
||||||
message,
|
|
||||||
}: {
|
|
||||||
activityId?: string
|
|
||||||
message: string
|
|
||||||
}) => {
|
|
||||||
if (activityId) {
|
|
||||||
logger.progress(activityId, message)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exposes to API to register routes manually or by scanning the filesystem from a
|
* Exposes to API to register routes manually or by scanning the filesystem from a
|
||||||
* source directory.
|
* source directory.
|
||||||
@@ -69,19 +49,7 @@ export class RoutesLoader {
|
|||||||
/**
|
/**
|
||||||
* Routes collected manually or by scanning directories
|
* Routes collected manually or by scanning directories
|
||||||
*/
|
*/
|
||||||
#routes: Record<
|
#routes: Record<string, Record<string, RouteDescriptor>> = {}
|
||||||
string,
|
|
||||||
Record<string, ScannedRouteDescriptor | FileSystemRouteDescriptor>
|
|
||||||
> = {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An eventual activity id for information tracking
|
|
||||||
*/
|
|
||||||
readonly #activityId?: string
|
|
||||||
|
|
||||||
constructor({ activityId }: { activityId?: string }) {
|
|
||||||
this.#activityId = activityId
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the route path from its relative file path.
|
* Creates the route path from its relative file path.
|
||||||
@@ -96,10 +64,9 @@ export class RoutesLoader {
|
|||||||
if (segment.startsWith("[")) {
|
if (segment.startsWith("[")) {
|
||||||
segment = segment.replace(PARAM_SEGMENT_MATCHER, (_, group) => {
|
segment = segment.replace(PARAM_SEGMENT_MATCHER, (_, group) => {
|
||||||
if (params[group]) {
|
if (params[group]) {
|
||||||
log({
|
logger.debug(
|
||||||
activityId: this.#activityId,
|
`Duplicate parameters found in route ${relativePath} (${group})`
|
||||||
message: `Duplicate parameters found in route ${relativePath} (${group})`,
|
)
|
||||||
})
|
|
||||||
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Duplicate parameters found in route ${relativePath} (${group}). Make sure that all parameters are unique.`
|
`Duplicate parameters found in route ${relativePath} (${group}). Make sure that all parameters are unique.`
|
||||||
@@ -122,7 +89,7 @@ export class RoutesLoader {
|
|||||||
async #getRoutesForFile(
|
async #getRoutesForFile(
|
||||||
routePath: string,
|
routePath: string,
|
||||||
absolutePath: string
|
absolutePath: string
|
||||||
): Promise<ScannedRouteDescriptor[]> {
|
): Promise<RouteDescriptor[]> {
|
||||||
const routeExports = await dynamicImport(absolutePath)
|
const routeExports = await dynamicImport(absolutePath)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -161,10 +128,9 @@ export class RoutesLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!HTTP_METHODS.includes(key as RouteVerb)) {
|
if (!HTTP_METHODS.includes(key as RouteVerb)) {
|
||||||
log({
|
logger.debug(
|
||||||
activityId: this.#activityId,
|
`Skipping handler ${key} in ${absolutePath}. Invalid HTTP method: ${key}.`
|
||||||
message: `Skipping handler ${key} in ${absolutePath}. Invalid HTTP method: ${key}.`,
|
)
|
||||||
})
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,14 +138,15 @@ export class RoutesLoader {
|
|||||||
})
|
})
|
||||||
.map((key) => {
|
.map((key) => {
|
||||||
return {
|
return {
|
||||||
route: routePath,
|
isRoute: true,
|
||||||
|
matcher: routePath,
|
||||||
method: key as RouteVerb,
|
method: key as RouteVerb,
|
||||||
handler: routeExports[key],
|
handler: routeExports[key],
|
||||||
optedOutOfAuth: !shouldAuthenticate,
|
optedOutOfAuth: !shouldAuthenticate,
|
||||||
shouldAppendAdminCors: shouldApplyCors && routeType === "admin",
|
shouldAppendAdminCors: shouldApplyCors && routeType === "admin",
|
||||||
shouldAppendAuthCors: shouldApplyCors && routeType === "auth",
|
shouldAppendAuthCors: shouldApplyCors && routeType === "auth",
|
||||||
shouldAppendStoreCors: shouldApplyCors && routeType === "store",
|
shouldAppendStoreCors: shouldApplyCors && routeType === "store",
|
||||||
} satisfies ScannedRouteDescriptor
|
} satisfies RouteDescriptor
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,18 +199,16 @@ export class RoutesLoader {
|
|||||||
/**
|
/**
|
||||||
* Register a route
|
* Register a route
|
||||||
*/
|
*/
|
||||||
registerRoute(route: ScannedRouteDescriptor | FileSystemRouteDescriptor) {
|
registerRoute(route: RouteDescriptor) {
|
||||||
this.#routes[route.route] = this.#routes[route.route] ?? {}
|
this.#routes[route.matcher] = this.#routes[route.matcher] ?? {}
|
||||||
const trackedRoute = this.#routes[route.route]
|
const trackedRoute = this.#routes[route.matcher]
|
||||||
trackedRoute[route.method] = route
|
trackedRoute[route.method] = route
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register one or more routes
|
* Register one or more routes
|
||||||
*/
|
*/
|
||||||
registerRoutes(
|
registerRoutes(routes: RouteDescriptor[]) {
|
||||||
routes: (ScannedRouteDescriptor | FileSystemRouteDescriptor)[]
|
|
||||||
) {
|
|
||||||
routes.forEach((route) => this.registerRoute(route))
|
routes.forEach((route) => this.registerRoute(route))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,14 +217,16 @@ export class RoutesLoader {
|
|||||||
* manually.
|
* manually.
|
||||||
*/
|
*/
|
||||||
getRoutes() {
|
getRoutes() {
|
||||||
return Object.keys(this.#routes).reduce<
|
return Object.keys(this.#routes).reduce<RouteDescriptor[]>(
|
||||||
(ScannedRouteDescriptor | FileSystemRouteDescriptor)[]
|
(result, routePattern) => {
|
||||||
>((result, routePattern) => {
|
const methodsRoutes = this.#routes[routePattern]
|
||||||
const methodsRoutes = this.#routes[routePattern]
|
Object.keys(methodsRoutes).forEach((method) => {
|
||||||
Object.keys(methodsRoutes).forEach((method) => {
|
const route = methodsRoutes[method]
|
||||||
result.push(methodsRoutes[method])
|
result.push(route)
|
||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
}, [])
|
},
|
||||||
|
[]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { MiddlewareVerb } from "./types"
|
import { MiddlewareVerb, RouteVerb } from "./types"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Route represents both the middleware/routes defined via the
|
* Route represents both the middleware/routes defined via the
|
||||||
* "defineMiddlewares" method and the routes scanned from
|
* "defineMiddlewares" method and the routes scanned from
|
||||||
* the filesystem. The later one's must be marked with "isAppRoute = true".
|
* the filesystem.
|
||||||
*/
|
*/
|
||||||
type Route = {
|
type Route = {
|
||||||
/**
|
/**
|
||||||
@@ -17,10 +17,9 @@ type Route = {
|
|||||||
handler?: any
|
handler?: any
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Must be true when the route is discovered via the fileystem
|
* The HTTP method specified as a single value
|
||||||
* scanning.
|
|
||||||
*/
|
*/
|
||||||
isAppRoute?: boolean
|
method?: RouteVerb
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The HTTP methods this route is supposed to handle.
|
* The HTTP methods this route is supposed to handle.
|
||||||
@@ -48,39 +47,39 @@ type Route = {
|
|||||||
* - static
|
* - static
|
||||||
* - params
|
* - params
|
||||||
*/
|
*/
|
||||||
type RoutesBranch = {
|
type RoutesBranch<T extends Route> = {
|
||||||
global: {
|
global: {
|
||||||
routes: Route[]
|
routes: T[]
|
||||||
children?: {
|
children?: {
|
||||||
[segment: string]: RoutesBranch
|
[segment: string]: RoutesBranch<T>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
regex: {
|
regex: {
|
||||||
routes: Route[]
|
routes: T[]
|
||||||
children?: {
|
children?: {
|
||||||
[segment: string]: RoutesBranch
|
[segment: string]: RoutesBranch<T>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wildcard: {
|
wildcard: {
|
||||||
routes: Route[]
|
routes: T[]
|
||||||
children?: {
|
children?: {
|
||||||
[segment: string]: RoutesBranch
|
[segment: string]: RoutesBranch<T>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
params: {
|
params: {
|
||||||
routes: Route[]
|
routes: T[]
|
||||||
children?: {
|
children?: {
|
||||||
[segment: string]: RoutesBranch
|
[segment: string]: RoutesBranch<T>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static: {
|
static: {
|
||||||
routes: Route[]
|
routes: T[]
|
||||||
children?: {
|
children?: {
|
||||||
[segment: string]: RoutesBranch
|
[segment: string]: RoutesBranch<T>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,29 +90,42 @@ type RoutesBranch = {
|
|||||||
* like structure and then sort them back to a flat array based upon the
|
* like structure and then sort them back to a flat array based upon the
|
||||||
* priorities of different types of nodes.
|
* priorities of different types of nodes.
|
||||||
*/
|
*/
|
||||||
export class RoutesSorter {
|
export class RoutesSorter<T extends Route> {
|
||||||
|
/**
|
||||||
|
* The order in which the routes will be sorted. This
|
||||||
|
* can be overridden at the time of call the sort
|
||||||
|
* method.
|
||||||
|
*/
|
||||||
|
#orderBy: [
|
||||||
|
keyof RoutesBranch<T>,
|
||||||
|
keyof RoutesBranch<T>,
|
||||||
|
keyof RoutesBranch<T>,
|
||||||
|
keyof RoutesBranch<T>,
|
||||||
|
keyof RoutesBranch<T>
|
||||||
|
] = ["global", "wildcard", "regex", "static", "params"]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Input routes
|
* Input routes
|
||||||
*/
|
*/
|
||||||
#routesToProcess: Route[]
|
#routesToProcess: T[]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Intermediate tree representation for sorting routes
|
* Intermediate tree representation for sorting routes
|
||||||
*/
|
*/
|
||||||
#routesTree: {
|
#routesTree: {
|
||||||
[segment: string]: RoutesBranch
|
[segment: string]: RoutesBranch<T>
|
||||||
} = {
|
} = {
|
||||||
root: this.#createBranch(),
|
root: this.#createBranch(),
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(routes: Route[]) {
|
constructor(routes: T[]) {
|
||||||
this.#routesToProcess = routes
|
this.#routesToProcess = routes
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an empty branch with known nodes
|
* Creates an empty branch with known nodes
|
||||||
*/
|
*/
|
||||||
#createBranch(): RoutesBranch {
|
#createBranch(): RoutesBranch<T> {
|
||||||
return {
|
return {
|
||||||
global: {
|
global: {
|
||||||
routes: [],
|
routes: [],
|
||||||
@@ -162,14 +174,14 @@ export class RoutesSorter {
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
#processRoute(route: Route) {
|
#processRoute(route: T) {
|
||||||
const segments = route.matcher.split("/").filter((s) => s.length)
|
const segments = route.matcher.split("/").filter((s) => s.length)
|
||||||
let parent = this.#routesTree["root"]
|
let parent = this.#routesTree["root"]
|
||||||
|
|
||||||
segments.forEach((segment, index) => {
|
segments.forEach((segment, index) => {
|
||||||
let bucket: keyof RoutesBranch = "static"
|
let bucket: keyof RoutesBranch<T> = "static"
|
||||||
|
|
||||||
if (!route.methods) {
|
if (!route.methods && !route.method) {
|
||||||
bucket = "global"
|
bucket = "global"
|
||||||
} else if (segment.startsWith("*")) {
|
} else if (segment.startsWith("*")) {
|
||||||
bucket = "wildcard"
|
bucket = "wildcard"
|
||||||
@@ -194,40 +206,51 @@ export class RoutesSorter {
|
|||||||
/**
|
/**
|
||||||
* Returns an array of sorted routes for a given branch.
|
* Returns an array of sorted routes for a given branch.
|
||||||
*/
|
*/
|
||||||
#sortBranch(routeBranch: { [segment: string]: RoutesBranch }) {
|
#sortBranch(
|
||||||
|
routeBranch: { [segment: string]: RoutesBranch<T> },
|
||||||
|
orderBy: [
|
||||||
|
keyof RoutesBranch<T>,
|
||||||
|
keyof RoutesBranch<T>,
|
||||||
|
keyof RoutesBranch<T>,
|
||||||
|
keyof RoutesBranch<T>,
|
||||||
|
keyof RoutesBranch<T>
|
||||||
|
]
|
||||||
|
) {
|
||||||
const branchRoutes = Object.keys(routeBranch).reduce<{
|
const branchRoutes = Object.keys(routeBranch).reduce<{
|
||||||
global: Route[]
|
global: T[]
|
||||||
wildcard: Route[]
|
wildcard: T[]
|
||||||
regex: Route[]
|
regex: T[]
|
||||||
params: Route[]
|
params: T[]
|
||||||
static: Route[]
|
static: T[]
|
||||||
}>(
|
}>(
|
||||||
(result, branchKey) => {
|
(result, branchKey) => {
|
||||||
const node = routeBranch[branchKey]
|
const node = routeBranch[branchKey]
|
||||||
|
|
||||||
result.global.push(...node.global.routes)
|
result.global.push(...node.global.routes)
|
||||||
if (node.global.children) {
|
if (node.global.children) {
|
||||||
result.global.push(...this.#sortBranch(node.global.children))
|
result.global.push(...this.#sortBranch(node.global.children, orderBy))
|
||||||
}
|
}
|
||||||
|
|
||||||
result.wildcard.push(...node.wildcard.routes)
|
result.wildcard.push(...node.wildcard.routes)
|
||||||
if (node.wildcard.children) {
|
if (node.wildcard.children) {
|
||||||
result.wildcard.push(...this.#sortBranch(node.wildcard.children))
|
result.wildcard.push(
|
||||||
|
...this.#sortBranch(node.wildcard.children, orderBy)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
result.regex.push(...node.regex.routes)
|
result.regex.push(...node.regex.routes)
|
||||||
if (node.regex.children) {
|
if (node.regex.children) {
|
||||||
result.regex.push(...this.#sortBranch(node.regex.children))
|
result.regex.push(...this.#sortBranch(node.regex.children, orderBy))
|
||||||
}
|
}
|
||||||
|
|
||||||
result.static.push(...node.static.routes)
|
result.static.push(...node.static.routes)
|
||||||
if (node.static.children) {
|
if (node.static.children) {
|
||||||
result.static.push(...this.#sortBranch(node.static.children))
|
result.static.push(...this.#sortBranch(node.static.children, orderBy))
|
||||||
}
|
}
|
||||||
|
|
||||||
result.params.push(...node.params.routes)
|
result.params.push(...node.params.routes)
|
||||||
if (node.params.children) {
|
if (node.params.children) {
|
||||||
result.params.push(...this.#sortBranch(node.params.children))
|
result.params.push(...this.#sortBranch(node.params.children, orderBy))
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@@ -244,19 +267,33 @@ export class RoutesSorter {
|
|||||||
/**
|
/**
|
||||||
* Concatenating routes as per their priority.
|
* Concatenating routes as per their priority.
|
||||||
*/
|
*/
|
||||||
const routes: Route[] = branchRoutes.global
|
return orderBy.reduce<T[]>((result, branch) => {
|
||||||
.concat(branchRoutes.wildcard)
|
result = result.concat(branchRoutes[branch])
|
||||||
.concat(branchRoutes.regex)
|
return result
|
||||||
.concat(branchRoutes.static)
|
}, [])
|
||||||
.concat(branchRoutes.params)
|
|
||||||
return routes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sort the input routes
|
* Returns the intermediate representation of routes as a tree.
|
||||||
*/
|
*/
|
||||||
sort() {
|
getTree() {
|
||||||
|
return this.#routesTree
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort the input routes. You can optionally specify a custom
|
||||||
|
* orderBy array. Defaults to: ["global", "wildcard", "regex", "static", "params"]
|
||||||
|
*/
|
||||||
|
sort(
|
||||||
|
orderBy?: [
|
||||||
|
keyof RoutesBranch<T>,
|
||||||
|
keyof RoutesBranch<T>,
|
||||||
|
keyof RoutesBranch<T>,
|
||||||
|
keyof RoutesBranch<T>,
|
||||||
|
keyof RoutesBranch<T>
|
||||||
|
]
|
||||||
|
) {
|
||||||
this.#routesToProcess.map((route) => this.#processRoute(route))
|
this.#routesToProcess.map((route) => this.#processRoute(route))
|
||||||
return this.#sortBranch(this.#routesTree)
|
return this.#sortBranch(this.#routesTree, orderBy ?? this.#orderBy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,20 +34,6 @@ export type AsyncRouteHandler = (
|
|||||||
|
|
||||||
export type RouteHandler = SyncRouteHandler | AsyncRouteHandler
|
export type RouteHandler = SyncRouteHandler | AsyncRouteHandler
|
||||||
|
|
||||||
export type RouteImplementation = {
|
|
||||||
method?: RouteVerb
|
|
||||||
handler: RouteHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
export type RouteConfig = {
|
|
||||||
optedOutOfAuth?: boolean
|
|
||||||
routeType?: "admin" | "store" | "auth"
|
|
||||||
shouldAppendAdminCors?: boolean
|
|
||||||
shouldAppendStoreCors?: boolean
|
|
||||||
shouldAppendAuthCors?: boolean
|
|
||||||
routes?: RouteImplementation[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MiddlewareFunction =
|
export type MiddlewareFunction =
|
||||||
| MedusaRequestHandler
|
| MedusaRequestHandler
|
||||||
| ((...args: any[]) => any)
|
| ((...args: any[]) => any)
|
||||||
@@ -67,7 +53,11 @@ export type ParserConfigArgs = {
|
|||||||
export type ParserConfig = false | ParserConfigArgs
|
export type ParserConfig = false | ParserConfigArgs
|
||||||
|
|
||||||
export type MiddlewareRoute = {
|
export type MiddlewareRoute = {
|
||||||
|
/**
|
||||||
|
* @deprecated. Instead use {@link MiddlewareRoute.methods}
|
||||||
|
*/
|
||||||
method?: MiddlewareVerb | MiddlewareVerb[]
|
method?: MiddlewareVerb | MiddlewareVerb[]
|
||||||
|
methods?: MiddlewareVerb[]
|
||||||
matcher: string | RegExp
|
matcher: string | RegExp
|
||||||
bodyParser?: ParserConfig
|
bodyParser?: ParserConfig
|
||||||
middlewares?: MiddlewareFunction[]
|
middlewares?: MiddlewareFunction[]
|
||||||
@@ -78,49 +68,38 @@ export type MiddlewaresConfig = {
|
|||||||
routes?: MiddlewareRoute[]
|
routes?: MiddlewareRoute[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RouteDescriptor = {
|
|
||||||
absolutePath: string
|
|
||||||
relativePath: string
|
|
||||||
route: string
|
|
||||||
priority: number
|
|
||||||
config?: RouteConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Route descriptor refers represents a route either scanned
|
* Route descriptor refers represents a route either scanned
|
||||||
* from the filesystem or registered manually. It does not
|
* from the filesystem or registered manually. It does not
|
||||||
* represent a middleware
|
* represent a middleware
|
||||||
*/
|
*/
|
||||||
export type ScannedRouteDescriptor = {
|
export type RouteDescriptor = {
|
||||||
route: string
|
matcher: string
|
||||||
method: RouteVerb
|
method: RouteVerb
|
||||||
handler: RouteHandler
|
handler: RouteHandler
|
||||||
optedOutOfAuth: boolean
|
optedOutOfAuth: boolean
|
||||||
|
isRoute: true
|
||||||
routeType?: "admin" | "store" | "auth"
|
routeType?: "admin" | "store" | "auth"
|
||||||
|
absolutePath?: string
|
||||||
|
relativePath?: string
|
||||||
shouldAppendAdminCors: boolean
|
shouldAppendAdminCors: boolean
|
||||||
shouldAppendStoreCors: boolean
|
shouldAppendStoreCors: boolean
|
||||||
shouldAppendAuthCors: boolean
|
shouldAppendAuthCors: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FileSystem route description represents a route scanned from
|
* Represents a middleware
|
||||||
* the filesystem
|
|
||||||
*/
|
*/
|
||||||
export type FileSystemRouteDescriptor = ScannedRouteDescriptor & {
|
export type MiddlewareDescriptor = {
|
||||||
absolutePath: string
|
|
||||||
relativePath: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ScannedMiddlewareDescriptor = {
|
|
||||||
matcher: string
|
matcher: string
|
||||||
method?: MiddlewareVerb | MiddlewareVerb[]
|
methods?: MiddlewareVerb | MiddlewareVerb[]
|
||||||
handler: MiddlewareFunction
|
handler: MiddlewareFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BodyParserConfigRoute = {
|
export type BodyParserConfigRoute = {
|
||||||
matcher: string
|
matcher: string
|
||||||
method?: MiddlewareVerb | MiddlewareVerb[]
|
methods: MiddlewareVerb | MiddlewareVerb[]
|
||||||
config?: ParserConfig
|
config: ParserConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GlobalMiddlewareDescriptor = {
|
export type GlobalMiddlewareDescriptor = {
|
||||||
|
|||||||
@@ -16,7 +16,11 @@ import zod, { ZodRawShape } from "zod"
|
|||||||
*/
|
*/
|
||||||
export function defineMiddlewares<
|
export function defineMiddlewares<
|
||||||
Route extends {
|
Route extends {
|
||||||
|
/**
|
||||||
|
* @deprecated. Instead use {@link MiddlewareRoute.methods}
|
||||||
|
*/
|
||||||
method?: MiddlewareVerb | MiddlewareVerb[]
|
method?: MiddlewareVerb | MiddlewareVerb[]
|
||||||
|
methods?: MiddlewareVerb[]
|
||||||
matcher: string | RegExp
|
matcher: string | RegExp
|
||||||
bodyParser?: ParserConfig
|
bodyParser?: ParserConfig
|
||||||
additionalDataValidator?: ZodRawShape
|
additionalDataValidator?: ZodRawShape
|
||||||
@@ -38,7 +42,8 @@ export function defineMiddlewares<
|
|||||||
return {
|
return {
|
||||||
errorHandler,
|
errorHandler,
|
||||||
routes: routes.map((route) => {
|
routes: routes.map((route) => {
|
||||||
const { middlewares, additionalDataValidator, ...rest } = route
|
let { middlewares, method, methods, additionalDataValidator, ...rest } =
|
||||||
|
route
|
||||||
const customMiddleware: MedusaRequestHandler[] = []
|
const customMiddleware: MedusaRequestHandler[] = []
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,8 +59,13 @@ export function defineMiddlewares<
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!methods) {
|
||||||
|
methods = Array.isArray(method) ? method : method ? [method] : method
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...rest,
|
...rest,
|
||||||
|
methods,
|
||||||
middlewares: customMiddleware.concat(middlewares || []),
|
middlewares: customMiddleware.concat(middlewares || []),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ import {
|
|||||||
ContainerRegistrationKeys,
|
ContainerRegistrationKeys,
|
||||||
remoteQueryObjectFromString,
|
remoteQueryObjectFromString,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { MedusaNextFunction, MedusaRequest } from "../types"
|
import type {
|
||||||
|
MedusaNextFunction,
|
||||||
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "../types"
|
||||||
|
|
||||||
export function maybeApplyLinkFilter({
|
export function maybeApplyLinkFilter({
|
||||||
entryPoint,
|
entryPoint,
|
||||||
@@ -13,7 +17,7 @@ export function maybeApplyLinkFilter({
|
|||||||
}) {
|
}) {
|
||||||
return async function linkFilter(
|
return async function linkFilter(
|
||||||
req: MedusaRequest,
|
req: MedusaRequest,
|
||||||
_,
|
_: MedusaResponse,
|
||||||
next: MedusaNextFunction
|
next: MedusaNextFunction
|
||||||
) {
|
) {
|
||||||
const filterableFields = req.filterableFields
|
const filterableFields = req.filterableFields
|
||||||
|
|||||||
38
packages/core/framework/src/http/utils/wrap-handler.ts
Normal file
38
packages/core/framework/src/http/utils/wrap-handler.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import type {
|
||||||
|
MedusaNextFunction,
|
||||||
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
MiddlewareFunction,
|
||||||
|
RouteHandler,
|
||||||
|
} from "../types"
|
||||||
|
|
||||||
|
export const wrapHandler = <T extends RouteHandler | MiddlewareFunction>(
|
||||||
|
fn: T
|
||||||
|
) => {
|
||||||
|
async function wrappedHandler(
|
||||||
|
req: MedusaRequest,
|
||||||
|
res: MedusaResponse,
|
||||||
|
next: MedusaNextFunction
|
||||||
|
) {
|
||||||
|
const req_ = req as MedusaRequest & { errors?: Error[] }
|
||||||
|
if (req_?.errors?.length) {
|
||||||
|
return res.status(400).json({
|
||||||
|
errors: req_.errors,
|
||||||
|
message:
|
||||||
|
"Provided request body contains errors. Please check the data and retry the request",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await fn(req, res, next)
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
next(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fn.name) {
|
||||||
|
Object.defineProperty(wrappedHandler, "name", { value: fn.name })
|
||||||
|
}
|
||||||
|
return wrappedHandler as T
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ import {
|
|||||||
validateAndTransformBody,
|
validateAndTransformBody,
|
||||||
validateAndTransformQuery,
|
validateAndTransformQuery,
|
||||||
} from "@medusajs/framework"
|
} from "@medusajs/framework"
|
||||||
import { MiddlewareRoute, unlessPath } from "@medusajs/framework/http"
|
import { MiddlewareRoute } from "@medusajs/framework/http"
|
||||||
import { DEFAULT_BATCH_ENDPOINTS_SIZE_LIMIT } from "../../../utils/middlewares"
|
import { DEFAULT_BATCH_ENDPOINTS_SIZE_LIMIT } from "../../../utils/middlewares"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
import {
|
import {
|
||||||
@@ -116,12 +116,9 @@ export const adminInventoryRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
method: ["DELETE"],
|
method: ["DELETE"],
|
||||||
matcher: "/admin/inventory-items/:id/location-levels/:location_id",
|
matcher: "/admin/inventory-items/:id/location-levels/:location_id",
|
||||||
middlewares: [
|
middlewares: [
|
||||||
unlessPath(
|
validateAndTransformQuery(
|
||||||
/.*\/location-levels\/batch/,
|
AdminGetInventoryItemParams,
|
||||||
validateAndTransformQuery(
|
QueryConfig.retrieveTransformQueryConfig
|
||||||
AdminGetInventoryItemParams,
|
|
||||||
QueryConfig.retrieveTransformQueryConfig
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -129,16 +126,10 @@ export const adminInventoryRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
method: ["POST"],
|
method: ["POST"],
|
||||||
matcher: "/admin/inventory-items/:id/location-levels/:location_id",
|
matcher: "/admin/inventory-items/:id/location-levels/:location_id",
|
||||||
middlewares: [
|
middlewares: [
|
||||||
unlessPath(
|
validateAndTransformBody(AdminUpdateInventoryLocationLevel),
|
||||||
/.*\/location-levels\/batch/,
|
validateAndTransformQuery(
|
||||||
validateAndTransformBody(AdminUpdateInventoryLocationLevel)
|
AdminGetInventoryItemParams,
|
||||||
),
|
QueryConfig.retrieveTransformQueryConfig
|
||||||
unlessPath(
|
|
||||||
/.*\/location-levels\/batch/,
|
|
||||||
validateAndTransformQuery(
|
|
||||||
AdminGetInventoryItemParams,
|
|
||||||
QueryConfig.retrieveTransformQueryConfig
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MiddlewareRoute, unlessPath } from "@medusajs/framework/http"
|
import { MiddlewareRoute } from "@medusajs/framework/http"
|
||||||
import {
|
import {
|
||||||
validateAndTransformBody,
|
validateAndTransformBody,
|
||||||
validateAndTransformQuery,
|
validateAndTransformQuery,
|
||||||
@@ -37,12 +37,9 @@ export const adminPaymentRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
method: ["GET"],
|
method: ["GET"],
|
||||||
matcher: "/admin/payments/:id",
|
matcher: "/admin/payments/:id",
|
||||||
middlewares: [
|
middlewares: [
|
||||||
unlessPath(
|
validateAndTransformQuery(
|
||||||
/.*\/payments\/payment-providers/,
|
AdminGetPaymentParams,
|
||||||
validateAndTransformQuery(
|
queryConfig.retrieveTransformQueryConfig
|
||||||
AdminGetPaymentParams,
|
|
||||||
queryConfig.retrieveTransformQueryConfig
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,11 +3,7 @@ import {
|
|||||||
validateAndTransformBody,
|
validateAndTransformBody,
|
||||||
validateAndTransformQuery,
|
validateAndTransformQuery,
|
||||||
} from "@medusajs/framework"
|
} from "@medusajs/framework"
|
||||||
import {
|
import { maybeApplyLinkFilter, MiddlewareRoute } from "@medusajs/framework/http"
|
||||||
maybeApplyLinkFilter,
|
|
||||||
MiddlewareRoute,
|
|
||||||
unlessPath,
|
|
||||||
} from "@medusajs/framework/http"
|
|
||||||
import multer from "multer"
|
import multer from "multer"
|
||||||
import { DEFAULT_BATCH_ENDPOINTS_SIZE_LIMIT } from "../../../utils/middlewares"
|
import { DEFAULT_BATCH_ENDPOINTS_SIZE_LIMIT } from "../../../utils/middlewares"
|
||||||
import { createBatchBody } from "../../utils/validators"
|
import { createBatchBody } from "../../utils/validators"
|
||||||
@@ -117,12 +113,9 @@ export const adminProductRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
method: ["GET"],
|
method: ["GET"],
|
||||||
matcher: "/admin/products/:id",
|
matcher: "/admin/products/:id",
|
||||||
middlewares: [
|
middlewares: [
|
||||||
unlessPath(
|
validateAndTransformQuery(
|
||||||
/.*\/products\/(batch|export|import)/,
|
AdminGetProductParams,
|
||||||
validateAndTransformQuery(
|
QueryConfig.retrieveProductQueryConfig
|
||||||
AdminGetProductParams,
|
|
||||||
QueryConfig.retrieveProductQueryConfig
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -130,16 +123,10 @@ export const adminProductRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
method: ["POST"],
|
method: ["POST"],
|
||||||
matcher: "/admin/products/:id",
|
matcher: "/admin/products/:id",
|
||||||
middlewares: [
|
middlewares: [
|
||||||
unlessPath(
|
validateAndTransformBody(AdminUpdateProduct),
|
||||||
/.*\/products\/(batch|export|import)/,
|
validateAndTransformQuery(
|
||||||
validateAndTransformBody(AdminUpdateProduct)
|
AdminGetProductParams,
|
||||||
),
|
QueryConfig.retrieveProductQueryConfig
|
||||||
unlessPath(
|
|
||||||
/.*\/products\/(batch|export|import)/,
|
|
||||||
validateAndTransformQuery(
|
|
||||||
AdminGetProductParams,
|
|
||||||
QueryConfig.retrieveProductQueryConfig
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -147,12 +134,9 @@ export const adminProductRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
method: ["DELETE"],
|
method: ["DELETE"],
|
||||||
matcher: "/admin/products/:id",
|
matcher: "/admin/products/:id",
|
||||||
middlewares: [
|
middlewares: [
|
||||||
unlessPath(
|
validateAndTransformQuery(
|
||||||
/.*\/products\/(batch|export|import)/,
|
AdminGetProductParams,
|
||||||
validateAndTransformQuery(
|
QueryConfig.retrieveProductQueryConfig
|
||||||
AdminGetProductParams,
|
|
||||||
QueryConfig.retrieveProductQueryConfig
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -198,12 +182,9 @@ export const adminProductRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
method: ["GET"],
|
method: ["GET"],
|
||||||
matcher: "/admin/products/:id/variants/:variant_id",
|
matcher: "/admin/products/:id/variants/:variant_id",
|
||||||
middlewares: [
|
middlewares: [
|
||||||
unlessPath(
|
validateAndTransformQuery(
|
||||||
/.*\/variants\/batch/,
|
AdminGetProductVariantParams,
|
||||||
validateAndTransformQuery(
|
QueryConfig.retrieveVariantConfig
|
||||||
AdminGetProductVariantParams,
|
|
||||||
QueryConfig.retrieveVariantConfig
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -211,16 +192,10 @@ export const adminProductRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
method: ["POST"],
|
method: ["POST"],
|
||||||
matcher: "/admin/products/:id/variants/:variant_id",
|
matcher: "/admin/products/:id/variants/:variant_id",
|
||||||
middlewares: [
|
middlewares: [
|
||||||
unlessPath(
|
validateAndTransformBody(AdminUpdateProductVariant),
|
||||||
/.*\/variants\/batch/,
|
validateAndTransformQuery(
|
||||||
validateAndTransformBody(AdminUpdateProductVariant)
|
AdminGetProductParams,
|
||||||
),
|
QueryConfig.retrieveProductQueryConfig
|
||||||
unlessPath(
|
|
||||||
/.*\/variants\/batch/,
|
|
||||||
validateAndTransformQuery(
|
|
||||||
AdminGetProductParams,
|
|
||||||
QueryConfig.retrieveProductQueryConfig
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -228,12 +203,9 @@ export const adminProductRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
method: ["DELETE"],
|
method: ["DELETE"],
|
||||||
matcher: "/admin/products/:id/variants/:variant_id",
|
matcher: "/admin/products/:id/variants/:variant_id",
|
||||||
middlewares: [
|
middlewares: [
|
||||||
unlessPath(
|
validateAndTransformQuery(
|
||||||
/.*\/variants\/batch/,
|
AdminGetProductParams,
|
||||||
validateAndTransformQuery(
|
QueryConfig.retrieveProductQueryConfig
|
||||||
AdminGetProductParams,
|
|
||||||
QueryConfig.retrieveProductQueryConfig
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import {
|
|||||||
validateAndTransformBody,
|
validateAndTransformBody,
|
||||||
validateAndTransformQuery,
|
validateAndTransformQuery,
|
||||||
} from "@medusajs/framework"
|
} from "@medusajs/framework"
|
||||||
import { MiddlewareRoute, unlessPath } from "@medusajs/framework/http"
|
import { MiddlewareRoute } from "@medusajs/framework/http"
|
||||||
import { DEFAULT_BATCH_ENDPOINTS_SIZE_LIMIT } from "../../../utils/middlewares"
|
import { DEFAULT_BATCH_ENDPOINTS_SIZE_LIMIT } from "../../../utils/middlewares"
|
||||||
import { createBatchBody } from "../../utils/validators"
|
import { createBatchBody } from "../../utils/validators"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
@@ -65,12 +65,9 @@ export const adminPromotionRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
method: ["GET"],
|
method: ["GET"],
|
||||||
matcher: "/admin/promotions/:id/:rule_type",
|
matcher: "/admin/promotions/:id/:rule_type",
|
||||||
middlewares: [
|
middlewares: [
|
||||||
unlessPath(
|
validateAndTransformQuery(
|
||||||
/.*\/promotions\/rule-attribute-options/,
|
AdminGetPromotionRuleTypeParams,
|
||||||
validateAndTransformQuery(
|
QueryConfig.retrieveTransformQueryConfig
|
||||||
AdminGetPromotionRuleTypeParams,
|
|
||||||
QueryConfig.retrieveTransformQueryConfig
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -44,18 +44,18 @@ export const adminWorkflowsExecutionsMiddlewares: MiddlewareRoute[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
method: ["POST"],
|
method: ["POST"],
|
||||||
matcher: "/admin/workflows-executions/:id/run",
|
matcher: "/admin/workflows-executions/:workflow_id/run",
|
||||||
middlewares: [validateAndTransformBody(AdminCreateWorkflowsRun)],
|
middlewares: [validateAndTransformBody(AdminCreateWorkflowsRun)],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
method: ["POST"],
|
method: ["POST"],
|
||||||
|
|
||||||
matcher: "/admin/workflows-executions/:id/steps/success",
|
matcher: "/admin/workflows-executions/:workflow_id/steps/success",
|
||||||
middlewares: [validateAndTransformBody(AdminCreateWorkflowsAsyncResponse)],
|
middlewares: [validateAndTransformBody(AdminCreateWorkflowsAsyncResponse)],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
method: ["POST"],
|
method: ["POST"],
|
||||||
matcher: "/admin/workflows-executions/:id/steps/failure",
|
matcher: "/admin/workflows-executions/:workflow_id/steps/failure",
|
||||||
middlewares: [validateAndTransformBody(AdminCreateWorkflowsAsyncResponse)],
|
middlewares: [validateAndTransformBody(AdminCreateWorkflowsAsyncResponse)],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
MedusaResponse,
|
MedusaResponse,
|
||||||
Query,
|
Query,
|
||||||
} from "@medusajs/framework"
|
} from "@medusajs/framework"
|
||||||
import { ApiRoutesLoader } from "@medusajs/framework/http"
|
import { ApiLoader } from "@medusajs/framework/http"
|
||||||
import { Tracer } from "@medusajs/framework/telemetry"
|
import { Tracer } from "@medusajs/framework/telemetry"
|
||||||
import type { SpanExporter } from "@opentelemetry/sdk-trace-node"
|
import type { SpanExporter } from "@opentelemetry/sdk-trace-node"
|
||||||
import type { NodeSDKConfiguration } from "@opentelemetry/sdk-node"
|
import type { NodeSDKConfiguration } from "@opentelemetry/sdk-node"
|
||||||
@@ -66,7 +66,7 @@ export function instrumentHttpLayer() {
|
|||||||
* Instrumenting the route handler to report traces to
|
* Instrumenting the route handler to report traces to
|
||||||
* OpenTelemetry
|
* OpenTelemetry
|
||||||
*/
|
*/
|
||||||
ApiRoutesLoader.traceRoute = (handler) => {
|
ApiLoader.traceRoute = (handler) => {
|
||||||
return async (req, res) => {
|
return async (req, res) => {
|
||||||
if (shouldExcludeResource(req.originalUrl)) {
|
if (shouldExcludeResource(req.originalUrl)) {
|
||||||
return await handler(req, res)
|
return await handler(req, res)
|
||||||
@@ -95,7 +95,7 @@ export function instrumentHttpLayer() {
|
|||||||
* Instrumenting the middleware handler to report traces to
|
* Instrumenting the middleware handler to report traces to
|
||||||
* OpenTelemetry
|
* OpenTelemetry
|
||||||
*/
|
*/
|
||||||
ApiRoutesLoader.traceMiddleware = (handler) => {
|
ApiLoader.traceMiddleware = (handler) => {
|
||||||
return async (
|
return async (
|
||||||
req: MedusaRequest<any>,
|
req: MedusaRequest<any>,
|
||||||
res: MedusaResponse,
|
res: MedusaResponse,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Express } from "express"
|
import { Express } from "express"
|
||||||
import { join } from "path"
|
import { join } from "path"
|
||||||
import qs from "qs"
|
import qs from "qs"
|
||||||
import { RoutesLoader } from "@medusajs/framework/http"
|
import { ApiLoader } from "@medusajs/framework/http"
|
||||||
import { MedusaContainer, PluginDetails } from "@medusajs/framework/types"
|
import { MedusaContainer, PluginDetails } from "@medusajs/framework/types"
|
||||||
import { ConfigModule } from "@medusajs/framework/config"
|
import { ConfigModule } from "@medusajs/framework/config"
|
||||||
|
|
||||||
@@ -39,13 +39,10 @@ export default async ({ app, container, plugins }: Options) => {
|
|||||||
* "/products/:id" route.
|
* "/products/:id" route.
|
||||||
*/
|
*/
|
||||||
sourcePaths.push(
|
sourcePaths.push(
|
||||||
|
join(__dirname, "../api"),
|
||||||
...plugins.map((pluginDetails) => {
|
...plugins.map((pluginDetails) => {
|
||||||
return join(pluginDetails.resolve, "api")
|
return join(pluginDetails.resolve, "api")
|
||||||
}),
|
})
|
||||||
/**
|
|
||||||
* Register the Medusa CORE API routes using the file based routing.
|
|
||||||
*/
|
|
||||||
join(__dirname, "../api")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -58,7 +55,7 @@ export default async ({ app, container, plugins }: Options) => {
|
|||||||
// Adding this here temporarily
|
// Adding this here temporarily
|
||||||
// Test: (packages/medusa/src/api/routes/admin/currencies/update-currency.ts)
|
// Test: (packages/medusa/src/api/routes/admin/currencies/update-currency.ts)
|
||||||
try {
|
try {
|
||||||
await new RoutesLoader({
|
await new ApiLoader({
|
||||||
app: app,
|
app: app,
|
||||||
sourceDir: sourcePaths,
|
sourceDir: sourcePaths,
|
||||||
baseRestrictedFields: restrictedFields?.store,
|
baseRestrictedFields: restrictedFields?.store,
|
||||||
|
|||||||
@@ -5792,6 +5792,7 @@ __metadata:
|
|||||||
jsonwebtoken: ^9.0.2
|
jsonwebtoken: ^9.0.2
|
||||||
lodash: 4.17.21
|
lodash: 4.17.21
|
||||||
morgan: ^1.9.1
|
morgan: ^1.9.1
|
||||||
|
path-to-regexp: ^0.1.10
|
||||||
pg: ^8.13.0
|
pg: ^8.13.0
|
||||||
rimraf: ^3.0.2
|
rimraf: ^3.0.2
|
||||||
supertest: ^4.0.2
|
supertest: ^4.0.2
|
||||||
@@ -26955,6 +26956,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"path-to-regexp@npm:^0.1.10":
|
||||||
|
version: 0.1.12
|
||||||
|
resolution: "path-to-regexp@npm:0.1.12"
|
||||||
|
checksum: 1c6ff10ca169b773f3bba943bbc6a07182e332464704572962d277b900aeee81ac6aa5d060ff9e01149636c30b1f63af6e69dd7786ba6e0ddb39d4dee1f0645b
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"path-to-regexp@npm:^6.2.0":
|
"path-to-regexp@npm:^6.2.0":
|
||||||
version: 6.2.2
|
version: 6.2.2
|
||||||
resolution: "path-to-regexp@npm:6.2.2"
|
resolution: "path-to-regexp@npm:6.2.2"
|
||||||
|
|||||||
Reference in New Issue
Block a user