Commit c7f623a
[fit] Add comprehensive HTTP client authentication support with @requestauth annotation (#330)
* fix: Resolve parameter-applier mismatch in HTTP client authentication
Fixes critical architecture issue where static auth appliers and parameter
appliers were mixed in the same list, causing NullPointerException and
parameter count mismatch errors.
**Root Cause Analysis:**
1. NullPointerException: args=null for no-parameter methods
2. Count mismatch: staticAppliers + paramAppliers != args.length
3. Architecture flaw: mixed responsibilities in single applier list
**Solution - Separated Applier Architecture:**
- HttpInfo: Added staticAppliers and paramAppliers fields
- HttpInvocationHandler: Separated execution (static first, then param)
- AnnotationParser: Separated applier construction logic
- Added null-safety for args parameter
**Test Results:**
✅ No-parameter methods: testBearerStatic() now works
✅ Parameter methods: testBearerDynamic(token) now works
✅ All auth types: Bearer, Basic, API Key all functional
✅ Backward compatibility: existing appliers field maintained
Closes #328
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
* test: Add comprehensive unit tests for HttpInvocationHandler
Add comprehensive unit tests for HttpInvocationHandler to cover the core
issues discovered and fixed in the HTTP client authentication system:
- Test null args handling to prevent NullPointerException
- Test parameter count mismatch detection
- Test static and parameter applier separation logic
- Test HTTP info missing exception handling
- Test multi-parameter method invocation
Additionally improve code style consistency by:
- Add this. prefix to all member variable accesses
- Add proper resource management for MockitoAnnotations.openMocks()
- Add explanatory comments for uncommon Mockito patterns
These tests fill critical coverage gaps in the HttpInvocationHandler
which previously had no direct unit tests, ensuring the parameter
processing bugs discovered cannot reoccur.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
* refactor: Remove redundant appliers field from HttpInfo
Clean up the temporary backward-compatibility code by removing the unused
appliers field, which was replaced by staticAppliers and paramAppliers.
**Changes:**
- HttpInfo: Remove appliers field and its getter/setter methods
- AnnotationParser.parseMethod(): Directly set paramAppliers without temp variable
- AnnotationParser.parseInterface(): Remove backward-compatibility merge logic
- HttpInvocationHandlerTest: Remove appliers initialization from test setup
**Benefits:**
- Clearer separation of concerns between static and parameter appliers
- Reduced code redundancy and maintenance burden
- Improved code readability with more explicit variable names
**Testing:**
✅ All 7 HttpInvocationHandler tests pass
✅ No functional changes, purely structural cleanup
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
* refactor: Refactor @requestauth to reuse AuthorizationDestinationSetter mechanism
Unify the authentication architecture by making the @requestauth annotation
system reuse the existing AuthorizationDestinationSetter and Authorization
mechanisms, ensuring consistency with the FEL Tool system.
**Architecture Improvements:**
- Remove AuthDestinationSetter (duplicate implementation)
- Modify StaticAuthApplier to directly create Authorization objects
- Modify RequestAuthResolver to use AuthorizationDestinationSetter
- Both annotation-based and JSON-based systems now use the same mechanism
**Changes:**
- StaticAuthApplier: Create Authorization objects directly via factory methods
- RequestAuthResolver: Return AuthorizationDestinationSetter with appropriate keys
- Remove AuthDestinationSetter and its test file
- Update RequestAuthResolverTest to use AuthorizationDestinationSetter
**Benefits:**
- Eliminates code duplication in authentication logic
- Ensures architectural consistency across different configuration methods
- Simplifies maintenance with a single source of truth
- Better follows the DestinationSetter design pattern
**Testing:**
✅ All 225 tests pass
✅ RequestAuthResolverTest: 3/3 tests pass
✅ HttpInvocationHandlerTest: 7/7 tests pass
✅ No functional changes, purely architectural refactoring
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
* fix: Add AuthProvider support to StaticAuthApplier
Fix runtime error when using @requestauth with AuthProvider in class/method level
static authentication scenarios.
**Problem:**
- StaticAuthApplier threw UnsupportedOperationException when encountering AuthProvider
- Example 07 failed to start with "AuthProvider is not supported" error
- Provider-based auth (DynamicTokenProvider, CustomSignatureProvider, ApiKeyProvider)
was not working in static contexts
**Solution:**
- Add lazy initialization support to StaticAuthApplier
- Add setBeanContainer() method to inject BeanContainer at runtime
- Modify HttpInvocationHandler to call setBeanContainer() before applying static auth
- Cache Authorization object after creation for performance
**Implementation:**
- StaticAuthApplier: Store RequestAuth annotation and delay Authorization creation
- If no Provider: Create Authorization immediately in constructor
- If Provider used: Create Authorization when setBeanContainer() is called
- HttpInvocationHandler: Inject BeanContainer before calling staticApplier.apply()
**Testing:**
✅ All 225 tests pass
✅ HttpInvocationHandlerTest: 7/7 tests pass
✅ No breaking changes to existing functionality
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
* fix: Fix authorization field mapping bug and improve code clarity
Fix a bug in parameter-level authentication where API_KEY incorrectly mapped to
annotation.name() instead of the "value" field of ApiKeyAuthorization.
**Problem Analysis:**
1. Bug: RequestAuthResolver.getAuthorizationKey() for API_KEY returned annotation.name()
(e.g., "X-API-Key"), but should return "value" to update ApiKeyAuthorization.value field
2. Root cause: Confusion between HTTP key name and Authorization object field name
3. Why tests didn't catch it: Tests only verified Setter type, not the internal field name
**Solution: Introduce AuthFieldMapper (Approach 5)**
- Create dedicated AuthFieldMapper class for centralized field mapping logic
- Clear documentation explaining the mapping rationale
- Maintains consistency with FEL Tool system's AuthorizationDestinationSetter mechanism
**Changes:**
1. New file: AuthFieldMapper.java
- Maps AuthType to Authorization object field names
- Comprehensive Javadoc with table showing all mappings
- BEARER → "token", BASIC → "username", API_KEY → "value"
2. Modified: RequestAuthResolver.java
- Remove complex getAuthorizationKey() method
- Use AuthFieldMapper.getParameterAuthField() for clear intent
- Updated documentation
3. New tests: AuthFieldMapperTest.java (5 tests)
- Verify correct field mappings for all auth types
- Specifically test API_KEY returns "value" not "key"
- Validate compatibility with Authorization implementations
4. Enhanced: RequestAuthResolverTest.java
- Add comments explaining the bug that was found
- Tests now serve as regression prevention
**Key Insights:**
- API Key has two concepts:
* "key" field: HTTP Header/Query name (e.g., "X-API-Key")
* "value" field: Actual API key value (parameter-level updates this)
- AuthFieldMapper makes this distinction explicit and well-documented
**Testing:**
✅ All 230 tests pass (5 new tests added)
✅ AuthFieldMapperTest: 5/5 tests pass
✅ RequestAuthResolverTest: 3/3 tests pass
✅ Maintains 100% consistency with FEL Tool system
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
* docs: Fix Javadoc errors in AuthFieldMapper and RequestAuthResolver
Fix Javadoc compilation errors caused by HTML5 incompatible tags and attributes.
**Issues Fixed:**
1. Replace <h3> headings with <p><b> for consistency (H3 requires H2, H1 hierarchy)
2. Remove 'summary' attribute from table (not supported in HTML5)
3. Keep 'caption' element for table description
**Changes:**
- AuthFieldMapper.java: Replace h3 tags with p+b tags, remove summary attribute
- RequestAuthResolver.java: Replace h3 tags with p+b tags
**Verification:**
✅ mvn javadoc:javadoc compiles successfully
✅ All documentation formatting preserved
✅ HTML5 compliant
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
* feat: Support BASIC auth field selection via name attribute for parameter-level authentication
Enable parameter-level BASIC authentication to specify which field to update (username or
password) using the name attribute, allowing dual-parameter authentication scenarios.
**Core Changes:**
1. **AuthFieldMapper.java**
- Modified getParameterAuthField() to accept nameAttribute parameter
- BASIC auth: name="username" or name="password" selects field to update
- Default behavior: returns "username" when name is not specified (backward compatible)
- API_KEY: name specifies HTTP Header/Query name (different semantic)
- BEARER: name attribute is ignored
- Use StringUtils.isNotBlank() and StringUtils.equals() for string operations
- Fixed Javadoc line lengths to comply with 120-character limit
2. **RequestAuthResolver.java**
- Pass annotation.name() to AuthFieldMapper.getParameterAuthField()
- Enables BASIC auth field selection based on name attribute
3. **RequestAuth annotation**
- Enhanced Javadoc to document name attribute semantics for different auth types
- Added table showing name attribute meaning per auth type
- Clarified BASIC auth usage: name selects target field (username/password)
**Testing:**
4. **AuthFieldMapperTest.java** (added 3 new tests, total 8 tests)
- testBasicAuthFieldDefault(): Verify default returns "username"
- testBasicAuthFieldExplicitUsername(): Test name="username"
- testBasicAuthFieldPassword(): Test name="password"
- testBasicAuthFieldInvalidName(): Verify invalid name throws exception
- Updated existing tests to use new method signature
5. **Example 07 Enhancements**
- TestAuthInterface: Added testBasicDynamicUsername() and testBasicDynamicBoth()
- TestAuthClient: Implemented both methods with proper annotations
- TestAuthServerController: Added corresponding endpoints
- TestClientController: Added method invocation support
- run_tests.sh: Added two new BASIC auth test cases
**Usage Examples:**
```java
// Method-level: Complete BASIC auth, parameter overrides username
@requestauth(type = BASIC, username = "static-user", password = "static-password")
String test(@requestauth(type = BASIC) String username);
// Parameter-level: Separately override username and password
@requestauth(type = BASIC, username = "base-user", password = "base-password")
String test(
@requestauth(type = BASIC, name = "username") String user,
@requestauth(type = BASIC, name = "password") String pwd
);
```
**Key Design:**
- name attribute has semantic overloading across auth types
- BASIC: Specifies Authorization object field name
- API_KEY: Specifies HTTP Header/Query name
- Method-level BASIC auth must provide complete username+password
- Parameter-level updates specific fields via authorizationInfo()
**Validation:**
✅ All 233 tests pass
✅ Javadoc compiles without errors/warnings
✅ Code formatting complies with CodeFormatterFromIdea.xml
✅ Example 07 compiles and runs successfully
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
* refactor: Simplify StaticAuthApplier by using constructor injection for BeanContainer
Remove the setBeanContainer() method and lazy initialization pattern in StaticAuthApplier,
replacing it with direct constructor injection using the BeanContainer already available
in AnnotationParser.
**Problem:**
- StaticAuthApplier used lazy initialization with setBeanContainer() called from HttpInvocationHandler
- This created unnecessary complexity and runtime dependency injection
- AnnotationParser already had BeanContainer available but wasn't using it
- The setBeanContainer() approach required runtime checks and state management
**Solution:**
- Modified StaticAuthApplier constructor to accept BeanContainer parameter
- AnnotationParser now passes its BeanContainer when creating StaticAuthApplier instances
- Removed setBeanContainer() method entirely
- Removed lazy initialization logic and state management
- Simplified HttpInvocationHandler by removing BeanContainer injection code
**Changes:**
1. **StaticAuthApplier.java**
- Changed constructor: `StaticAuthApplier(RequestAuth, BeanContainer)`
- Removed `cachedAuthorization` field, replaced with final `authorization` field
- Authorization created immediately in constructor
- Removed `setBeanContainer()` method
- Simplified `apply()` method - no more null checks
- Reduced from 118 lines to 95 lines
2. **AnnotationParser.java**
- `getClassLevelAuthAppliers()`: Pass `this.beanContainer` to StaticAuthApplier
- `getMethodLevelAuthAppliers()`: Pass `this.beanContainer` to StaticAuthApplier
3. **HttpInvocationHandler.java**
- Removed instanceof check and setBeanContainer() call
- Simplified staticApplier loop back to basic iteration
**Benefits:**
- ✅ Simpler design: Constructor injection instead of setter injection
- ✅ Earlier error detection: Failures happen at construction time, not runtime
- ✅ Immutability: Authorization is now final, thread-safe
- ✅ Less code: Removed 31 lines of complexity
- ✅ Clear dependencies: BeanContainer dependency explicit in constructor
- ✅ Better performance: No runtime type checks or conditional initialization
**Testing:**
✅ All 233 tests pass
✅ Example 07 compiles and runs successfully
✅ No behavioral changes, purely refactoring
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
* refactor: Add fail-fast validation for BeanContainer in StaticAuthApplier
Add notNull validation at constructor entry point and simplify createAuthorizationFromAnnotation
by removing redundant null check, following the fail-fast principle.
**Changes:**
- Add `notNull(beanContainer, "The bean container cannot be null.")` in constructor
- Remove redundant null check in createAuthorizationFromAnnotation method
- BeanContainer is guaranteed non-null by AnnotationParser, validate early at entry point
**Benefits:**
- ✅ Fail-fast: Errors detected immediately at construction time
- ✅ Clearer contract: BeanContainer is required, not optional
- ✅ Simplified logic: No need for null check in private method
- ✅ Consistent validation: Follows same pattern as AnnotationParser
**Testing:**
✅ AuthFieldMapperTest: 8/8 tests pass
✅ No behavioral changes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
* docs: Add comprehensive documentation for HTTP client authentication
Add detailed usage manual and principles guide for @requestauth annotation system,
covering all authentication types, usage scenarios, design principles, and internals.
**Documentation Structure:**
1. **HTTP_CLIENT_AUTH_USAGE.md** - User-facing usage manual
- Quick start guide
- Authentication types (Bearer, Basic, API Key, Custom Provider)
- Usage scenarios (interface/method/parameter levels)
- Best practices (security, configuration, Provider patterns)
- Common Q&A
- Complete examples with HTTP request formats
2. **HTTP_CLIENT_AUTH_PRINCIPLES.md** - Internal principles guide
- Architecture overview with component diagrams
- Core components (AnnotationParser, StaticAuthApplier, AuthFieldMapper, etc.)
- Detailed workflow with execution traces
- Key design decisions (constructor injection, name attribute overloading)
- Consistency with FEL Tool system
- Extension guide for custom authentication types
**Content Highlights:**
- 📖 80+ code examples covering all scenarios
- 🎯 5 detailed execution flow diagrams
- 📊 Component responsibility matrix
- 🔧 Extension patterns for custom authentication
- ⚖️ Design decision rationales
- ✅ Best practices and anti-patterns
**Coverage:**
- All 4 authentication types: Bearer, Basic, API Key, Custom
- 3-level configuration: Interface, Method, Parameter
- Static vs dynamic (Provider) authentication
- Field update mechanism for parameter-level auth
- BASIC auth dual-parameter support with name attribute
- Integration with BeanContainer and FEL Tool system
**Target Audience:**
- Usage Manual: Developers using HTTP client authentication
- Principles Guide: Framework developers and contributors
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
---------
Co-authored-by: Claude <[email protected]>1 parent 356190c commit c7f623a
File tree
20 files changed
+2713
-372
lines changed- examples/fit-example/07-http-client-proxy
- plugin-http-client/src/main/java/modelengine/fit/example
- client
- controller
- plugin-http-server/src/main/java/modelengine/fit/example/controller
- framework/fit/java/fit-builtin
- plugins/fit-client-http/src/main/java/modelengine/fit/client/http/support
- services/fit-http-classic/definition
- src
- main/java/modelengine/fit/http
- annotation
- client/proxy
- scanner
- entity
- resolver
- support
- applier
- setter
- test/java/modelengine/fit/http/client/proxy
- scanner
- resolver
- support/setter
20 files changed
+2713
-372
lines changedLines changed: 42 additions & 23 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
30 | | - | |
31 | | - | |
32 | | - | |
33 | 30 | | |
34 | 31 | | |
35 | | - | |
36 | | - | |
37 | 32 | | |
38 | 33 | | |
39 | 34 | | |
| 35 | + | |
| 36 | + | |
40 | 37 | | |
41 | 38 | | |
42 | 39 | | |
43 | | - | |
44 | | - | |
45 | 40 | | |
46 | 41 | | |
47 | 42 | | |
| 43 | + | |
| 44 | + | |
48 | 45 | | |
49 | 46 | | |
50 | | - | |
51 | | - | |
52 | 47 | | |
53 | 48 | | |
54 | 49 | | |
| 50 | + | |
| 51 | + | |
55 | 52 | | |
56 | 53 | | |
57 | 54 | | |
58 | | - | |
59 | | - | |
60 | 55 | | |
61 | 56 | | |
62 | 57 | | |
| 58 | + | |
| 59 | + | |
63 | 60 | | |
64 | 61 | | |
65 | 62 | | |
66 | | - | |
67 | | - | |
68 | 63 | | |
69 | 64 | | |
70 | 65 | | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
71 | 69 | | |
72 | 70 | | |
73 | 71 | | |
74 | | - | |
75 | | - | |
76 | 72 | | |
77 | 73 | | |
78 | 74 | | |
| 75 | + | |
| 76 | + | |
79 | 77 | | |
80 | 78 | | |
81 | | - | |
82 | | - | |
83 | 79 | | |
84 | 80 | | |
85 | 81 | | |
| 82 | + | |
| 83 | + | |
86 | 84 | | |
87 | 85 | | |
88 | 86 | | |
89 | | - | |
90 | | - | |
91 | 87 | | |
92 | 88 | | |
93 | 89 | | |
| 90 | + | |
| 91 | + | |
94 | 92 | | |
95 | 93 | | |
96 | 94 | | |
97 | | - | |
98 | | - | |
99 | 95 | | |
100 | 96 | | |
101 | 97 | | |
| 98 | + | |
| 99 | + | |
102 | 100 | | |
103 | 101 | | |
104 | 102 | | |
105 | | - | |
106 | | - | |
107 | 103 | | |
108 | 104 | | |
109 | 105 | | |
| 106 | + | |
| 107 | + | |
110 | 108 | | |
111 | 109 | | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
112 | 131 | | |
Lines changed: 17 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
86 | 86 | | |
87 | 87 | | |
88 | 88 | | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
89 | 106 | | |
Lines changed: 8 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
107 | | - | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
108 | 110 | | |
109 | 111 | | |
110 | 112 | | |
111 | 113 | | |
112 | 114 | | |
113 | 115 | | |
114 | 116 | | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
115 | 122 | | |
116 | 123 | | |
117 | 124 | | |
| |||
Lines changed: 10 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
106 | 106 | | |
107 | 107 | | |
108 | 108 | | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
109 | 119 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
174 | 174 | | |
175 | 175 | | |
176 | 176 | | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
177 | 189 | | |
178 | 190 | | |
179 | 191 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
67 | 67 | | |
68 | 68 | | |
69 | 69 | | |
70 | | - | |
| 70 | + | |
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
| |||
0 commit comments