Skip to content

Commit

Permalink
feat: Allow to pass a list of handlers to the router decorator
Browse files Browse the repository at this point in the history
Allow to pass a list of handlers to the router decorator
in order to allow custom route specific middleware
in opposition to @Middleware that apply a middleware
to specific routes directly before/after medusa auth system
  • Loading branch information
adrien2p committed Feb 23, 2022
1 parent 3d6074d commit 737355b
Show file tree
Hide file tree
Showing 9 changed files with 47 additions and 44 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ dist
**/node_modules
tsconfig.tsbuildinfo
uploads
coverage
coverage
coverage-badges
23 changes: 13 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,9 @@ export default class ProductService extends MedusaProductService {
And finally, we need to add the subscriber to the connection. There are different ways to achieve
this. We will see, as an example below, a way to attach a request scoped subscribers.

Every middleware decorated with the `@Middleware` decorator will be applied globally on the specified route
before/after medusa authentication. Otherwise, to apply a middleware directly to a route you can have a look to the `@Router` decorator.

<details>
<summary>Click to see the example!</summary>

Expand Down Expand Up @@ -594,16 +597,16 @@ npm run start

Here is the list of the provided decorators.

| Decorator | Description | Option |
| ---------------------- | ---------------------- | ----------------------
| `@Entity(/*...*/)` | Decorate an entity | `{ resolutionKey?: string; override?: Type<TOverride>; };`
| `@Repository(/*...*/)` | Decorate a repository | `{ resolutionKey?: string; override?: Type<TOverride>; };`
| `@Service(/*...*/)` | Decorate a service | `{ scope?: LifetimeType; resolutionKey?: string; override?: Type<TOverride>; };`
| `@Middleware(/*...*/)` | Decorate a middleware | `{ requireAuth: boolean; string; routes: MedusaRouteOptions[]; };`
| `@Router(/*...*/)` | Decorate a router | `{ router: RoutesInjectionRouterConfiguration[]; };`
| `@Validator(/*...*/)` | Decorate a validator | `{ override: Type<TOverride>; };`
| `@Migration(/*...*/)` | Decorate a migration |
| `@OnMedusaEntityEvent.\*.\*(/*...*/)`| Can be used to send the right event type or register the handler to an event| `(entity: TEntity, { async? boolean; metatype?: Type<unknown> })`
| Decorator | Description | Option |
| ---------------------- | ---------------------- | ----------------------
| `@Entity(/*...*/)` | Decorate an entity | `{ resolutionKey?: string; override?: Type<TOverride>; };`
| `@Repository(/*...*/)` | Decorate a repository | `{ resolutionKey?: string; override?: Type<TOverride>; };`
| `@Service(/*...*/)` | Decorate a service | `{ scope?: LifetimeType; resolutionKey?: string; override?: Type<TOverride>; };`
| `@Middleware(/*...*/)` | Decorate a middleware | `{ requireAuth: boolean; string; routes: MedusaRouteOptions[]; };`
| `@Router(/*...*/)` | Decorate a router, can provide a list of handlers that can include route related middleware| `{ router: RoutesInjectionRouterConfiguration[]; };`
| `@Validator(/*...*/)` | Decorate a validator | `{ override: Type<TOverride>; };`
| `@Migration(/*...*/)` | Decorate a migration |
| `@OnMedusaEntityEvent.\*.\*(/*...*/)`| Can be used to send the right event type or register the handler to an event | `(entity: TEntity, { async? boolean; metatype?: Type<unknown> })`

# Monitoring

Expand Down
1 change: 0 additions & 1 deletion coverage-bages/badge-branches.svg

This file was deleted.

1 change: 0 additions & 1 deletion coverage-bages/badge-functions.svg

This file was deleted.

1 change: 0 additions & 1 deletion coverage-bages/badge-lines.svg

This file was deleted.

1 change: 0 additions & 1 deletion coverage-bages/badge-statements.svg

This file was deleted.

6 changes: 3 additions & 3 deletions src/decorators/tests/components.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe('components', () => {
@Router({
routes: [
{
handler: () => '',
handlers: [() => ''],
method: 'get',
path: '',
requiredAuth: true,
Expand All @@ -78,8 +78,8 @@ describe('components', () => {
path: '',
requiredAuth: true,
});
expect(metadata.routes[0].handler).toBeDefined();
expect(metadata.routes[0].handler('')).toEqual('');
expect(metadata.routes[0].handlers).toBeDefined();
expect(metadata.routes[0].handlers[0]('')).toEqual('');
});
});

Expand Down
53 changes: 28 additions & 25 deletions src/loaders/routes.loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,38 @@ import { GetInjectableOptions, MedusaAuthenticatedRequest, RoutesInjectionRouter
import { Express, NextFunction, Request, Response } from 'express';

export function authenticatedRoutesLoader(routesOptions: GetInjectableOptions<'router'>, app: Express): void {
for (const routeOptions of routesOptions) {
routeOptions.routes.forEach((route) => {
if (route.requiredAuth) {
registerRoute(app, route);
}
});
}
for (const routeOptions of routesOptions) {
routeOptions.routes.forEach((route) => {
if (route.requiredAuth) {
registerRoute(app, route);
}
});
}
}

export function unauthenticatedRoutesLoader(routesOptions: GetInjectableOptions<'router'>, app: Express): void {
for (const routeOptions of routesOptions) {
routeOptions.routes.forEach((route) => {
if (!route.requiredAuth) {
registerRoute(app, route);
}
});
}
for (const routeOptions of routesOptions) {
routeOptions.routes.forEach((route) => {
if (!route.requiredAuth) {
registerRoute(app, route);
}
});
}
}

function registerRoute(app: Express, route: RoutesInjectionRouterConfiguration): void {
const { method, path, handler } = route;
app[method.toLowerCase()](
path,
async (req: MedusaAuthenticatedRequest | Request, res: Response, next: NextFunction) => {
try {
return await handler(req, res);
} catch (e) {
return next(e);
}
}
);
const { method, path, handlers } = route;
app[method.toLowerCase()](
path,
handlers.map(handler => {
return async (req: MedusaAuthenticatedRequest | Request, res: Response, next: NextFunction) => {
try {
await handler(req, res);
return next();
} catch (e) {
return next(e);
}
};
})
);
}
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export type RoutesInjectionRouterConfiguration = {
requiredAuth: boolean;
method: string;
path: string;
handler: (...args: unknown[]) => void;
handlers: ((...args: unknown[]) => void)[];
};

/**
Expand Down

0 comments on commit 737355b

Please sign in to comment.