Commit fbdd391d authored by nanahira's avatar nanahira

provide Cache API thing & cmd.use(CommandUserLock("field"))

parent 91e5937e
This diff is collapsed.
...@@ -44,11 +44,12 @@ ...@@ -44,11 +44,12 @@
"testEnvironment": "node" "testEnvironment": "node"
}, },
"dependencies": { "dependencies": {
"aragami": "^1.1.1", "@koishijs/cache": "^0.1.3",
"aragami": "^1.1.2",
"koishi-thirdeye": "^11.1.21" "koishi-thirdeye": "^11.1.21"
}, },
"peerDependencies": { "peerDependencies": {
"koishi": "^4.11.1" "koishi": "^4.11.4"
}, },
"devDependencies": { "devDependencies": {
"@koishijs/plugin-console": "^5.1.0", "@koishijs/plugin-console": "^5.1.0",
...@@ -77,7 +78,8 @@ ...@@ -77,7 +78,8 @@
"koishi": { "koishi": {
"service": { "service": {
"implements": [ "implements": [
"aragami" "aragami",
"cache"
] ]
} }
} }
......
import { AragamiConfig } from './config'; import { AragamiConfig } from './config';
import { import {
DefinePlugin, DefinePlugin,
Inject,
koishiRegistrar, koishiRegistrar,
LifecycleEvents, LifecycleEvents,
PartialDeep, PartialDeep,
PluginDef,
PluginSchema, PluginSchema,
Provide, Provide,
UsePlugin,
} from 'koishi-thirdeye'; } from 'koishi-thirdeye';
import { Channel, Command, Context, Session, User } from 'koishi'; import { Channel, Command, Context, Session, User } from 'koishi';
import { Aragami, AragamiOptions, WrapDecoratorBuilder } from 'aragami'; import {
Aragami,
AragamiOptions,
CacheKey,
WrapDecoratorBuilder,
} from 'aragami';
export * from './config'; export * from './config';
export * from 'aragami'; export * from 'aragami';
import Cache from '@koishijs/cache';
declare module 'koishi' { declare module 'koishi' {
interface Context { interface Context {
...@@ -18,6 +27,50 @@ declare module 'koishi' { ...@@ -18,6 +27,50 @@ declare module 'koishi' {
} }
} }
class AdaptingCache {
table: string;
key: string;
value: any;
@CacheKey()
cacheKey() {
return `${this.table}:${this.key}`;
}
}
type CopyType<T> = { [P in keyof T]: T[P] };
@DefinePlugin()
export class AragamiCacheProvider extends Cache {
constructor(ctx: Context) {
super(ctx);
}
@Inject()
private aragami: CopyType<AragamiPlugin>;
async clear(table: string) {
return this.aragami.clear(AdaptingCache, table);
}
async get(table: string, key: string) {
const entry = await this.aragami.get(AdaptingCache, `${table}:${key}`);
return entry?.value;
}
async set(table: string, key: string, value: any, maxAge?: number) {
const entry = new AdaptingCache();
entry.table = table;
entry.key = key;
entry.value = value;
await this.aragami.set(entry, { ttl: maxAge ?? 0 });
}
async delete(table: string, key: string) {
await this.aragami.del(AdaptingCache, `${table}:${key}`);
}
}
@PluginSchema(AragamiConfig) @PluginSchema(AragamiConfig)
@Provide('aragami', { immediate: true }) @Provide('aragami', { immediate: true })
@DefinePlugin({ name: 'cache-aragami' }) @DefinePlugin({ name: 'cache-aragami' })
...@@ -29,16 +82,28 @@ export default class AragamiPlugin extends Aragami implements LifecycleEvents { ...@@ -29,16 +82,28 @@ export default class AragamiPlugin extends Aragami implements LifecycleEvents {
super(_config.getConfig()); super(_config.getConfig());
} }
@UsePlugin()
loadAdaptingProvider() {
//console.log('loadAdaptingProvider');
return PluginDef(AragamiCacheProvider);
}
onDisconnect() { onDisconnect() {
return this.destroy(); return this.destroy();
} }
private commandLock<C extends Command>( private commandLock<
cmd: C, U extends User.Field = never,
G extends Channel.Field = never,
A extends any[] = any[],
// eslint-disable-next-line @typescript-eslint/ban-types
O extends {} = {},
>(
cmd: Command<U, G, A, O>,
fields: string[], fields: string[],
info: { idFactory: (session: Session) => string; prefix: string }, info: { idFactory: (session: Session) => string; prefix: string },
) { ) {
return cmd.action((argv) => { return cmd.action((argv, ...args: any[]) => {
if (!argv.session) return argv.next(); if (!argv.session) return argv.next();
const id = info.idFactory(argv.session); const id = info.idFactory(argv.session);
if (!id) return argv.next(); if (!id) return argv.next();
...@@ -49,22 +114,43 @@ export default class AragamiPlugin extends Aragami implements LifecycleEvents { ...@@ -49,22 +114,43 @@ export default class AragamiPlugin extends Aragami implements LifecycleEvents {
}, true); }, true);
} }
commandUserLock<C extends Command>(cmd: C, fields: (keyof User)[]) { commandUserLock<
return this.commandLock(cmd.userFields(fields) as C, fields, { T extends User.Field,
U extends User.Field = never,
G extends Channel.Field = never,
A extends any[] = any[],
// eslint-disable-next-line @typescript-eslint/ban-types
O extends {} = {},
>(cmd: Command<U, G, A, O>, fields: T[]) {
return this.commandLock(cmd.userFields(fields), fields as string[], {
idFactory: (session: Session) => session.userId, idFactory: (session: Session) => session.userId,
prefix: 'user', prefix: 'user',
}); });
} }
commandChannelLock<C extends Command>(cmd: C, fields: (keyof Channel)[]) { commandChannelLock<
return this.commandLock(cmd.channelFields(fields) as C, fields, { T extends Channel.Field,
U extends User.Field = never,
G extends Channel.Field = never,
A extends any[] = any[],
// eslint-disable-next-line @typescript-eslint/ban-types
O extends {} = {},
>(cmd: Command<U, G, A, O>, fields: T[]) {
return this.commandLock(cmd.channelFields(fields), fields as string[], {
idFactory: (session: Session) => session.channelId, idFactory: (session: Session) => session.channelId,
prefix: 'channel', prefix: 'channel',
}); });
} }
commandGuildLock<C extends Command>(cmd: C, fields: (keyof Channel)[]) { commandGuildLock<
return this.commandLock(cmd.channelFields(fields) as C, fields, { T extends Channel.Field,
U extends User.Field = never,
G extends Channel.Field = never,
A extends any[] = any[],
// eslint-disable-next-line @typescript-eslint/ban-types
O extends {} = {},
>(cmd: Command<U, G, A, O>, fields: T[]) {
return this.commandLock(cmd.channelFields(fields), fields as string[], {
idFactory: (session: Session) => session.guildId || session.channelId, idFactory: (session: Session) => session.guildId || session.channelId,
prefix: 'guild', prefix: 'guild',
}); });
...@@ -75,20 +161,44 @@ export const { UseCache, UseLock } = new WrapDecoratorBuilder( ...@@ -75,20 +161,44 @@ export const { UseCache, UseLock } = new WrapDecoratorBuilder(
(o) => ((o.ctx || o.__ctx) as Context).aragami, (o) => ((o.ctx || o.__ctx) as Context).aragami,
).build(); ).build();
export const CommandUserLock = koishiRegistrar.decorateCommandTransformer( export const CommandUserLock =
(ctx, cmd, ...fields: (keyof User)[]) => <T extends User.Field>(...fields: T[]) =>
ctx.aragami.commandUserLock(cmd, fields), <
); U extends User.Field = never,
G extends Channel.Field = never,
A extends any[] = any[],
// eslint-disable-next-line @typescript-eslint/ban-types
O extends {} = {},
>(
cmd: Command<U, G, A, O>,
) =>
cmd.ctx.aragami.commandUserLock(cmd, fields);
export const CommandChannelLock = koishiRegistrar.decorateCommandTransformer( export const CommandChannelLock =
(ctx, cmd, ...fields: (keyof Channel)[]) => <T extends Channel.Field>(...fields: T[]) =>
ctx.aragami.commandChannelLock(cmd, fields), <
); U extends User.Field = never,
G extends Channel.Field = never,
A extends any[] = any[],
// eslint-disable-next-line @typescript-eslint/ban-types
O extends {} = {},
>(
cmd: Command<U, G, A, O>,
) =>
cmd.ctx.aragami.commandChannelLock(cmd, fields);
export const CommandGuildLock = koishiRegistrar.decorateCommandTransformer( export const CommandGuildLock =
(ctx, cmd, ...fields: (keyof Channel)[]) => <T extends Channel.Field>(...fields: T[]) =>
ctx.aragami.commandGuildLock(cmd, fields), <
); U extends User.Field = never,
G extends Channel.Field = never,
A extends any[] = any[],
// eslint-disable-next-line @typescript-eslint/ban-types
O extends {} = {},
>(
cmd: Command<U, G, A, O>,
) =>
cmd.ctx.aragami.commandGuildLock(cmd, fields);
export const PutLockingUser = koishiRegistrar.decorateCommandPut( export const PutLockingUser = koishiRegistrar.decorateCommandPut(
(info, ...fields: (keyof User)[]) => info.argv.session.user, (info, ...fields: (keyof User)[]) => info.argv.session.user,
......
...@@ -3,6 +3,12 @@ import TargetPlugin, { UseCache } from '../src'; ...@@ -3,6 +3,12 @@ import TargetPlugin, { UseCache } from '../src';
import { CacheKey, WithKey } from 'aragami'; import { CacheKey, WithKey } from 'aragami';
import { DefinePlugin, Provide, StarterPlugin } from 'koishi-thirdeye'; import { DefinePlugin, Provide, StarterPlugin } from 'koishi-thirdeye';
declare module '@koishijs/cache' {
interface Tables {
aragami: any;
}
}
describe('Test of plugin.', () => { describe('Test of plugin.', () => {
let app: Context; let app: Context;
...@@ -12,6 +18,8 @@ describe('Test of plugin.', () => { ...@@ -12,6 +18,8 @@ describe('Test of plugin.', () => {
// redis: { uri: 'redis://localhost:6379' }, // redis: { uri: 'redis://localhost:6379' },
}); });
await app.start(); await app.start();
// console.log(app.aragami);
// console.log(app.__cache__);
}); });
class User { class User {
...@@ -49,4 +57,14 @@ describe('Test of plugin.', () => { ...@@ -49,4 +57,14 @@ describe('Test of plugin.', () => {
await instance.getUser('Satori', 18); await instance.getUser('Satori', 18);
await expect(app.aragami.has(User, 'Satori')).resolves.toBeTruthy(); await expect(app.aragami.has(User, 'Satori')).resolves.toBeTruthy();
}); });
it('should work with builtin cache', async () => {
expect(app.__cache__).toBeDefined();
const table = app.cache('aragami');
expect(table).toBeDefined();
await table.set('foo', { bar: 'baz' }, 10);
await expect(table.get('foo')).resolves.toEqual({ bar: 'baz' });
await new Promise((resolve) => setTimeout(resolve, 15));
await expect(table.get('foo')).resolves.toBeUndefined();
});
}); });
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