Skip to content

Commit

Permalink
feat: impl transaction decorator (#124)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: ximin.cxm <[email protected]>
  • Loading branch information
qile222 and ximin.cxm committed Jun 29, 2023
1 parent 113f408 commit 4896615
Show file tree
Hide file tree
Showing 13 changed files with 292 additions and 0 deletions.
4 changes: 4 additions & 0 deletions core/transaction-decorator/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Change Log

All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
23 changes: 23 additions & 0 deletions core/transaction-decorator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# @eggjs/tegg-transaction-decorator

事务注解

## Usage
```ts
export class Foo {

@Transactional({ propagation: PropagationType.ALWAYS_NEW })
async bar() {
await this.foo();
}

@Transactional({ propagation: PropagationType.REQUIRED })
async foo(msg) {
console.log('has msg: ', msg);
}

}

```

Foo.bar 始终会在一个独立的事务中执行,而 Foo.foo 会在 Foo.bar 的事务中执行
5 changes: 5 additions & 0 deletions core/transaction-decorator/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './src/decorator/Transactional';
export * from './src/Common';
export * from './src/model/TransactionMetadata';
export * from './src/builder/TransactionMetaBuilder';
export * from './src/util/TransactionMetadataUtil';
54 changes: 54 additions & 0 deletions core/transaction-decorator/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "@eggjs/tegg-transaction-decorator",
"version": "3.9.0",
"description": "tegg transaction decorator",
"keywords": [
"egg",
"typescript",
"decorator",
"transaction",
"tegg"
],
"author": "qile222 <[email protected]>",
"homepage": "https://github.com/eggjs/tegg",
"repository": {
"type": "git",
"url": "[email protected]:eggjs/tegg.git",
"directory": "core/transaction-decorator"
},
"dependencies": {
"@eggjs/core-decorator": "^3.8.0",
"@eggjs/tegg-common-util": "^3.8.0",
"@eggjs/tegg-metadata": "^3.8.0"
},
"scripts": {
"test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha",
"clean": "tsc -b --clean",
"tsc": "npm run clean && tsc -p ./tsconfig.json",
"tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json",
"prepublishOnly": "npm run tsc:pub"
},
"publishConfig": {
"access": "public"
},
"engines": {
"node": ">=14.0.0"
},
"license": "MIT",
"main": "dist/index.js",
"files": [
"dist/**/*.js",
"dist/**/*.d.ts"
],
"bugs": {
"url": "https://github.com/eggjs/tegg/issues"
},
"devDependencies": {
"@types/mocha": "^10.0.1",
"@types/node": "^20.2.4",
"cross-env": "^7.0.3",
"mocha": "^10.2.0",
"ts-node": "^10.9.1",
"typescript": "^5.0.4"
}
}
11 changes: 11 additions & 0 deletions core/transaction-decorator/src/Common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export enum PropagationType {
/** 不管是当前调用栈是否存在事务,始终让当前函数在新的事务中执行 */
ALWAYS_NEW = 'ALWAYS_NEW',
/** 如果当前调用栈存在事务则复用,否则创建一个 */
REQUIRED = 'REQUIRED',
}

export interface TransactionalParams {
/** 事务传播方式,默认 REQUIRED */
propagation?: PropagationType;
}
18 changes: 18 additions & 0 deletions core/transaction-decorator/src/builder/TransactionMetaBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { EggProtoImplClass } from '@eggjs/tegg';
import { TransactionMetadata } from '../model/TransactionMetadata';
import { TransactionMetadataUtil } from '../util/TransactionMetadataUtil';

export class TransactionMetaBuilder {
private readonly clazz: EggProtoImplClass;

constructor(clazz: EggProtoImplClass) {
this.clazz = clazz;
}

build(): TransactionMetadata[] {
if (!TransactionMetadataUtil.isTransactionClazz(this.clazz)) {
return [];
}
return TransactionMetadataUtil.getTransactionMetadataList(this.clazz);
}
}
19 changes: 19 additions & 0 deletions core/transaction-decorator/src/decorator/Transactional.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { EggProtoImplClass } from '@eggjs/tegg';
import { TransactionalParams, PropagationType } from '../Common';
import { TransactionMetadataUtil } from '../util/TransactionMetadataUtil';

export function Transactional(params?: TransactionalParams) {
const propagation = params?.propagation || PropagationType.REQUIRED;
if (!Object.values(PropagationType).includes(propagation)) {
throw new Error(`unknown propagation type ${propagation}`);
}

return function(target: any, propertyKey: PropertyKey) {
const constructor: EggProtoImplClass = target.constructor;
TransactionMetadataUtil.setIsTransactionClazz(constructor);
TransactionMetadataUtil.addTransactionMetadata(constructor, {
propagation,
method: propertyKey,
});
};
}
6 changes: 6 additions & 0 deletions core/transaction-decorator/src/model/TransactionMetadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { PropagationType } from '../Common';

