From bcb3d7cef5a76e2c55ac29b1050a935025392e20 Mon Sep 17 00:00:00 2001 From: npmstudy Date: Tue, 12 Dec 2023 14:13:35 +0800 Subject: [PATCH] chore: refact mergeDeep to class --- package.json | 12 ++- packages/app/package.json | 3 +- packages/app/src/__tests__/app.test.ts | 109 +++++++++++++++++++++++ packages/app/src/__tests__/index.test.ts | 14 +-- packages/app/src/index.ts | 78 ++++++++-------- packages/core/src/index.ts | 54 ++++++----- packages/core/src/server.ts | 24 ++--- 7 files changed, 216 insertions(+), 78 deletions(-) create mode 100644 packages/app/src/__tests__/app.test.ts diff --git a/package.json b/package.json index 7a0e6d6..3697568 100644 --- a/package.json +++ b/package.json @@ -59,5 +59,13 @@ ], "dependencies": { "@changesets/cli": "^2.26.2" - } -} \ No newline at end of file + }, + "version": "1.0.0", + "main": "commitlint.config.js", + "directories": { + "doc": "docs", + "example": "example" + }, + "keywords": [], + "author": "" +} diff --git a/packages/app/package.json b/packages/app/package.json index d05b86f..0f733f4 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -26,8 +26,9 @@ "awesome-keywords" ], "devDependencies": { + "@types/koa": "^2.13.10", "@vitest/coverage-v8": "^1.0.1", - "happy-dom": "^6.0.4", + "supertest": "^6.3.3", "vitest": "^1.0.1" }, "dependencies": { diff --git a/packages/app/src/__tests__/app.test.ts b/packages/app/src/__tests__/app.test.ts new file mode 100644 index 0000000..f6fcc9a --- /dev/null +++ b/packages/app/src/__tests__/app.test.ts @@ -0,0 +1,109 @@ +import supertest from 'supertest'; +import { describe, expect, it } from 'vitest'; + +import { createApp, AppServer } from '../index'; + +describe('app', async () => { + const rpc: AppServer = await createApp({ + name: 'tomapp', + base: import.meta.url, + port: 3001, + debug: false, + mount: './f', + }); + + rpc.fn('a', function (a) { + // console.dir(rpc.fn); + // console.dir(this); + // this.render('user', { user: { name: 'alfred' } }); + return { a: a }; + }); + + rpc.fn('/a', function (a) { + return { a: a }; + }); + + rpc.fn('/b', function (a) { + if (this.method === 'POST') { + return { a: a, method: 'post' }; + } + if (this.method === 'GET') { + return { a: a, method: 'get' }; + } + }); + + rpc.fn('com.yourcompany.a', function (a: string) { + return { a: a }; + }); + + rpc.fn('com.yourcompany.b', function (a: string) { + return `${this.path} , ${a} `; + }); + + rpc.add({ + '/add': function (a: string, b: string) { + return { a: a }; + }, + }); + + const request = supertest(rpc.callback()); + + it('should start === rpc.callback', async () => { + const request2 = supertest(rpc.start(30001)); + const res = await request2.get('/api/a?$p=["hello"]'); + expect(res.type).toEqual('application/json'); + expect(res.status).toEqual(200); + expect(res.body).toEqual({ a: 'hello' }); + }); + + it('should access /a', async () => { + const res = await request.get('/api/a?$p=["hello"]'); + expect(res.type).toEqual('application/json'); + expect(res.status).toEqual(200); + expect(res.body).toEqual({ a: 'hello' }); + }); + + it('should access no routers[key]', async () => { + // if (!['POST', 'PUT', 'PATCH'].includes(ctx.method) && !ctx.query.$p) { + const res = await request.get('/api/a123'); + expect(res.type).toEqual('text/plain'); + expect(res.status).toEqual(200); + expect(res.text).toEqual('not match $p param, no process'); + }); + + it('should access no routers[key]', async () => { + const res = await request.get('/api/a123?$p=["hello"]'); + expect(res.type).toEqual('text/plain'); + expect(res.status).toEqual(200); + expect(res.text).toEqual('[fn plugin] no fn repsonse, please check your fn if exist'); + }); + + it('should access /add', async () => { + const res = await request.get('/api/add?$p=["hello"]'); + expect(res.type).toEqual('application/json'); + expect(res.status).toEqual(200); + expect(res.body).toEqual({ a: 'hello' }); + }); + + it('should access GET /b', async () => { + const res = await request.get('/api/b?$p=["hello1"]'); + expect(res.type).toEqual('application/json'); + expect(res.status).toEqual(200); + expect(res.body).toEqual({ a: 'hello1', method: 'get' }); + }); + + it('should access POST /b', async () => { + const res = await request.post('/api/b').send(['hello']).set('Accept', 'application/json'); + + expect(res.type).toEqual('application/json'); + expect(res.status).toEqual(200); + expect(res.body).toEqual({ a: 'hello', method: 'post' }); + }); + + it('should access com.yourcompany.b', async () => { + const res = await request.get('/api/com/yourcompany/b?$p=["hello"]'); + expect(res.type).toEqual('text/plain'); + expect(res.status).toEqual(200); + expect(res.text).toEqual('/com/yourcompany/b , hello '); + }); +}); diff --git a/packages/app/src/__tests__/index.test.ts b/packages/app/src/__tests__/index.test.ts index 2ae6c18..6c3b7a6 100644 --- a/packages/app/src/__tests__/index.test.ts +++ b/packages/app/src/__tests__/index.test.ts @@ -1,9 +1,13 @@ -import { describe, expect, it } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; -// import mount from '../mount'; - -describe('lib', () => { +describe('app', async () => { it('should render lib', () => { - // expect(lib()).toBe('lib'); + expect('lib').toBe('lib'); + }); + + it('mock console.dir', () => { + const spy = vi.spyOn(console, 'dir'); + console.dir('2323'); + expect(spy).toHaveBeenCalled(); }); }); diff --git a/packages/app/src/index.ts b/packages/app/src/index.ts index 2163147..6d2682b 100644 --- a/packages/app/src/index.ts +++ b/packages/app/src/index.ts @@ -1,5 +1,7 @@ -import { createServer, combine } from '@tomrpc/core'; +import { createServer, combine, RpcServer } from '@tomrpc/core'; +import type { IIndexServerConfig } from '@tomrpc/core'; // import mount from '@tomrpc/mount'; +import exp from 'constants'; import debug from 'debug'; import { init } from './init'; @@ -46,55 +48,59 @@ interface IJwt { unless?: any; getToken; } -interface IConfig { - name: string | 'tomapp'; +interface IAppConfig1 { + name?: string | 'tomapp'; base?: string; port?: number | 3000; debug?: boolean | false; mount?: string; - buildin: { + buildin?: { serve?: IServe; cors?: ICors; view?: IView; jwt?: IJwt; }; } -export async function createApp(cfg: IConfig) { - const rpc = createServer( - mergeDeep( - { - base: import.meta.url, - }, - cfg - ) - ); - if (cfg.buildin.cors) { - const cors = new Cors(cfg.buildin.cors); - rpc.plugin(cors); - } +type IAppConfig = IAppConfig1 & IIndexServerConfig; - if (cfg.buildin.serve) { - const serve = new Serve(cfg.buildin.serve); - rpc.plugin(serve); - } +export class AppServer extends RpcServer { + constructor(cfg?: IAppConfig) { + super(cfg); + + if (cfg?.buildin?.cors) { + const cors = new Cors(cfg?.buildin?.cors); + this.plugin(cors); + } + + if (cfg?.buildin?.serve) { + const serve = new Serve(cfg?.buildin?.serve); + this.plugin(serve); + } - if (cfg.buildin.jwt) { - const jwt = new Jwt(cfg.buildin.jwt); - rpc.plugin(jwt); + if (cfg?.buildin?.jwt) { + const jwt = new Jwt(cfg?.buildin?.jwt); + this.plugin(jwt); + } } - return mergeDeep(rpc, { - jwt: function (cb) { - // const mw = combine([cb]); - // console.dir(rpc.config); - // rpc.init.push(mw); - }, - render: function (path, cb) { - // console.dir('render'); - const view = new View(cfg.buildin.view); + public render(path, cb): void { + // console.dir('render'); + if (this.config?.buildin?.view) { + const view = new View(this.config?.buildin?.view); const mw = combine([view.proxy(), cb]); - rpc.app.use(mw); - }, - }); + this.app.use(mw); + } + } +} + +export function createApp(cfg?: IAppConfig): AppServer { + return new AppServer( + mergeDeep( + { + base: import.meta.url, + }, + cfg || {} + ) + ); } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 403ff8d..ca42b6b 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -2,7 +2,7 @@ import debug from 'debug'; import compose from 'koa-compose'; import { Fn, IFnConfig } from './fn'; -import { RpcServer, IRpcServerConfig } from './server'; +import { BaseRpcServer, IRpcServerConfig } from './server'; import { mergeDeep } from './utils'; export * from './plugin'; @@ -14,31 +14,41 @@ export * from './utils'; const log = debug('@tomrpc/core/index'); export const combine = compose; -interface IServerConfig { +export interface IServerConfig { base?: string; fn?: IFnConfig; } -type IIndexServerConfig = IRpcServerConfig & IServerConfig; +export type IIndexServerConfig = IRpcServerConfig & IServerConfig; + +export class RpcServer extends BaseRpcServer { + private fnPlugin; + public base; + public before; + public after; + + constructor(cfg?: IIndexServerConfig) { + super(cfg); + this.fnPlugin = new Fn(mergeDeep({}, cfg.fn)); + this.plugin(this.fnPlugin); + + this.base = '.'; + this.init = this['config']['hooks']['init']; + this.before = this['config']['hooks']['before']; + this.load = this['config']['hooks']['load']; + this.after = this['config']['hooks']['after']; + } + + public fn(key, fun) { + log('use rpc.fn add fn =' + key); + this.fnPlugin.fn(key, fun); + } + public add(items) { + log('use rpc.add add fn =' + items); + this.fnPlugin.add(items); + } +} export function createServer(cfg?: IIndexServerConfig) { - const rpc = new RpcServer(mergeDeep({ fn: {} }, cfg)); - const fn = new Fn(mergeDeep({}, cfg.fn)); - rpc.plugin(fn); - - return mergeDeep(rpc, { - base: '.', - init: rpc['config']['hooks']['init'], - before: rpc['config']['hooks']['before'], - load: rpc['config']['hooks']['load'], - after: rpc['config']['hooks']['after'], - fn: function (key, fun) { - log('use rpc.fn add fn =' + key); - fn.fn(key, fun); - }, - add: function (items) { - log('use rpc.add add fn =' + items); - fn.add(items); - }, - }); + return new RpcServer(mergeDeep({ fn: {} }, cfg)); } diff --git a/packages/core/src/server.ts b/packages/core/src/server.ts index b1793de..5649c6c 100644 --- a/packages/core/src/server.ts +++ b/packages/core/src/server.ts @@ -20,14 +20,14 @@ export const LifeCycleConfig = { }, }, - before: async (server: RpcServer) => { + before: async (server: BaseRpcServer) => { const app = server.app; const loadMiddlewares = server.config.hooks.before; loadMiddlewares.forEach((mw: Middleware) => { app.use(mw); }); }, - init: async (server: RpcServer) => { + init: async (server: BaseRpcServer) => { log('init'); // console.dir(server); const app = server.app; @@ -37,14 +37,14 @@ export const LifeCycleConfig = { app.use(mw); }); }, - load: async (server: RpcServer) => { + load: async (server: BaseRpcServer) => { const app = server.app; const loadMiddlewares = server.config.hooks.load; loadMiddlewares.forEach((mw: Middleware) => { app.use(mw); }); }, - after: async (server: RpcServer) => { + after: async (server: BaseRpcServer) => { const app = server.app; const loadMiddlewares = server.config.hooks.after; loadMiddlewares.forEach((mw: Middleware) => { @@ -62,17 +62,17 @@ export interface IRpcServerConfig { after: Array; default(): Promise; }; - before?(server: RpcServer): Promise; - init?(server: RpcServer): Promise; - load?(server: RpcServer): Promise; - after?(server: RpcServer): Promise; + before?(server: BaseRpcServer): Promise; + init?(server: BaseRpcServer): Promise; + load?(server: BaseRpcServer): Promise; + after?(server: BaseRpcServer): Promise; } /** * @type {RpcServer} The Server maintains a server to bind one of the Strategy * objects. The Server does not know the concrete class of a strategy. It * should work with all strategies via the Strategy interface. */ -export class RpcServer { +export class BaseRpcServer { private plugins: Strategy[] = []; app: Application; use; @@ -81,9 +81,9 @@ export class RpcServer { load: [], before: {}, }; - config; - init: []; - load: []; + public config; + public init: []; + public load: []; /** * Usually, the Server accepts a strategy through the constructor, but also * provides a setter to change it at runtime.