Skip to content

Commit

Permalink
feat: impl Host decorator (#48)
Browse files Browse the repository at this point in the history
  • Loading branch information
killagu authored Aug 16, 2022
1 parent 80b2a8c commit 65dc7a8
Show file tree
Hide file tree
Showing 23 changed files with 386 additions and 17 deletions.
1 change: 1 addition & 0 deletions core/controller-decorator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from './src/decorator/Acl';
export * from './src/decorator/http/HTTPController';
export * from './src/decorator/http/HTTPMethod';
export * from './src/decorator/http/HTTPParam';
export * from './src/decorator/http/Host';
export * from './src/builder/ControllerMetaBuilderFactory';
export * from './src/builder/ControllerMetaBuilder';
export * from './src/util/ControllerMetadataUtil';
Expand Down
27 changes: 27 additions & 0 deletions core/controller-decorator/src/decorator/http/Host.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import ControllerInfoUtil from '../../util/ControllerInfoUtil';
import { EggProtoImplClass } from '@eggjs/core-decorator';
import MethodInfoUtil from '../../util/MethodInfoUtil';
import assert from 'assert';

export function Host(host: string) {
function classHost(constructor: EggProtoImplClass) {
ControllerInfoUtil.addControllerHost(host, constructor);
}

function methodHOst(target: any, propertyKey: PropertyKey) {
assert(typeof propertyKey === 'string',
`[controller/${target.name}] expect method name be typeof string, but now is ${String(propertyKey)}`);
const controllerClazz = target.constructor as EggProtoImplClass;
const methodName = propertyKey as string;

MethodInfoUtil.setMethodHost(host, controllerClazz, methodName);
}

return function(target: any, propertyKey?: PropertyKey) {
if (propertyKey === undefined) {
classHost(target);
} else {
methodHOst(target, propertyKey);
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ export class HTTPControllerMetaBuilder {
const protoName = property!.name as string;
const needAcl = ControllerInfoUtil.hasControllerAcl(this.clazz);
const aclCode = ControllerInfoUtil.getControllerAcl(this.clazz);
const host = ControllerInfoUtil.getControllerHost(this.clazz);
const metadata = new HTTPControllerMeta(
clazzName, protoName, controllerName, httpPath, httpMiddlewares, methods, needAcl, aclCode);
clazzName, protoName, controllerName, httpPath, httpMiddlewares, methods, needAcl, aclCode, host);
ControllerMetadataUtil.setControllerMetadata(this.clazz, metadata);
for (const method of metadata.methods) {
const realPath = metadata.getMethodRealPath(method);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,13 @@ export class HTTPControllerMethodMetaBuilder {
const middlewares = MethodInfoUtil.getMethodMiddlewares(this.clazz, this.methodName);
const needAcl = MethodInfoUtil.hasMethodAcl(this.clazz, this.methodName);
const aclCode = MethodInfoUtil.getMethodAcl(this.clazz, this.methodName);
const host = MethodInfoUtil.getMethodHost(this.clazz, this.methodName);
const realPath = parentPath
? path.posix.join(parentPath, httpPath)
: httpPath;
const paramTypeMap = this.buildParamType(realPath);
const priority = this.getPriority();
return new HTTPMethodMeta(this.methodName, httpPath!, httpMethod!, middlewares, contextIndex, paramTypeMap, priority, needAcl, aclCode);
return new HTTPMethodMeta(
this.methodName, httpPath!, httpMethod!, middlewares, contextIndex, paramTypeMap, priority, needAcl, aclCode, host);
}
}
10 changes: 10 additions & 0 deletions core/controller-decorator/src/model/HTTPControllerMeta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export class HTTPControllerMeta implements ControllerMetadata {
public readonly methods: readonly HTTPMethodMeta[];
public readonly needAcl: boolean;
public readonly aclCode?: string;
public readonly host?: string;

constructor(
className: string,
Expand All @@ -24,6 +25,7 @@ export class HTTPControllerMeta implements ControllerMetadata {
methods: HTTPMethodMeta[],
needAcl: boolean,
aclCode: string | undefined,
host: string | undefined,
) {
this.protoName = protoName;
this.controllerName = controllerName;
Expand All @@ -33,6 +35,7 @@ export class HTTPControllerMeta implements ControllerMetadata {
this.methods = methods;
this.needAcl = needAcl;
this.aclCode = aclCode;
this.host = host;
}

getMethodRealPath(method: HTTPMethodMeta) {
Expand All @@ -42,6 +45,13 @@ export class HTTPControllerMeta implements ControllerMetadata {
return method.path;
}

getMethodHost(method: HTTPMethodMeta): string | undefined {
if (this.host) {
return this.host;
}
return method.host;
}

getMethodName(method: HTTPMethodMeta) {
return `${method.method} ${this.controllerName}.${method.name}`;
}
Expand Down
3 changes: 3 additions & 0 deletions core/controller-decorator/src/model/HTTPMethodMeta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export class HTTPMethodMeta implements MethodMeta {
public readonly priority: number;
public readonly needAcL: boolean;
public readonly aclCode: string | undefined;
public readonly host: string | undefined;

constructor(
name: string,
Expand All @@ -84,6 +85,7 @@ export class HTTPMethodMeta implements MethodMeta {
priority: number,
needAcl: boolean,
aclCode: string | undefined,
host: string | undefined,
) {
this.name = name;
this.path = path;
Expand All @@ -94,6 +96,7 @@ export class HTTPMethodMeta implements MethodMeta {
this.priority = priority;
this.needAcL = needAcl;
this.aclCode = aclCode;
this.host = host;
}
}

Expand Down
9 changes: 9 additions & 0 deletions core/controller-decorator/src/util/ControllerInfoUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { EggProtoImplClass, MetadataUtil } from '@eggjs/core-decorator';

export const CONTROLLER_TYPE = Symbol.for('EggPrototype#controllerType');
export const CONTROLLER_NAME = Symbol.for('EggPrototype#controllerName');
export const CONTROLLER_HOST = Symbol.for('EggPrototype#controllerHost');
export const CONTROLLER_MIDDLEWARES = Symbol.for('EggPrototype#controller#middlewares');
export const CONTROLLER_ACL = Symbol.for('EggPrototype#controller#acl');

Expand Down Expand Up @@ -43,4 +44,12 @@ export default class ControllerInfoUtil {
static getControllerAcl(clazz: EggProtoImplClass): string | undefined {
return MetadataUtil.getMetaData(CONTROLLER_ACL, clazz);
}

static addControllerHost(host: string, clazz: EggProtoImplClass) {
MetadataUtil.defineMetaData(CONTROLLER_HOST, host, clazz);
}

static getControllerHost(clazz: EggProtoImplClass): string | undefined {
return MetadataUtil.getMetaData(CONTROLLER_HOST, clazz);
}
}
11 changes: 11 additions & 0 deletions core/controller-decorator/src/util/MethodInfoUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ControllerTypeLike, MiddlewareFunc } from '../model';
import { MapUtil } from '@eggjs/tegg-common-util';

const METHOD_CONTROLLER_TYPE_MAP = Symbol.for('EggPrototype#controller#mthods');
const METHOD_CONTROLLER_HOST = Symbol.for('EggPrototype#controller#mthods#host');
const METHOD_CONTEXT_INDEX = Symbol.for('EggPrototype#controller#method#context');
const METHOD_MIDDLEWARES = Symbol.for('EggPrototype#method#middlewares');
const METHOD_ACL = Symbol.for('EggPrototype#method#acl');
Expand Down Expand Up @@ -58,4 +59,14 @@ export default class MethodInfoUtil {
const methodAclMap: MethodAclMap | undefined = MetadataUtil.getMetaData(METHOD_ACL, clazz);
return methodAclMap?.get(methodName);
}

static setMethodHost(host: string, clazz: EggProtoImplClass, methodName: string) {
const methodControllerMap: METHOD_MAP = MetadataUtil.initOwnMapMetaData(METHOD_CONTROLLER_HOST, clazz, new Map());
methodControllerMap.set(methodName, host);
}

static getMethodHost(clazz: EggProtoImplClass, methodName: string): string | undefined {
const methodControllerMap: METHOD_MAP | undefined = MetadataUtil.getMetaData(METHOD_CONTROLLER_HOST, clazz);
return methodControllerMap?.get(methodName);
}
}
13 changes: 13 additions & 0 deletions core/controller-decorator/test/fixtures/HostController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Host } from '../../src/decorator/http/Host';

@Host('foo.eggjs.com')
export class HostController {
async hello(): Promise<void> {
return;
}

@Host('bar.eggjs.com')
async bar(): Promise<void> {
return;
}
}
16 changes: 16 additions & 0 deletions core/controller-decorator/test/http/Host.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import assert from 'assert';
import { HostController } from '../fixtures/HostController';
import ControllerInfoUtil from '../../src/util/ControllerInfoUtil';
import MethodInfoUtil from '../../src/util/MethodInfoUtil';

describe('test/Host.test.ts', () => {
it('controller Host work', () => {
const controllerHost = ControllerInfoUtil.getControllerHost(HostController);
assert(controllerHost === 'foo.eggjs.com');
});

it('method Host work', () => {
const methodHost = MethodInfoUtil.getMethodHost(HostController, 'bar');
assert(methodHost === 'bar.eggjs.com');
});
});
20 changes: 20 additions & 0 deletions plugin/controller/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,23 @@ export class FooController {
// 具体 name 值可以查看 path-to-regexp
@HTTPParam({ name: '0' }) id: string
```

### Host
Host 注解,用于指定 HTTP 方法仅在 host 匹配时执行。
可以添加在类/方法上。添加在类上时,对类上所有方法生效,添加在方法上时,只对当前方法生效。方法上的注解可以覆盖类上的注解

```ts
// app/controller/FooController.ts
import { Host } from '@eggjs/tegg';
@Host('foo.eggjs.com')
export class FooController {
// 仅能通过 foo.eggjs.com 访问
async hello() {
}

// 仅能通过 bar.eggjs.com 访问
@Host('bar.eggjs.com')
async bar() {
}
}
```
14 changes: 12 additions & 2 deletions plugin/controller/lib/RootProtoManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,22 @@ export class RootProtoManager {
// <method, GetRootProtoCallback[]>
protoMap: Map<string, GetRootProtoCallback[]> = new Map();

registerRootProto(method: string, cb: GetRootProtoCallback) {
const cbList = MapUtil.getOrStore(this.protoMap, method, []);
registerRootProto(method: string, host: string, cb: GetRootProtoCallback) {
const cbList = MapUtil.getOrStore(this.protoMap, method + host, []);
cbList.push(cb);
}

getRootProto(ctx: EggContext): EggPrototype | undefined {
const hostCbList = this.protoMap.get(ctx.method + ctx.host);
if (hostCbList) {
for (const cb of hostCbList) {
const proto = cb(ctx);
if (proto) {
return proto;
}
}
}

const cbList = this.protoMap.get(ctx.method);
if (!cbList) {
return;
Expand Down
15 changes: 14 additions & 1 deletion plugin/controller/lib/impl/http/HTTPControllerRegister.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export class HTTPControllerRegister implements ControllerRegister {
static instance?: HTTPControllerRegister;

private readonly router: KoaRouter<any, Context>;
private readonly checkRouters: Map<string, KoaRouter<any, Context>>;
private readonly eggContainerFactory: typeof EggContainerFactory;
private controllerProtos: EggPrototype[] = [];

Expand All @@ -32,6 +33,8 @@ export class HTTPControllerRegister implements ControllerRegister {

constructor(router: KoaRouter<any, Context>, eggContainerFactory: typeof EggContainerFactory) {
this.router = router;
this.checkRouters = new Map();
this.checkRouters.set('default', router);
this.eggContainerFactory = eggContainerFactory;
}

Expand All @@ -43,6 +46,7 @@ export class HTTPControllerRegister implements ControllerRegister {
static clean() {
if (this.instance) {
this.instance.controllerProtos = [];
this.instance.checkRouters.clear();
}
this.instance = undefined;
}
Expand All @@ -57,11 +61,20 @@ export class HTTPControllerRegister implements ControllerRegister {
}
const allMethods = Array.from(methodMap.keys())
.sort((a, b) => b.priority - a.priority);

for (const method of allMethods) {
const controllerProto = methodMap.get(method)!;
const controllerMeta = controllerProto.getMetaData(CONTROLLER_META_DATA) as HTTPControllerMeta;
const methodRegister = new HTTPMethodRegister(
controllerProto, controllerMeta, method, this.router, this.checkRouters, this.eggContainerFactory);
methodRegister.checkDuplicate();
}

for (const method of allMethods) {
const controllerProto = methodMap.get(method)!;
const controllerMeta = controllerProto.getMetaData(CONTROLLER_META_DATA) as HTTPControllerMeta;
const methodRegister = new HTTPMethodRegister(
controllerProto, controllerMeta, method, this.router, this.eggContainerFactory);
controllerProto, controllerMeta, method, this.router, this.checkRouters, this.eggContainerFactory);
methodRegister.register(rootProtoManager);
}
}
Expand Down
Loading

0 comments on commit 65dc7a8

Please sign in to comment.