336 lines
18 KiB
Markdown
336 lines
18 KiB
Markdown
# Partner Authentication Refactoring - Visual Guide
|
|
|
|
## Before vs After Architecture
|
|
|
|
### ❌ Before Refactoring
|
|
|
|
```
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ testPartnerAuth_post() Controller │
|
|
│ (Testing authentication) │
|
|
└────────────────────┬─────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ authenticateAndCache() │
|
|
│ ┌────────────────────────────────────────────┐ │
|
|
│ │ 1. Make API call to SatLoc │ │
|
|
│ │ 2. Validate response │ │
|
|
│ │ 3. Build auth data │ │
|
|
│ │ 4. Cache in Redis ← ⚠️ SIDE EFFECT │ │
|
|
│ │ 5. Return auth data │ │
|
|
│ └────────────────────────────────────────────┘ │
|
|
└──────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌────────────────┐
|
|
│ Redis Cache │ ← ⚠️ Polluted during testing
|
|
└────────────────┘
|
|
|
|
⚠️ Problems:
|
|
- Testing pollutes cache
|
|
- Tight coupling of concerns
|
|
- Side effects in test endpoints
|
|
```
|
|
|
|
### ✅ After Refactoring
|
|
|
|
```
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ testPartnerAuth_post() Controller │
|
|
│ (Testing authentication) │
|
|
└────────────────────┬─────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ authenticate() │
|
|
│ ┌────────────────────────────────────────────┐ │
|
|
│ │ 1. Make API call to SatLoc │ │
|
|
│ │ 2. Validate response │ │
|
|
│ │ 3. Build auth data │ │
|
|
│ │ 4. Return auth data │ │
|
|
│ │ │ │
|
|
│ │ ✅ NO CACHING - Pure function │ │
|
|
│ └────────────────────────────────────────────┘ │
|
|
└──────────────────────────────────────────────────────────┘
|
|
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ Production Flow │
|
|
└────────────────────┬─────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ authenticateAndCache() │
|
|
│ ┌────────────────────────────────────────────┐ │
|
|
│ │ 1. Call authenticate() ← REUSES │ │
|
|
│ │ 2. Cache result in Redis │ │
|
|
│ │ 3. Return auth data │ │
|
|
│ └────────────────────────────────────────────┘ │
|
|
└──────────────────────┬───────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌────────────────┐
|
|
│ Redis Cache │ ← ✅ Only cached in production
|
|
└────────────────┘
|
|
|
|
✅ Benefits:
|
|
- Separation of concerns
|
|
- No cache pollution during testing
|
|
- Code reusability
|
|
- Single source of truth
|
|
```
|
|
|
|
## Method Comparison
|
|
|
|
### authenticate() - Pure Authentication
|
|
|
|
```
|
|
┌─────────────────────────────────────────────┐
|
|
│ authenticate(credentials) │
|
|
├─────────────────────────────────────────────┤
|
|
│ │
|
|
│ INPUT: │
|
|
│ • credentials { username, password } │
|
|
│ • customerId (for logging) │
|
|
│ │
|
|
│ PROCESS: │
|
|
│ 1. Call SatLoc API /AuthenticateAPIUser │
|
|
│ 2. Parse response │
|
|
│ 3. Build auth data structure │
|
|
│ 4. Return auth data │
|
|
│ │
|
|
│ OUTPUT: │
|
|
│ { │
|
|
│ userId: "...", │
|
|
│ companyId: "...", │
|
|
│ expiresAt: 1234567890, │
|
|
│ lastHealthCheck: 1234567890, │
|
|
│ originalResponse: {...} │
|
|
│ } │
|
|
│ │
|
|
│ SIDE EFFECTS: None ✅ │
|
|
│ │
|
|
└─────────────────────────────────────────────┘
|
|
```
|
|
|
|
### authenticateAndCache() - With Caching
|
|
|
|
```
|
|
┌─────────────────────────────────────────────┐
|
|
│ authenticateAndCache(credentials) │
|
|
├─────────────────────────────────────────────┤
|
|
│ │
|
|
│ INPUT: │
|
|
│ • credentials { username, password } │
|
|
│ • customerId (for caching key) │
|
|
│ │
|
|
│ PROCESS: │
|
|
│ 1. Call authenticate() ← REUSES │
|
|
│ └─ Returns auth data │
|
|
│ 2. Calculate TTL from expiresAt │
|
|
│ 3. Store in Redis with TTL │
|
|
│ 4. Return auth data │
|
|
│ │
|
|
│ OUTPUT: │
|
|
│ { │
|
|
│ userId: "...", │
|
|
│ companyId: "...", │
|
|
│ expiresAt: 1234567890, │
|
|
│ lastHealthCheck: 1234567890, │
|
|
│ originalResponse: {...} │
|
|
│ } │
|
|
│ │
|
|
│ SIDE EFFECTS: │
|
|
│ • Stores auth data in Redis ✅ │
|
|
│ • Sets TTL on cache entry ✅ │
|
|
│ │
|
|
└─────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Usage Decision Tree
|
|
|
|
```
|
|
Need to authenticate?
|
|
│
|
|
▼
|
|
┌───────────────────────┐
|
|
│ What's the use case? │
|
|
└───────────┬───────────┘
|
|
│
|
|
┌───────────────┼───────────────┐
|
|
│ │
|
|
▼ ▼
|
|
┌───────────────┐ ┌───────────────┐
|
|
│ Testing? │ │ Production? │
|
|
│ Validation? │ │ Normal flow? │
|
|
└───────┬───────┘ └───────┬───────┘
|
|
│ │
|
|
▼ ▼
|
|
┌────────────────────┐ ┌────────────────────┐
|
|
│ authenticate() │ │authenticateAndCache│
|
|
│ │ │ │
|
|
│ ✅ No caching │ │ ✅ With caching │
|
|
│ ✅ Pure function │ │ ✅ Reuses auth() │
|
|
│ ✅ For testing │ │ ✅ For production │
|
|
└────────────────────┘ └────────────────────┘
|
|
```
|
|
|
|
## Code Examples
|
|
|
|
### Example 1: Testing Endpoint
|
|
|
|
```javascript
|
|
// controllers/partner.js
|
|
|
|
async function testPartnerAuth_post(req, res) {
|
|
const { customerId, partnerId, username, password } = req.body;
|
|
|
|
// Validate partner system user
|
|
const partnerSystemUser = await findAndValidatePartnerSystemUser(
|
|
customerId, partnerId, { requireActive: true, username, password }
|
|
);
|
|
|
|
// Get partner service
|
|
const partnerService = partnerServiceFactory.getService(partnerCode);
|
|
const credentials = partnerConfig.getApiCredentials(partnerSystemUser, partnerCode);
|
|
|
|
// ✅ Use authenticate() - no caching for testing
|
|
const authResult = await partnerService.authenticate(credentials, customerId);
|
|
|
|
res.json({
|
|
authSuccess: true,
|
|
userId: authResult.userId,
|
|
companyId: authResult.companyId
|
|
});
|
|
}
|
|
```
|
|
|
|
### Example 2: Production Flow
|
|
|
|
```javascript
|
|
// services/partner_sync_service.js
|
|
|
|
async function syncPartnerData(customerId, partnerId) {
|
|
const partnerService = partnerServiceFactory.getService('SATLOC');
|
|
const credentials = getCredentials(customerId);
|
|
|
|
// ✅ Use authenticateAndCache() - with caching for production
|
|
const authData = await partnerService.authenticateAndCache(credentials, customerId);
|
|
|
|
// Use authData for API calls
|
|
const aircraftList = await partnerService.getAircraftList(authData);
|
|
|
|
return aircraftList;
|
|
}
|
|
```
|
|
|
|
### Example 3: Getting Cached or Fresh Auth
|
|
|
|
```javascript
|
|
// services/satloc_service.js
|
|
|
|
async getApiCredentials(credentials, customerId) {
|
|
// Check cache first
|
|
const cached = await this.cache.getAuth(this.partnerCode, customerId);
|
|
|
|
if (cached && cached.expiresAt > Date.now()) {
|
|
// ✅ Return cached auth
|
|
return cached;
|
|
}
|
|
|
|
// Cache miss - authenticate and cache
|
|
// ✅ Uses authenticateAndCache() internally
|
|
const authData = await this.authenticateAndCache(credentials, customerId);
|
|
|
|
return authData;
|
|
}
|
|
```
|
|
|
|
## Sequence Diagrams
|
|
|
|
### Testing Flow (No Caching)
|
|
|
|
```
|
|
┌──────────┐ ┌────────────┐ ┌─────────┐ ┌───────────┐
|
|
│ Client │ │ Controller │ │ Service │ │ SatLoc API│
|
|
└────┬─────┘ └─────┬──────┘ └────┬────┘ └─────┬─────┘
|
|
│ │ │ │
|
|
│ POST testAuth │ │ │
|
|
│──────────────>│ │ │
|
|
│ │ │ │
|
|
│ │ authenticate()│ │
|
|
│ │──────────────>│ │
|
|
│ │ │ │
|
|
│ │ │ GET /Auth... │
|
|
│ │ │─────────────>│
|
|
│ │ │ │
|
|
│ │ │ Success │
|
|
│ │ │<─────────────│
|
|
│ │ │ │
|
|
│ │ authData │ │
|
|
│ │<──────────────│ │
|
|
│ │ │ │
|
|
│ 200 OK │ │ │
|
|
│<──────────────│ │ │
|
|
│ │ │ │
|
|
|
|
✅ NO CACHE OPERATIONS - Clean test
|
|
```
|
|
|
|
### Production Flow (With Caching)
|
|
|
|
```
|
|
┌──────────┐ ┌─────────┐ ┌─────────┐ ┌───────────┐ ┌───────┐
|
|
│ Sync Job │ │ Service │ │ Service │ │ SatLoc API│ │ Redis │
|
|
└────┬─────┘ └────┬────┘ └────┬────┘ └─────┬─────┘ └───┬───┘
|
|
│ │ │ │ │
|
|
│ sync data │ │ │ │
|
|
│─────────────>│ │ │ │
|
|
│ │ │ │ │
|
|
│ │authenticateAn│ │ │
|
|
│ │dCache() │ │ │
|
|
│ │─────────────>│ │ │
|
|
│ │ │ │ │
|
|
│ │ │authenticate()│ │
|
|
│ │ │─────────────>│ │
|
|
│ │ │ │ │
|
|
│ │ │ GET /Auth... │ │
|
|
│ │ │─────────────>│ │
|
|
│ │ │ │ │
|
|
│ │ │ Success │ │
|
|
│ │ │<─────────────│ │
|
|
│ │ │ │ │
|
|
│ │ │ authData │ │
|
|
│ │ │<─────────────│ │
|
|
│ │ │ │ │
|
|
│ │ │ setAuth() │ │
|
|
│ │ │──────────────────────────>│
|
|
│ │ │ │ │
|
|
│ │ │ OK │ │
|
|
│ │ │<──────────────────────────│
|
|
│ │ │ │ │
|
|
│ │ authData │ │ │
|
|
│ │<─────────────│ │ │
|
|
│ │ │ │ │
|
|
│ data synced │ │ │ │
|
|
│<─────────────│ │ │ │
|
|
│ │ │ │ │
|
|
|
|
✅ CACHED FOR REUSE - Efficient production flow
|
|
```
|
|
|
|
## Benefits Summary
|
|
|
|
| Aspect | Before | After |
|
|
|--------|--------|-------|
|
|
| **Test Isolation** | ❌ Tests pollute cache | ✅ Tests are isolated |
|
|
| **Code Reuse** | ❌ Duplicated logic | ✅ Shared authenticate() |
|
|
| **Maintainability** | ❌ Coupled concerns | ✅ Separated concerns |
|
|
| **Debugging** | ❌ Cache interference | ✅ Clear flow |
|
|
| **Performance** | ❌ Unnecessary caching | ✅ Cache only when needed |
|
|
| **Testing** | ❌ Complex mocking | ✅ Simple unit tests |
|
|
|
|
---
|
|
|
|
This refactoring provides a cleaner, more maintainable architecture while maintaining full backward compatibility!
|