Skip to content

Commit b3bf9ca

Browse files
authored
refactor(exception): exception filter need to handle inheritance of error clazz (#255)
1 parent 43649d0 commit b3bf9ca

File tree

6 files changed

+63
-12
lines changed

6 files changed

+63
-12
lines changed

src/exception/utils.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { Constructable, Container } from '@artus/injection';
33
import { EXCEPTION_FILTER_DEFAULT_SYMBOL, EXCEPTION_FILTER_MAP_INJECT_ID } from './constant';
44
import { ArtusStdError } from './impl';
55
import { ExceptionFilterMapType, ExceptionFilterType } from './types';
6+
import { isClass } from '../utils/is';
7+
68

79
export const matchExceptionFilterClazz = (err: Error, container: Container): Constructable<ExceptionFilterType> | null => {
810
const filterMap: ExceptionFilterMapType = container.get(EXCEPTION_FILTER_MAP_INJECT_ID, {
@@ -11,18 +13,27 @@ export const matchExceptionFilterClazz = (err: Error, container: Container): Co
1113
if (!filterMap) {
1214
return null;
1315
}
14-
let targetFilterClazz: Constructable<ExceptionFilterType> | null = null;
16+
17+
// handle ArtusStdError with code simply
1518
if (err instanceof ArtusStdError && filterMap.has(err.code)) {
16-
// handle ArtusStdError with code simply
17-
targetFilterClazz = filterMap.get(err.code);
18-
} else if (filterMap.has(err['constructor'] as Constructable<Error>)) {
19-
// handle CustomErrorClazz
20-
targetFilterClazz = filterMap.get(err['constructor'] as Constructable<Error>);
21-
} else if (filterMap.has(EXCEPTION_FILTER_DEFAULT_SYMBOL)) {
22-
// handle default ExceptionFilter
23-
targetFilterClazz = filterMap.get(EXCEPTION_FILTER_DEFAULT_SYMBOL);
19+
return filterMap.get(err.code);
20+
}
21+
22+
// handle CustomErrorClazz, loop inherit class
23+
let errConstructor: Function = err['constructor'];
24+
while(isClass(errConstructor)) { // until parent/self is not class
25+
if (filterMap.has(errConstructor as Constructable<Error>)) {
26+
return filterMap.get(errConstructor as Constructable<Error>);
27+
}
28+
errConstructor = Object.getPrototypeOf(errConstructor); // get parent clazz by prototype
2429
}
25-
return targetFilterClazz;
30+
31+
// handle default ExceptionFilter
32+
if (filterMap.has(EXCEPTION_FILTER_DEFAULT_SYMBOL)) {
33+
return filterMap.get(EXCEPTION_FILTER_DEFAULT_SYMBOL);
34+
}
35+
36+
return null;
2637
};
2738

2839
export const matchExceptionFilter = (err: Error, container: Container): ExceptionFilterType | null => {

test/exception_filter.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ describe('test/exception_filter.test.ts', () => {
2727
['custom', 'TestCustomError'],
2828
['wrapped', 'APP:WRAPPED_ERROR'],
2929
['APP:TEST_ERROR', 'APP:TEST_ERROR'],
30+
['inherit', 'TestInheritError'],
31+
['defaultInherit', 'TestDefaultInheritError'],
3032
]) {
3133
const mockErrorService = app.container.get(MockErrorService);
3234
try {

test/fixtures/exception_filter/bootstrap.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ async function main() {
2424
"TestAppCodeExceptionHandler",
2525
"TestWrappedExceptionHandler",
2626
"TestCustomExceptionHandler",
27+
"TestDefaultInheritExceptionHandler",
28+
"TestInheritExceptionHandler",
2729
],
2830
},
2931
source: "app",

test/fixtures/exception_filter/error.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,15 @@ export class TestWrappedError extends ArtusStdError {
1212
export class TestCustomError extends Error {
1313
name = 'TestCustomError';
1414
}
15+
16+
export class TestDefaultInheritError extends Error {
17+
name = 'TestDefaultInheritError';
18+
}
19+
20+
export class TestBaseError extends Error {
21+
name = 'TestBaseError';
22+
}
23+
24+
export class TestInheritError extends TestBaseError {
25+
name = 'TestInheritError';
26+
}

test/fixtures/exception_filter/filter.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ArtusStdError, Catch, Inject } from '../../../src';
22
import { ExceptionFilterType } from '../../../src/exception/types';
3-
import { TestCustomError, TestWrappedError } from './error';
3+
import { TestBaseError, TestCustomError, TestWrappedError } from './error';
44

55
@Catch()
66
export class TestDefaultExceptionHandler implements ExceptionFilterType {
@@ -41,3 +41,23 @@ export class TestCustomExceptionHandler implements ExceptionFilterType {
4141
this.mockSet.add(err.name);
4242
}
4343
}
44+
45+
@Catch(Error)
46+
export class TestDefaultInheritExceptionHandler implements ExceptionFilterType {
47+
@Inject('mock_exception_set')
48+
mockSet: Set<string>;
49+
50+
async catch(err: Error) {
51+
this.mockSet.add(err.name);
52+
}
53+
}
54+
55+
@Catch(TestBaseError)
56+
export class TestInheritExceptionHandler implements ExceptionFilterType {
57+
@Inject('mock_exception_set')
58+
mockSet: Set<string>;
59+
60+
async catch(err: TestBaseError) {
61+
this.mockSet.add(err.name);
62+
}
63+
}

test/fixtures/exception_filter/service.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Injectable } from '@artus/injection';
22
import { ArtusStdError } from '../../../src';
3-
import { TestCustomError, TestWrappedError } from './error';
3+
import { TestCustomError, TestDefaultInheritError, TestInheritError, TestWrappedError } from './error';
44

55
@Injectable()
66
export default class MockErrorService {
@@ -13,6 +13,10 @@ export default class MockErrorService {
1313
throw new TestCustomError();
1414
case "wrapped":
1515
throw new TestWrappedError();
16+
case "inherit":
17+
throw new TestInheritError();
18+
case "defaultInherit":
19+
throw new TestDefaultInheritError();
1620
default:
1721
throw new ArtusStdError(target);
1822
}

0 commit comments

Comments
 (0)