Angular Interceptor Chain Implementation
This challenge focuses on building a flexible interceptor chain in Angular. Interceptors are a powerful mechanism for modifying HTTP requests and responses globally, but managing multiple interceptors can become complex. This task requires you to design and implement a system that allows for easy addition, removal, and reordering of interceptors within a chain, ensuring each interceptor has the opportunity to modify requests or responses before they reach the backend or the application.
Problem Description
You need to create an Angular service that manages a chain of HTTP interceptors. This service should allow you to:
- Add Interceptors: Add new interceptor functions to the chain. Each interceptor function should accept a request object and return a modified request object (or the original if no modification is needed).
- Remove Interceptors: Remove interceptors from the chain by their index or by a unique identifier.
- Reorder Interceptors: Change the order of interceptors in the chain.
- Execute the Chain: Provide a method that takes an HTTP request object and executes the interceptor chain, applying each interceptor sequentially. The final modified request object should be returned.
- Handle Errors: If any interceptor throws an error, the chain execution should stop, and the error should be propagated.
Key Requirements:
- The interceptor chain should be implemented as an Angular service.
- Interceptors should be functions that accept and return
HttpClientRequestobjects (you can use the standard AngularHttpClientrequest object for this). - The service should provide methods for adding, removing, and reordering interceptors.
- The chain execution should be synchronous.
- The service should be testable.
Expected Behavior:
- Adding an interceptor should insert it into the chain at the specified position.
- Removing an interceptor should remove it from the chain without affecting other interceptors.
- Reordering interceptors should change their execution order.
- Executing the chain should apply each interceptor in the specified order, modifying the request object as needed.
- Error handling should prevent the chain from continuing after an error occurs.
Edge Cases to Consider:
- Empty interceptor chain.
- Adding an interceptor at an invalid index.
- Removing an interceptor that doesn't exist.
- Interceptors throwing errors.
- Multiple interceptors modifying the same property of the request object.
Examples
Example 1:
Input:
- Initial chain: []
- Add interceptor 1: (req) => ({...req, header: 'Interceptor 1'})
- Add interceptor 2: (req) => ({...req, header: 'Interceptor 2'})
- Execute chain with: {url: 'https://example.com', method: 'GET'}
Output:
{ url: 'https://example.com', method: 'GET', header: 'Interceptor 2' }
Explanation: Interceptor 1 adds 'Interceptor 1' to the header, and Interceptor 2 overwrites it with 'Interceptor 2'.
Example 2:
Input:
- Initial chain: [interceptor1, interceptor2]
- interceptor1: (req) => ({...req, method: 'POST'})
- interceptor2: (req) => ({...req, body: {data: 'test'}})
- Execute chain with: {url: 'https://example.com', method: 'GET'}
Output:
{ url: 'https://example.com', method: 'POST', body: { data: 'test' } }
Explanation: Interceptor 1 changes the method to POST, and Interceptor 2 adds a body.
Example 3: (Error Handling)
Input:
- Initial chain: [interceptor1, interceptor2]
- interceptor1: (req) => ({...req, method: 'POST'})
- interceptor2: (req) => { throw new Error("Interceptor Error"); }
- Execute chain with: {url: 'https://example.com', method: 'GET'}
Output:
Error: Interceptor Error
Explanation: Interceptor 2 throws an error, halting the chain execution and propagating the error.
Constraints
- The service must be written in TypeScript and compatible with Angular 14 or later.
- The interceptor functions should be pure functions (no side effects).
- The chain execution should be synchronous.
- The service should be designed for testability.
- The maximum number of interceptors in the chain should be limited to 10.
Notes
- Consider using an array to store the interceptors.
- Think about how to handle errors gracefully within the chain.
- Focus on creating a clean and maintainable design.
- You don't need to implement the actual HTTP request sending; this challenge focuses solely on the interceptor chain logic. You can use a mock
HttpClientRequestobject for testing. - Consider using unique identifiers for interceptors to make removal easier.