diff --git a/packages/bootstrap/test/index.test.ts b/packages/bootstrap/test/index.test.ts index cd2c2dd4e3e2..576b5b79d256 100644 --- a/packages/bootstrap/test/index.test.ts +++ b/packages/bootstrap/test/index.test.ts @@ -6,6 +6,7 @@ import { IMidwayContainer, IConfigurationOptions, MidwayFrameworkType, + MidwayContextLogger } from '@midwayjs/core'; import { clearAllLoggers } from '@midwayjs/logger'; import { join } from 'path'; @@ -90,6 +91,10 @@ class TestFrameworkUnit implements IMidwayFramework { diff --git a/packages/core/package.json b/packages/core/package.json index ed7ff8e79a29..9bd0d5b42278 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -6,8 +6,8 @@ "typings": "dist/index.d.ts", "scripts": { "build": "midway-bin build -c", - "test": "midway-bin test --ts --forceExit", - "cov": "midway-bin cov --ts --forceExit", + "test": "midway-bin test --ts", + "cov": "midway-bin cov --ts", "link": "npm link" }, "keywords": [ diff --git a/packages/core/src/baseFramework.ts b/packages/core/src/baseFramework.ts index cc7e6d65afdf..8abc16e36e2f 100644 --- a/packages/core/src/baseFramework.ts +++ b/packages/core/src/baseFramework.ts @@ -3,7 +3,7 @@ import { ILifeCycle, IMidwayApplication, IMidwayBootstrapOptions, - IMidwayContainer, + IMidwayContainer, IMidwayContext, IMidwayFramework, MidwayFrameworkType, MidwayProcessTypeEnum, @@ -16,10 +16,11 @@ import { listModule, LOGGER_KEY, } from '@midwayjs/decorator'; -import { ILogger, loggers, LoggerOptions } from '@midwayjs/logger'; +import { ILogger, loggers, LoggerOptions, IMidwayLogger } from '@midwayjs/logger'; import { isAbsolute, join, dirname } from 'path'; -import { createMidwayLogger } from './logger'; +import { createMidwayLogger, MidwayContextLogger } from './logger'; import { safeRequire } from './util'; +import { MidwayRequestContainer } from './context/requestContainer'; function buildLoadDir(baseDir, dir) { if (!isAbsolute(dir)) { @@ -33,21 +34,27 @@ function setupAppDir(baseDir: string) { } export abstract class BaseFramework< - APP extends IMidwayApplication, - T extends IConfigurationOptions -> implements IMidwayFramework { + APP extends IMidwayApplication, + CTX extends IMidwayContext, + OPT extends IConfigurationOptions, +> implements IMidwayFramework { protected isTsMode = true; protected baseDir: string; protected appDir: string; protected applicationContext: IMidwayContainer; protected logger: ILogger; protected appLogger: ILogger; - public configurationOptions: T; + public configurationOptions: OPT; public app: APP; protected pkg: object; + protected defaultContext = {}; + protected BaseContextLoggerClass: any; - public configure(options: T): BaseFramework { + public configure(options: OPT): BaseFramework { this.configurationOptions = options; + this.BaseContextLoggerClass = options.ContextLoggerClass || this.getDefaultContextLoggerClass(); + this.logger = options.logger; + this.appLogger = options.appLogger; return this; } @@ -107,12 +114,13 @@ export abstract class BaseFramework< protected async initializeLogger(options: IMidwayBootstrapOptions) { if (!this.logger) { - this.logger = createMidwayLogger(this, 'coreLogger'); + this.logger = new Proxy(createMidwayLogger(this, 'coreLogger'), {}); + (this.logger as IMidwayLogger).updateDefaultLabel(this.getFrameworkName()); } if (!this.appLogger) { this.appLogger = createMidwayLogger(this, 'logger', { fileLogName: 'midway-app.log', - }); + }) as IMidwayLogger; } } @@ -214,6 +222,15 @@ export abstract class BaseFramework< public abstract run(): Promise; + protected setContextLoggerClass(BaseContextLogger: any) { + this.BaseContextLoggerClass = BaseContextLogger; + } + + protected createContextLogger(ctx: CTX, name?: string): ILogger { + const appLogger = this.getLogger(name); + return new this.BaseContextLoggerClass(ctx, appLogger); + } + public async stop(): Promise { await this.beforeStop(); await this.containerStop(); @@ -227,7 +244,8 @@ export abstract class BaseFramework< return this.baseDir; } - protected defineApplicationProperties(applicationProperties: object = {}) { + protected defineApplicationProperties(applicationProperties: object = {}, whiteList = []) { + const self = this; const defaultApplicationProperties = { getBaseDir: () => { return this.baseDir; @@ -276,7 +294,35 @@ export abstract class BaseFramework< getProjectName: () => { return this.getProjectName(); }, + + createAnonymousContext(extendCtx?: CTX) { + const ctx = extendCtx || Object.create(self.defaultContext); + if (!ctx.startTime) { + ctx.startTime = Date.now(); + } + if (!ctx.logger) { + ctx.logger = self.createContextLogger(ctx); + } + if (!ctx.requestContext) { + ctx.requestContext = new MidwayRequestContainer(ctx, this.getApplicationContext()); + ctx.requestContext.ready(); + } + if (!ctx.getLogger) { + ctx.getLogger = (name) => { + return self.createContextLogger(ctx, name); + }; + } + return ctx; + }, + + setContextLoggerClass(BaseContextLogger: any) { + return self.setContextLoggerClass(BaseContextLogger); + }, + }; + for (const method of whiteList) { + delete defaultApplicationProperties[method]; + } Object.assign( this.app, defaultApplicationProperties, @@ -354,4 +400,13 @@ export abstract class BaseFramework< public getProjectName() { return this.pkg?.['name'] || ''; } + + public getFrameworkName() { + return this.getFrameworkType().toString(); + } + + public getDefaultContextLoggerClass(): any { + return MidwayContextLogger; + } + } diff --git a/packages/core/src/context/applicationContext.ts b/packages/core/src/context/applicationContext.ts index 25fac445e52b..cb6fcb80932e 100644 --- a/packages/core/src/context/applicationContext.ts +++ b/packages/core/src/context/applicationContext.ts @@ -246,7 +246,7 @@ export class BaseApplicationContext const def = this.registry.getDefinition(identifier); if (!isPathEqual(definition.srcPath, def.srcPath)) { throw new Error( - `${identifier} path = ${definition.srcPath} is exist (${def.srcPath})!` + `${identifier} path = ${definition.srcPath} already exist (${def.srcPath})!` ); } } diff --git a/packages/core/src/interface.ts b/packages/core/src/interface.ts index fce3edc0259b..fcca24d48403 100644 --- a/packages/core/src/interface.ts +++ b/packages/core/src/interface.ts @@ -292,7 +292,18 @@ export enum MidwayProcessTypeEnum { */ export interface IMidwayLogger extends ILogger {} -export interface IMidwayApplication { +export interface IMidwayContext { + /** + * Custom properties. + */ + [key: string]: any; + requestContext: IMidwayContainer; + logger: ILogger; + getLogger(name?: string): ILogger; + startTime: number; +} + +export interface IMidwayApplication { getBaseDir(): string; getAppDir(): string; getEnv(): string; @@ -304,12 +315,8 @@ export interface IMidwayApplication { getCoreLogger(): ILogger; createLogger(name: string, options: LoggerOptions): ILogger; getProjectName(): string; -} - -export interface IMidwayContext { - getRequestContext?(): IMidwayContainer; - requestContext: IMidwayContainer; - logger: ILogger; + createAnonymousContext(...args): T; + setContextLoggerClass(BaseContextLoggerClass: any): void; } /** @@ -331,7 +338,11 @@ export interface IMidwayBootstrapOptions { disableConflictCheck?: boolean; } -export interface IConfigurationOptions {} +export interface IConfigurationOptions { + logger?: ILogger; + appLogger?: ILogger; + ContextLoggerClass?: any; +} export interface IMidwayFramework { app: APP; @@ -345,12 +356,14 @@ export interface IMidwayFramework { protected contextLogger: ILogger; public ctx: T; - constructor(ctx, contextLogger: ILogger) { + constructor(ctx, contextLogger: ILogger, contextLabelHandler?) { this.ctx = ctx; this.contextLogger = contextLogger; } diff --git a/packages/core/test/baseFramework.test.ts b/packages/core/test/baseFramework.test.ts index 754cb61c52ea..1f44886b739f 100644 --- a/packages/core/test/baseFramework.test.ts +++ b/packages/core/test/baseFramework.test.ts @@ -10,6 +10,7 @@ import * as path from 'path'; import { clearAllModule, clearContainerCache, + MidwayContextLogger, MidwayRequestContainer, } from '../src'; import * as mm from 'mm'; @@ -244,7 +245,7 @@ describe('/test/baseFramework.test.ts', () => { // __dirname, // './fixtures/app-with-conflict/base-app-decorator/src/lib/' // ); - // const s = `baseService path = ${p}/userManager.ts is exist (${p}/service.ts)!`; + // const s = `baseService path = ${p}/userManager.ts already exist (${p}/service.ts)!`; // assert.ok(callback.withArgs(s).calledOnce); // }); @@ -593,6 +594,20 @@ describe('/test/baseFramework.test.ts', () => { expect(framework.getApplication().getFrameworkType()).toEqual(framework.getFrameworkType()); expect(framework.getApplication().getProjectName()).toEqual(framework.getProjectName()); + // test context + class CustomContextLogger extends MidwayContextLogger { + formatContextLabel(): string { + return 'bbbb'; + } + } + framework.getApplication().setContextLoggerClass(CustomContextLogger); + expect(framework.getApplication().createAnonymousContext().startTime).toBeDefined(); + const ctxLogger = framework.getApplication().createAnonymousContext().getLogger(); + ctxLogger.info('ctx logger'); + + expect(framework.getApplication().createAnonymousContext().requestContext).toBeDefined(); + expect(framework.getApplication().createAnonymousContext().logger).toBeDefined(); + await framework.stop(); }); }); diff --git a/packages/core/test/context/midwayContainer.test.ts b/packages/core/test/context/midwayContainer.test.ts index f221e5d45e8e..55a711308bfd 100644 --- a/packages/core/test/context/midwayContainer.test.ts +++ b/packages/core/test/context/midwayContainer.test.ts @@ -208,4 +208,14 @@ describe('/test/context/midwayContainer.test.ts', () => { expect(sapp.getConfig().a).to.equal(3); }); + it('should test autoload', async () => { + const container = new MidwayContainer(); + container.load({ + loadDir: path.join(__dirname, '../fixtures/base-app-autoload/src'), + }); + + await container.ready(); + assert((container.registry as unknown as Map).has('userService')); + }); + }); diff --git a/packages/core/test/fixtures/base-app-autoload/package.json b/packages/core/test/fixtures/base-app-autoload/package.json new file mode 100644 index 000000000000..b7dd10831ca4 --- /dev/null +++ b/packages/core/test/fixtures/base-app-autoload/package.json @@ -0,0 +1,3 @@ +{ + "name": "ali-demo-aspect" +} diff --git a/packages/core/test/fixtures/base-app-autoload/src/home.ts b/packages/core/test/fixtures/base-app-autoload/src/home.ts new file mode 100644 index 000000000000..13881aa7e7d3 --- /dev/null +++ b/packages/core/test/fixtures/base-app-autoload/src/home.ts @@ -0,0 +1,17 @@ +import { Autoload, Init, Provide, Scope, ScopeEnum } from '@midwayjs/decorator'; + +@Autoload() +@Provide() +@Scope(ScopeEnum.Singleton) +export class UserService { + + idx = 0; + + @Init() + async initService() { + this.idx++; + } + + async getUser() { + } +} diff --git a/packages/core/test/loader.test.ts b/packages/core/test/loader.test.ts index 14da43c0122f..5ff869654f23 100644 --- a/packages/core/test/loader.test.ts +++ b/packages/core/test/loader.test.ts @@ -409,7 +409,7 @@ describe('/test/loader.test.ts', () => { __dirname, './fixtures/app-with-conflict/base-app-decorator/src/lib/' ); - const s = `baseService path = ${p}/userManager.ts is exist (${p}/service.ts)!`; + const s = `baseService path = ${p}/userManager.ts already exist (${p}/service.ts)!`; assert.ok(callback.withArgs(s).calledOnce); }); diff --git a/packages/core/test/util.ts b/packages/core/test/util.ts index 535bf8ef2737..9d0ed585d042 100644 --- a/packages/core/test/util.ts +++ b/packages/core/test/util.ts @@ -1,9 +1,10 @@ -import { BaseFramework, IMidwayApplication, IMidwayBootstrapOptions, MidwayFrameworkType } from '../src'; +import { BaseFramework, IMidwayApplication, IMidwayBootstrapOptions, MidwayFrameworkType, IMidwayContext } from '../src'; type mockApp = {} & IMidwayApplication; type mockAppOptions = {}; +type mockContext = IMidwayContext; -export class MockFramework extends BaseFramework { +export class MockFramework extends BaseFramework { getApplication(): mockApp { return this.app; @@ -21,4 +22,7 @@ export class MockFramework extends BaseFramework { this.app = {} as IMidwayApplication; } + getDefaultContextLoggerClass() { + return super.getDefaultContextLoggerClass(); + } } diff --git a/packages/decorator/package.json b/packages/decorator/package.json index 1884d83b6307..54da7c7ec087 100644 --- a/packages/decorator/package.json +++ b/packages/decorator/package.json @@ -6,15 +6,15 @@ "typings": "dist/index.d.ts", "scripts": { "build": "midway-bin build -c", - "test": "midway-bin test --ts --forceExit", - "cov": "midway-bin cov --ts --forceExit", + "test": "midway-bin test --ts", + "cov": "midway-bin cov --ts", "ci": "npm run test", "link": "npm link" }, "dependencies": { "@types/express": "^4.17.8", "@types/koa": "^2.11.4", - "camelcase": "^5.0.0", + "camelcase": "^6.2.0", "class-transformer": "^0.3.1", "joi": "^17.2.1", "reflect-metadata": "^0.1.13" diff --git a/packages/decorator/src/annotation/autoload.ts b/packages/decorator/src/annotation/autoload.ts new file mode 100644 index 000000000000..20e6f362ab20 --- /dev/null +++ b/packages/decorator/src/annotation/autoload.ts @@ -0,0 +1,7 @@ +import { savePreloadModule } from '../common/decoratorManager'; + +export function Autoload() { + return function (target) { + savePreloadModule(target); + }; +} diff --git a/packages/decorator/src/annotation/index.ts b/packages/decorator/src/annotation/index.ts index a423baf81c54..07d34c3cba2b 100644 --- a/packages/decorator/src/annotation/index.ts +++ b/packages/decorator/src/annotation/index.ts @@ -8,3 +8,4 @@ export * from './pipeline'; export * from './validate'; export * from './rule'; export * from './aspect'; +export * from './autoload'; diff --git a/packages/decorator/src/common/decoratorManager.ts b/packages/decorator/src/common/decoratorManager.ts index 8e69ded3afe4..f364979cc1c6 100644 --- a/packages/decorator/src/common/decoratorManager.ts +++ b/packages/decorator/src/common/decoratorManager.ts @@ -15,7 +15,7 @@ import { TAGGED, TAGGED_CLS, TAGGED_PROP, -} from './constant'; +} from '../constant'; import { DUPLICATED_INJECTABLE_DECORATOR, @@ -27,7 +27,7 @@ import { getParamNames, classNamed, isNullOrUndefined } from '../util'; const debug = require('util').debuglog('decorator:manager'); -export type decoratorKey = string | symbol; +export type DecoratorKey = string | symbol; export const PRELOAD_MODULE_KEY = 'INJECTION_PRELOAD_MODULE_KEY'; @@ -57,21 +57,21 @@ export class DecoratorManager extends Map { this.set(key, new Set()); } - static getDecoratorClassKey(decoratorNameKey: decoratorKey) { + static getDecoratorClassKey(decoratorNameKey: DecoratorKey) { return decoratorNameKey.toString() + '_CLS'; } - static getDecoratorMethodKey(decoratorNameKey: decoratorKey) { + static getDecoratorMethodKey(decoratorNameKey: DecoratorKey) { return decoratorNameKey.toString() + '_METHOD'; } - static getDecoratorClsMethodPrefix(decoratorNameKey: decoratorKey) { + static getDecoratorClsMethodPrefix(decoratorNameKey: DecoratorKey) { return decoratorNameKey.toString() + '_CLS_METHOD'; } static getDecoratorClsMethodKey( - decoratorNameKey: decoratorKey, - methodKey: decoratorKey + decoratorNameKey: DecoratorKey, + methodKey: DecoratorKey ) { return ( DecoratorManager.getDecoratorClsMethodPrefix(decoratorNameKey) + @@ -81,8 +81,8 @@ export class DecoratorManager extends Map { } static getDecoratorMethod( - decoratorNameKey: decoratorKey, - methodKey: decoratorKey + decoratorNameKey: DecoratorKey, + methodKey: DecoratorKey ) { return ( DecoratorManager.getDecoratorMethodKey(decoratorNameKey) + @@ -195,7 +195,7 @@ export class DecoratorManager extends Map { * @param target target class * @param propertyName */ - saveMetadata(decoratorNameKey: decoratorKey, data, target, propertyName?) { + saveMetadata(decoratorNameKey: DecoratorKey, data, target, propertyName?) { if (propertyName) { const dataKey = DecoratorManager.getDecoratorMethod( decoratorNameKey, @@ -226,7 +226,7 @@ export class DecoratorManager extends Map { * @param propertyName */ attachMetadata( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, data, target, propertyName?: string, @@ -262,7 +262,7 @@ export class DecoratorManager extends Map { * @param target * @param propertyName */ - getMetadata(decoratorNameKey: decoratorKey, target, propertyName?) { + getMetadata(decoratorNameKey: DecoratorKey, target, propertyName?) { if (propertyName) { const dataKey = DecoratorManager.getDecoratorMethod( decoratorNameKey, @@ -293,7 +293,7 @@ export class DecoratorManager extends Map { * @param propertyName */ savePropertyDataToClass( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, data, target, propertyName @@ -319,7 +319,7 @@ export class DecoratorManager extends Map { * @param groupBy */ attachPropertyDataToClass( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, data, target, propertyName, @@ -345,7 +345,7 @@ export class DecoratorManager extends Map { * @param propertyName */ getPropertyDataFromClass( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, target, propertyName ) { @@ -365,7 +365,7 @@ export class DecoratorManager extends Map { * @param decoratorNameKey * @param target */ - listPropertyDataFromClass(decoratorNameKey: decoratorKey, target) { + listPropertyDataFromClass(decoratorNameKey: DecoratorKey, target) { const originMap = DecoratorManager.getMetadata( this.injectClassMethodKeyPrefix, target @@ -393,7 +393,7 @@ const manager = new DecoratorManager(); * @param target */ export function saveClassMetadata( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, data, target ) { @@ -408,7 +408,7 @@ export function saveClassMetadata( * @param groupBy */ export function attachClassMetadata( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, data: any, target, groupBy?: string @@ -422,14 +422,14 @@ export function attachClassMetadata( ); } -const testKeyMap = new Map(); +const testKeyMap = new Map(); /** * get data from class * @param decoratorNameKey * @param target */ -export function getClassMetadata(decoratorNameKey: decoratorKey, target) { +export function getClassMetadata(decoratorNameKey: DecoratorKey, target) { if (testKeyMap.size > 0 && testKeyMap.has(decoratorNameKey)) { throw testKeyMap.get(decoratorNameKey); } @@ -437,7 +437,7 @@ export function getClassMetadata(decoratorNameKey: decoratorKey, target) { } // TODO 因 https://github.com/microsoft/TypeScript/issues/38820 等 4.0 发布移除掉 -export function throwErrorForTest(key: decoratorKey, e: Error) { +export function throwErrorForTest(key: DecoratorKey, e: Error) { if (e) { testKeyMap.set(key, e); } else { @@ -455,7 +455,7 @@ export function throwErrorForTest(key: decoratorKey, e: Error) { * @param method */ export function saveMethodDataToClass( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, data, target, method @@ -478,7 +478,7 @@ export function saveMethodDataToClass( * @param method */ export function attachMethodDataToClass( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, data, target, method @@ -500,7 +500,7 @@ export function attachMethodDataToClass( * @param method */ export function getMethodDataFromClass( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, target, method ) { @@ -514,7 +514,7 @@ export function getMethodDataFromClass( * @param target */ export function listMethodDataFromClass( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, target ) { return manager.listPropertyDataFromClass(decoratorNameKey, target); @@ -529,7 +529,7 @@ export function listMethodDataFromClass( * @param method */ export function saveMethodMetadata( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, data, target, method @@ -546,7 +546,7 @@ export function saveMethodMetadata( * @param method */ export function attachMethodMetadata( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, data, target, method @@ -562,7 +562,7 @@ export function attachMethodMetadata( * @param method */ export function getMethodMetadata( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, target, method ) { @@ -577,7 +577,7 @@ export function getMethodMetadata( * @param propertyName */ export function savePropertyDataToClass( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, data, target, propertyName @@ -598,7 +598,7 @@ export function savePropertyDataToClass( * @param propertyName */ export function attachPropertyDataToClass( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, data, target, propertyName @@ -618,7 +618,7 @@ export function attachPropertyDataToClass( * @param propertyName */ export function getPropertyDataFromClass( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, target, propertyName ) { @@ -635,7 +635,7 @@ export function getPropertyDataFromClass( * @param target */ export function listPropertyDataFromClass( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, target ) { return manager.listPropertyDataFromClass(decoratorNameKey, target); @@ -649,7 +649,7 @@ export function listPropertyDataFromClass( * @param propertyName */ export function savePropertyMetadata( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, data, target, propertyName @@ -665,7 +665,7 @@ export function savePropertyMetadata( * @param propertyName */ export function attachPropertyMetadata( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, data, target, propertyName @@ -680,7 +680,7 @@ export function attachPropertyMetadata( * @param propertyName */ export function getPropertyMetadata( - decoratorNameKey: decoratorKey, + decoratorNameKey: DecoratorKey, target, propertyName ) { @@ -699,7 +699,7 @@ export function savePreloadModule(target) { * list preload module */ export function listPreloadModule(): any[] { - return manager.listModule(PRELOAD_MODULE_KEY); + return listModule(PRELOAD_MODULE_KEY); } /** @@ -707,7 +707,7 @@ export function listPreloadModule(): any[] { * @param decoratorNameKey * @param target */ -export function saveModule(decoratorNameKey: decoratorKey, target) { +export function saveModule(decoratorNameKey: DecoratorKey, target) { return manager.saveModule(decoratorNameKey, target); } @@ -715,15 +715,20 @@ export function saveModule(decoratorNameKey: decoratorKey, target) { * list module from decorator key * @param decoratorNameKey */ -export function listModule(decoratorNameKey: decoratorKey): any[] { - return manager.listModule(decoratorNameKey); +export function listModule(decoratorNameKey: DecoratorKey, filter?:(module) => boolean): any[] { + const modules = manager.listModule(decoratorNameKey); + if (filter) { + return modules.filter(filter); + } else { + return modules; + } } /** * reset module * @param decoratorNameKey */ -export function resetModule(decoratorNameKey: decoratorKey): void { +export function resetModule(decoratorNameKey: DecoratorKey): void { return manager.resetModule(decoratorNameKey); } diff --git a/packages/decorator/src/common/metadata.ts b/packages/decorator/src/common/metadata.ts index b29c25e414fa..069f4cf29cc1 100644 --- a/packages/decorator/src/common/metadata.ts +++ b/packages/decorator/src/common/metadata.ts @@ -1,4 +1,4 @@ -import { NAMED_TAG } from './constant'; +import { NAMED_TAG } from '../constant'; import { TagPropsMetadata } from '../interface'; export class Metadata implements TagPropsMetadata { diff --git a/packages/decorator/src/common/constant.ts b/packages/decorator/src/constant.ts similarity index 91% rename from packages/decorator/src/common/constant.ts rename to packages/decorator/src/constant.ts index 5d1b4addf164..efa7e80f431c 100644 --- a/packages/decorator/src/common/constant.ts +++ b/packages/decorator/src/constant.ts @@ -35,6 +35,12 @@ export const RPC_DUBBO_KEY = 'rpc:dubbo'; // microservice export const MS_CONSUMER_KEY = 'ms:consumer'; export const MS_PRODUCER_KEY = 'ms:producer'; +export const MS_PROVIDER_KEY = 'ms:provider'; + +// rpc method +export const MS_GRPC_METHOD_KEY = 'ms:grpc:method'; +export const MS_DUBBO_METHOD_KEY = 'ms:dubbo:method'; +export const MS_HSF_METHOD_KEY = 'ms:hsf:method'; // framework export const CONFIG_KEY = 'config'; diff --git a/packages/decorator/src/index.ts b/packages/decorator/src/index.ts index ae16172a1b72..28bf306b0032 100644 --- a/packages/decorator/src/index.ts +++ b/packages/decorator/src/index.ts @@ -1,6 +1,6 @@ export * from './interface'; export * from './annotation'; -export * from './common/constant'; +export * from './constant'; export * from './common/decoratorManager'; export * from './common/errMsg'; export * from './common/metadata'; @@ -18,6 +18,7 @@ export * from './framework/plugin'; export * from './framework/app'; export * from './ws/webSocketController'; export * from './ws/webSocketEvent'; +export * from './microservice/provider'; export * from './microservice/consumer'; export * from './microservice/rabbitmqListener'; export * from './util/index'; diff --git a/packages/decorator/src/interface.ts b/packages/decorator/src/interface.ts index 4184d9038d39..7d10558a32d3 100644 --- a/packages/decorator/src/interface.ts +++ b/packages/decorator/src/interface.ts @@ -40,3 +40,25 @@ export interface TagClsMetadata { export interface ReflectResult { [key: string]: TagPropsMetadata[]; } + +export enum MSProviderType { + DUBBO = 'dubbo', + GRPC = 'gRPC', + HSF = 'hsf', +} + +/** + * all decorator metadata format + */ +export namespace DecoratorMetadata { + + export interface GRPCClassMetadata { + serviceName?: string; + package?: string; + } + + export interface ProviderClassMetadata { + type: MSProviderType, + metadata: GRPCClassMetadata + } +} diff --git a/packages/decorator/src/microservice/provider.ts b/packages/decorator/src/microservice/provider.ts new file mode 100644 index 000000000000..0b286ece700d --- /dev/null +++ b/packages/decorator/src/microservice/provider.ts @@ -0,0 +1,68 @@ +import { + ScopeEnum, + saveClassMetadata, + saveModule, + MS_PROVIDER_KEY, + attachClassMetadata, + MS_GRPC_METHOD_KEY, + MS_DUBBO_METHOD_KEY, + MS_HSF_METHOD_KEY, + MSProviderType, + DecoratorMetadata, +} from '../'; +import { Scope } from '../annotation'; + +export function Provider(type: MSProviderType.GRPC, metadata?: DecoratorMetadata.GRPCClassMetadata): ClassDecorator; +export function Provider(type: MSProviderType.DUBBO, metadata?: any): ClassDecorator; +export function Provider(type: MSProviderType, metadata: any = {}): ClassDecorator { + return (target: any) => { + saveModule(MS_PROVIDER_KEY, target); + saveClassMetadata(MS_PROVIDER_KEY, { + type, + metadata, + }, target); + Scope(ScopeEnum.Request)(target); + }; +} + +export function GrpcMethod(methodName?: string): MethodDecorator { + return (target, propertyName, descriptor: PropertyDescriptor) => { + attachClassMetadata( + MS_GRPC_METHOD_KEY, + { + methodName: methodName || propertyName + }, + target + ); + + return descriptor; + }; +} + +export function DubboMethod(methodName?: string): MethodDecorator { + return (target, propertyName, descriptor: PropertyDescriptor) => { + attachClassMetadata( + MS_DUBBO_METHOD_KEY, + { + methodName: methodName || propertyName + }, + target + ); + + return descriptor; + }; +} + +export function HSFMethod(methodName?: string): MethodDecorator { + return (target, propertyName, descriptor: PropertyDescriptor) => { + attachClassMetadata( + MS_HSF_METHOD_KEY, + { + methodName: methodName || propertyName + }, + target + ); + + return descriptor; + }; +} diff --git a/packages/decorator/src/rpc/hsf.ts b/packages/decorator/src/rpc/hsf.ts index 2e4c157d8c50..9548c462c0be 100644 --- a/packages/decorator/src/rpc/hsf.ts +++ b/packages/decorator/src/rpc/hsf.ts @@ -8,6 +8,11 @@ export interface HSFOpts { namespace?: string; } +/** + * @deprecated + * @param hsfOption + * @constructor + */ export function HSF(hsfOption: HSFOpts = {}): ClassDecorator { return (target: any) => { saveModule(HSF_KEY, target); diff --git a/packages/decorator/test/annotation/autoload.test.ts b/packages/decorator/test/annotation/autoload.test.ts new file mode 100644 index 000000000000..bac9275fc4fb --- /dev/null +++ b/packages/decorator/test/annotation/autoload.test.ts @@ -0,0 +1,18 @@ +import { listPreloadModule, Autoload } from '../../src'; + +describe('/test/annotation/autoload.test.ts', () => { + it('test preload key in module', () => { + + @Autoload() + class LoadA { + } + @Autoload() + class LoadB { + } + + const modules = listPreloadModule(); + expect(modules.length).toEqual(2); + expect(modules[0]).toEqual(LoadA); + expect(modules[1]).toEqual(LoadB); + }); +}); diff --git a/packages/decorator/test/microservice/provider.test.ts b/packages/decorator/test/microservice/provider.test.ts new file mode 100644 index 000000000000..b7bd45e8a416 --- /dev/null +++ b/packages/decorator/test/microservice/provider.test.ts @@ -0,0 +1,37 @@ +import { + getClassMetadata, + getObjectDefProps, + listModule, + MS_PROVIDER_KEY, + ScopeEnum, + DecoratorMetadata, + MSProviderType, + Provider +} from '../../src'; +import ProviderClassMetadata = DecoratorMetadata.ProviderClassMetadata; + +@Provider(MSProviderType.GRPC, { package: 'test' }) +class TestFun { +} + +@Provider(MSProviderType.DUBBO) +class TestFun1 { +} + +describe('/test/microservice/provider.test.ts', () => { + it('test consumer decorator', () => { + const meta: ProviderClassMetadata = getClassMetadata(MS_PROVIDER_KEY, TestFun); + expect(meta).toEqual({ type: MSProviderType.GRPC, metadata: { package: 'test' } }); + + const meta2: ProviderClassMetadata = getClassMetadata(MS_PROVIDER_KEY, TestFun1); + expect(meta2).toEqual({ type: MSProviderType.DUBBO, metadata: {} }); + + const def = getObjectDefProps(TestFun); + expect(def).toEqual({ + scope: ScopeEnum.Request, + }); + + const m = listModule(MS_PROVIDER_KEY); + expect(m.length).toEqual(2); + }); +}); diff --git a/packages/faas/src/framework.ts b/packages/faas/src/framework.ts index 380464ab43b0..2ad4b346f3fc 100644 --- a/packages/faas/src/framework.ts +++ b/packages/faas/src/framework.ts @@ -14,7 +14,6 @@ import { listModule, listPreloadModule, MidwayFrameworkType, - MidwayRequestContainer, REQUEST_OBJ_CTX_KEY, } from '@midwayjs/core'; @@ -31,6 +30,7 @@ const LOCK_KEY = '_faas_starter_start_key'; export class MidwayFaaSFramework extends BaseFramework< IMidwayFaaSApplication, + FaaSContext, IFaaSConfigurationOptions > { protected defaultHandlerMethod = 'handler'; @@ -214,18 +214,13 @@ export class MidwayFaaSFramework extends BaseFramework< .getEnvironmentService() .getCurrentEnvironment(); } - if (!context.logger) { - context.logger = this.logger; - } - if (!context.requestContext) { - context.requestContext = new MidwayRequestContainer( - context, - this.getApplicationContext() - ); - } if (!context.hooks) { context.hooks = new MidwayHooks(context, this.app); } + if (!context.logger) { + context.logger = this.logger; + } + this.app.createAnonymousContext(context); return context; } @@ -337,6 +332,10 @@ export class MidwayFaaSFramework extends BaseFramework< disableError: true, })); } + + public getFrameworkName() { + return 'midway:faas' + } } function covertId(cls, method) { diff --git a/packages/faas/src/interface.ts b/packages/faas/src/interface.ts index ba027c71e0a0..bdc4a0c195e0 100644 --- a/packages/faas/src/interface.ts +++ b/packages/faas/src/interface.ts @@ -1,10 +1,19 @@ -import { MidwayRequestContainer, IMidwayApplication, IMidwayLogger } from '@midwayjs/core'; +import { MidwayRequestContainer, IMidwayApplication, IConfigurationOptions, IMidwayContext } from '@midwayjs/core'; import { FaaSHTTPContext } from '@midwayjs/faas-typings'; import type { MidwayHooks } from './hooks'; +import { ILogger } from '@midwayjs/logger'; + +export interface FaaSContext extends FaaSHTTPContext, IMidwayContext { + logger: ILogger; + env: string; + requestContext: MidwayRequestContainer; + originContext: any; + hooks: MidwayHooks; +} export type FaaSMiddleware = (() => (context: FaaSContext, next: () => Promise) => any) | string; -export interface IMidwayFaaSApplication extends IMidwayApplication { +export interface IMidwayFaaSApplication extends IMidwayApplication { getInitializeContext(); use(middleware: FaaSMiddleware); useMiddleware(mw: string[]); @@ -33,15 +42,12 @@ export interface FunctionHandler { handler(...args); } -export interface FaaSContext extends FaaSHTTPContext { - logger: IMidwayLogger; - env: string; - requestContext: MidwayRequestContainer; - originContext: any; - hooks: MidwayHooks; -} -export interface IFaaSConfigurationOptions { +export type Application = IMidwayFaaSApplication; + +export type Context = FaaSContext; + +export interface IFaaSConfigurationOptions extends IConfigurationOptions { config?: object; middleware?: string[]; initializeContext?: object; diff --git a/packages/grpc/README.md b/packages/grpc/README.md new file mode 100644 index 000000000000..54dc6edba4db --- /dev/null +++ b/packages/grpc/README.md @@ -0,0 +1,12 @@ +# midway-web + +[![Package Quality](http://npm.packagequality.com/shield/midway-web.svg)](http://packagequality.com/#?package=midway-web) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/midwayjs/midway/pulls) + +this is a sub package for midway. + +Document: [https://midwayjs.org/midway](https://midwayjs.org/midway) + +## License + +[MIT]((http://github.com/midwayjs/midway/blob/master/LICENSE)) diff --git a/packages/grpc/jest.config.js b/packages/grpc/jest.config.js new file mode 100644 index 000000000000..bb2f28760e4c --- /dev/null +++ b/packages/grpc/jest.config.js @@ -0,0 +1,8 @@ +const path = require('path'); + +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testPathIgnorePatterns: ['/test/fixtures'], + coveragePathIgnorePatterns: ['/test/'], +}; diff --git a/packages/grpc/package.json b/packages/grpc/package.json new file mode 100644 index 000000000000..bfd1e42b680d --- /dev/null +++ b/packages/grpc/package.json @@ -0,0 +1,45 @@ +{ + "name": "@midwayjs/grpc", + "version": "2.5.5", + "description": "Midway Framework for gRPC", + "main": "dist/index", + "typings": "dist/index.d.ts", + "scripts": { + "build": "midway-bin build -c", + "test": "midway-bin test --ts", + "cov": "midway-bin cov --ts", + "ci": "npm run test" + }, + "keywords": [ + "midway", + "IoC", + "web", + "scene", + "express" + ], + "files": [ + "dist/**/*.js", + "dist/**/*.d.ts" + ], + "license": "MIT", + "devDependencies": { + "@midwayjs/cli": "^1.2.36", + "@midwayjs/mock": "^2.6.9", + "fs-extra": "^8.0.1" + }, + "dependencies": { + "@midwayjs/core": "^2.6.9", + "@midwayjs/decorator": "^2.4.8", + "@midwayjs/logger": "^2.6.9", + "@grpc/grpc-js": "^1.2.1", + "@grpc/proto-loader": "^0.5.5", + "camelcase": "^6.2.0", + "pascal-case": "^3.1.2" + }, + "author": "Harry Chen ", + "repository": { + "type": "git", + "url": "http://github.com/midwayjs/midway.git" + }, + "gitHead": "a603d2348d6141f8f723901498f03a162a037708" +} diff --git a/packages/grpc/src/comsumer/clients.ts b/packages/grpc/src/comsumer/clients.ts new file mode 100644 index 000000000000..3cf2236556fd --- /dev/null +++ b/packages/grpc/src/comsumer/clients.ts @@ -0,0 +1,58 @@ +import { Autoload, Config, Init, Logger, Provide, Scope, ScopeEnum } from '@midwayjs/decorator'; +import { credentials, loadPackageDefinition } from '@grpc/grpc-js'; +import { DefaultConfig } from '../interface'; +import { loadProto } from '../util'; +import * as camelCase from 'camelcase'; +import { ILogger } from '@midwayjs/logger'; + +@Autoload() +@Provide('clients') +@Scope(ScopeEnum.Singleton) +export class GRPCClients extends Map { + + @Config('grpc') + grpcConfig: DefaultConfig; + + @Logger() + logger: ILogger; + + @Init() + async initService() { + if (!this.grpcConfig['services']) { + this.logger.error('Please set gRPC services in your config["grpc"]'); + return; + } + for(const cfg of this.grpcConfig['services']) { + const packageDefinition = await loadProto({ + loaderOptions: cfg.loaderOptions, + protoPath: cfg.protoPath, + }); + const packageProto: any = loadPackageDefinition(packageDefinition)[cfg.package]; + for (const definition in packageDefinition) { + if (!packageDefinition[definition]['format']) { + const serviceName = definition.replace(`${cfg.package}.`, ''); + const connectionService = new packageProto[serviceName](cfg.url, credentials.createInsecure()); + for(const methodName of Object.keys(packageDefinition[definition])) { + const originMethod = connectionService[methodName]; + connectionService[methodName] = async (...args) => { + return new Promise((resolve, reject) => { + originMethod.call(connectionService, args[0], (err, response) => { + if (err) { + reject(err); + } + resolve(response); + }); + }); + }; + connectionService[camelCase(methodName)] = connectionService[methodName]; + } + this.set(definition, connectionService); + } + } + } + } + + getService(serviceName: string): T { + return this.get(serviceName); + } +} diff --git a/packages/grpc/src/comsumer/config.default.ts b/packages/grpc/src/comsumer/config.default.ts new file mode 100644 index 000000000000..e6a1c4b2d6ba --- /dev/null +++ b/packages/grpc/src/comsumer/config.default.ts @@ -0,0 +1 @@ +export const grpc = {}; diff --git a/packages/grpc/src/comsumer/configuration.ts b/packages/grpc/src/comsumer/configuration.ts new file mode 100644 index 000000000000..f29c816eae13 --- /dev/null +++ b/packages/grpc/src/comsumer/configuration.ts @@ -0,0 +1,20 @@ +import { Configuration, Logger } from '@midwayjs/decorator'; +import { setLogger } from '@grpc/grpc-js'; +import { ILogger } from '@midwayjs/logger'; +import { join } from 'path'; + +@Configuration({ + namespace: 'grpc', + importConfigs: [ + join(__dirname, 'config.default'), + ] +}) +export class AutoConfiguration { + + @Logger() + logger: ILogger; + + async onReady() { + setLogger(this.logger); + } +} diff --git a/packages/grpc/src/index.ts b/packages/grpc/src/index.ts new file mode 100644 index 000000000000..93e22ea83832 --- /dev/null +++ b/packages/grpc/src/index.ts @@ -0,0 +1,5 @@ +export * from './interface'; +export { MidwayGRPCFramework as Framework } from './provider/framework'; +export { AutoConfiguration as Configuration } from './comsumer/configuration'; +export { GRPCClients as Clients } from './comsumer/clients'; +export { loadProto, createGRPCConsumer } from './util'; diff --git a/packages/grpc/src/interface.ts b/packages/grpc/src/interface.ts new file mode 100644 index 000000000000..0f178b78639e --- /dev/null +++ b/packages/grpc/src/interface.ts @@ -0,0 +1,45 @@ +import { IConfigurationOptions, IMidwayApplication, IMidwayContext } from '@midwayjs/core'; +import { Server, ServerCredentials, Metadata } from '@grpc/grpc-js'; + +export interface IMidwayGRPCContext extends IMidwayContext { + metadata: Metadata; + method: string; +} +export type IMidwayGRPCApplication = IMidwayApplication & Server; + +export type Application = IMidwayGRPCApplication; + +export type Context = IMidwayGRPCContext; + +export interface IGRPCServiceOptions { + /** + * application gRPC connection string + */ + url?: string; + /** + * proto path + */ + protoPath?: string; + + /** + * protobuf package name + */ + package?: string; + + loaderOptions?: object; + + credentials?: ServerCredentials; +} + +export interface IMidwayGRPFrameworkOptions extends IConfigurationOptions { + /** + * gRPC Server connection url, default is localhost:6565 + */ + url?: string; + services: Pick[]; + loaderOptions?: object; +} + +export interface DefaultConfig extends IConfigurationOptions { + services: IGRPCServiceOptions[]; +} diff --git a/packages/grpc/src/provider/framework.ts b/packages/grpc/src/provider/framework.ts new file mode 100644 index 000000000000..00436b98de4f --- /dev/null +++ b/packages/grpc/src/provider/framework.ts @@ -0,0 +1,139 @@ +import { Server, ServerCredentials, setLogger, ServerUnaryCall, sendUnaryData } from '@grpc/grpc-js'; +import { + BaseFramework, + getClassMetadata, + IMidwayBootstrapOptions, + listModule, + MidwayFrameworkType, +} from '@midwayjs/core'; + +import { + DecoratorMetadata, + getProviderId, + MS_PROVIDER_KEY, + MSProviderType +} from '@midwayjs/decorator'; +import { + IMidwayGRPCApplication, IMidwayGRPCContext, IMidwayGRPFrameworkOptions, +} from '../interface'; +import { MidwayGRPCContextLogger } from './logger'; +import { pascalCase } from 'pascal-case'; +import * as camelCase from 'camelcase'; +import { loadProto } from '../util'; +import { PackageDefinition } from '@grpc/proto-loader'; + +export class MidwayGRPCFramework extends BaseFramework< + IMidwayGRPCApplication, + IMidwayGRPCContext, + IMidwayGRPFrameworkOptions + > { + public app: IMidwayGRPCApplication; + private server: Server; + + async applicationInitialize(options: Partial) { + // set logger to grpc server + setLogger(this.logger); + const server: Server = new Server({ + 'grpc.max_receive_message_length': -1, + 'grpc.max_send_message_length': -1, + }); + + this.app = server as IMidwayGRPCApplication; + this.server = server; + } + + protected async afterContainerReady( + options: Partial + ): Promise { + await this.loadService(); + } + + protected async loadService() { + // find all code service + const gRPCModules = listModule(MS_PROVIDER_KEY, (module) => { + const info: DecoratorMetadata.ProviderClassMetadata = getClassMetadata(MS_PROVIDER_KEY, module); + return info.type === MSProviderType.GRPC; + }); + + this.logger.info(`Find ${gRPCModules.length} class has gRPC provider decorator`); + + // get definition from proto file + const serviceClassDefinition: Map = new Map(); + for (const service of this.configurationOptions.services) { + const definitions = await loadProto({ + protoPath: service.protoPath, + loaderOptions: this.configurationOptions.loaderOptions, + }); + serviceClassDefinition.set(service.package, definitions); + } + + // register method to service + for (const module of gRPCModules) { + const provideId = getProviderId(module); + const info: DecoratorMetadata.ProviderClassMetadata = getClassMetadata(MS_PROVIDER_KEY, module); + const classMetadata = info.metadata as DecoratorMetadata.GRPCClassMetadata; + let serviceName = classMetadata.serviceName || pascalCase(provideId); + + if (serviceClassDefinition.has(classMetadata?.package)) { + const serviceInstance = {}; + const serviceDefinition: any = serviceClassDefinition.get(classMetadata.package)[`${classMetadata?.package}.${serviceName}`]; + + for (const method in serviceDefinition) { + serviceInstance[method] = async (call: ServerUnaryCall, callback: sendUnaryData) => { + const ctx = { method, metadata: call.metadata } as any; + this.app.createAnonymousContext(ctx); + try { + const service = await ctx.requestContext.getAsync(module); + const result = await service[camelCase(method)]?.apply(service, [call.request]); + callback(null, result); + } catch (err) { + callback(err); + } + }; + } + this.server.addService(serviceDefinition, serviceInstance); + this.logger.info(`Proto ${classMetadata?.package}.${serviceName} found and add to gRPC server`); + } + } + } + + public async run(): Promise { + return new Promise((resolve, reject) => { + this.server.bindAsync(`${this.configurationOptions.url || 'localhost:6565'}`, ServerCredentials.createInsecure(), (err: Error | null, bindPort: number) => { + if (err) { + reject(err); + } + + this.server.start(); + this.logger.info(`Server port = ${bindPort} start success`); + resolve(); + }); + }); + } + + public async beforeStop() { + this.server.tryShutdown(() => { + this.logger.info('Server shutdown success'); + }); + } + + public getFrameworkType(): MidwayFrameworkType { + return MidwayFrameworkType.MS_GRPC; + } + + public getApplication(): IMidwayGRPCApplication { + return this.app; + } + + public getServer() { + return this.server; + } + + public getFrameworkName() { + return 'midway:gRPC' + } + + public getDefaultContextLoggerClass(): any { + return MidwayGRPCContextLogger; + } +} diff --git a/packages/grpc/src/provider/logger.ts b/packages/grpc/src/provider/logger.ts new file mode 100644 index 000000000000..608bf67f3146 --- /dev/null +++ b/packages/grpc/src/provider/logger.ts @@ -0,0 +1,17 @@ +import { MidwayContextLogger } from '@midwayjs/core'; +import { IMidwayGRPCContext } from '../interface'; + +export class MidwayGRPCContextLogger extends MidwayContextLogger< + IMidwayGRPCContext + > { + formatContextLabel() { + const ctx = this.ctx; + const use = Date.now() - ctx.startTime; + return ( + ctx.method + + '/' + + use + + 'ms' + ); + } +} diff --git a/packages/grpc/src/util.ts b/packages/grpc/src/util.ts new file mode 100644 index 000000000000..58272bb15780 --- /dev/null +++ b/packages/grpc/src/util.ts @@ -0,0 +1,27 @@ +import * as protoLoader from '@grpc/proto-loader'; +import { IGRPCServiceOptions } from './interface'; +import { GRPCClients } from './comsumer/clients'; + +export const loadProto = (options: { + protoPath: string; + loaderOptions?: any; +}) => { + return protoLoader.loadSync(options.protoPath, Object.assign({ + keepCase: true, + longs: String, + enums: String, + defaults: true, + oneofs: true + }, options.loaderOptions || {})); +}; + +export const createGRPCConsumer = async (options: IGRPCServiceOptions): Promise => { + const clients = new GRPCClients(); + options.url = options.url || 'localhost:6565'; + clients.grpcConfig = { + services: [options] + } + + await clients.initService(); + return Array.from(clients.values())[0]; +} diff --git a/packages/grpc/test/fixtures/base-app-multiple-service/package.json b/packages/grpc/test/fixtures/base-app-multiple-service/package.json new file mode 100644 index 000000000000..621cdc6a4174 --- /dev/null +++ b/packages/grpc/test/fixtures/base-app-multiple-service/package.json @@ -0,0 +1,3 @@ +{ + "name": "ali-demo" +} diff --git a/packages/grpc/test/fixtures/base-app-multiple-service/src/config/config.default.ts b/packages/grpc/test/fixtures/base-app-multiple-service/src/config/config.default.ts new file mode 100644 index 000000000000..c46e2bca9292 --- /dev/null +++ b/packages/grpc/test/fixtures/base-app-multiple-service/src/config/config.default.ts @@ -0,0 +1,12 @@ +import { DefaultConfig } from '../../../../../src'; +import { join } from 'path'; + +export const grpc = { + services: [ + { + url: 'localhost:6565', + protoPath: join(__dirname, '../../../proto/helloworld.proto'), + package: 'helloworld' + } + ] +} as DefaultConfig; diff --git a/packages/grpc/test/fixtures/base-app-multiple-service/src/configuration.ts b/packages/grpc/test/fixtures/base-app-multiple-service/src/configuration.ts new file mode 100644 index 000000000000..524fbcb91431 --- /dev/null +++ b/packages/grpc/test/fixtures/base-app-multiple-service/src/configuration.ts @@ -0,0 +1,14 @@ +import { Configuration } from '@midwayjs/decorator'; +import * as grpc from '../../../../src'; +import { join } from 'path'; + +@Configuration({ + imports: [ + grpc + ], + importConfigs: [ + join(__dirname, './config'), + ] +}) +export class AutoConfiguration { +} diff --git a/packages/grpc/test/fixtures/base-app-multiple-service/src/interface.ts b/packages/grpc/test/fixtures/base-app-multiple-service/src/interface.ts new file mode 100644 index 000000000000..12447457d823 --- /dev/null +++ b/packages/grpc/test/fixtures/base-app-multiple-service/src/interface.ts @@ -0,0 +1,28 @@ +import { Metadata } from '@grpc/grpc-js'; + +export namespace hero { + export interface HeroService { + findOne(data: HeroById, metadata?: Metadata): Promise; + } + export interface HeroById { + id?: number; + } + export interface Hero { + id?: number; + name?: string; + } +} + +export namespace helloworld { + export interface Greeter { + sayHello (request: HelloRequest): Promise + } + + export interface HelloRequest { + name: string; + } + + export interface HelloReply { + message: string; + } +} diff --git a/packages/grpc/test/fixtures/base-app-multiple-service/src/provider/greeter.ts b/packages/grpc/test/fixtures/base-app-multiple-service/src/provider/greeter.ts new file mode 100644 index 000000000000..74a9fc15378a --- /dev/null +++ b/packages/grpc/test/fixtures/base-app-multiple-service/src/provider/greeter.ts @@ -0,0 +1,19 @@ +import { MSProviderType, Provider, Provide, GrpcMethod } from '@midwayjs/decorator'; +import { helloworld } from '../interface'; + +/** + * package helloworld + * service Greeter + */ +@Provide() +@Provider(MSProviderType.GRPC, { package: 'helloworld' }) +export class Greeter implements helloworld.Greeter { + + /** + * Implements the SayHello RPC method. + */ + @GrpcMethod() + async sayHello(request: helloworld.HelloRequest) { + return { message: 'Hello ' + request.name } + } +} diff --git a/packages/grpc/test/fixtures/base-app-multiple-service/src/provider/hero.ts b/packages/grpc/test/fixtures/base-app-multiple-service/src/provider/hero.ts new file mode 100644 index 000000000000..3dd6e26771fe --- /dev/null +++ b/packages/grpc/test/fixtures/base-app-multiple-service/src/provider/hero.ts @@ -0,0 +1,29 @@ +import { GrpcMethod, MSProviderType, Provider, Provide, Inject, Init } from '@midwayjs/decorator'; +import { helloworld, hero } from '../interface'; +import { Clients } from '../../../../../src'; + +@Provide() +@Provider(MSProviderType.GRPC, { package: 'hero' }) +export class HeroService implements hero.HeroService { + + @Inject('grpc:clients') + grpcClients: Clients; + + greeterService: helloworld.Greeter; + + @Init() + async init() { + this.greeterService = this.grpcClients.getService('helloworld.Greeter'); + } + + @GrpcMethod() + async findOne(data) { + const result = await this.greeterService.sayHello({ + name: 'harry' + }); + return { + id: 1, + name: 'bbbb-' + result.message, + }; + } +} diff --git a/packages/grpc/test/fixtures/base-app/package.json b/packages/grpc/test/fixtures/base-app/package.json new file mode 100644 index 000000000000..621cdc6a4174 --- /dev/null +++ b/packages/grpc/test/fixtures/base-app/package.json @@ -0,0 +1,3 @@ +{ + "name": "ali-demo" +} diff --git a/packages/grpc/test/fixtures/base-app/src/configuration.ts b/packages/grpc/test/fixtures/base-app/src/configuration.ts new file mode 100644 index 000000000000..023d2b63e20d --- /dev/null +++ b/packages/grpc/test/fixtures/base-app/src/configuration.ts @@ -0,0 +1,10 @@ +import { Configuration } from '@midwayjs/decorator'; +import * as grpc from '../../../../src'; + +@Configuration({ + imports: [ + grpc + ], +}) +export class AutoConfiguration { +} diff --git a/packages/grpc/test/fixtures/base-app/src/interface.ts b/packages/grpc/test/fixtures/base-app/src/interface.ts new file mode 100644 index 000000000000..12447457d823 --- /dev/null +++ b/packages/grpc/test/fixtures/base-app/src/interface.ts @@ -0,0 +1,28 @@ +import { Metadata } from '@grpc/grpc-js'; + +export namespace hero { + export interface HeroService { + findOne(data: HeroById, metadata?: Metadata): Promise; + } + export interface HeroById { + id?: number; + } + export interface Hero { + id?: number; + name?: string; + } +} + +export namespace helloworld { + export interface Greeter { + sayHello (request: HelloRequest): Promise + } + + export interface HelloRequest { + name: string; + } + + export interface HelloReply { + message: string; + } +} diff --git a/packages/grpc/test/fixtures/base-app/src/provider/greeter.ts b/packages/grpc/test/fixtures/base-app/src/provider/greeter.ts new file mode 100644 index 000000000000..abc7b070b6f2 --- /dev/null +++ b/packages/grpc/test/fixtures/base-app/src/provider/greeter.ts @@ -0,0 +1,23 @@ +import { MSProviderType, Provider, Provide, Inject } from '@midwayjs/decorator'; +import { helloworld } from '../interface'; +import { ILogger } from '@midwayjs/logger'; + +/** + * package helloworld + * service Greeter + */ +@Provide() +@Provider(MSProviderType.GRPC, { package: 'helloworld' }) +export class Greeter implements helloworld.Greeter { + + @Inject() + logger: ILogger; + + /** + * Implements the SayHello RPC method. + */ + async sayHello(request: helloworld.HelloRequest) { + this.logger.info('this is a context logger'); + return { message: 'Hello ' + request.name } + } +} diff --git a/packages/grpc/test/fixtures/proto/helloworld.proto b/packages/grpc/test/fixtures/proto/helloworld.proto new file mode 100644 index 000000000000..be878ce25fff --- /dev/null +++ b/packages/grpc/test/fixtures/proto/helloworld.proto @@ -0,0 +1,38 @@ +// Copyright 2015 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "io.grpc.examples.helloworld"; +option java_outer_classname = "HelloWorldProto"; +option objc_class_prefix = "HLW"; + +package helloworld; + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} diff --git a/packages/grpc/test/fixtures/proto/hero.proto b/packages/grpc/test/fixtures/proto/hero.proto new file mode 100644 index 000000000000..fa0c5d801a4b --- /dev/null +++ b/packages/grpc/test/fixtures/proto/hero.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package hero; + +service HeroService { + rpc FindOne (HeroById) returns (Hero) {} +} + +message HeroById { + int32 id = 1; +} + +message Hero { + int32 id = 1; + string name = 2; +} diff --git a/packages/grpc/test/index.test.ts b/packages/grpc/test/index.test.ts new file mode 100644 index 000000000000..475816290304 --- /dev/null +++ b/packages/grpc/test/index.test.ts @@ -0,0 +1,83 @@ +import { createServer, closeApp } from './utils'; +import { join } from 'path'; +import { createGRPCConsumer } from '../src'; + +export namespace hero { + export interface HeroService { + findOne(data: HeroById): Promise; + } + export interface HeroById { + id?: number; + } + export interface Hero { + id?: number; + name?: string; + } +} + +export namespace helloworld { + export interface Greeter { + sayHello (request: HelloRequest): Promise + } + + export interface HelloRequest { + name: string; + } + + export interface HelloReply { + message: string; + } +} + +describe('/test/index.test.ts', function () { + + it('should create gRPC server', async () => { + const app = await createServer('base-app', { + services: [ + { + protoPath: join(__dirname, 'fixtures/proto/helloworld.proto'), + package: 'helloworld', + } + ] + }); + + const service = await createGRPCConsumer({ + package: 'helloworld', + protoPath: join(__dirname, 'fixtures/proto/helloworld.proto'), + }); + + const result = await service.sayHello({ + name: 'harry' + }); + + expect(result).toEqual({ message: 'Hello harry' }) + await closeApp(app); + }); + + it('should create multiple grpc service in one server', async () => { + const app = await createServer('base-app-multiple-service', { + services: [ + { + protoPath: join(__dirname, 'fixtures/proto/hero.proto'), + package: 'hero', + }, + { + protoPath: join(__dirname, 'fixtures/proto/helloworld.proto'), + package: 'helloworld', + } + ] + }); + + const service: any = await createGRPCConsumer({ + package: 'hero', + protoPath: join(__dirname, 'fixtures/proto/hero.proto'), + }); + + const result = await service.findOne({ + id: 123 + }); + + expect(result).toEqual({ id: 1, name: 'bbbb-Hello harry' }) + await closeApp(app); + }); +}); diff --git a/packages/grpc/test/utils.ts b/packages/grpc/test/utils.ts new file mode 100644 index 000000000000..191de6b59ca1 --- /dev/null +++ b/packages/grpc/test/utils.ts @@ -0,0 +1,16 @@ +import { Framework, IMidwayGRPCApplication, IMidwayGRPFrameworkOptions } from '../src'; +import { join } from 'path'; +import { close, createApp } from '@midwayjs/mock'; + +/** + * create a gRPC server + * @param name + * @param options + */ +export async function createServer(name: string, options: IMidwayGRPFrameworkOptions): Promise { + return createApp(join(__dirname, 'fixtures', name), options, Framework); +} + +export async function closeApp(app) { + return close(app); +} diff --git a/packages/grpc/tsconfig.json b/packages/grpc/tsconfig.json new file mode 100644 index 000000000000..b1a4a0c8633f --- /dev/null +++ b/packages/grpc/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compileOnSave": true, + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "jsx": "react" + }, + "include": [ + "./src/**/*.ts" + ] +} diff --git a/packages/mock/src/utils.ts b/packages/mock/src/utils.ts index 92e94ced2435..cdb79b9236f4 100644 --- a/packages/mock/src/utils.ts +++ b/packages/mock/src/utils.ts @@ -162,11 +162,11 @@ export async function close( } if (isTestEnvironment()) { + // clean first + if (options.cleanLogsDir !== false && !isWin32()) { + await remove(join(newApp.getAppDir(), 'logs')); + } if (MidwayFrameworkType.WEB === newApp.getFrameworkType()) { - // clean first - if (options.cleanLogsDir !== false && !isWin32()) { - await remove(join(newApp.getAppDir(), 'logs')); - } if (options.cleanTempDir !== false && !isWin32()) { await remove(join(newApp.getAppDir(), 'run')); } diff --git a/packages/mock/test/new.test.ts b/packages/mock/test/new.test.ts index d55ac9ed0ade..83d3bbdd8f38 100644 --- a/packages/mock/test/new.test.ts +++ b/packages/mock/test/new.test.ts @@ -7,7 +7,6 @@ import { join } from 'path'; describe('/test/new.test.ts', () => { it('should test create app', async () => { const app = await createApp(join(__dirname, 'fixtures/base-app-decorator'), {}, Framework); - // TODO app 加上 egg-mock 的类型定义 const result = await createHttpRequest(app).get('/').query({ name: 'harry' }); expect(result.status).toBe(200); expect(result.text).toBe('hello world, harry'); diff --git a/packages/rabbitmq/package.json b/packages/rabbitmq/package.json index 1503b57336fc..ced07f7a1389 100644 --- a/packages/rabbitmq/package.json +++ b/packages/rabbitmq/package.json @@ -31,6 +31,7 @@ "dependencies": { "@midwayjs/core": "^2.6.13", "@midwayjs/decorator": "^2.6.8", + "@midwayjs/logger": "^2.6.9", "@types/amqplib": "^0.5.13", "amqplib": "^0.6.0" }, diff --git a/packages/rabbitmq/src/framework.ts b/packages/rabbitmq/src/framework.ts index 138e4fb3899c..11c9cd6d4a4f 100644 --- a/packages/rabbitmq/src/framework.ts +++ b/packages/rabbitmq/src/framework.ts @@ -6,7 +6,6 @@ import { listModule, listPropertyDataFromClass, MidwayFrameworkType, - MidwayRequestContainer, } from '@midwayjs/core'; import { @@ -25,18 +24,12 @@ import { MidwayRabbitMQContextLogger } from './logger'; export class MidwayRabbitMQFramework extends BaseFramework< IMidwayRabbitMQApplication, + IMidwayRabbitMQContext, IMidwayRabbitMQConfigurationOptions > { public app: IMidwayRabbitMQApplication; public consumerList = []; - public configure( - options: IMidwayRabbitMQConfigurationOptions - ): MidwayRabbitMQFramework { - this.configurationOptions = options; - return this; - } - async applicationInitialize(options) { this.app = (new RabbitMQServer( this.configurationOptions @@ -101,21 +94,20 @@ export class MidwayRabbitMQFramework extends BaseFramework< async (data?: ConsumeMessage) => { const ctx = { channel: this.app.getChannel(), - startTime: Date.now(), queueName: listenerOptions.queueName, } as IMidwayRabbitMQContext; - ctx.logger = new MidwayRabbitMQContextLogger(ctx, this.appLogger); - const requestContainer = new MidwayRequestContainer( - ctx, - this.getApplicationContext() - ); - ctx.requestContext = requestContainer; - ctx.getRequestContext = () => { - return requestContainer; - }; - const ins = await requestContainer.getAsync(providerId); + this.app.createAnonymousContext(ctx); + const ins = await ctx.requestContext.getAsync(providerId); await ins[listenerOptions.propertyKey].call(ins, data); } ); } + + public getFrameworkName() { + return 'midway:rabbitmq' + } + + public getDefaultContextLoggerClass(): any { + return MidwayRabbitMQContextLogger; + } } diff --git a/packages/rabbitmq/src/interface.ts b/packages/rabbitmq/src/interface.ts index 3b9377a48348..0f6cae37dfb6 100644 --- a/packages/rabbitmq/src/interface.ts +++ b/packages/rabbitmq/src/interface.ts @@ -1,4 +1,4 @@ -import { IMidwayApplication, IMidwayContext } from '@midwayjs/core'; +import { IConfigurationOptions, IMidwayApplication, IMidwayContext } from '@midwayjs/core'; import { ConsumeMessage, Options } from 'amqplib/properties'; import { RabbitMQListenerOptions } from '@midwayjs/decorator'; import * as amqp from 'amqplib'; @@ -14,7 +14,7 @@ export interface IRabbitMQApplication { close(): Promise; } -export type IMidwayRabbitMQApplication = IMidwayApplication & IRabbitMQApplication; +export type IMidwayRabbitMQApplication = IMidwayApplication & IRabbitMQApplication; export interface IRabbitMQExchange { name: string, @@ -22,7 +22,7 @@ export interface IRabbitMQExchange { options?: Options.AssertExchange } -export type IMidwayRabbitMQConfigurationOptions = { +export interface IMidwayRabbitMQConfigurationOptions extends IConfigurationOptions { url: string | Options.Connect, socketOptions?: any; reconnectTime?: number; @@ -44,4 +44,6 @@ export enum RabbitMQChannelEvent { CHANNEL_DRAIN = 'ch_drain', } +export type Application = IMidwayRabbitMQApplication; +export type Context = IMidwayRabbitMQContext; export type ConfigType = string | amqp.Options.Connect; diff --git a/packages/socketio/package.json b/packages/socketio/package.json index e3148363795d..4e894bc66d66 100644 --- a/packages/socketio/package.json +++ b/packages/socketio/package.json @@ -33,6 +33,7 @@ "dependencies": { "@midwayjs/core": "^2.6.13", "@midwayjs/decorator": "^2.6.8", + "@midwayjs/logger": "^2.6.9", "socket.io": "^2.3.0" }, "author": "Harry Chen ", diff --git a/packages/socketio/src/framework.ts b/packages/socketio/src/framework.ts index 8330f872406e..e9e54b0380b5 100644 --- a/packages/socketio/src/framework.ts +++ b/packages/socketio/src/framework.ts @@ -5,7 +5,6 @@ import { IMidwayBootstrapOptions, listModule, MidwayFrameworkType, - MidwayRequestContainer, } from '@midwayjs/core'; import { @@ -21,21 +20,16 @@ import { WSEventInfo, WSEventTypeEnum, } from '@midwayjs/decorator'; +import { MidwaySocketIOContextLogger } from './logger'; export class MidwaySocketIOFramework extends BaseFramework< IMidwaySocketIOApplication, + IMidwaySocketIOContext, IMidwaySocketIOConfigurationOptions > { applicationInitialize(options: IMidwayBootstrapOptions) {} public app: IMidwaySocketIOApplication; - public configure( - options: IMidwaySocketIOConfigurationOptions - ): MidwaySocketIOFramework { - this.configurationOptions = options; - return this; - } - protected async afterContainerDirectoryLoad( options: Partial ) { @@ -51,10 +45,7 @@ export class MidwaySocketIOFramework extends BaseFramework< } this.app.use((socket, next) => { - socket.requestContext = new MidwayRequestContainer( - socket, - this.getApplicationContext() - ); + this.app.createAnonymousContext(socket); socket.requestContext.registerObject('socket', socket); next(); }); @@ -225,4 +216,12 @@ export class MidwaySocketIOFramework extends BaseFramework< } } } + + public getFrameworkName() { + return 'midway:socketIO' + } + + public getDefaultContextLoggerClass(): any { + return MidwaySocketIOContextLogger + } } diff --git a/packages/socketio/src/interface.ts b/packages/socketio/src/interface.ts index f1d8ed4dce6b..8e9cccb76e8e 100644 --- a/packages/socketio/src/interface.ts +++ b/packages/socketio/src/interface.ts @@ -1,15 +1,20 @@ import * as SocketIO from 'socket.io'; -import { IMidwayApplication, IMidwayContext } from '@midwayjs/core'; +import { IConfigurationOptions, IMidwayApplication, IMidwayContext } from '@midwayjs/core'; import { Server as HttpServer } from 'http'; import { Server as HttpsServer } from 'https'; -export type IMidwaySocketIOApplication = IMidwayApplication & { +export type IMidwaySocketIOApplication = IMidwayApplication & { use(fn: (socket: IMidwaySocketIOContext, fn: (err?: any) => void) => void): SocketIO.Namespace; } & SocketIO.Server; export type IMidwaySocketIOConfigurationOptions = { port?: number; webServer?: HttpServer | HttpsServer; -} & SocketIO.ServerOptions; +} & SocketIO.ServerOptions & IConfigurationOptions; export type IMidwaySocketIOContext = SocketIO.Socket & IMidwayContext; + + +export type Application = IMidwaySocketIOApplication; + +export type Context = IMidwaySocketIOContext; diff --git a/packages/socketio/src/logger.ts b/packages/socketio/src/logger.ts new file mode 100644 index 000000000000..2ad654051cf1 --- /dev/null +++ b/packages/socketio/src/logger.ts @@ -0,0 +1,26 @@ +import { MidwayContextLogger } from '@midwayjs/core'; +import { IMidwaySocketIOContext } from './interface'; + +export class MidwaySocketIOContextLogger extends MidwayContextLogger< + IMidwaySocketIOContext + > { + formatContextLabel() { + // format: '[$userId/$ip/$traceId/$use_ms $method $url]' + // const userId = req?.['session']?.['userId'] || '-'; + // const traceId = '-'; + // const use = Date.now() - this.ctx.startTime; + // return ( + // userId + + // '/' + + // req.ip + + // '/' + + // traceId + + // '/' + + // use + + // 'ms ' + + // req.method + + // ' ' + + // req.url + return ''; + } +} diff --git a/packages/socketio/test/index.test.ts b/packages/socketio/test/index.test.ts index 3e8c4a334c4c..67a650ea9637 100644 --- a/packages/socketio/test/index.test.ts +++ b/packages/socketio/test/index.test.ts @@ -1,5 +1,5 @@ import * as socketClient from 'socket.io-client'; -import { closeApp, creatApp } from './utils'; +import { closeApp, createServer } from './utils'; function createClient(opts: SocketIOClient.ConnectOpts) { let url = 'http://127.0.0.1:' + opts.port; @@ -11,7 +11,7 @@ function createClient(opts: SocketIOClient.ConnectOpts) { describe('/test/index.test.ts', () => { it('should test create socket app and use default namespace', async () => { - const app = await creatApp('base-app', { port: 3000}); + const app = await createServer('base-app', { port: 3000}); const client = await createClient({ port: '3000', }); diff --git a/packages/socketio/test/utils.ts b/packages/socketio/test/utils.ts index 3c890957f74b..8a7d904fc2c0 100644 --- a/packages/socketio/test/utils.ts +++ b/packages/socketio/test/utils.ts @@ -7,7 +7,7 @@ import { close, createApp } from '@midwayjs/mock'; * @param name * @param options */ -export async function creatApp(name: string, options: IMidwaySocketIOConfigurationOptions = {}): Promise { +export async function createServer(name: string, options: IMidwaySocketIOConfigurationOptions = {}): Promise { return createApp(join(__dirname, 'fixtures', name), options, Framework); } diff --git a/packages/web-express/package.json b/packages/web-express/package.json index 4488829a525e..1705640a0897 100644 --- a/packages/web-express/package.json +++ b/packages/web-express/package.json @@ -31,6 +31,7 @@ "dependencies": { "@midwayjs/core": "^2.6.13", "@midwayjs/decorator": "^2.6.8", + "@midwayjs/logger": "^2.6.9", "express": "^4.17.1" }, "author": "Harry Chen ", diff --git a/packages/web-express/src/framework.ts b/packages/web-express/src/framework.ts index c3c73972ff29..cff4ad6af1b3 100644 --- a/packages/web-express/src/framework.ts +++ b/packages/web-express/src/framework.ts @@ -8,7 +8,6 @@ import { IMidwayBootstrapOptions, listModule, MidwayFrameworkType, - MidwayRequestContainer, } from '@midwayjs/core'; import { @@ -41,6 +40,7 @@ import { MidwayExpressContextLogger } from './logger'; export class MidwayExpressFramework extends BaseFramework< IMidwayExpressApplication, + IMidwayExpressContext, IMidwayExpressConfigurationOptions > { public app: IMidwayExpressApplication; @@ -52,13 +52,6 @@ export class MidwayExpressFramework extends BaseFramework< }> = []; private server: Server; - public configure( - options: IMidwayExpressConfigurationOptions - ): MidwayExpressFramework { - this.configurationOptions = options; - return this; - } - async applicationInitialize(options: Partial) { this.app = (express() as unknown) as IMidwayExpressApplication; this.defineApplicationProperties({ @@ -72,16 +65,10 @@ export class MidwayExpressFramework extends BaseFramework< }); this.app.use((req, res, next) => { const ctx = { req, res } as IMidwayExpressContext; - ctx.logger = new MidwayExpressContextLogger(ctx, this.appLogger); - ctx.startTime = Date.now(); - ctx.requestContext = new MidwayRequestContainer( - ctx, - this.getApplicationContext() - ); + this.app.createAnonymousContext(ctx); (req as any).requestContext = ctx.requestContext; ctx.requestContext.registerObject('req', req); ctx.requestContext.registerObject('res', res); - ctx.requestContext.ready(); next(); }); } @@ -204,7 +191,7 @@ export class MidwayExpressFramework extends BaseFramework< const providerId = getProviderId(module); if (providerId) { if (this.controllerIds.indexOf(providerId) > -1) { - throw new Error(`controller identifier [${providerId}] is exists!`); + throw new Error(`controller identifier [${providerId}] already exists!`); } this.controllerIds.push(providerId); await this.preRegisterRouter(module, providerId); @@ -346,7 +333,19 @@ export class MidwayExpressFramework extends BaseFramework< } } + public async beforeStop() { + this.server.close(); + } + public getServer() { return this.server; } + + public getFrameworkName() { + return 'midway:express' + } + + public getDefaultContextLoggerClass() { + return MidwayExpressContextLogger; + } } diff --git a/packages/web-express/src/interface.ts b/packages/web-express/src/interface.ts index 02418a24b971..ef8a35490ebf 100644 --- a/packages/web-express/src/interface.ts +++ b/packages/web-express/src/interface.ts @@ -1,5 +1,5 @@ -import { IMidwayApplication, IMidwayContext } from '@midwayjs/core'; -import { Application, Request, Response, RequestHandler, NextFunction } from 'express'; +import { IConfigurationOptions, IMidwayApplication, IMidwayContext } from '@midwayjs/core'; +import { Application as ExpressApplication, Request, Response, RequestHandler, NextFunction } from 'express'; import { RouterParamValue } from "@midwayjs/decorator"; /** @@ -17,9 +17,8 @@ export type IMidwayExpressNext = NextFunction; export type IMidwayExpressContext = IMidwayContext & { req: Request; res: Response; - startTime: number; } -export type IMidwayExpressApplication = IMidwayApplication & Application & { +export type IMidwayExpressApplication = IMidwayApplication & ExpressApplication & { generateController( controllerMapping: string, routeArgsInfo?: RouterParamValue[], @@ -27,7 +26,7 @@ export type IMidwayExpressApplication = IMidwayApplication & Application & { ): Middleware; }; -export interface IMidwayExpressConfigurationOptions { +export interface IMidwayExpressConfigurationOptions extends IConfigurationOptions { /** * application http port */ @@ -53,3 +52,7 @@ export type Middleware = RequestHandler; export interface IWebMiddleware { resolve(): Middleware; } + +export type Application = IMidwayExpressApplication; + +export type Context = IMidwayExpressContext; diff --git a/packages/web-koa/package.json b/packages/web-koa/package.json index b3c64f47364c..0837409dbc3b 100644 --- a/packages/web-koa/package.json +++ b/packages/web-koa/package.json @@ -32,6 +32,7 @@ "dependencies": { "@midwayjs/core": "^2.6.13", "@midwayjs/decorator": "^2.6.8", + "@midwayjs/logger": "^2.6.9", "koa": "^2.13.0", "koa-router": "^9.4.0" }, diff --git a/packages/web-koa/src/framework.ts b/packages/web-koa/src/framework.ts index 6c0952f093ad..d0215b5002f6 100644 --- a/packages/web-koa/src/framework.ts +++ b/packages/web-koa/src/framework.ts @@ -5,11 +5,9 @@ import { getPropertyDataFromClass, getPropertyMetadata, getProviderId, - IMidwayApplication, - IMidwayBootstrapOptions, + IMidwayBootstrapOptions, IMidwayContext, listModule, MidwayFrameworkType, - MidwayRequestContainer, } from '@midwayjs/core'; import { @@ -42,18 +40,18 @@ import { readFileSync } from 'fs'; import { Server } from 'net'; export abstract class MidwayKoaBaseFramework< - T, - U extends IMidwayApplication & IMidwayKoaApplicationPlus, - CustomContext -> extends BaseFramework { - public app: U; + APP extends IMidwayKoaApplicationPlus, + CTX extends IMidwayContext, + OPT +> extends BaseFramework { + public app: APP; private controllerIds: string[] = []; public prioritySortRouters: Array<{ priority: number; router: Router; }> = []; - public getApplication(): U { + public getApplication(): APP { return this.app; } @@ -132,7 +130,7 @@ export abstract class MidwayKoaBaseFramework< const providerId = getProviderId(module); if (providerId) { if (this.controllerIds.indexOf(providerId) > -1) { - throw new Error(`controller identifier [${providerId}] is exists!`); + throw new Error(`controller identifier [${providerId}] already exists!`); } this.controllerIds.push(providerId); await this.preRegisterRouter(module, providerId); @@ -273,32 +271,19 @@ export abstract class MidwayKoaBaseFramework< } export class MidwayKoaFramework extends MidwayKoaBaseFramework< - IMidwayKoaConfigurationOptions, IMidwayKoaApplication, - IMidwayKoaContext + IMidwayKoaContext, + IMidwayKoaConfigurationOptions > { private server: Server; - public configure( - options: IMidwayKoaConfigurationOptions - ): MidwayKoaFramework { - this.configurationOptions = options; - return this; - } - async applicationInitialize(options: Partial) { this.app = new koa< DefaultState, IMidwayKoaContext >() as IMidwayKoaApplication; this.app.use(async (ctx, next) => { - ctx.logger = new MidwayKoaContextLogger(ctx, this.appLogger); - ctx.startTime = Date.now(); - ctx.requestContext = new MidwayRequestContainer( - ctx, - this.getApplicationContext() - ); - await ctx.requestContext.ready(); + this.app.createAnonymousContext(ctx); await next(); }); @@ -354,11 +339,23 @@ export class MidwayKoaFramework extends MidwayKoaBaseFramework< } } + public async beforeStop() { + this.server.close(); + } + public getFrameworkType(): MidwayFrameworkType { return MidwayFrameworkType.WEB_KOA; } + public getFrameworkName() { + return 'midway:koa'; + } + public getServer() { return this.server; } + + public getDefaultContextLoggerClass() { + return MidwayKoaContextLogger; + } } diff --git a/packages/web-koa/src/index.ts b/packages/web-koa/src/index.ts index f55e61b0cc0d..453523828961 100644 --- a/packages/web-koa/src/index.ts +++ b/packages/web-koa/src/index.ts @@ -2,4 +2,5 @@ export { MidwayKoaFramework as Framework, MidwayKoaBaseFramework, } from './framework'; +export { MidwayKoaContextLogger } from './logger'; export * from './interface'; diff --git a/packages/web-koa/src/interface.ts b/packages/web-koa/src/interface.ts index 90d0aec4623c..714c51b207d7 100644 --- a/packages/web-koa/src/interface.ts +++ b/packages/web-koa/src/interface.ts @@ -1,10 +1,10 @@ -import { IMidwayApplication, IMidwayContext } from '@midwayjs/core'; +import { IConfigurationOptions, IMidwayApplication, IMidwayContext } from '@midwayjs/core'; import * as koa from 'koa'; -import { Context, DefaultState, Middleware, Next } from 'koa'; +import { Context as KoaContext, DefaultState, Middleware, Next } from 'koa'; import { RouterParamValue } from '@midwayjs/decorator'; -export type IMidwayKoaContext = IMidwayContext & Context; -export type IMidwayKoaApplication = IMidwayApplication & koa & { +export type IMidwayKoaContext = IMidwayContext & KoaContext; +export type IMidwayKoaApplication = IMidwayApplication & koa & { generateController( controllerMapping: string, routeArgsInfo?: RouterParamValue[], @@ -15,11 +15,11 @@ export type IMidwayKoaApplication = IMidwayApplication & koa extends IMidwayApplication { use(...args); } -export interface IMidwayKoaConfigurationOptions { +export interface IMidwayKoaConfigurationOptions extends IConfigurationOptions { /** * application http port */ @@ -43,3 +43,7 @@ export type MiddlewareParamArray = Array; } + +export type Application = IMidwayKoaApplication; + +export type Context = IMidwayKoaContext; diff --git a/packages/web/src/framework/singleProcess.ts b/packages/web/src/framework/singleProcess.ts index ad56b290b9dd..d69d838266d9 100644 --- a/packages/web/src/framework/singleProcess.ts +++ b/packages/web/src/framework/singleProcess.ts @@ -10,6 +10,7 @@ import { resolve } from 'path'; import { readFileSync } from 'fs'; import { Server } from 'net'; import { LoggerOptions } from '@midwayjs/logger'; +import { MidwayKoaContextLogger } from '@midwayjs/koa'; export class SingleProcess implements IMidwayFramework { @@ -127,4 +128,12 @@ export class SingleProcess public getServer() { return this.server; } + + public getFrameworkName() { + return 'midway:web' + } + + public getDefaultContextLoggerClass() { + return MidwayKoaContextLogger; + } } diff --git a/packages/web/src/framework/web.ts b/packages/web/src/framework/web.ts index 24ad13fd018a..e1b8a7d3a64a 100644 --- a/packages/web/src/framework/web.ts +++ b/packages/web/src/framework/web.ts @@ -12,9 +12,9 @@ import { Application, Context, Router, EggLogger } from 'egg'; import { loggers } from '@midwayjs/logger'; export class MidwayWebFramework extends MidwayKoaBaseFramework< - IMidwayWebConfigurationOptions, Application, - Context + Context, + IMidwayWebConfigurationOptions > { public app: Application; public configurationOptions: IMidwayWebConfigurationOptions; @@ -58,11 +58,7 @@ export class MidwayWebFramework extends MidwayKoaBaseFramework< // TODO 单进程模式下区分进程类型?? return MidwayProcessTypeEnum.APPLICATION; }, - - getLogger: name => { - return this.getLogger(name); - }, - }); + }, ['createAnonymousContext']); Object.defineProperty(this.app, 'applicationContext', { get() { diff --git a/packages/web/src/interface.ts b/packages/web/src/interface.ts index cd563c0ecc26..0dbcd758aeec 100644 --- a/packages/web/src/interface.ts +++ b/packages/web/src/interface.ts @@ -27,11 +27,14 @@ declare module 'egg' { generateController?(controllerMapping: string); generateMiddleware?(middlewareId: string): Promise>; createLogger(name: string, options: LoggerOptions); - getProjectName(); + getProjectName(): string; + setContextLoggerClass(BaseContextLoggerClass: any): void; } interface Context { requestContext: IMidwayContainer; + getLogger(name?: string): EggLogger & ILogger; + startTime: number; } }