Commit a13f8e41 authored by nanahira's avatar nanahira

add UsingService

parent 4f828a42
This diff is collapsed.
...@@ -13,6 +13,7 @@ import { ...@@ -13,6 +13,7 @@ import {
KoishiDoRegister, KoishiDoRegister,
KoishiDoRegisterKeys, KoishiDoRegisterKeys,
KoishiOnContextScope, KoishiOnContextScope,
KoishiPartialUsing,
KoishiRouteDef, KoishiRouteDef,
KoishiServiceInjectSym, KoishiServiceInjectSym,
KoishiServiceInjectSymKeys, KoishiServiceInjectSymKeys,
...@@ -313,7 +314,21 @@ export const InjectLogger = (name?: string) => ...@@ -313,7 +314,21 @@ export const InjectLogger = (name?: string) =>
); );
export const Caller = () => export const Caller = () =>
InjectSystem((obj) => { InjectSystem((obj) => {
const ctx = obj.__ctx; const targetCtx: Context = obj[Context.current] || obj.__ctx;
const targetCtx: Context = ctx[Context.current] || ctx;
return targetCtx; return targetCtx;
}); });
export function UsingService(
...services: (keyof Context.Services)[]
): ClassDecorator & MethodDecorator {
return (obj, key?) => {
for (const service of services) {
if (!key) {
// fallback to KoishiAddUsingList
Metadata.appendUnique(KoishiAddUsingList, service)(obj.constructor);
} else {
Metadata.appendUnique(KoishiPartialUsing, service)(obj, key);
}
}
};
}
...@@ -20,6 +20,7 @@ export const KoishiServiceProvideSym = 'KoishiServiceProvideSym'; ...@@ -20,6 +20,7 @@ export const KoishiServiceProvideSym = 'KoishiServiceProvideSym';
export const KoishiSystemInjectSym = 'KoishiSystemInjectSym'; export const KoishiSystemInjectSym = 'KoishiSystemInjectSym';
export const KoishiSystemInjectSymKeys = 'KoishiSystemInjectSymKeys'; export const KoishiSystemInjectSymKeys = 'KoishiSystemInjectSymKeys';
export const KoishiAddUsingList = 'KoishiAddUsingList'; export const KoishiAddUsingList = 'KoishiAddUsingList';
export const KoishiPartialUsing = 'KoishiPartialUsing';
// metadata map // metadata map
...@@ -31,6 +32,7 @@ export interface MetadataArrayMap { ...@@ -31,6 +32,7 @@ export interface MetadataArrayMap {
KoishiServiceInjectSymKeys: string; KoishiServiceInjectSymKeys: string;
KoishiSystemInjectSymKeys: string; KoishiSystemInjectSymKeys: string;
KoishiAddUsingList: keyof Context.Services; KoishiAddUsingList: keyof Context.Services;
KoishiPartialUsing: keyof Context.Services;
} }
export interface MetadataMap { export interface MetadataMap {
......
...@@ -6,6 +6,7 @@ import { ...@@ -6,6 +6,7 @@ import {
Schema, Schema,
User, User,
WebSocketLayer, WebSocketLayer,
Plugin,
} from 'koishi'; } from 'koishi';
import { import {
CommandPutConfig, CommandPutConfig,
...@@ -16,6 +17,7 @@ import { ...@@ -16,6 +17,7 @@ import {
KoishiDoRegisterKeys, KoishiDoRegisterKeys,
KoishiModulePlugin, KoishiModulePlugin,
KoishiOnContextScope, KoishiOnContextScope,
KoishiPartialUsing,
KoishiServiceInjectSym, KoishiServiceInjectSym,
KoishiServiceInjectSymKeys, KoishiServiceInjectSymKeys,
KoishiServiceProvideSym, KoishiServiceProvideSym,
...@@ -89,10 +91,8 @@ export function DefinePlugin<T = any>( ...@@ -89,10 +91,8 @@ export function DefinePlugin<T = any>(
__wsLayers: WebSocketLayer[]; __wsLayers: WebSocketLayer[];
_handleSystemInjections() { _handleSystemInjections() {
// console.log('Handling system injection');
const injectKeys = reflector.getArray(KoishiSystemInjectSymKeys, this); const injectKeys = reflector.getArray(KoishiSystemInjectSymKeys, this);
for (const key of injectKeys) { for (const key of injectKeys) {
// console.log(`Processing ${key}`);
const valueFunction = reflector.get(KoishiSystemInjectSym, this, key); const valueFunction = reflector.get(KoishiSystemInjectSym, this, key);
Object.defineProperty(this, key, { Object.defineProperty(this, key, {
configurable: true, configurable: true,
...@@ -103,10 +103,8 @@ export function DefinePlugin<T = any>( ...@@ -103,10 +103,8 @@ export function DefinePlugin<T = any>(
} }
_handleServiceInjections() { _handleServiceInjections() {
// console.log('Handling service injection');
const injectKeys = reflector.getArray(KoishiServiceInjectSymKeys, this); const injectKeys = reflector.getArray(KoishiServiceInjectSymKeys, this);
for (const key of injectKeys) { for (const key of injectKeys) {
// console.log(`Processing ${key}`);
const name = reflector.get(KoishiServiceInjectSym, this, key); const name = reflector.get(KoishiServiceInjectSym, this, key);
Object.defineProperty(this, key, { Object.defineProperty(this, key, {
enumerable: true, enumerable: true,
...@@ -231,22 +229,13 @@ export function DefinePlugin<T = any>( ...@@ -231,22 +229,13 @@ export function DefinePlugin<T = any>(
} }
} }
_registerDeclarationsFor(methodKey: keyof C & string) { _registerDeclarationsProcess(methodKey: keyof C & string, ctx: Context) {
// console.log(`Handling declaration for ${methodKey}`);
const regData = reflector.get(KoishiDoRegister, this, methodKey); const regData = reflector.get(KoishiDoRegister, this, methodKey);
if (!regData) {
return;
}
// console.log(`Type: ${regData.type}`);
const baseContext = getContextFromFilters(
this.__ctx,
reflector.getArray(KoishiOnContextScope, this, methodKey),
);
switch (regData.type) { switch (regData.type) {
case 'middleware': case 'middleware':
const { data: midPrepend } = const { data: midPrepend } =
regData as DoRegisterConfig<'middleware'>; regData as DoRegisterConfig<'middleware'>;
baseContext.middleware( ctx.middleware(
(session, next) => this[methodKey](session, next), (session, next) => this[methodKey](session, next),
midPrepend, midPrepend,
); );
...@@ -254,7 +243,7 @@ export function DefinePlugin<T = any>( ...@@ -254,7 +243,7 @@ export function DefinePlugin<T = any>(
case 'onevent': case 'onevent':
const { data: eventData } = regData as DoRegisterConfig<'onevent'>; const { data: eventData } = regData as DoRegisterConfig<'onevent'>;
const eventName = eventData.name; const eventName = eventData.name;
baseContext.on( ctx.on(
eventName, eventName,
(...args: any[]) => this[methodKey](...args), (...args: any[]) => this[methodKey](...args),
eventData.prepend, eventData.prepend,
...@@ -264,18 +253,18 @@ export function DefinePlugin<T = any>( ...@@ -264,18 +253,18 @@ export function DefinePlugin<T = any>(
const { data: beforeEventData } = const { data: beforeEventData } =
regData as DoRegisterConfig<'beforeEvent'>; regData as DoRegisterConfig<'beforeEvent'>;
const beforeEventName = beforeEventData.name; const beforeEventName = beforeEventData.name;
baseContext.before( ctx.before(
beforeEventName, beforeEventName,
(...args: any[]) => this[methodKey](...args), (...args: any[]) => this[methodKey](...args),
beforeEventData.prepend, beforeEventData.prepend,
); );
case 'plugin': case 'plugin':
this._applyInnerPlugin(baseContext, methodKey); this._applyInnerPlugin(ctx, methodKey);
break; break;
case 'command': case 'command':
const { data: commandData } = const { data: commandData } =
regData as DoRegisterConfig<'command'>; regData as DoRegisterConfig<'command'>;
let command = baseContext.command( let command = ctx.command(
commandData.def, commandData.def,
commandData.desc, commandData.desc,
commandData.config, commandData.config,
...@@ -312,13 +301,13 @@ export function DefinePlugin<T = any>( ...@@ -312,13 +301,13 @@ export function DefinePlugin<T = any>(
const realPath = routeData.path.startsWith('/') const realPath = routeData.path.startsWith('/')
? routeData.path ? routeData.path
: `/${routeData.path}`; : `/${routeData.path}`;
baseContext.router[routeData.method](realPath, (ctx, next) => ctx.router[routeData.method](realPath, (ctx, next) =>
this[methodKey](ctx, next), this[methodKey](ctx, next),
); );
break; break;
case 'ws': case 'ws':
const { data: wsPath } = regData as DoRegisterConfig<'ws'>; const { data: wsPath } = regData as DoRegisterConfig<'ws'>;
const layer = baseContext.router.ws(wsPath, (socket, req) => const layer = ctx.router.ws(wsPath, (socket, req) =>
this[methodKey](socket, req), this[methodKey](socket, req),
); );
this.__wsLayers.push(layer); this.__wsLayers.push(layer);
...@@ -328,19 +317,44 @@ export function DefinePlugin<T = any>( ...@@ -328,19 +317,44 @@ export function DefinePlugin<T = any>(
} }
} }
_registerDeclarationsFor(methodKey: keyof C & string) {
if (!reflector.get(KoishiDoRegister, this, methodKey)) {
return;
}
const ctx = getContextFromFilters(
this.__ctx,
reflector.getArray(KoishiOnContextScope, this, methodKey),
);
const partialUsing = reflector.getArray(
KoishiPartialUsing,
this,
methodKey,
);
if (partialUsing.length) {
const name = `${options.name || originalClass.name}-${methodKey}`;
const innerPlugin: Plugin.Object = {
name,
using: partialUsing,
apply: (innerCtx) =>
this._registerDeclarationsProcess(methodKey, innerCtx),
};
ctx.plugin(innerPlugin);
} else {
this._registerDeclarationsProcess(methodKey, ctx);
}
}
_registerDeclarations() { _registerDeclarations() {
const methodKeys = reflector.getArray( const methodKeys = reflector.getArray(
KoishiDoRegisterKeys, KoishiDoRegisterKeys,
this, this,
) as (keyof C & string)[]; ) as (keyof C & string)[];
// console.log(methodKeys);
methodKeys.forEach((methodKey) => methodKeys.forEach((methodKey) =>
this._registerDeclarationsFor(methodKey), this._registerDeclarationsFor(methodKey),
); );
} }
_handleServiceProvide(immediate: boolean) { _handleServiceProvide(immediate: boolean) {
// console.log(`Handling service provide`);
const providingServices = [ const providingServices = [
...reflector.getArray(KoishiServiceProvideSym, originalClass), ...reflector.getArray(KoishiServiceProvideSym, originalClass),
...reflector.getArray(KoishiServiceProvideSym, this), ...reflector.getArray(KoishiServiceProvideSym, this),
...@@ -352,7 +366,6 @@ export function DefinePlugin<T = any>( ...@@ -352,7 +366,6 @@ export function DefinePlugin<T = any>(
} }
_registerAfterInit() { _registerAfterInit() {
// console.log(`Handling after init.`);
this.__ctx.on('ready', async () => { this.__ctx.on('ready', async () => {
if (typeof this.onConnect === 'function') { if (typeof this.onConnect === 'function') {
await this.onConnect(); await this.onConnect();
......
import { DefinePlugin } from '../src/register';
import { BasePlugin } from '../src/base-plugin';
import { Caller, Provide } from '../src/decorators';
import { App } from 'koishi';
declare module 'koishi' {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Context {
interface Services {
callerTester: CallerTester;
}
}
}
@Provide('callerTester')
@DefinePlugin()
class CallerTester extends BasePlugin<any> {
@Caller()
caller: string;
}
describe('Caller', () => {
let app: App;
beforeEach(async () => {
app = new App();
app.plugin(CallerTester);
await app.start();
});
it('should put caller with correct values', async () => {
const ctx1 = app.any();
const ctx2 = app.any();
const caller1 = ctx1.callerTester.caller;
const caller2 = ctx2.callerTester.caller;
expect(caller1).toEqual(ctx1);
expect(caller2).toEqual(ctx2);
expect(app.callerTester.caller).toEqual(app);
});
});
import { App, Context } from 'koishi'; import { App, Context } from 'koishi';
import { DefinePlugin } from '../src/register'; import { DefinePlugin } from '../src/register';
import { Inject, Provide, UseEvent } from '../src/decorators'; import { Inject, Provide, UseEvent, UsingService } from '../src/decorators';
import { BasePlugin } from '../src/base-plugin';
declare module 'koishi' { declare module 'koishi' {
// eslint-disable-next-line @typescript-eslint/no-namespace // eslint-disable-next-line @typescript-eslint/no-namespace
...@@ -10,24 +11,39 @@ declare module 'koishi' { ...@@ -10,24 +11,39 @@ declare module 'koishi' {
myEagerProvider: MyEagerProvider; myEagerProvider: MyEagerProvider;
myConsumer: MyConsumer; myConsumer: MyConsumer;
myUsingConsumer: MyUsingConsumer; myUsingConsumer: MyUsingConsumer;
myPartialConsumer: MyPartialConsumer;
dummyProvider: any;
} }
} }
interface EventMap {
'pang'(message: string): Promise<string>;
'pong'(message: string): Promise<string>;
}
} }
@Provide('myProvider') @Provide('myProvider')
@DefinePlugin() @DefinePlugin()
class MyProvider { class MyProvider extends BasePlugin<any> {
ping() { ping() {
return 'pong'; return 'pong';
} }
dispose() {
return this.ctx.dispose();
}
} }
@Provide('myEagerProvider', { immediate: true }) @Provide('myEagerProvider', { immediate: true })
@DefinePlugin() @DefinePlugin()
class MyEagerProvider { class MyEagerProvider extends BasePlugin<any> {
ping() { ping() {
return 'pong eager'; return 'pong eager';
} }
dispose() {
return this.ctx.dispose();
}
} }
@Provide('myConsumer', { immediate: true }) @Provide('myConsumer', { immediate: true })
...@@ -74,6 +90,32 @@ class MyUsingConsumer { ...@@ -74,6 +90,32 @@ class MyUsingConsumer {
this.eagerPongResult = this.myEagerProvider.ping(); this.eagerPongResult = this.myEagerProvider.ping();
} }
} }
emitResult: string;
}
@Provide('myPartialConsumer', { immediate: true })
@DefinePlugin()
class MyPartialConsumer {
@Inject()
dummyProvider: number;
pongResult: string;
@UsingService('dummyProvider')
@UseEvent('pang')
async onPang(content: string) {
const msg = `pang: ${content}`;
console.log(msg);
return msg;
}
@UseEvent('pong')
async onPong(content: string) {
const msg = `pong: ${content}`;
console.log(msg);
return msg;
}
} }
describe('On service', () => { describe('On service', () => {
...@@ -111,4 +153,28 @@ describe('On service', () => { ...@@ -111,4 +153,28 @@ describe('On service', () => {
//expect(app.myUsingConsumer.eagerPongResult).toBe('pong eager'); //expect(app.myUsingConsumer.eagerPongResult).toBe('pong eager');
//expect(app.myUsingConsumer.pongResult).toBe('pong'); //expect(app.myUsingConsumer.pongResult).toBe('pong');
}); });
/*
it('Should handle partial using deps', async () => {
Context.service('dummyProvider');
app = new App();
app.on('service', (name) => {
console.log('service', name);
});
await app.start();
app.plugin(MyPartialConsumer);
expect(app.myPartialConsumer).toBeDefined();
expect(await app.waterfall('pang', 'hello')).toEqual('hello');
expect(await app.waterfall('pong', 'hello')).toEqual('pong: hello');
app.dummyProvider = { foo: 'bar' };
expect(await app.waterfall('pang', 'hello')).toEqual('pang: hello');
expect(await app.waterfall('pong', 'hello')).toEqual('pong: hello');
app.dummyProvider = undefined;
expect(await app.waterfall('pang', 'hi')).toEqual('hi');
expect(await app.waterfall('pong', 'hi')).toEqual('pong: hi');
app.dummyProvider = { foo: 'baz' };
expect(await app.waterfall('pang', 'hi')).toEqual('pang: hi');
expect(await app.waterfall('pong', 'hi')).toEqual('pong: hi');
});
*/
}); });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment