Project

General

Profile

Task #28 » base_api_client.dart

Milad Khnefes, 02/01/2026 02:32 PM

 
import 'dart:async';
import 'dart:io';

import 'package:http/http.dart' as http;
import '../constants/constants.dart';
import 'client.dart';

abstract class BaseApiClient extends ApiClient {
/// GET request with dynamic response type
Future<T?> get<T>(
String url,
T Function(String) customCallback, {
bool debug = false,
Map<String, dynamic> queryParams = const {},
}) async {
try {
var response = await super.rawGet(url, queryParams: queryParams);
return handleResponse<T>(response, customCallback, debug);
} catch (e, stackTrace) {
if (!e.toString().contains('refresh')) {
_logException(stackTrace, e);
}
return null;
}
}

/// POST request with dynamic response type
Future<T?> post<T>(
String url,
T Function(String) customCallback,
String jsonBody, {
bool debug = false,
}) async {
try {
var response = await super.rawPost(url, jsonBody);
return handleResponse<T>(
response,
customCallback,
debug,
jsonBody: jsonBody,
);
} catch (e, stackTrace) {
if (!e.toString().contains('USER_TOKEN_ALREADY_EXISTS')) {
Constants.logger.e(e.toString(), e, stackTrace);
}
return null;
}
}

/// POST multipart request with dynamic response type
Future<T?> postMultipart<T>(
String url,
T Function(String) customCallback,
File? image,
dynamic requestMapObject, {
bool debug = false,
}) async {
try {
var response = await super.rawPostMultipart(url, image, requestMapObject);
return handleResponse<T>(response, customCallback, debug);
} catch (e, stackTrace) {
Constants.logger.e(e.toString(), e, stackTrace);
return null;
}
}

/// PUT request with dynamic response type
Future<T?> put<T>(
String url,
T Function(String) customCallback,
String jsonBody, {
bool debug = false,
bool needsToken = true,
}) async {
try {
var response = await super.rawPut(url, jsonBody);
return handleResponse<T>(
response,
customCallback,
debug,
jsonBody: jsonBody,
);
} catch (e, stackTrace) {
Constants.logger.e(e.toString(), e, stackTrace);
return null;
}
}

/// DELETE request with dynamic response type
Future<T?> delete<T>(
String url,
T Function(String) customCallback,
String jsonBody, {
bool debug = false,
Map<String, dynamic> queryParams = const {},
}) async {
try {
var response = await super.rawDelete(url, jsonBody);
return handleResponse<T>(
response,
customCallback,
debug,
jsonBody: jsonBody,
);
} catch (e, stackTrace) {
_logException(stackTrace, e);
return null;
}
}

/// Handle HTTP response and convert to custom type
T? handleResponse<T>(
http.Response response,
T Function(String) customCallback,
bool debug, {
String? jsonBody,
}) {
if (response.body.contains('USER_TOKEN_ALREADY_EXISTS')) {
return null;
}

if (!Constants.isSuccessCode(response.statusCode)) {
_ensureExceptionWasLogged(
'received a status code of: ${response.statusCode} when i tried ${response.request?.url.path} with params: ${response.request?.url.queryParameters}\nand body: $jsonBody\nIt Said: ${response.body}',
);
if (response.statusCode == 404) {
return null;
}
}

if (response.body.contains('"success":false')) {
_ensureExceptionWasLogged(
'Request Failure: ${response.request?.url.path} with params: ${response.request?.url.queryParameters}\nand body: $jsonBody\nIt Said: ${response.body}',
);
}

String jsonString = response.body;
if (debug) {
Constants.logger.i(
'Debugging ${response.request?.method}: ${response.request?.url.path}\nWith Params: ${response.request?.url.queryParameters}\nAnd JSON Body: $jsonBody\nResponse was: ${response.body}',
);
}

if (response.statusCode == 408) {
throw Exception('Timeout');
}

T result = customCallback(jsonString);
return result;
}

void _ensureExceptionWasLogged(String errorMessage) {
try {
throw Exception(errorMessage);
} catch (e, stackTrace) {
Constants.logger.e(e.toString(), e, stackTrace);
}
}

void _logException(StackTrace stackTrace, dynamic e) {
Constants.logger.e(e.toString(), e, stackTrace);
}
}
(12-12/12)