Commit a13f8e41 authored by nanahira's avatar nanahira

add UsingService

parent 4f828a42
This diff is collapsed.
......@@ -13,6 +13,7 @@ import {
KoishiDoRegister,
KoishiDoRegisterKeys,
KoishiOnContextScope,
KoishiPartialUsing,
KoishiRouteDef,
KoishiServiceInjectSym,
KoishiServiceInjectSymKeys,
......@@ -313,7 +314,21 @@ export const InjectLogger = (name?: string) =>
);
export const Caller = () =>
InjectSystem((obj) => {
const ctx = obj.__ctx;
const targetCtx: Context = ctx[Context.current] || ctx;
const targetCtx: Context = obj[Context.current] || obj.__ctx;
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';
export const KoishiSystemInjectSym = 'KoishiSystemInjectSym';
export const KoishiSystemInjectSymKeys = 'KoishiSystemInjectSymKeys';
export const KoishiAddUsingList = 'KoishiAddUsingList';
export const KoishiPartialUsing = 'KoishiPartialUsing';
// metadata map
......@@ -31,6 +32,7 @@ export interface MetadataArrayMap {
KoishiServiceInjectSymKeys: string;
KoishiSystemInjectSymKeys: string;
KoishiAddUsingList: keyof Context.Services;
KoishiPartialUsing: keyof Context.Services;
}
export interface MetadataMap {
......
......@@ -6,6 +6,7 @@ import {
Schema,
User,
WebSocketLayer,
Plugin,
} from 'koishi';
import {
CommandPutConfig,
......@@ -16,6 +17,7 @@ import {
KoishiDoRegisterKeys,
KoishiModulePlugin,
KoishiOnContextScope,
KoishiPartialUsing,
KoishiServiceInjectSym,
KoishiServiceInjectSymKeys,
KoishiServiceProvideSym,
......@@ -89,10 +91,8 @@ export function DefinePlugin<T = any>(
__wsLayers: WebSocketLayer[];
_handleSystemInjections() {
// console.log('Handling system injection');
const injectKeys = reflector.getArray(KoishiSystemInjectSymKeys, this);
for (const key of injectKeys) {
// console.log(`Processing ${key}`);
const valueFunction = reflector.get(KoishiSystemInjectSym, this, key);
Object.defineProperty(this, key, {
configurable: true,
......@@ -103,10 +103,8 @@ export function DefinePlugin<T = any>(
}
_handleServiceInjections() {
// console.log('Handling service injection');
const injectKeys = reflector.getArray(KoishiServiceInjectSymKeys, this);
for (const key of injectKeys) {
// console.log(`Processing ${key}`);
const name = reflector.get(KoishiServiceInjectSym, this, key);
Object.defineProperty(this, key, {
enumerable: true,
......@@ -231,22 +229,13 @@ export function DefinePlugin<T = any>(
}
}
_registerDeclarationsFor(methodKey: keyof C & string) {
// console.log(`Handling declaration for ${methodKey}`);
_registerDeclarationsProcess(methodKey: keyof C & string, ctx: Context) {
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) {
case 'middleware':
const { data: midPrepend } =
regData as DoRegisterConfig<'middleware'>;
baseContext.middleware(
ctx.middleware(
(session, next) => this[methodKey](session, next),
midPrepend,
);
......@@ -254,7 +243,7 @@ export function DefinePlugin<T = any>(
case 'onevent':
const { data: eventData } = regData as DoRegisterConfig<'onevent'>;
const eventName = eventData.name;
baseContext.on(
ctx.on(
eventName,
(...args: any[]) => this[methodKey](...args),
eventData.prepend,
......@@ -264,18 +253,18 @@ export function DefinePlugin<T = any>(
const { data: beforeEventData } =
regData as DoRegisterConfig<'beforeEvent'>;
const beforeEventName = beforeEventData.name;
baseContext.before(
ctx.before(
beforeEventName,
(...args: any[]) => this[methodKey](...args),
beforeEventData.prepend,
);
case 'plugin':
this._applyInnerPlugin(baseContext, methodKey);
this._applyInnerPlugin(ctx, methodKey);
break;
case 'command':
const { data: commandData } =
regData as DoRegisterConfig<'command'>;
let command = baseContext.command(
let command = ctx.command(
commandData.def,
commandData.desc,
commandData.config,
......@@ -312,13 +301,13 @@ export function DefinePlugin<T = any>(
const realPath = routeData.path.startsWith('/')
? routeData.path
: `/${routeData.path}`;
baseContext.router[routeData.method](realPath, (ctx, next) =>
ctx.router[routeData.method](realPath, (ctx, next) =>
this[methodKey](ctx, next),
);
break;
case '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.__wsLayers.push(layer);
......@@ -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() {
const methodKeys = reflector.getArray(
KoishiDoRegisterKeys,
this,
) as (keyof C & string)[];
// console.log(methodKeys);
methodKeys.forEach((methodKey) =>
this._registerDeclarationsFor(methodKey),
);
}
_handleServiceProvide(immediate: boolean) {
// console.log(`Handling service provide`);
const providingServices = [
...reflector.getArray(KoishiServiceProvideSym, originalClass),
...reflector.getArray(KoishiServiceProvideSym, this),
......@@ -352,7 +366,6 @@ export function DefinePlugin<T = any>(
}
_registerAfterInit() {
// console.log(`Handling after init.`);
this.__ctx.on('ready', async () => {
if (typeof this.onConnect === 'function') {
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 { 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' {
// eslint-disable-next-line @typescript-eslint/no-namespace
......@@ -10,24 +11,39 @@ declare module 'koishi' {
myEagerProvider: MyEagerProvider;
myConsumer: MyConsumer;
myUsingConsumer: MyUsingConsumer;
myPartialConsumer: MyPartialConsumer;
dummyProvider: any;
}
}
interface EventMap {
'pang'(message: string): Promise<string>;
'pong'(message: string): Promise<string>;
}
}
@Provide('myProvider')
@DefinePlugin()
class MyProvider {
class MyProvider extends BasePlugin<any> {
ping() {
return 'pong';
}
dispose() {
return this.ctx.dispose();
}
}
@Provide('myEagerProvider', { immediate: true })
@DefinePlugin()
class MyEagerProvider {
class MyEagerProvider extends BasePlugin<any> {
ping() {
return 'pong eager';
}
dispose() {
return this.ctx.dispose();
}
}
@Provide('myConsumer', { immediate: true })
......@@ -74,6 +90,32 @@ class MyUsingConsumer {
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', () => {
......@@ -111,4 +153,28 @@ describe('On service', () => {
//expect(app.myUsingConsumer.eagerPongResult).toBe('pong eager');
//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