Commit cec83e5c authored by nanahira's avatar nanahira

put away from koishi

parent 9e28dd4b
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -39,7 +39,7 @@
"testEnvironment": "node"
},
"devDependencies": {
"@koishijs/plugin-database-memory": "^1.2.0",
"@cosmotype/driver-memory": "^1.0.2",
"@types/jest": "^27.4.1",
"@types/lodash": "^4.14.180",
"@types/node": "^17.0.23",
......@@ -55,7 +55,7 @@
"typescript": "^4.6.3"
},
"peerDependencies": {
"koishi": "^4.6.0"
"cosmotype": "^1.0.3"
},
"dependencies": {
"lodash": "^4.17.21",
......
import { ModelClassType, ModelFieldDef } from './def';
import { Metadata } from './meta/meta';
import { Flatten, Keys, Tables } from 'koishi';
import { Flatten, Keys } from 'cosmotype';
import { inferType } from './utils';
export const DefineModel = (name: Keys<Tables>): ClassDecorator =>
export class ModelDecorators<Tables = any> {
DefineModel = (name: Keys<Tables>): ClassDecorator =>
Metadata.set('ModelTableName', name);
export const ModelField =
ModelField =
<T = any>(def?: ModelFieldDef<T>): PropertyDecorator =>
(obj, key) =>
Metadata.set(
......@@ -15,23 +16,22 @@ export const ModelField =
'ModelFieldKeys',
)(obj, key);
export const Primary = (): PropertyDecorator =>
Primary = (): PropertyDecorator =>
Metadata.set('ModelPrimaryKey', { autoIncrement: false });
export const PrimaryGenerated = (): PropertyDecorator =>
PrimaryGenerated = (): PropertyDecorator =>
Metadata.set('ModelPrimaryKey', { autoIncrement: true });
export const Foreign = <K extends Keys<Tables>>(
Foreign = <K extends Keys<Tables>>(
referencingTable: K,
referencingColumn: Keys<Flatten<Tables[K]>>,
): PropertyDecorator =>
): PropertyDecorator =>
Metadata.set('ModelForeignKey', [referencingTable, referencingColumn]);
export const Unique = (
identifier: string | number = '_default',
): PropertyDecorator => Metadata.append('ModelUnique', identifier);
Unique = (identifier: string | number = '_default'): PropertyDecorator =>
Metadata.append('ModelUnique', identifier);
export const ChildModel =
ChildModel =
(cls?: ModelClassType): PropertyDecorator =>
(obj, key) => {
if (!cls) {
......@@ -39,3 +39,4 @@ export const ChildModel =
}
Metadata.set('ChildModel', cls, 'ChildModelKeys')(obj, key);
};
}
import { Context, Field, Flatten, Keys, Model, Tables } from 'koishi';
import { Database, Field, Flatten, Keys, Model } from 'cosmotype';
import { ModelClassType } from './def';
import { reflector } from './meta/meta';
import _ from 'lodash';
class ModelRegistrar<T = any> {
class TableRegistrar<Tables, T = any> {
constructor(private cls: ModelClassType<T>, private prefix = '') {}
getTableName() {
......@@ -123,7 +122,7 @@ class ModelRegistrar<T = any> {
for (const key in childDict) {
const child = childDict[key];
const prefix = this.prefix + key + '.';
const childReg = new ModelRegistrar(child, prefix);
const childReg = new TableRegistrar<Tables>(child, prefix);
internal = { ...internal, ...childReg.getInternal() };
}
return internal;
......@@ -135,7 +134,7 @@ class ModelRegistrar<T = any> {
for (const key of Object.keys(children)) {
const child = children[key];
if (child) {
const childRegistrar = new ModelRegistrar(
const childRegistrar = new TableRegistrar<Tables>(
child,
this.prefix + key + '.',
);
......@@ -159,35 +158,39 @@ class ModelRegistrar<T = any> {
}
}
export function registerModel(
ctx: Context,
cls: { new (...args: any[]): any },
) {
const registrar = new ModelRegistrar(cls);
export class ModelRegistrar<Tables> {
constructor(private readonly model: Database<Tables>) {}
registerModel(cls: ModelClassType<Flatten<Tables[Keys<Tables>]>>);
registerModel(cls: ModelClassType) {
const registrar = new TableRegistrar(cls);
const tableName = registrar.getTableName();
if (!tableName) {
throw new Error(`Model of ${cls.name} is not defined`);
}
ctx.model.extend(tableName, ...registrar.getModelResult());
Object.assign(ctx.model.tables[tableName].internal, registrar.getInternal());
}
this.model.extend(tableName, ...registrar.getModelResult());
Object.assign(
this.model.tables[tableName].internal,
registrar.getInternal(),
);
}
export function mixinModel<K extends Keys<Tables>>(
ctx: Context,
mixinModel<K extends Keys<Tables>>(
tableName: K,
classDict: {
[F in Keys<Tables[K]>]?: ModelClassType<Flatten<Tables[K][F]>>;
},
) {
) {
for (const _key in classDict) {
const key = _key as Keys<Tables[K]>;
const cls = classDict[key];
const registrar = new ModelRegistrar<any>(cls, key + '.');
const registrar = new TableRegistrar<any>(cls, key + '.');
const result = registrar.getModelResult();
ctx.model.extend(tableName, ...result);
this.model.extend(tableName, ...result);
Object.assign(
ctx.model.tables[tableName].internal,
this.model.tables[tableName].internal,
registrar.getInternal(),
);
}
}
}
import {
ChildModel,
DefineModel,
Foreign,
ModelField,
PrimaryGenerated,
Unique,
} from '../src/decorators';
import { App } from 'koishi';
import { registerModel } from '../src/register';
import * as MemoryDatabase from '@koishijs/plugin-database-memory';
import MemoryDriver from '@cosmotype/driver-memory';
import { ModelDecorators } from '../src/decorators';
import { Database } from 'cosmotype';
import { ModelRegistrar } from '../src/register';
declare module 'koishi' {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface Tables {
interface Tables {
dress: Dress;
}
}
const decorators = new ModelDecorators<Tables>();
class DressProperty {
@ModelField('string(8)')
@decorators.ModelField('string(8)')
color: string;
@ModelField('integer(7)')
@decorators.ModelField('integer(7)')
size: string;
getProperty() {
......@@ -28,40 +19,41 @@ class DressProperty {
}
}
@DefineModel('dress')
@decorators.DefineModel('dress')
class Dress {
@PrimaryGenerated()
@ModelField('integer(11)')
@decorators.PrimaryGenerated()
@decorators.ModelField('integer(11)')
id: number;
@Unique()
@ModelField()
@decorators.Unique()
@decorators.ModelField()
name: string; // test if it can infer type
getName() {
return this.name;
}
@ModelField('integer(11)')
@Foreign('dress', 'id')
@decorators.ModelField('integer(11)')
@decorators.Foreign('dress', 'id')
parentId: number;
@ChildModel()
@decorators.ChildModel()
properties: DressProperty;
}
describe('Model test', () => {
let app: App;
let model: Database<Tables>;
beforeEach(async () => {
app = new App();
registerModel(app, Dress);
app.plugin(MemoryDatabase);
await app.start();
model = new Database();
const registrar = new ModelRegistrar(model);
registrar.registerModel(Dress);
const database = new MemoryDriver(model, {});
await database.start();
});
it('should register model fields', () => {
const { dress } = app.model.tables;
const { dress } = model.tables;
expect(dress.fields.id.type).toBe('integer');
expect(dress.fields.id.length).toBe(11);
expect(dress.fields.name.type).toBe('string');
......@@ -74,7 +66,7 @@ describe('Model test', () => {
});
it('should register model extras', () => {
const { dress } = app.model.tables;
const { dress } = model.tables;
expect(dress.primary[0]).toBe('id');
expect(dress.unique[0][0]).toBe('name');
expect(dress.foreign.parentId).toStrictEqual(['dress', 'id']);
......@@ -83,7 +75,7 @@ describe('Model test', () => {
});
it('should make class instance', async () => {
await app.database.upsert('dress', [
await model.upsert('dress', [
{
id: 777,
name: 'Dress of Shigma',
......@@ -93,7 +85,7 @@ describe('Model test', () => {
},
},
]);
const [dress] = await app.database.get('dress', { id: 777 });
const [dress] = await model.get('dress', { id: 777 });
expect(dress).toBeInstanceOf(Dress);
expect(dress.id).toBe(777);
expect(dress.getName()).toBe('Dress of Shigma');
......
import { ChildModel, ModelField } from '../src/decorators';
import { App } from 'koishi';
import { mixinModel } from '../src/register';
import * as MemoryDatabase from '@koishijs/plugin-database-memory';
import MemoryDriver from '@cosmotype/driver-memory';
import { ModelDecorators } from '../src/decorators';
import { Database } from 'cosmotype';
import { ModelRegistrar } from '../src/register';
declare module 'koishi' {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface User {
interface User {
id: string;
dress: Dress;
shirt: Shirt;
}
}
interface Tables {
user: User;
}
const decorators = new ModelDecorators<Tables>();
class WearingPreference {
@ModelField('string(8)')
@decorators.ModelField('string(8)')
color: string;
@ModelField('string(12)')
@decorators.ModelField('string(12)')
shape: string;
format() {
......@@ -23,14 +27,14 @@ class WearingPreference {
}
class Wearing {
@ModelField('string(3)')
@decorators.ModelField('string(3)')
size: string;
getSize() {
return this.size;
}
@ChildModel()
@decorators.ChildModel()
preference: WearingPreference;
}
......@@ -46,17 +50,30 @@ class Shirt extends Wearing {
}
describe('Model test', () => {
let app: App;
let model: Database<Tables>;
beforeEach(async () => {
app = new App();
app.plugin(MemoryDatabase);
mixinModel(app, 'user', { dress: Dress, shirt: Shirt });
await app.start();
model = new Database();
const registrar = new ModelRegistrar(model);
model.extend(
'user',
{
id: 'string(8)',
},
{
primary: 'id',
},
);
registrar.mixinModel('user', {
dress: Dress,
shirt: Shirt,
});
const database = new MemoryDriver(model, {});
await database.start();
});
it('should register model fields', () => {
const { user } = app.model.tables;
const { user } = model.tables;
expect(user.fields['dress.size'].type).toBe('string');
expect(user.fields['dress.size'].length).toBe(3);
expect(user.fields['shirt.size'].type).toBe('string');
......@@ -81,7 +98,7 @@ describe('Model test', () => {
});
it('should make class instance', async () => {
await app.database.upsert('user', [
await model.upsert('user', [
{
id: '111111',
dress: {
......@@ -100,7 +117,7 @@ describe('Model test', () => {
},
},
]);
const [user] = await app.database.get('user', { id: '111111' });
const [user] = await model.get('user', { id: '111111' });
expect(user.dress).toBeInstanceOf(Dress);
expect(user.dress.getDressSize()).toBe('S');
expect(user.dress.preference).toBeInstanceOf(WearingPreference);
......
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