nocobase/packages/server/src/application.ts
chenos c2ff7882bc
feat: database next (#130)
* FIX: database test with sqlite

* more types

* filter test

* split filter parser

* filter test

* filter test: hasMany

* define inverse association for belongsTo & hasMany

* chore: console.log

* repository count method

* chore: Collection

* repository filter & appends & fields & expect

* repository: sort option

* chore: test

* add: test

* find & findAndCount

* chore: test

* database-next: update guard

* database-next: update guard associationKeysToBeUpdate

* chore: comment

* update-guard OneToOne Association

* has one repository

* support through table value

* belongs to many repository

* has many repository

* has many repository find

* fix: has many find and count

* clean code

* add count method

* chore: multiple relation

* chore: single relation

* repository find

* relation repository builder

* repository count

* repository count test

* fix test

* close db afterEach test

* sort with associations

* repository update

* has many repository: destroy

* belongs to many repository: destroy

* add transaction decorator

* belongs to many with transaction

* has many with transaction

* clean types

* clean types

* clean types

* repository transaction

* fix test

* single relation repository with transaction

* single relation repository with transaction

* fix: test

* fix: option parser fields append

* fix: typo

* fix: string type

* fix: import

* collection field methods

* cleanup

* collection sync

* fix: import

* fix: test

* collection update field

* collection update options

* database hook

* database test

* database event test

* update database event

* add async emmit mixin

* async model event

* database import

* fix: model hook type

* fix: collection event

* recall model.init on collection update

* skip redefine collection test

* skip collection model update

* add model hook class

* global model event support

* chore

* chore

* change utils import

* add field types

* database import

* more import test

* test case

* fix: through model init...

* bugfix

* fix

* update database import

* collection sync by foreachModel

* fix collection model sync

* update

* add field types

* custom operator

* sqlite array field

* postgresql array field

* array query escape

* mysql array operators

* date operators

* array field sqlite fix

* association operator

* date operator empty & notEmpty

* fix: fields import

* fix array field nested association

* filter parse prepare

* fix test

* string field empty

* add date operator test

* field option types

* fix typo

* fix: operator name conflict

* rename function

Co-authored-by: Chareice <chareice@live.com>
2021-12-06 21:12:54 +08:00

178 lines
4.3 KiB
TypeScript

import Koa from 'koa';
import { Command, CommandOptions } from 'commander';
import Database, { DatabaseOptions, TableOptions } from '@nocobase/database';
import Resourcer, { ResourceOptions } from '@nocobase/resourcer';
import { PluginType, Plugin, PluginOptions } from './plugin';
import { registerActions } from '@nocobase/actions';
import {
createCli,
createI18n,
createDatabase,
createResourcer,
registerMiddlewares,
} from './helper';
import { i18n, InitOptions } from 'i18next';
import { applyMixins, AsyncEmitter } from '@nocobase/utils';
export interface ResourcerOptions {
prefix?: string;
}
export interface ApplicationOptions {
database?: DatabaseOptions;
resourcer?: ResourcerOptions;
bodyParser?: any;
cors?: any;
dataWrapping?: boolean;
registerActions?: boolean;
i18n?: i18n | InitOptions;
}
interface DefaultState {
currentUser?: any;
[key: string]: any;
}
interface DefaultContext {
db: Database;
resourcer: Resourcer;
[key: string]: any;
}
interface MiddlewareOptions {
name?: string;
resourceName?: string;
resourceNames?: string[];
insertBefore?: string;
insertAfter?: string;
}
interface ActionsOptions {
resourceName?: string;
resourceNames?: string[];
}
export class Application<StateT = DefaultState, ContextT = DefaultContext>
extends Koa
implements AsyncEmitter
{
public readonly db: Database;
public readonly resourcer: Resourcer;
public readonly cli: Command;
public readonly i18n: i18n;
protected plugins = new Map<string, Plugin>();
constructor(options: ApplicationOptions) {
super();
this.db = createDatabase(options);
this.resourcer = createResourcer(options);
this.cli = createCli(this, options);
this.i18n = createI18n(options);
registerMiddlewares(this, options);
if (options.registerActions !== false) {
registerActions(this);
}
}
use<NewStateT = {}, NewContextT = {}>(
middleware: Koa.Middleware<StateT & NewStateT, ContextT & NewContextT>,
options?: MiddlewareOptions,
) {
// @ts-ignore
return super.use(middleware);
}
collection(options: TableOptions) {
return this.db.table(options);
}
resource(options: ResourceOptions) {
return this.resourcer.define(options);
}
actions(handlers: any, options?: ActionsOptions) {
return this.resourcer.registerActions(handlers);
}
command(nameAndArgs: string, opts?: CommandOptions) {
return this.cli.command(nameAndArgs, opts);
}
findCommand(name: string): Command {
return (this.cli as any)._findCommand(name);
}
plugin(options?: PluginType | PluginOptions, ext?: PluginOptions): Plugin {
if (typeof options === 'string') {
return this.plugin(require(options).default, ext);
}
let instance: Plugin;
if (typeof options === 'function') {
try {
// @ts-ignore
instance = new options({
name: options.name,
...ext,
app: this,
});
if (!(instance instanceof Plugin)) {
throw new Error('plugin must be instanceof Plugin');
}
} catch (err) {
// console.log(err);
instance = new Plugin({
name: options.name,
...ext,
// @ts-ignore
load: options,
app: this,
});
}
} else if (typeof options === 'object') {
const plugin = options.plugin || Plugin;
instance = new plugin({
name: options.plugin ? plugin.name : undefined,
...options,
...ext,
app: this,
});
}
const name = instance.getName();
if (this.plugins.has(name)) {
throw new Error(`plugin name [${name}] is repeated`);
}
this.plugins.set(name, instance);
return instance;
}
async load() {
await this.emitAsync('plugins.beforeLoad');
for (const [name, plugin] of this.plugins) {
await this.emitAsync(`plugins.${name}.beforeLoad`);
await plugin.load();
await this.emitAsync(`plugins.${name}.afterLoad`);
}
await this.emitAsync('plugins.afterLoad');
}
async parse(argv = process.argv) {
await this.load();
return this.cli.parseAsync(argv);
}
async destroy() {
await this.db.close();
}
emitAsync: (event: string | symbol, ...args: any[]) => Promise<boolean>;
}
applyMixins(Application, [AsyncEmitter]);
export default Application;