import 'dart:typed_data'; import 'dart:convert'; import 'package:flutter_paperless_mobile/core/model/error_message.dart'; import 'package:flutter_paperless_mobile/core/type/json.dart'; import 'package:flutter_paperless_mobile/di_initializer.dart'; import 'package:http/http.dart'; import 'package:injectable/injectable.dart'; /// /// Convenience class which handles timeout errors. /// @Injectable(as: BaseClient) @Named("timeoutClient") class TimeoutClient implements BaseClient { static const Duration requestTimeout = Duration(seconds: 25); @override Future send(BaseRequest request) async { return getIt().send(request).timeout( requestTimeout, onTimeout: () => Future.error(const ErrorMessage(ErrorCode.requestTimedOut)), ); } @override void close() { getIt().close(); } @override Future delete(Uri url, {Map? headers, Object? body, Encoding? encoding}) async { return _handle400Error( await getIt() .delete(url, headers: headers, body: body, encoding: encoding) .timeout( requestTimeout, onTimeout: () => Future.error(const ErrorMessage(ErrorCode.requestTimedOut)), ), ); } @override Future get(Uri url, {Map? headers}) async { return _handle400Error( await getIt().get(url, headers: headers).timeout( requestTimeout, onTimeout: () => Future.error(const ErrorMessage(ErrorCode.requestTimedOut)), ), ); } @override Future head(Uri url, {Map? headers}) async { return _handle400Error( await getIt().head(url, headers: headers).timeout( requestTimeout, onTimeout: () => Future.error(const ErrorMessage(ErrorCode.requestTimedOut)), ), ); } @override Future patch(Uri url, {Map? headers, Object? body, Encoding? encoding}) async { return _handle400Error( await getIt() .patch(url, headers: headers, body: body, encoding: encoding) .timeout( requestTimeout, onTimeout: () => Future.error(const ErrorMessage(ErrorCode.requestTimedOut)), ), ); } @override Future post(Uri url, {Map? headers, Object? body, Encoding? encoding}) async { return _handle400Error( await getIt().post(url, headers: headers, body: body, encoding: encoding).timeout( requestTimeout, onTimeout: () => Future.error(const ErrorMessage(ErrorCode.requestTimedOut)), ), ); } @override Future put(Uri url, {Map? headers, Object? body, Encoding? encoding}) async { return _handle400Error( await getIt().put(url, headers: headers, body: body, encoding: encoding).timeout( requestTimeout, onTimeout: () => Future.error(const ErrorMessage(ErrorCode.requestTimedOut)), ), ); } @override Future read(Uri url, {Map? headers}) async { return getIt().read(url, headers: headers).timeout( requestTimeout, onTimeout: () => Future.error(const ErrorMessage(ErrorCode.requestTimedOut)), ); } @override Future readBytes(Uri url, {Map? headers}) { return getIt().readBytes(url, headers: headers).timeout( requestTimeout, onTimeout: () => Future.error(const ErrorMessage(ErrorCode.requestTimedOut)), ); } Response _handle400Error(Response response) { if (response.statusCode == 400) { // try to parse contained error message, otherwise return response final JSON json = jsonDecode(utf8.decode(response.bodyBytes)); final Map errorMessages = {}; //TODO: This could be simplified, look at error message format of paperless-ngx for (final entry in json.entries) { if (entry.value is List) { errorMessages.putIfAbsent(entry.key, () => (entry.value as List).cast().first); } else if (entry.value is String) { errorMessages.putIfAbsent(entry.key, () => entry.value); } else { errorMessages.putIfAbsent(entry.key, () => entry.value.toString()); } } throw errorMessages; } return response; } }