sendRequest method
Send API request using Firebase configuration
Implementation
Future<ApiResponse> sendRequest({
required String apiKey,
required Map<String, dynamic> requestBody,
}) async {
final startTime = DateTime.now();
Trace? trace;
try {
// Get API definition from cache
final apiDefinition = _firebaseService.getApiDefinition(apiKey);
if (apiDefinition == null) {
return ApiResponse.error('API definition not found for key: $apiKey');
}
// Get base URL from cache
final baseUrl = _firebaseService.baseUrl;
if (baseUrl == null) {
return ApiResponse.error('Base URL not configured');
}
// Construct final URL (service prefix already included in endpoint if needed)
final finalUrl = '$baseUrl${apiDefinition.endpoint}';
// Prepare headers with defaults
final headers = <String, String>{};
// Add default headers first
headers['Content-Type'] = 'application/json';
headers['Accept'] = 'application/json';
// Add bearer token if set
final bearerToken = _firebaseService.bearerToken;
if (bearerToken != null && bearerToken.isNotEmpty) {
headers['Authorization'] = 'Bearer $bearerToken';
}
// Override with API definition headers if provided
if (apiDefinition.headers.isNotEmpty) {
headers.addAll(apiDefinition.headers.cast<String, String>());
}
// Create Firebase Performance trace for this network call
final performance = FirebasePerformance.instance;
trace = performance.newTrace('api_request_$apiKey');
await trace.start();
// Set custom attributes for tracking (without exposing sensitive URLs)
trace.putAttribute('api_key', apiKey);
trace.putAttribute('request_type', apiDefinition.requestType.toUpperCase());
// Note: endpoint, service, and URL are not logged for security
// Making request...
// Make HTTP request
http.StreamedResponse streamed;
final requestType = apiDefinition.requestType.toUpperCase();
switch (requestType) {
case 'GET':
case 'DELETE':
// For GET/DELETE: Merge default params with requestBody for query parameters
final queryParams = Map<String, dynamic>.from(apiDefinition.params);
queryParams.addAll(requestBody);
final uri = Uri.parse(finalUrl).replace(queryParameters: queryParams.map((k, v) => MapEntry(k, '$v')));
final req = http.Request(requestType, uri)..headers.addAll(headers.cast<String, String>());
streamed = await _client.send(req);
// Calculate request payload size for query params
final requestPayloadSize = queryParams.isNotEmpty ? jsonEncode(queryParams).length : 0;
trace.setMetric('request_payload_size', requestPayloadSize);
break;
case 'POST':
case 'PUT':
case 'PATCH':
// For POST/PUT/PATCH: Use requestBody directly as the body (no merging with default params)
final uri = Uri.parse(finalUrl);
final req = http.Request(requestType, uri)
..headers.addAll(headers.cast<String, String>())
..body = jsonEncode(requestBody);
streamed = await _client.send(req);
// Calculate request payload size
final requestPayloadSize = requestBody.isNotEmpty ? jsonEncode(requestBody).length : 0;
trace.setMetric('request_payload_size', requestPayloadSize);
break;
default:
await trace.stop();
return ApiResponse.error('Unsupported request type: ${apiDefinition.requestType}');
}
// Read response body
final bodyStr = await streamed.stream.bytesToString();
dynamic body;
try {
body = bodyStr.isNotEmpty ? jsonDecode(bodyStr) : null;
} catch (_) {
body = bodyStr; // not JSON
}
final duration = DateTime.now().difference(startTime);
// Update trace with response metrics
trace.putAttribute('status_code', streamed.statusCode.toString());
trace.setMetric('response_payload_size', bodyStr.length);
trace.setMetric('duration_ms', duration.inMilliseconds);
// Stop the trace and record it
await trace.stop();
return ApiResponse.success(
body,
statusCode: streamed.statusCode,
);
} catch (e) {
final duration = DateTime.now().difference(startTime);
// Stop trace with error status
if (trace != null) {
try {
trace.putAttribute('error', e.toString());
trace.setMetric('duration_ms', duration.inMilliseconds);
await trace.stop();
} catch (_) {
// Ignore trace errors
}
}
// Error occurred - logging disabled for security
return ApiResponse.error('Unexpected error: ${e.toString()}');
}
}