148 lines
4.7 KiB
Dart
148 lines
4.7 KiB
Dart
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 = "";
|
|
|
|
final Map<String, String> _headers = {
|
|
"Content-Type": _applicationJson,
|
|
"Accept": _applicationJson
|
|
};
|
|
Duration _timeout = const Duration(seconds: Constants.apiRequestTimeoutLimit);
|
|
|
|
Future<http.Response> fetch<T>(String route) async {
|
|
try {
|
|
_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);
|
|
handleRestErrors(response);
|
|
return response;
|
|
} on TimeoutException {
|
|
throw ServiceException(
|
|
code: ErrorCode.socketTimeout, message: _errorTimeout);
|
|
} on SocketException {
|
|
throw ServiceException(
|
|
code: ErrorCode.socketError, message: _errorNoConnection);
|
|
}
|
|
}
|
|
|
|
Future<http.Response> post<T>(String route,
|
|
{Map<String, String?>? fields,
|
|
List<File>? files,
|
|
Map<String, String>? additionalFiles}) async {
|
|
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>);
|
|
}
|
|
|
|
if (files != null && files.isNotEmpty) {
|
|
for (var element in files) {
|
|
request.files.add(await http.MultipartFile.fromPath(
|
|
'file[${files.indexOf(element) + 1}]', element.path));
|
|
}
|
|
}
|
|
|
|
if (additionalFiles != null && additionalFiles.isNotEmpty) {
|
|
List<String> keys = additionalFiles.keys.toList();
|
|
additionalFiles.forEach((key, value) {
|
|
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));
|
|
});
|
|
}
|
|
|
|
_logger.d(
|
|
"Requesting POST API endpoint '${uri.toString()}' and ${request.files.length} files");
|
|
var multiResponse = await request.send();
|
|
var response = await http.Response.fromStream(multiResponse);
|
|
handleRestErrors(response);
|
|
return response;
|
|
} on TimeoutException {
|
|
throw ServiceException(
|
|
code: ErrorCode.socketTimeout, message: _errorTimeout);
|
|
} on SocketException {
|
|
throw ServiceException(
|
|
code: ErrorCode.socketError, message: _errorNoConnection);
|
|
}
|
|
}
|
|
|
|
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) {
|
|
if (response.statusCode != HttpStatus.ok &&
|
|
response.statusCode != HttpStatus.noContent) {
|
|
if (response.headers.containsKey(HttpHeaders.contentTypeHeader)) {
|
|
ContentType responseContentType =
|
|
ContentType.parse(response.headers[HttpHeaders.contentTypeHeader]!);
|
|
|
|
if (ContentType.json.primaryType == responseContentType.primaryType &&
|
|
ContentType.json.subType == responseContentType.subType) {
|
|
var parsedBody = convert(response);
|
|
throw RestServiceException(response.statusCode,
|
|
responseBody: parsedBody);
|
|
}
|
|
}
|
|
|
|
throw RestServiceException(response.statusCode);
|
|
}
|
|
}
|
|
|
|
@override
|
|
convert(http.Response response) {
|
|
return RestError.fromJson(json.decode(response.body));
|
|
}
|
|
}
|