fbmobile/lib/core/services/api.dart

149 lines
4.7 KiB
Dart
Raw Normal View History

2021-02-02 14:33:23 +00:00
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:logger/logger.dart';
import '../../constants.dart';
import '../../core/enums/error_code.dart';
import '../../core/error/rest_service_exception.dart';
import '../../core/error/service_exception.dart';
import '../../core/services/api_error_converter.dart';
import '../models/rest/rest_error.dart';
import '../util/logger.dart';
class Api implements ApiErrorConverter {
final Logger _logger = getLogger();
static const String _errorNoConnection = 'No internet connection';
static const String _errorTimeout = 'Request timed out';
static const String _formDataApiKey = 'apikey';
static const String _applicationJson = "application/json";
String _url = "";
String _apiKey = "";
2023-01-16 00:44:34 +00:00
final Map<String, String> _headers = {
"Content-Type": _applicationJson,
"Accept": _applicationJson
};
2023-01-04 20:17:54 +00:00
Duration _timeout = const Duration(seconds: Constants.apiRequestTimeoutLimit);
2021-02-02 14:33:23 +00:00
Future<http.Response> fetch<T>(String route) async {
try {
2023-01-16 00:44:34 +00:00
_logger.d(
"Requesting GET API endpoint '${_url + route}' with headers '$_headers' and maximum timeout '$_timeout'");
var response = await http
.get(Uri.parse(_url + route), headers: _headers)
.timeout(_timeout);
2021-02-02 14:33:23 +00:00
handleRestErrors(response);
return response;
} on TimeoutException {
2023-01-16 00:44:34 +00:00
throw ServiceException(
code: ErrorCode.socketTimeout, message: _errorTimeout);
2021-02-02 14:33:23 +00:00
} on SocketException {
2023-01-16 00:44:34 +00:00
throw ServiceException(
code: ErrorCode.socketError, message: _errorNoConnection);
2021-02-02 14:33:23 +00:00
}
}
Future<http.Response> post<T>(String route,
2023-01-16 00:44:34 +00:00
{Map<String, String?>? fields,
List<File>? files,
Map<String, String>? additionalFiles}) async {
2021-02-02 14:33:23 +00:00
try {
var uri = Uri.parse(_url + route);
var request = http.MultipartRequest('POST', uri)
..headers['Content-Type'] = _applicationJson
..headers["Accept"] = _applicationJson;
if (_apiKey.isNotEmpty) {
request.fields[_formDataApiKey] = _apiKey;
}
if (fields != null && fields.isNotEmpty) {
request.fields.addAll(fields as Map<String, String>);
2021-02-02 14:33:23 +00:00
}
if (files != null && files.isNotEmpty) {
2023-01-04 20:17:54 +00:00
for (var element in files) {
2023-01-16 00:44:34 +00:00
request.files.add(await http.MultipartFile.fromPath(
'file[${files.indexOf(element) + 1}]', element.path));
2023-01-04 20:17:54 +00:00
}
2021-02-02 14:33:23 +00:00
}
2023-01-04 20:17:54 +00:00
if (additionalFiles != null && additionalFiles.isNotEmpty) {
2021-02-02 14:33:23 +00:00
List<String> keys = additionalFiles.keys.toList();
additionalFiles.forEach((key, value) {
2023-01-16 00:44:34 +00:00
var index = files != null
? files.length + keys.indexOf(key) + 1
: keys.indexOf(key) + 1;
request.files.add(http.MultipartFile.fromString('file[$index]', value,
filename: key));
2021-02-02 14:33:23 +00:00
});
}
2023-01-16 00:44:34 +00:00
_logger.d(
"Requesting POST API endpoint '${uri.toString()}' and ${request.files.length} files");
2021-02-02 14:33:23 +00:00
var multiResponse = await request.send();
var response = await http.Response.fromStream(multiResponse);
handleRestErrors(response);
return response;
} on TimeoutException {
2023-01-16 00:44:34 +00:00
throw ServiceException(
code: ErrorCode.socketTimeout, message: _errorTimeout);
2021-02-02 14:33:23 +00:00
} on SocketException {
2023-01-16 00:44:34 +00:00
throw ServiceException(
code: ErrorCode.socketError, message: _errorNoConnection);
2021-02-02 14:33:23 +00:00
}
}
void setUrl(String url) {
_url = url + Constants.apiUrlSuffix;
}
void removeUrl() {
_url = "";
}
void setTimeout(Duration timeout) {
_timeout = timeout;
}
void addApiKeyAuthorization(apiKey) {
_apiKey = apiKey;
}
void removeApiKeyAuthorization() {
_apiKey = "";
}
/// if there's a JSON response body in error case, the RestServiceException will
/// have a json decoded object. Replace this with a custom
/// conversion method by overwriting the interface if needed
void handleRestErrors(http.Response response) {
2023-01-16 00:44:34 +00:00
if (response.statusCode != HttpStatus.ok &&
response.statusCode != HttpStatus.noContent) {
if (response.headers.containsKey(HttpHeaders.contentTypeHeader)) {
2023-01-16 00:44:34 +00:00
ContentType responseContentType =
ContentType.parse(response.headers[HttpHeaders.contentTypeHeader]!);
if (ContentType.json.primaryType == responseContentType.primaryType &&
ContentType.json.subType == responseContentType.subType) {
var parsedBody = convert(response);
2023-01-16 00:44:34 +00:00
throw RestServiceException(response.statusCode,
responseBody: parsedBody);
2021-02-02 14:33:23 +00:00
}
}
2023-01-04 20:17:54 +00:00
throw RestServiceException(response.statusCode);
2021-02-02 14:33:23 +00:00
}
}
@override
convert(http.Response response) {
return RestError.fromJson(json.decode(response.body));
}
}