Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: impl Inject Model #43

Merged
merged 1 commit into from
Jul 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions core/orm-decorator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ export class App extends Bone {
## Use Model

```ts
import { ContextProto } from '@eggjs/tegg';
import { ContextProto, Inject } from '@eggjs/tegg';
import { App } from './model/App';

@ContextProto()
export class AppService {
// TODO impl inject Bone for context
App: typeof App = App;
@Inject()
App: typeof App;

async createApp(data: {
name: string;
Expand Down
4 changes: 4 additions & 0 deletions core/orm-decorator/src/decorator/Model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ export interface ModelParams {
tableName?: string;
}

export const MODEL_PROTO_IMPL_TYPE = 'MODEL_PROTO';

export function Model(param?: ModelParams) {
return function(clazz: EggProtoImplClass) {
ModelInfoUtil.setIsModel(true, clazz);
const func = ContextProto({
name: clazz.name,
accessLevel: AccessLevel.PUBLIC,
protoImplType: MODEL_PROTO_IMPL_TYPE,
});
if (param?.tableName) {
ModelInfoUtil.setTableName(param.tableName, clazz);
Expand Down
4 changes: 2 additions & 2 deletions core/orm-decorator/src/util/ModelMetadataUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { ModelMetadata } from '../model/ModelMetadata';
export const MODEL_METADATA = Symbol.for('EggPrototype#model#metadata');

export class ModelMetadataUtil {
static setControllerMetadata(clazz: EggProtoImplClass, metaData: ModelMetadata) {
static setModelMetadata(clazz: EggProtoImplClass, metaData: ModelMetadata) {
MetadataUtil.defineMetaData(MODEL_METADATA, metaData, clazz);
}

static getControllerMetadata(clazz): ModelMetadata | undefined {
static getModelMetadata(clazz): ModelMetadata | undefined {
return MetadataUtil.getOwnMetaData(MODEL_METADATA, clazz);
}
}
6 changes: 6 additions & 0 deletions plugin/orm/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { DataSourceManager } from './lib/DataSourceManager';
import { LeoricRegister } from './lib/LeoricRegister';
import { ModelProtoManager } from './lib/ModelProtoManager';
import { ModelProtoHook } from './lib/ModelProtoHook';
import { MODEL_PROTO_IMPL_TYPE } from '@eggjs/tegg-orm-decorator';
import ContextModelProto from './lib/ContextModelProto';
import { ContextModeObject } from './lib/ContextModeObject';

export default class OrmAppBootHook {
private readonly app: Application;
Expand All @@ -17,10 +20,13 @@ export default class OrmAppBootHook {
this.modelProtoManager = new ModelProtoManager();
this.leoricRegister = new LeoricRegister(this.modelProtoManager, this.dataSourceManager);
this.modelProtoHook = new ModelProtoHook(this.modelProtoManager);
this.app.eggPrototypeCreatorFactory.registerPrototypeCreator(MODEL_PROTO_IMPL_TYPE, ContextModelProto.createProto);
this.app.leoricRegister = this.leoricRegister;
}

configWillLoad() {
this.app.eggPrototypeLifecycleUtil.registerLifecycle(this.modelProtoHook);
this.app.eggObjectFactory.registerEggObjectCreateMethod(ContextModelProto, ContextModeObject.createObject);
}

configDidLoad() {
Expand Down
70 changes: 70 additions & 0 deletions plugin/orm/lib/ContextModeObject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import assert from 'assert';
import {
EggContext,
EggObject,
EggObjectLifeCycleContext,
EggObjectStatus,
} from '@eggjs/tegg-runtime';
import { EggPrototype } from '@eggjs/tegg-metadata';
import { EggPrototypeName, EggObjectName } from '@eggjs/tegg';
import { Id, IdenticalUtil } from '@eggjs/tegg-lifecycle';
import { Bone } from 'leoric';
import ContextModelProto from './ContextModelProto';
import { EGG_CONTEXT } from '@eggjs/egg-module-common';

export class ContextModeObject implements EggObject {
private status: EggObjectStatus = EggObjectStatus.PENDING;
id: Id;
readonly name: EggPrototypeName;
private _obj: typeof Bone;
readonly proto: ContextModelProto;
readonly ctx: EggContext;

constructor(name: EggObjectName, proto: ContextModelProto, ctx: EggContext) {
this.name = name;
this.proto = proto;
this.ctx = ctx;
this.id = IdenticalUtil.createObjectId(this.proto.id, this.ctx.id);
}

async init() {
const ctx = this.ctx;
const clazz = class ContextModelClass extends this.proto.model {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
static get name() {
return super.name;
}

static get ctx() {
return ctx.get(EGG_CONTEXT);
}

// custom setter always execute before define [CTX] when new Instance(super(opts) calling), if custom setter requires ctx, it should not be undefined
get ctx() {
return ctx.get(EGG_CONTEXT);
}
};
this._obj = clazz;
this.status = EggObjectStatus.READY;
}

injectProperty() {
throw new Error('never call ModelObject#injectProperty');
}

get isReady() {
return this.status === EggObjectStatus.READY;
}

get obj() {
return this._obj;
}

static async createObject(name: EggObjectName, proto: EggPrototype, _: EggObjectLifeCycleContext, ctx?: EggContext): Promise<ContextModeObject> {
assert(ctx, 'ctx must be defined for ContextModelObject');
const modelObject = new ContextModeObject(name, proto as ContextModelProto, ctx);
await modelObject.init();
return modelObject;
}
}
59 changes: 59 additions & 0 deletions plugin/orm/lib/ContextModelProto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { EggPrototype, LoadUnit, EggPrototypeLifecycleContext } from '@eggjs/tegg-metadata';
import {
AccessLevel,
EggPrototypeName,
ObjectInitType,
QualifierInfo,
QualifierUtil,
MetadataUtil,
MetaDataKey,
} from '@eggjs/tegg';
import { Id, IdenticalUtil } from '@eggjs/tegg-lifecycle';
import { Bone } from 'leoric';

export default class ContextModelProto implements EggPrototype {
private readonly qualifiers: QualifierInfo[];
readonly accessLevel = AccessLevel.PUBLIC;
id: Id;
readonly initType = ObjectInitType.CONTEXT;
readonly injectObjects = [];
readonly loadUnitId: string;
readonly moduleName: string;
readonly name: EggPrototypeName;
readonly model: typeof Bone;

constructor(loadUnit: LoadUnit, model: typeof Bone) {
this.model = model;
this.id = IdenticalUtil.createProtoId(loadUnit.id, `leoric:${model.name}`);
this.loadUnitId = loadUnit.id;
this.moduleName = loadUnit.name;
this.name = model.name;
this.qualifiers = QualifierUtil.getProtoQualifiers(model);
}

constructEggObject(): object {
return {};
}

getMetaData<T>(metadataKey: MetaDataKey): T | undefined {
return MetadataUtil.getMetaData(metadataKey, this.model);
}

verifyQualifier(qualifier: QualifierInfo): boolean {
const selfQualifiers = this.qualifiers.find(t => t.attribute === qualifier.attribute);
return selfQualifiers?.value === qualifier.value;
}

verifyQualifiers(qualifiers: QualifierInfo[]): boolean {
for (const qualifier of qualifiers) {
if (!this.verifyQualifier(qualifier)) {
return false;
}
}
return true;
}

static createProto(ctx: EggPrototypeLifecycleContext): ContextModelProto {
return new ContextModelProto(ctx.loadUnit, ctx.clazz as typeof Bone);
}
}
9 changes: 6 additions & 3 deletions plugin/orm/lib/LeoricRegister.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import Base from 'sdk-base';
import { ModelProtoManager } from './ModelProtoManager';
import { DataSourceManager, OrmConfig } from './DataSourceManager';
import Realm from 'leoric';
import { hookNames } from 'leoric/src/setup_hooks';
import { ModelMetadata, ModelMetadataUtil } from '@eggjs/tegg-orm-decorator';

export class LeoricRegister {
export class LeoricRegister extends Base {
private readonly modelProtoManager: ModelProtoManager;
private readonly dataSourceManager: DataSourceManager;
private readonly realmMap: Map<string, any>;
readonly realmMap: Map<string, any>;

constructor(modelProtoManager: ModelProtoManager, dataSourceManager: DataSourceManager) {
super();
this.modelProtoManager = modelProtoManager;
this.dataSourceManager = dataSourceManager;
this.realmMap = new Map();
Expand Down Expand Up @@ -51,7 +53,7 @@ export class LeoricRegister {

async register() {
for (const { proto, clazz } of this.modelProtoManager.getProtos()) {
const metadata = ModelMetadataUtil.getControllerMetadata(clazz);
const metadata = ModelMetadataUtil.getModelMetadata(clazz);
if (!metadata) throw new Error(`not found metadata for model ${proto.id}`);
const realm = this.getOrCreateRealm(metadata.dataSource);
realm.models[clazz.name] = clazz;
Expand All @@ -71,5 +73,6 @@ export class LeoricRegister {
}
await Promise.all(Array.from(this.realmMap.values())
.map(realm => realm.connect()));
this.ready(true);
}
}
2 changes: 1 addition & 1 deletion plugin/orm/lib/ModelProtoHook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class ModelProtoHook implements LifecycleHook<EggPrototypeLifecycleContex
}
const builder = new ModelMetaBuilder(ctx.clazz);
const metadata = builder.build();
ModelMetadataUtil.setControllerMetadata(ctx.clazz, metadata);
ModelMetadataUtil.setModelMetadata(ctx.clazz, metadata);
this.modelProtoManager.addProto(ctx.clazz, obj);
}
}
1 change: 1 addition & 0 deletions plugin/orm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"@eggjs/tegg-metadata": "^1.3.0",
"@eggjs/tegg-orm-decorator": "^1.3.1",
"@eggjs/tegg-runtime": "^1.3.0",
"@eggjs/tegg-lifecycle": "^1.0.0",
"@types/koa-router": "^7.0.40",
"koa-compose": "^3.2.1",
"leoric": "^2.6.1"
Expand Down
23 changes: 23 additions & 0 deletions plugin/orm/test/fixtures/apps/orm-app/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Application } from 'egg';
import { Logger } from 'leoric';

export default class OrmAppHook {
private readonly app: Application;

constructor(app: Application) {
this.app = app;
}

async didLoad() {
await this.app.leoricRegister.ready();
const app = this.app;
for (const realm of this.app.leoricRegister.realmMap.values()) {
realm.driver.logger = new Logger({
logQuery(sql, _, options) {
const path = options.Model?.ctx?.path;
app.logger.info('sql: %s path: %s', sql, path);
},
});
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ContextProto } from '@eggjs/tegg';
import { ContextProto, Inject } from '@eggjs/tegg';
import { App } from './model/App';

@ContextProto()
export class AppService {
// TODO impl inject Bone for context
App: typeof App = App;
@Inject()
App: typeof App;

async createApp(data: {
name: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ContextProto } from '@eggjs/tegg';
import { ContextProto, Inject } from '@eggjs/tegg';
import { Pkg } from './model/Pkg';

@ContextProto()
export class PkgService {
// TODO impl inject Bone for context
Pkg: typeof Pkg = Pkg;
@Inject()
Pkg: typeof Pkg;

async createPkg(data: {
name: string;
Expand Down
8 changes: 8 additions & 0 deletions plugin/orm/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,12 @@ describe('test/orm.test.ts', () => {
assert(findModel.name === 'egg_before_create_hook');
assert(findModel.desc === 'the framework');
});

it('ctx should inject with Model', async () => {
ctx = await app.mockModuleContext();
const appService = await ctx.getEggObject(AppService);
app.mockLog();
await appService.findApp('egg');
app.expectLog(/sql: SELECT \* FROM `apps` WHERE `name` = 'egg' LIMIT 1 path: \//);
});
});
10 changes: 10 additions & 0 deletions plugin/orm/typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,17 @@ import '@eggjs/tegg-plugin';

import { DataType } from 'leoric';
import { AttributeOptions } from '@eggjs/tegg-orm-decorator';
import { LeoricRegister } from '../lib/LeoricRegister';

declare module '@eggjs/tegg-orm-decorator' {
export declare function Attribute(dataType: DataType, options?: AttributeOptions): (target: any, propertyKey: PropertyKey) => void;
}

declare module 'egg' {
export interface TeggOrmApplication {
leoricRegister: LeoricRegister;
}

interface Application extends TeggOrmApplication {
}
}