-
-
Notifications
You must be signed in to change notification settings - Fork 285
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(di): add support chain interceptor on the same method #2915
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,6 @@ import {catchError} from "@tsed/core"; | |
import {DITest} from "../../node/index.js"; | ||
import {InterceptorContext} from "../interfaces/InterceptorContext.js"; | ||
import {InterceptorMethods} from "../interfaces/InterceptorMethods.js"; | ||
import {InjectorService} from "../services/InjectorService.js"; | ||
import {getInterceptorOptions, Intercept} from "./intercept.js"; | ||
import {Interceptor} from "./interceptor.js"; | ||
import {Service} from "./service.js"; | ||
|
@@ -14,7 +13,17 @@ class MyInterceptor implements InterceptorMethods { | |
const r = typeof context.args[0] === "string" ? undefined : new Error(`Error message`); | ||
const retValue = context.next(r); | ||
|
||
return `${retValue} - ${context.options || ""} - intercepted`; | ||
return `${retValue} - ${context.options || ""} - intercepted 1`; | ||
} | ||
} | ||
|
||
@Interceptor() | ||
class MyInterceptor2 implements InterceptorMethods { | ||
intercept(context: InterceptorContext<any>) { | ||
const r = typeof context.args[0] === "string" ? undefined : new Error(`Error message`); | ||
const retValue = context.next(r); | ||
|
||
return `${retValue} - ${context.options || ""} - intercepted 2`; | ||
} | ||
} | ||
|
||
|
@@ -24,6 +33,18 @@ class ServiceTest { | |
exec(param: string) { | ||
return `Original data - ${param}`; | ||
} | ||
|
||
@Intercept(MyInterceptor, "options data") | ||
@Intercept(MyInterceptor2, "options data") | ||
chained(param: string) { | ||
return `Chained - ${param}`; | ||
} | ||
Romakita marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
@Intercept(MyInterceptor2, "options data") | ||
@Intercept(MyInterceptor, "options data") | ||
reverseChained(param: string) { | ||
return `Chained - ${param}`; | ||
} | ||
} | ||
|
||
@Service() | ||
|
@@ -49,7 +70,31 @@ describe("@Intercept", () => { | |
expect(getInterceptorOptions(ServiceTest, "exec")).toEqual("options data"); | ||
|
||
// THEN | ||
expect(result).toEqual("Original data - param data - options data - intercepted"); | ||
expect(result).toEqual("Original data - param data - options data - intercepted 1"); | ||
}); | ||
it("should chained interceptor", async () => { | ||
// GIVEN | ||
const serviceTest = await DITest.invoke<ServiceTest>(ServiceTest)!; | ||
|
||
// WHEN | ||
const result = serviceTest.chained("param data"); | ||
|
||
expect(getInterceptorOptions(ServiceTest, "chained")).toEqual("options data"); | ||
|
||
// THEN | ||
expect(result).toEqual("Chained - param data - options data - intercepted 2 - options data - intercepted 1"); | ||
}); | ||
it("should chained interceptor (reversed)", async () => { | ||
// GIVEN | ||
const serviceTest = await DITest.invoke<ServiceTest>(ServiceTest)!; | ||
|
||
// WHEN | ||
const result = serviceTest.reverseChained("param data"); | ||
|
||
expect(getInterceptorOptions(ServiceTest, "reverseChained")).toEqual("options data"); | ||
|
||
// THEN | ||
expect(result).toEqual("Chained - param data - options data - intercepted 1 - options data - intercepted 2"); | ||
Comment on lines
+75
to
+97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enhance test coverage for interceptor chaining While the basic chaining tests are good, consider adding:
Example additional test: it("should handle errors in middle of chain", async () => {
// GIVEN
const serviceTest = await DITest.invoke<ServiceTest>(ServiceTest, [
{
token: MyInterceptor2,
use: {
intercept: vi.fn().mockImplementation(() => {
throw new Error("Chain error");
})
}
}
]);
// WHEN
const error = catchError(() => serviceTest.chained("param data"));
// THEN
expect(error?.message).toEqual("Chain error");
}); 🧰 Tools🪛 Biome (1.9.4)[error] 77-77: Forbidden non-null assertion. (lint/style/noNonNullAssertion) [error] 89-89: Forbidden non-null assertion. (lint/style/noNonNullAssertion) |
||
}); | ||
it("should intercept the method and mock interceptor", async () => { | ||
// GIVEN | ||
|
@@ -88,7 +133,7 @@ describe("@Intercept", () => { | |
const result = serviceTest.exec("param data"); | ||
|
||
// THEN | ||
expect(result).toEqual("Original data - param data - options data - intercepted"); | ||
expect(result).toEqual("Original data - param data - options data - intercepted 1"); | ||
}); | ||
it("should intercept the method and throw error", async () => { | ||
// GIVEN | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,4 +1,4 @@ | ||||||||||||||||||||||||
import {classOf, decorateMethodsOf, DecoratorParameters, decoratorTypeOf, DecoratorTypes, Store, Type} from "@tsed/core"; | ||||||||||||||||||||||||
import {classOf, decorateMethodsOf, DecoratorParameters, decoratorTypeOf, DecoratorTypes, nameOf, Store, Type} from "@tsed/core"; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
import {DI_INTERCEPTOR_OPTIONS, DI_INVOKE_OPTIONS} from "../constants/constants.js"; | ||||||||||||||||||||||||
import {inject} from "../fn/inject.js"; | ||||||||||||||||||||||||
|
@@ -19,7 +19,7 @@ export function bindIntercept(target: any, propertyKey: string | symbol, token: | |||||||||||||||||||||||
|
||||||||||||||||||||||||
Store.fromMethod(klass, propertyKey).set(DI_INTERCEPTOR_OPTIONS, options); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
descriptor!.value = function (...args: any[]) { | ||||||||||||||||||||||||
function newMethod(...args: any[]) { | ||||||||||||||||||||||||
const next = (err?: Error) => { | ||||||||||||||||||||||||
if (!err) { | ||||||||||||||||||||||||
return originalMethod.apply(this, args); | ||||||||||||||||||||||||
|
@@ -50,7 +50,12 @@ export function bindIntercept(target: any, propertyKey: string | symbol, token: | |||||||||||||||||||||||
}, | ||||||||||||||||||||||||
next | ||||||||||||||||||||||||
); | ||||||||||||||||||||||||
}; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
descriptor!.value = newMethod; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
Reflect.deleteProperty(klass.prototype, propertyKey); | ||||||||||||||||||||||||
Reflect.defineProperty(klass.prototype, propertyKey, descriptor!); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
Comment on lines
+55
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider preserving method properties during replacement The current implementation might lose method metadata during property replacement. Consider using - Reflect.deleteProperty(klass.prototype, propertyKey);
- Reflect.defineProperty(klass.prototype, propertyKey, descriptor!);
+ const originalDescriptor = Object.getOwnPropertyDescriptor(klass.prototype, propertyKey);
+ Reflect.defineProperty(klass.prototype, propertyKey, {
+ ...originalDescriptor,
+ value: newMethod
+ }); 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Biome (1.9.4)[error] 55-55: Forbidden non-null assertion. (lint/style/noNonNullAssertion) [error] 58-58: Forbidden non-null assertion. (lint/style/noNonNullAssertion) |
||||||||||||||||||||||||
return descriptor; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Consider adding validation in MyInterceptor2
MyInterceptor2 duplicates error checking logic from MyInterceptor. Consider extracting common validation into a base class or utility function.