export interface TransactionMetadata {
propagation: PropagationType;
method: PropertyKey;
}
26 changes: 26 additions & 0 deletions core/transaction-decorator/src/util/TransactionMetadataUtil.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { EggProtoImplClass, MetadataUtil } from '@eggjs/tegg';
import { TransactionMetadata } from '../model/TransactionMetadata';

export const TRANSACTION_META_DATA = Symbol.for('EggPrototype#transaction#metaData');
export const IS_TRANSACTION_CLAZZ = Symbol.for('EggPrototype#IS_TRANSACTION_CLAZZ');

export class TransactionMetadataUtil {

static setIsTransactionClazz(clazz: EggProtoImplClass) {
MetadataUtil.defineMetaData(IS_TRANSACTION_CLAZZ, true, clazz);
}

static isTransactionClazz(clazz: EggProtoImplClass): boolean {
return MetadataUtil.getBooleanMetaData(IS_TRANSACTION_CLAZZ, clazz);
}

static addTransactionMetadata(clazz: EggProtoImplClass, data: TransactionMetadata) {
const list = MetadataUtil.initOwnArrayMetaData<TransactionMetadata>(TRANSACTION_META_DATA, clazz, []);
list.push(data);
}

static getTransactionMetadataList(clazz: EggProtoImplClass): TransactionMetadata[] {
return MetadataUtil.getArrayMetaData<TransactionMetadata>(TRANSACTION_META_DATA, clazz);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import assert from 'assert';
import { TransactionMetadataUtil } from '../../src/util/TransactionMetadataUtil';
import { TransactionMetaBuilder } from '../../src/builder/TransactionMetaBuilder';
import { PropagationType } from '../../src/Common';
import { Foo, Bar, FooBar, BarFoo } from '../fixtures/transaction';
import { Transactional } from '../../src/decorator/Transactional';

describe('test/builder/TransactionMetaBuilder.test.ts', () => {

it('should build meta data success', () => {
assert.ok(TransactionMetadataUtil.isTransactionClazz(Foo));

const fooBuilder = new TransactionMetaBuilder(Foo);
assert.deepStrictEqual(fooBuilder.build(), [{
propagation: PropagationType.REQUIRED,
method: 'defaultPropagation',
}, {
propagation: PropagationType.REQUIRED,
method: 'requiredPropagation',
}, {
propagation: PropagationType.ALWAYS_NEW,
method: 'alwaysNewPropagation',
}]);

assert.ok(TransactionMetadataUtil.isTransactionClazz(Bar));
const barBuilder = new TransactionMetaBuilder(Bar);
assert.deepStrictEqual(barBuilder.build(), [{
propagation: PropagationType.REQUIRED,
method: 'foo',
}, {
propagation: PropagationType.ALWAYS_NEW,
method: 'bar',
}]);

assert.ok(TransactionMetadataUtil.isTransactionClazz(FooBar));
const fooBarBuilder = new TransactionMetaBuilder(FooBar);
assert.deepStrictEqual(fooBarBuilder.build(), [{
propagation: PropagationType.ALWAYS_NEW,
method: 'foo',
}]);

const barFooBuilder = new TransactionMetaBuilder(BarFoo);
assert.ok(!TransactionMetadataUtil.isTransactionClazz(BarFoo));
assert.deepStrictEqual(barFooBuilder.build(), []);

assert.throws(() => {
Transactional({ propagation: 'xx' as PropagationType });
}, new Error('unknown propagation type xx'));
});

});
52 changes: 52 additions & 0 deletions core/transaction-decorator/test/fixtures/transaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Transactional } from '../../src/decorator/Transactional';
import { PropagationType } from '../../src/Common';

export class Foo {

@Transactional()
async defaultPropagation(msg) {
console.log('msg: ', msg);
}

@Transactional({})
async requiredPropagation(msg) {
console.log('msg: ', msg);
}

@Transactional({ propagation: PropagationType.ALWAYS_NEW })
async alwaysNewPropagation(msg) {
console.log('msg: ', msg);
}

}

export class Bar {

@Transactional()
async foo(msg) {
console.log('msg: ', msg);
}

@Transactional({ propagation: PropagationType.ALWAYS_NEW })
async bar(msg) {
console.log('msg: ', msg);
}

}

export class FooBar {

@Transactional({ propagation: PropagationType.ALWAYS_NEW })
async foo(msg) {
console.log('msg: ', msg);
}

}

export class BarFoo {

async foo(msg) {
console.log('msg: ', msg);
}

}
11 changes: 11 additions & 0 deletions core/transaction-decorator/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"baseUrl": "./"
},
"exclude": [
"dist",
"node_modules"
]
}
12 changes: 12 additions & 0 deletions core/transaction-decorator/tsconfig.pub.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"baseUrl": "./"
},
"exclude": [
"dist",
"node_modules",
"test"
]
}

0 comments on commit 4896615

Please sign in to comment.