Skip to content
This repository has been archived by the owner on Dec 23, 2021. It is now read-only.

Commit

Permalink
feat: event hook
Browse files Browse the repository at this point in the history
  • Loading branch information
Soontao committed Aug 1, 2020
1 parent 5d551c7 commit ebd57b9
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 67 deletions.
69 changes: 30 additions & 39 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
}
],
"dependencies": {
"@types/express": "^4.17.7",
"@newdash/newdash": "^5.12.0",
"@odata/metadata": "^0.1.13",
"@odata/parser": "^0.1.39",
Expand All @@ -55,7 +56,6 @@
"@types/body-parser": "1.19.0",
"@types/cors": "^2.8.6",
"@types/event-stream": "^3.3.34",
"@types/express": "^4.16.0",
"@types/jest": "^26.0.3",
"@types/jsonstream": "^0.8.30",
"@types/mongodb": "^3.1.1",
Expand All @@ -64,8 +64,8 @@
"@types/request": "^2.48.5",
"@types/request-promise": "^4.1.42",
"@types/uuid": "^8.0.0",
"@typescript-eslint/eslint-plugin": "^3.5.0",
"@typescript-eslint/parser": "^3.5.0",
"@typescript-eslint/eslint-plugin": "^3.7.1",
"@typescript-eslint/parser": "^3.7.1",
"JSONStream": "^1.3.5",
"benchmark": "^2.1.4",
"eslint": "^7.2.0",
Expand Down
7 changes: 6 additions & 1 deletion src/lib/middlewares.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,15 @@ export function ensureODataHeaders(req: Request, res: Response, next?: NextFunct
export function withSwaggerDocument(sm: ServiceMetadata) {
return async (req: Request, res: Response, next: NextFunction) => {
try {

const metadata = sm.document('xml');
const service = await parse(metadata);

const swaggerDoc = convert(service.entitySets, { host: `${req.get('host')}`, basePath: `${dirname(req.path)}` }, service.version);
const swaggerDoc = convert(service.entitySets, {
host: `${req.get('host')}`,
basePath: `${dirname(req.baseUrl)}`
}, service.version);

req['swaggerDoc'] = swaggerDoc;
// res.json(swaggerDoc);
next();
Expand Down
4 changes: 3 additions & 1 deletion src/lib/typeorm/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const KEY_CONN_NAME = 'odata:controller:connection';
* @param connectionName typeorm connection name
*/
export function withConnection(connectionName: string = 'default') {
return function(controller: typeof TypedService) {
return function (controller: typeof TypedService) {
Reflect.defineMetadata(KEY_CONN_NAME, connectionName, controller);
};
}
Expand All @@ -32,3 +32,5 @@ export function getConnectionName(target: typeof TypedService | typeof BaseOData
* @alias typeorm createConnection
*/
export const createDBConnection = createConnection;

export { ConnectionOptions } from 'typeorm';
45 changes: 26 additions & 19 deletions src/lib/typeorm/controller.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @ts-nocheck
import isEmpty from '@newdash/newdash/isEmpty';
import { defaultParser, ODataQueryParam } from '@odata/parser';
import 'reflect-metadata';
Expand All @@ -18,19 +19,19 @@ import { transformQueryAst } from './visitor';
*/
export class TypedService<T extends typeof BaseODataModel = any> extends ODataController {

protected async _getConnection(ctx: ODataHttpContext) {
protected async _getConnection(ctx?: ODataHttpContext) {
return (await this._getQueryRunner(ctx)).connection;
}

protected async _getEntityManager(ctx: ODataHttpContext) {
protected async _getEntityManager(ctx?: ODataHttpContext) {
return (await this._getQueryRunner(ctx)).manager;
}

protected async _getQueryRunner(ctx: ODataHttpContext) {
protected async _getQueryRunner(ctx?: ODataHttpContext) {
return getOrCreateTransaction(getConnection(getConnectionName(this.constructor as typeof TypedService)), ctx);
}

protected async _getRepository(ctx: ODataHttpContext): Promise<Repository<InstanceType<T>>> {
protected async _getRepository(ctx?: ODataHttpContext): Promise<Repository<InstanceType<T>>> {
// @ts-ignore
return (await this._getConnection(ctx)).getRepository(this.elementType);
}
Expand All @@ -47,7 +48,7 @@ export class TypedService<T extends typeof BaseODataModel = any> extends ODataCo
* @param data data for read/create
* @param key key for update/delete
*/
private async _executeHooks(ctx: Partial<HookContext>) {
private async _executeHooks(ctx?: Partial<HookContext>) {

if (ctx.entityType == undefined) {
ctx.entityType = this.elementType;
Expand Down Expand Up @@ -86,7 +87,7 @@ export class TypedService<T extends typeof BaseODataModel = any> extends ODataCo


@odata.GET
async findOne(@odata.key key, @odata.context ctx: ODataHttpContext): Promise<InstanceType<T>> {
async findOne(@odata.key key, @odata.context ctx?: ODataHttpContext): Promise<InstanceType<T>> {
const repo = await this._getRepository(ctx);
const data = await repo.findOne(key);
if (isEmpty(data)) {
Expand All @@ -98,11 +99,11 @@ export class TypedService<T extends typeof BaseODataModel = any> extends ODataCo
return data;
}

async find(query: ODataQueryParam, ctx: ODataHttpContext): Promise<Array<InstanceType<T>>>;
async find(query: string, ctx: ODataHttpContext): Promise<Array<InstanceType<T>>>;
async find(query: ODataQuery, ctx: ODataHttpContext): Promise<Array<InstanceType<T>>>;
async find(query: ODataQueryParam, ctx?: ODataHttpContext): Promise<Array<InstanceType<T>>>;
async find(query: string, ctx?: ODataHttpContext): Promise<Array<InstanceType<T>>>;
async find(query: ODataQuery, ctx?: ODataHttpContext): Promise<Array<InstanceType<T>>>;
@odata.GET
async find(@odata.query query, @odata.context ctx: ODataHttpContext) {
async find(@odata.query query, @odata.context ctx?: ODataHttpContext) {

const conn = await this._getConnection(ctx);
const repo = await this._getRepository(ctx);
Expand Down Expand Up @@ -149,18 +150,21 @@ export class TypedService<T extends typeof BaseODataModel = any> extends ODataCo
}

@odata.POST
async create(@odata.body body: QueryDeepPartialEntity<InstanceType<T>>, @odata.context ctx: ODataHttpContext) {
async create(@odata.body body: QueryDeepPartialEntity<InstanceType<T>>, @odata.context ctx?: ODataHttpContext) {
const repo = await this._getRepository(ctx);
await this._executeHooks({ context: ctx, hookType: HookType.beforeCreate, data: body });
const instance = repo.create(body);
await this._executeHooks({ context: ctx, hookType: HookType.beforeCreate, data: instance });
// query the created item
const { identifiers: [id] } = await repo.insert(body);
const { identifiers: [id] } = await repo.insert(instance);
// and return it
return this.findOne(id, ctx);
const created = this.findOne(id, ctx);
await this._executeHooks({ context: ctx, hookType: HookType.afterSave, data: created });
return created;
}

// create or update
@odata.PUT
async save(@odata.key key, @odata.body body: QueryDeepPartialEntity<InstanceType<T>>, @odata.context ctx: ODataHttpContext) {
async save(@odata.key key, @odata.body body: QueryDeepPartialEntity<InstanceType<T>>, @odata.context ctx?: ODataHttpContext) {
const repo = await this._getRepository(ctx);
if (key) {
const item = await repo.findOne(key);
Expand All @@ -174,18 +178,21 @@ export class TypedService<T extends typeof BaseODataModel = any> extends ODataCo

// odata patch will not response any content
@odata.PATCH
async update(@odata.key key, @odata.body body: QueryDeepPartialEntity<InstanceType<T>>, @odata.context ctx: ODataHttpContext) {
async update(@odata.key key, @odata.body body: QueryDeepPartialEntity<InstanceType<T>>, @odata.context ctx?: ODataHttpContext) {
const repo = await this._getRepository(ctx);
await this._executeHooks({ context: ctx, hookType: HookType.beforeUpdate, data: body, key });
await repo.update(key, body);
const instance = repo.create(body);
await this._executeHooks({ context: ctx, hookType: HookType.beforeUpdate, data: instance, key });
await repo.update(key, instance);
await this._executeHooks({ context: ctx, hookType: HookType.afterSave, data: instance, key });
}

// odata delete will not response any content
@odata.DELETE
async delete(@odata.key key, @odata.context ctx: ODataHttpContext) {
async delete(@odata.key key, @odata.context ctx?: ODataHttpContext) {
const repo = await this._getRepository(ctx);
await this._executeHooks({ context: ctx, hookType: HookType.beforeDelete, key });
await repo.delete(key);
await this._executeHooks({ context: ctx, hookType: HookType.afterSave, key });
}

}
Expand Down
3 changes: 2 additions & 1 deletion src/lib/typeorm/hooks/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ export interface HookContext<T = any> {
* key for update/delete/read
*/
key?: any;

/**
* transaction entity manager
* (transaction) entity manager
*/
em: EntityManager;

Expand Down
3 changes: 0 additions & 3 deletions src/lib/typeorm/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,13 @@ const transactionStorage = new WeakMap<ODataHttpContext, QueryRunner>();
* @param ctx
*/
export async function getOrCreateTransaction(conn: Connection, ctx: ODataHttpContext): Promise<QueryRunner> {

if (!transactionStorage.has(ctx)) {
const qr = conn.createQueryRunner(); // pool required
await qr.connect();
await qr.startTransaction(); // begin transaction
transactionStorage.set(ctx, qr);
}

return transactionStorage.get(ctx);

}

async function releaseTransaction(qr: QueryRunner): Promise<void> {
Expand Down

0 comments on commit ebd57b9

Please sign in to comment.