This commit is contained in:
Phuoc Nguyen
2025-10-17 17:22:28 +07:00
parent 2125e85d40
commit 628c81ce13
86 changed files with 31339 additions and 1710 deletions

449
lib/core/network/README.md Normal file
View File

@@ -0,0 +1,449 @@
# API Integration Infrastructure - Worker App
## Overview
Comprehensive HTTP client infrastructure built with **Dio** and **Riverpod 3.0** for the Worker Flutter application. This setup provides robust API integration with authentication, caching, retry logic, error handling, and offline support.
## Architecture
```
lib/core/network/
├── dio_client.dart # Main HTTP client with Riverpod providers
├── api_interceptor.dart # Authentication, logging, and error interceptors
├── network_info.dart # Network connectivity monitoring
├── api_constants.dart # API endpoints and configuration
├── exceptions.dart # Custom exception definitions
└── failures.dart # Domain-level failure types
```
## Key Features
### 1. Dio HTTP Client (`dio_client.dart`)
**DioClient Class**
- Wrapper around Dio with full method support (GET, POST, PUT, PATCH, DELETE)
- File upload with multipart/form-data
- File download with progress tracking
- Cache management utilities
**Riverpod Providers**
- `dioProvider` - Configured Dio instance with all interceptors
- `dioClientProvider` - DioClient wrapper instance
- `cacheStoreProvider` - Hive-based cache storage
- `cacheOptionsProvider` - Cache configuration
**Configuration**
- Base URL: Configurable per environment (dev/staging/prod)
- Timeouts: 30s connection, 30s receive, 30s send
- Headers: JSON content-type, Vietnamese language by default
- Cache: 7-day max-stale, no caching on auth errors (401, 403)
### 2. Interceptors (`api_interceptor.dart`)
#### AuthInterceptor
- **Token Injection**: Automatically adds Bearer token to requests
- **Token Refresh**: Handles 401 errors with automatic token refresh
- **Public Endpoints**: Skips auth for login/OTP/register endpoints
- **Language Header**: Adds Vietnamese language preference
- **Storage**: Uses SharedPreferences for token persistence
#### LoggingInterceptor
- **Request Logging**: Method, URL, headers, body, query parameters
- **Response Logging**: Status code, response data (truncated)
- **Error Logging**: Error type, status code, error data
- **Security**: Sanitizes sensitive fields (password, OTP, tokens)
- **Format**: Beautiful formatted logs with separators
#### ErrorTransformerInterceptor
- **Dio Error Mapping**: Transforms DioException to custom exceptions
- **Status Code Handling**:
- 400 → ValidationException/BadRequestException
- 401 → UnauthorizedException/TokenExpiredException/InvalidOTPException
- 403 → ForbiddenException
- 404 → NotFoundException
- 409 → ConflictException
- 422 → ValidationException with field errors
- 429 → RateLimitException with retry-after
- 5xx → ServerException/ServiceUnavailableException
- **Connection Errors**: Timeout, NoInternet, etc.
#### RetryInterceptor
- **Exponential Backoff**: Configurable delay multiplier
- **Max Retries**: 3 attempts by default
- **Retry Conditions**:
- Connection timeout/errors
- 5xx server errors (except 501)
- 408 Request Timeout
- 429 Too Many Requests
- **Network Check**: Verifies connectivity before retrying
### 3. Network Monitoring (`network_info.dart`)
**NetworkInfo Interface**
- Connection status checking
- Connection type detection (WiFi, Mobile, Ethernet, etc.)
- Real-time connectivity monitoring via Stream
**NetworkStatus Class**
- Connection state (connected/disconnected)
- Connection type
- Timestamp
- Convenience methods (isWiFi, isMobile, isMetered)
**Riverpod Providers**
- `networkInfoProvider` - NetworkInfo implementation
- `isConnectedProvider` - Current connection status
- `connectionTypeProvider` - Current connection type
- `networkStatusStreamProvider` - Stream of status changes
- `NetworkStatusNotifier` - Reactive network status state
### 4. Error Handling
**Exceptions (`exceptions.dart`)**
- NetworkException - Base network error
- NoInternetException - No connectivity
- TimeoutException - Connection timeout
- ServerException - 5xx errors
- ServiceUnavailableException - 503 errors
- AuthException - Authentication errors (401, 403)
- ValidationException - Request validation errors
- NotFoundException - 404 errors
- ConflictException - 409 errors
- RateLimitException - 429 errors
- PaymentException - Payment-related errors
- CacheException - Cache errors
- StorageException - Local storage errors
- ParseException - JSON parsing errors
**Failures (`failures.dart`)**
- Immutable Freezed classes for domain-level errors
- User-friendly Vietnamese error messages
- Properties:
- `message` - Display message
- `isCritical` - Requires immediate attention
- `canRetry` - Can be retried
- `statusCode` - HTTP status if available
### 5. API Constants (`api_constants.dart`)
**Configuration**
- Base URLs (dev, staging, production)
- API version prefix (/v1)
- Timeout durations (30s)
- Retry configuration (3 attempts, exponential backoff)
- Cache durations (24h products, 1h profile, 48h categories)
- Request headers (JSON, Vietnamese language)
**Endpoints**
- Authentication: /auth/request-otp, /auth/verify-otp, /auth/register, etc.
- Loyalty: /loyalty/points, /loyalty/rewards, /loyalty/referral, etc.
- Products: /products, /products/search, /categories, etc.
- Orders: /orders, /payments, etc.
- Projects & Quotes: /projects, /quotes, etc.
- Chat: /chat/messages, /ws/chat (WebSocket)
- Account: /profile, /addresses, etc.
- Promotions & Notifications
## Usage Examples
### Basic GET Request
```dart
// Using DioClient with Riverpod
final dioClient = ref.watch(dioClientProvider);
try {
final response = await dioClient.get(
ApiConstants.getProducts,
queryParameters: {'page': '1', 'limit': '20'},
);
final products = response.data;
} on NoInternetException catch (e) {
// Handle no internet
} on ServerException catch (e) {
// Handle server error
}
```
### POST Request with Authentication
```dart
final dioClient = ref.watch(dioClientProvider);
try {
final response = await dioClient.post(
ApiConstants.createOrder,
data: {
'items': [...],
'deliveryAddress': {...},
'paymentMethod': 'COD',
},
);
final order = Order.fromJson(response.data);
} on ValidationException catch (e) {
// Handle validation errors
print(e.errors); // Map<String, List<String>>
}
```
### File Upload
```dart
final dioClient = ref.watch(dioClientProvider);
final formData = FormData.fromMap({
'name': 'John Doe',
'avatar': await MultipartFile.fromFile(
filePath,
filename: 'avatar.jpg',
),
});
try {
final response = await dioClient.uploadFile(
ApiConstants.uploadAvatar,
formData: formData,
onSendProgress: (sent, total) {
print('Upload progress: ${(sent / total * 100).toStringAsFixed(0)}%');
},
);
} catch (e) {
// Handle error
}
```
### Network Status Monitoring
```dart
// Check current connection status
final isConnected = await ref.watch(isConnectedProvider.future);
if (!isConnected) {
// Show offline message
}
// Listen to connection changes
ref.listen(
networkStatusStreamProvider,
(previous, next) {
next.whenData((status) {
if (status.isConnected) {
// Back online - sync data
} else {
// Offline - show message
}
});
},
);
```
### Cache Management
```dart
final dioClient = ref.watch(dioClientProvider);
// Clear all cache
await dioClient.clearCache();
// Clear specific endpoint cache
await dioClient.clearCacheByPath(ApiConstants.getProducts);
// Force refresh from network
final response = await dioClient.get(
ApiConstants.getProducts,
options: ApiRequestOptions.forceNetwork.toDioOptions(),
);
// Use cache-first strategy
final response = await dioClient.get(
ApiConstants.getCategories,
options: ApiRequestOptions.cached.toDioOptions(),
);
```
### Custom Error Handling
```dart
try {
final response = await dioClient.post(...);
} on ValidationException catch (e) {
// Show field-specific errors
e.errors?.forEach((field, messages) {
print('$field: ${messages.join(", ")}');
});
} on RateLimitException catch (e) {
// Show rate limit message
if (e.retryAfter != null) {
print('Try again in ${e.retryAfter} seconds');
}
} on TokenExpiredException catch (e) {
// Token refresh failed - redirect to login
ref.read(authProvider.notifier).logout();
} catch (e) {
// Generic error
print('Error: $e');
}
```
## Dependencies
```yaml
dependencies:
dio: ^5.4.3+1 # HTTP client
connectivity_plus: ^6.0.3 # Network monitoring
pretty_dio_logger: ^1.3.1 # Request/response logging
dio_cache_interceptor: ^3.5.0 # Response caching
dio_cache_interceptor_hive_store: ^3.2.2 # Hive storage for cache
flutter_riverpod: ^3.0.0 # State management
riverpod_annotation: ^3.0.0 # Code generation
shared_preferences: ^2.2.3 # Token storage
path_provider: ^2.1.3 # Cache directory
freezed_annotation: ^3.0.0 # Immutable models
```
## Configuration
### Environment-Specific Base URLs
Update `ApiConstants.baseUrl` based on build flavor:
```dart
// For dev environment
static const String baseUrl = devBaseUrl;
// For production
static const String baseUrl = prodBaseUrl;
```
### Timeout Configuration
Adjust timeouts in `ApiConstants`:
```dart
static const Duration connectionTimeout = Duration(milliseconds: 30000);
static const Duration receiveTimeout = Duration(milliseconds: 30000);
static const Duration sendTimeout = Duration(milliseconds: 30000);
```
### Retry Configuration
Customize retry behavior in `ApiConstants`:
```dart
static const int maxRetryAttempts = 3;
static const Duration initialRetryDelay = Duration(milliseconds: 1000);
static const Duration maxRetryDelay = Duration(milliseconds: 5000);
static const double retryDelayMultiplier = 2.0;
```
### Cache Configuration
Adjust cache settings in `cacheOptionsProvider`:
```dart
CacheOptions(
store: store,
maxStale: const Duration(days: 7),
hitCacheOnErrorExcept: [401, 403],
priority: CachePriority.high,
allowPostMethod: false,
);
```
## Testing
### Connection Testing
```dart
// Test network connectivity
final networkInfo = ref.watch(networkInfoProvider);
final isConnected = await networkInfo.isConnected;
final connectionType = await networkInfo.connectionType;
print('Connected: $isConnected');
print('Type: ${connectionType.displayNameVi}');
```
### API Endpoint Testing
```dart
// Test authentication endpoint
try {
final response = await dioClient.post(
ApiConstants.requestOtp,
data: {'phone': '+84912345678'},
);
print('OTP sent successfully');
} catch (e) {
print('Failed: $e');
}
```
## Best Practices
1. **Always use DioClient**: Don't create raw Dio instances
2. **Handle specific exceptions**: Catch specific error types for better UX
3. **Check connectivity**: Verify network status before critical requests
4. **Use cache strategically**: Cache static data (categories, products)
5. **Monitor network changes**: Listen to connectivity stream for sync
6. **Clear cache appropriately**: Clear on logout, version updates
7. **Log in debug only**: Disable logging in production
8. **Sanitize sensitive data**: Never log passwords, tokens, OTP codes
9. **Use retry wisely**: Don't retry POST/PUT/DELETE by default
10. **Validate responses**: Check response.data structure before parsing
## Future Enhancements
- [ ] Offline request queue implementation
- [ ] Request deduplication
- [ ] GraphQL support
- [ ] WebSocket integration for real-time chat
- [ ] Certificate pinning for security
- [ ] Request compression (gzip)
- [ ] Multi-part upload progress
- [ ] Background sync when network restored
- [ ] Advanced caching strategies (stale-while-revalidate)
- [ ] Request cancellation tokens
## Troubleshooting
### Issue: Token Refresh Loop
**Solution**: Check refresh token expiry and clear auth data if expired
### Issue: Cache Not Working
**Solution**: Verify CacheStore initialization and directory permissions
### Issue: Network Detection Fails
**Solution**: Add required permissions to AndroidManifest.xml and Info.plist
### Issue: Timeout on Large Files
**Solution**: Increase timeout or use download with progress callback
### Issue: Interceptor Order Matters
**Current Order**:
1. Logging (first - logs everything)
2. Auth (adds tokens)
3. Cache (caches responses)
4. Retry (retries failures)
5. Error Transformer (last - transforms errors)
## Support
For issues or questions about the API integration:
- Check logs for detailed error information
- Verify network connectivity using NetworkInfo
- Review interceptor configuration
- Check API endpoint constants
---
**Generated for Worker App**
Version: 1.0.0
Last Updated: 2025-10-17