131 lines
4.8 KiB
Dart
131 lines
4.8 KiB
Dart
// lib/services/retry_service.dart
|
|
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:environment_monitoring_app/services/api_service.dart';
|
|
import 'package:environment_monitoring_app/services/base_api_service.dart';
|
|
import 'package:environment_monitoring_app/services/ftp_service.dart';
|
|
|
|
/// A dedicated service to manage the queue of failed API and FTP requests
|
|
/// for manual resubmission.
|
|
class RetryService {
|
|
// Use singleton instances to avoid re-creating services unnecessarily.
|
|
final DatabaseHelper _dbHelper = DatabaseHelper();
|
|
final BaseApiService _baseApiService = BaseApiService();
|
|
final FtpService _ftpService = FtpService();
|
|
|
|
/// Adds a failed API request to the local database queue.
|
|
Future<void> addApiToQueue({
|
|
required String endpoint,
|
|
required String method, // e.g., 'POST' or 'POST_MULTIPART'
|
|
Map<String, dynamic>? body,
|
|
Map<String, String>? fields,
|
|
Map<String, File>? files,
|
|
}) async {
|
|
// We must convert File objects to their string paths before saving to JSON.
|
|
final serializableFiles = files?.map((key, value) => MapEntry(key, value.path));
|
|
|
|
final payload = {
|
|
'method': method,
|
|
'body': body,
|
|
'fields': fields,
|
|
'files': serializableFiles,
|
|
};
|
|
|
|
await _dbHelper.queueFailedRequest({
|
|
'type': 'api',
|
|
'endpoint_or_path': endpoint,
|
|
'payload': jsonEncode(payload),
|
|
'timestamp': DateTime.now().toIso8601String(),
|
|
'status': 'pending',
|
|
});
|
|
debugPrint("API request for endpoint '$endpoint' has been queued for retry.");
|
|
}
|
|
|
|
/// Adds a failed FTP upload to the local database queue.
|
|
Future<void> addFtpToQueue({
|
|
required String localFilePath,
|
|
required String remotePath,
|
|
}) async {
|
|
final payload = {'localFilePath': localFilePath};
|
|
await _dbHelper.queueFailedRequest({
|
|
'type': 'ftp',
|
|
'endpoint_or_path': remotePath,
|
|
'payload': jsonEncode(payload),
|
|
'timestamp': DateTime.now().toIso8601String(),
|
|
'status': 'pending',
|
|
});
|
|
debugPrint("FTP upload for file '$localFilePath' has been queued for retry.");
|
|
}
|
|
|
|
/// Retrieves all tasks currently in the 'pending' state from the queue.
|
|
Future<List<Map<String, dynamic>>> getPendingTasks() {
|
|
return _dbHelper.getPendingRequests();
|
|
}
|
|
|
|
/// Attempts to re-execute a single failed task from the queue.
|
|
/// Returns `true` on success, `false` on failure.
|
|
Future<bool> retryTask(int taskId) async {
|
|
final task = await _dbHelper.getRequestById(taskId);
|
|
if (task == null) {
|
|
debugPrint("Retry failed: Task with ID $taskId not found in the queue.");
|
|
return false;
|
|
}
|
|
|
|
bool success = false;
|
|
final payload = jsonDecode(task['payload'] as String);
|
|
|
|
try {
|
|
if (task['type'] == 'api') {
|
|
final endpoint = task['endpoint_or_path'] as String;
|
|
final method = payload['method'] as String;
|
|
|
|
debugPrint("Retrying API task $taskId: $method to $endpoint");
|
|
Map<String, dynamic> result;
|
|
|
|
if (method == 'POST_MULTIPART') {
|
|
// Reconstruct fields and files from the stored payload
|
|
final Map<String, String> fields = Map<String, String>.from(payload['fields'] ?? {});
|
|
final Map<String, File> files = (payload['files'] as Map<String, dynamic>?)
|
|
?.map((key, value) => MapEntry(key, File(value as String))) ?? {};
|
|
|
|
result = await _baseApiService.postMultipart(endpoint: endpoint, fields: fields, files: files);
|
|
} else { // Assume 'POST'
|
|
final Map<String, dynamic> body = Map<String, dynamic>.from(payload['body'] ?? {});
|
|
result = await _baseApiService.post(endpoint, body);
|
|
}
|
|
|
|
success = result['success'];
|
|
|
|
} else if (task['type'] == 'ftp') {
|
|
final remotePath = task['endpoint_or_path'] as String;
|
|
final localFile = File(payload['localFilePath'] as String);
|
|
|
|
debugPrint("Retrying FTP task $taskId: Uploading ${localFile.path} to $remotePath");
|
|
|
|
// Ensure the file still exists before attempting to re-upload
|
|
if (await localFile.exists()) {
|
|
final result = await _ftpService.uploadFile(localFile, remotePath);
|
|
// The FTP service already queues on failure, so we only care about success here.
|
|
success = result['success'];
|
|
} else {
|
|
debugPrint("Retry failed for FTP task $taskId: Source file no longer exists at ${localFile.path}");
|
|
success = false;
|
|
}
|
|
}
|
|
} catch (e) {
|
|
debugPrint("A critical error occurred while retrying task $taskId: $e");
|
|
success = false;
|
|
}
|
|
|
|
if (success) {
|
|
debugPrint("Task $taskId completed successfully. Removing from queue.");
|
|
await _dbHelper.deleteRequestFromQueue(taskId);
|
|
} else {
|
|
debugPrint("Retry attempt for task $taskId failed. It will remain in the queue.");
|
|
}
|
|
|
|
return success;
|
|
}
|
|
} |