Skip to content

Commit 4ebc102

Browse files
oscar60310kokokuo
andcommitted
refactor(serve): unite extension loader
- Use same container with core package instead of creating a new one (for external extensions). - Add two types of extensions: route middleware and formatter. - Bind Vulcan application to container. - Using centralized extension loader, remove the old loader in serve package. - Use new super class for all extensions. Co-authored-by: kokokuo <v6610688@gmail.com>
1 parent 253386b commit 4ebc102

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+414
-510
lines changed

packages/serve/src/containers/container.ts

+19-9
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,35 @@
11
import { Container as InversifyContainer } from 'inversify';
22
import { Container as CoreContainer } from '@vulcan-sql/core';
3-
import { routeGeneratorModule } from './modules';
3+
import {
4+
applicationModule,
5+
extensionModule,
6+
routeGeneratorModule,
7+
} from './modules';
48
import { ServeConfig } from '../models';
59

610
export class Container {
7-
private inversifyContainer = new InversifyContainer();
11+
private inversifyContainer?: InversifyContainer;
12+
private coreContainer?: CoreContainer;
813

914
public get<T>(type: symbol) {
10-
return this.inversifyContainer.get<T>(type);
15+
const instance = this.inversifyContainer?.get<T>(type);
16+
if (!instance)
17+
throw new Error(`Cannot resolve ${type.toString()} in container`);
18+
return instance;
1119
}
1220

1321
public async load(config: ServeConfig) {
14-
const coreContainer = new CoreContainer();
15-
await coreContainer.load(config);
16-
this.inversifyContainer.parent = coreContainer.getInversifyContainer();
22+
this.coreContainer = new CoreContainer();
23+
await this.coreContainer.load(config);
24+
this.inversifyContainer = this.coreContainer.getInversifyContainer();
1725
this.inversifyContainer.load(routeGeneratorModule());
26+
await this.inversifyContainer.loadAsync(extensionModule(config));
27+
await this.inversifyContainer.loadAsync(applicationModule());
1828
}
1929

20-
public unload() {
21-
this.inversifyContainer.parent?.unbindAll();
22-
this.inversifyContainer.unbindAll();
30+
public async unload() {
31+
await this.coreContainer?.unload();
32+
await this.inversifyContainer?.unbindAllAsync();
2333
}
2434

2535
public getInversifyContainer() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { VulcanApplication } from '@vulcan-sql/serve';
2+
import { AsyncContainerModule } from 'inversify';
3+
import { TYPES } from '../types';
4+
5+
export const applicationModule = () =>
6+
new AsyncContainerModule(async (bind) => {
7+
bind(TYPES.VulcanApplication).to(VulcanApplication);
8+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { ExtensionLoader } from '@vulcan-sql/core';
2+
import { AsyncContainerModule } from 'inversify';
3+
import { ServeConfig } from '../../models/serveConfig';
4+
import { BuiltInRouteMiddlewares } from '@vulcan-sql/serve/middleware';
5+
import { BuiltInFormatters } from '@vulcan-sql/serve/response-formatter';
6+
7+
export const extensionModule = (options: ServeConfig) =>
8+
new AsyncContainerModule(async (bind) => {
9+
const loader = new ExtensionLoader(options);
10+
// Internal extension modules
11+
12+
// route middlewares (single module)
13+
loader.loadInternalExtensionModule(BuiltInRouteMiddlewares);
14+
// formatter (single module)
15+
loader.loadInternalExtensionModule(BuiltInFormatters);
16+
17+
loader.bindExtensions(bind);
18+
});
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
export * from './routeGeneratorModule';
1+
export * from './routeGenerator';
2+
export * from './extension';
3+
export * from './application';

packages/serve/src/containers/modules/routeGeneratorModule.ts packages/serve/src/containers/modules/routeGenerator.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const routeGeneratorModule = () =>
1717
.to(RequestTransformer)
1818
.inSingletonScope();
1919

20-
// Request Transformer
20+
// Request Validator
2121
bind<IRequestValidator>(TYPES.RequestValidator)
2222
.to(RequestValidator)
2323
.inSingletonScope();
@@ -27,7 +27,7 @@ export const routeGeneratorModule = () =>
2727
.to(PaginationTransformer)
2828
.inSingletonScope();
2929

30-
// Roue Generator
30+
// Route Generator
3131
bind<RouteGenerator>(TYPES.RouteGenerator)
3232
.to(RouteGenerator)
3333
.inSingletonScope();

packages/serve/src/containers/types.ts

+3
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,7 @@ export const TYPES = {
88
// Application
99
AppConfig: Symbol.for('AppConfig'),
1010
VulcanApplication: Symbol.for('VulcanApplication'),
11+
// Extensions
12+
Extension_RouteMiddleware: Symbol.for('Extension_RouteMiddleware'),
13+
Extension_Formatter: Symbol.for('Extension_Formatter'),
1114
};

packages/serve/src/index.ts

-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,4 @@ export * from './lib/route';
22
export * from './lib/middleware';
33
export * from './lib/app';
44
export * from './models';
5-
export * from './models';
65
export * from './containers';

packages/serve/src/lib/app.ts

+14-17
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,33 @@ import { APISchema } from '@vulcan-sql/core';
22
import * as Koa from 'koa';
33
import * as KoaRouter from 'koa-router';
44
import { isEmpty, uniq } from 'lodash';
5-
import { BaseRouteMiddleware, BuiltInRouteMiddlewares } from './middleware';
65
import {
76
RestfulRoute,
87
BaseRoute,
98
APIProviderType,
109
GraphQLRoute,
1110
RouteGenerator,
1211
} from './route';
13-
import { AppConfig } from '../models';
14-
import { importExtensions, loadComponents } from './loader';
12+
import { inject, injectable, multiInject, optional } from 'inversify';
13+
import { TYPES } from '../containers';
14+
import { BaseRouteMiddleware } from '../models';
1515

16+
@injectable()
1617
export class VulcanApplication {
1718
private app: Koa;
18-
private config: AppConfig;
1919
private restfulRouter: KoaRouter;
2020
private graphqlRouter: KoaRouter;
2121
private generator: RouteGenerator;
22-
constructor(config: AppConfig, generator: RouteGenerator) {
23-
this.config = config;
22+
private routeMiddlewares: BaseRouteMiddleware[];
23+
24+
constructor(
25+
@inject(TYPES.RouteGenerator) generator: RouteGenerator,
26+
@multiInject(TYPES.Extension_RouteMiddleware)
27+
@optional()
28+
routeMiddlewares: BaseRouteMiddleware[] = []
29+
) {
2430
this.generator = generator;
31+
this.routeMiddlewares = routeMiddlewares;
2532
this.app = new Koa();
2633
this.restfulRouter = new KoaRouter();
2734
this.graphqlRouter = new KoaRouter();
@@ -77,17 +84,7 @@ export class VulcanApplication {
7784

7885
/** load built-in and extensions middleware classes for app used */
7986
public async useMiddleware() {
80-
// import extension middleware classes
81-
const classesOfExtension = await importExtensions(
82-
'middlewares',
83-
this.config.extensions
84-
);
85-
const map = await loadComponents<BaseRouteMiddleware>(
86-
[...BuiltInRouteMiddlewares, ...classesOfExtension],
87-
this.config
88-
);
89-
for (const name of Object.keys(map)) {
90-
const middleware = map[name];
87+
for (const middleware of this.routeMiddlewares) {
9188
this.app.use(middleware.handle.bind(middleware));
9289
}
9390
}

packages/serve/src/lib/loader.ts

-55
This file was deleted.

packages/serve/src/lib/middleware/built-in-middleware/auditLogMiddleware.ts packages/serve/src/lib/middleware/auditLogMiddleware.ts

+12-12
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
import { getLogger, ILogger, LoggerOptions } from '@vulcan-sql/core';
2-
import { BuiltInMiddleware } from '../middleware';
1+
import {
2+
getLogger,
3+
LoggerOptions,
4+
VulcanInternalExtension,
5+
} from '@vulcan-sql/core';
6+
import { BuiltInMiddleware } from '@vulcan-sql/serve/models';
37
import { KoaRouterContext, KoaNext } from '@vulcan-sql/serve/route';
4-
import { AppConfig } from '@vulcan-sql/serve/models';
58

6-
export class AuditLoggingMiddleware extends BuiltInMiddleware {
7-
private logger: ILogger;
8-
constructor(config: AppConfig) {
9-
super('audit-log', config);
10-
11-
// read logger options from config, if is undefined will set default value
12-
const options = this.getOptions() as LoggerOptions;
13-
this.logger = getLogger({ scopeName: 'AUDIT', options });
14-
}
9+
@VulcanInternalExtension('audit-log')
10+
export class AuditLoggingMiddleware extends BuiltInMiddleware<LoggerOptions> {
11+
private logger = getLogger({
12+
scopeName: 'AUDIT',
13+
options: this.getOptions(),
14+
});
1515

1616
public async handle(context: KoaRouterContext, next: KoaNext) {
1717
if (!this.enabled) return next();

packages/serve/src/lib/middleware/built-in-middleware/corsMiddleware.ts

-21
This file was deleted.

packages/serve/src/lib/middleware/built-in-middleware/index.ts

-20
This file was deleted.

packages/serve/src/lib/middleware/built-in-middleware/rateLimitMiddleware.ts

-22
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import * as cors from '@koa/cors';
2+
import { KoaRouterContext, KoaNext } from '@vulcan-sql/serve/route';
3+
import { BuiltInMiddleware } from '@vulcan-sql/serve/models';
4+
import { VulcanInternalExtension } from '@vulcan-sql/core';
5+
6+
export type CorsOptions = cors.Options;
7+
8+
@VulcanInternalExtension('cors')
9+
export class CorsMiddleware extends BuiltInMiddleware<CorsOptions> {
10+
private koaCors = cors(this.getOptions());
11+
12+
public async handle(context: KoaRouterContext, next: KoaNext) {
13+
if (!this.enabled) return next();
14+
return this.koaCors(context, next);
15+
}
16+
}
+21-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1-
// export non-default
2-
export { BaseRouteMiddleware } from './middleware';
3-
export * from './built-in-middleware';
1+
export * from './corsMiddleware';
2+
export * from './requestIdMiddleware';
3+
export * from './auditLogMiddleware';
4+
export * from './rateLimitMiddleware';
5+
export * from './response-format';
6+
7+
import { CorsMiddleware } from './corsMiddleware';
8+
import { RateLimitMiddleware } from './rateLimitMiddleware';
9+
import { RequestIdMiddleware } from './requestIdMiddleware';
10+
import { AuditLoggingMiddleware } from './auditLogMiddleware';
11+
import { ResponseFormatMiddleware } from './response-format';
12+
import { ClassType, ExtensionBase } from '@vulcan-sql/core';
13+
14+
// The order is the middleware running order
15+
export const BuiltInRouteMiddlewares: ClassType<ExtensionBase>[] = [
16+
CorsMiddleware,
17+
RateLimitMiddleware,
18+
RequestIdMiddleware,
19+
AuditLoggingMiddleware,
20+
ResponseFormatMiddleware,
21+
];

0 commit comments

Comments
 (0)