You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cloudsolutions-atoms/lib/api/api_client.dart

405 lines
15 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart';
import 'package:http/io_client.dart';
import '../exceptions/api_exception.dart';
typedef FactoryConstructor<U> = U Function(dynamic);
class APIError {
int? errorCode;
String? errorMessage;
APIError(this.errorCode, this.errorMessage);
Map<String, dynamic> toJson() => {'errorCode': errorCode, 'errorMessage': errorMessage};
@override
String toString() {
return jsonEncode(this);
}
}
APIException _throwAPIException(Response response) {
print(response.statusCode);
switch (response.statusCode) {
case 200:
APIError? apiError;
if (response.body.isNotEmpty) {
var jsonError = jsonDecode(response.body);
debugPrint(jsonError);
apiError = APIError(response.statusCode, jsonError[0]);
}
return APIException(APIException.BAD_REQUEST, error: apiError);
case 400:
APIError? apiError;
if (response.body.isNotEmpty) {
var jsonError = jsonDecode(response.body);
debugPrint("json error : $jsonError");
apiError = APIError(response.statusCode, jsonError[0]);
}
return APIException(APIException.BAD_REQUEST, error: apiError);
case 401:
return const APIException(APIException.UNAUTHORIZED);
case 403:
return const APIException(APIException.FORBIDDEN);
case 404:
return const APIException(APIException.NOT_FOUND);
case 500:
return const APIException(APIException.INTERNAL_SERVER_ERROR);
case 444:
var downloadUrl = response.headers["location"];
return APIException(APIException.UPGRADE_REQUIRED, arguments: downloadUrl);
default:
return const APIException(APIException.OTHER);
}
}
class ApiClient {
static final ApiClient _instance = ApiClient._internal();
ApiClient._internal();
factory ApiClient() => _instance;
Future<U> postJsonForObject<T, U>(FactoryConstructor<U> factoryConstructor, String url, T jsonObject,
{String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0, bool isFormData = false}) async {
var defaultHeaders = {'Accept': 'application/json'};
if (headers != null && headers.isNotEmpty) {
defaultHeaders.addAll(headers);
}
if (!kReleaseMode) {
debugPrint("Url:$url");
var bodyJson = json.encode(jsonObject);
debugPrint("body:$bodyJson");
}
var response = await postJsonForResponse(url, jsonObject, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes, isFormData: isFormData);
try {
var jsonData = jsonDecode(response.body);
if (jsonData != null) {
debugPrint(jsonData.runtimeType.toString());
return factoryConstructor(jsonData);
} else {
APIError? apiError;
apiError = APIError(response.statusCode, jsonData[0]);
throw APIException(APIException.BAD_REQUEST, error: apiError);
}
} catch (ex) {
if (ex is APIException) {
rethrow;
} else {
throw APIException(APIException.BAD_RESPONSE_FORMAT, arguments: ex);
}
}
}
Future<U> putJsonForObject<T, U>(FactoryConstructor<U> factoryConstructor, String url, T jsonObject,
{String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0, bool isFormData = false}) async {
var defaultHeaders = {'Accept': 'application/json'};
if (headers != null && headers.isNotEmpty) {
defaultHeaders.addAll(headers);
}
if (!kReleaseMode) {
debugPrint("Url:$url");
var bodyJson = json.encode(jsonObject);
debugPrint("body:$bodyJson");
}
var response = await putJsonForResponse(url, jsonObject, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes, isFormData: isFormData);
try {
var jsonData = jsonDecode(response.body);
if (jsonData != null) {
debugPrint(jsonData.runtimeType.toString());
return factoryConstructor(jsonData);
} else {
APIError? apiError;
apiError = APIError(response.statusCode, jsonData[0]);
throw APIException(APIException.BAD_REQUEST, error: apiError);
}
} catch (ex) {
if (ex is APIException) {
rethrow;
} else {
throw APIException(APIException.BAD_RESPONSE_FORMAT, arguments: ex);
}
}
}
Future<Response> postJsonForResponse<T>(String url, T jsonObject,
{String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0, bool isFormData = false}) async {
int currentRetryTime = retryTimes;
String? requestBody;
late Map<String, String> stringObj;
if (jsonObject != null) {
requestBody = jsonEncode(jsonObject);
if (headers == null) {
headers = {'Content-Type': 'application/json'};
} else {
headers['Content-Type'] = 'application/json';
}
}
if (!kReleaseMode) {
print("url:$url");
print("requestBody:$requestBody");
}
if (isFormData) {
headers = {'Content-Type': 'application/x-www-form-urlencoded'};
stringObj = ((jsonObject ?? {}) as Map<String, dynamic>).map((key, value) => MapEntry(key, value?.toString() ?? ""));
}
if (!kReleaseMode) {
print("url:$url");
print("requestBody:$requestBody");
}
Future<Response> retry(APIException exception) async {
if (currentRetryTime > 0) {
currentRetryTime -= 1;
debugPrint('will retry after 3 seconds...');
await Future.delayed(const Duration(seconds: 3));
return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers);
} else {
throw exception;
}
}
try {
return await _postForResponse(url, isFormData ? stringObj : requestBody, token: token, queryParameters: queryParameters, headers: headers);
} on SocketException catch (e) {
return await retry(APIException(APIException.OTHER, arguments: e));
} on HttpException catch (e) {
return await retry(APIException(APIException.OTHER, arguments: e));
} on TimeoutException catch (e) {
throw APIException(APIException.TIMEOUT, arguments: e);
} on ClientException catch (e) {
return await retry(APIException(APIException.OTHER, arguments: e));
}
}
Future<Response> putJsonForResponse<T>(String url, T jsonObject,
{String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0, bool isFormData = true}) async {
int currentRetryTime = retryTimes;
String? requestBody;
late Map<String, String> stringObj;
if (jsonObject != null) {
requestBody = jsonEncode(jsonObject);
if (headers == null) {
headers = {'Content-Type': 'application/json'};
} else {
headers['Content-Type'] = 'application/json';
}
}
if (!kReleaseMode) {
print(jsonObject);
}
if (isFormData) {
headers = {'Content-Type': 'application/x-www-form-urlencoded'};
stringObj = ((jsonObject ?? {}) as Map<String, dynamic>).map((key, value) => MapEntry(key, value?.toString() ?? ""));
}
if (!kReleaseMode) {
print("url:$url");
print("requestBody:$requestBody");
}
Future<Response> retry(APIException exception) async {
if (currentRetryTime > 0) {
currentRetryTime -= 1;
debugPrint('will retry after 3 seconds...');
await Future.delayed(const Duration(seconds: 3));
return await _putForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers);
} else {
throw exception;
}
}
try {
return await _postForResponse(url, isFormData ? stringObj : requestBody, token: token, queryParameters: queryParameters, headers: headers);
} on SocketException catch (e) {
return await retry(APIException(APIException.OTHER, arguments: e));
} on HttpException catch (e) {
return await retry(APIException(APIException.OTHER, arguments: e));
} on TimeoutException catch (e) {
throw APIException(APIException.TIMEOUT, arguments: e);
} on ClientException catch (e) {
return await retry(APIException(APIException.OTHER, arguments: e));
}
}
Future<Response> _postForResponse(String url, requestBody, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers}) async {
var defaultHeaders = <String, String>{};
if (token != null) {
defaultHeaders['Authorization'] = 'Bearer $token';
}
if (headers != null && headers.isNotEmpty) {
defaultHeaders.addAll(headers);
}
if (queryParameters != null) {
var queryString = Uri(queryParameters: queryParameters).query;
url = '$url?$queryString';
}
var response = await _post(Uri.parse(url), body: requestBody, headers: defaultHeaders).timeout(const Duration(seconds: 120));
if (response.statusCode >= 200 && response.statusCode < 300) {
return response;
} else {
throw _throwAPIException(response);
}
}
Future<Response> _putForResponse(String url, requestBody, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers}) async {
var defaultHeaders = <String, String>{};
if (token != null) {
defaultHeaders['Authorization'] = 'Bearer $token';
}
if (headers != null && headers.isNotEmpty) {
defaultHeaders.addAll(headers);
}
if (queryParameters != null) {
var queryString = Uri(queryParameters: queryParameters).query;
url = '$url?$queryString';
}
var response = await _put(Uri.parse(url), body: requestBody, headers: defaultHeaders).timeout(const Duration(seconds: 120));
if (response.statusCode >= 200 && response.statusCode < 300) {
return response;
} else {
throw _throwAPIException(response);
}
}
Future<Response> _deleteForResponse(String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers}) async {
var defaultHeaders = <String, String>{};
if (token != null) {
defaultHeaders['Authorization'] = 'Bearer $token';
}
if (headers != null && headers.isNotEmpty) {
defaultHeaders.addAll(headers);
}
if (queryParameters != null) {
var queryString = Uri(queryParameters: queryParameters).query;
url = '$url?$queryString';
}
var response = await _delete(Uri.parse(url), headers: defaultHeaders).timeout(const Duration(seconds: 60));
if (response.statusCode >= 200 && response.statusCode < 300) {
return response;
} else {
throw _throwAPIException(response);
}
}
Future<Response> getJsonForResponse<T>(String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0}) async {
int currentRetryTime = retryTimes;
if (headers == null) {
headers = {'Content-Type': 'application/json'};
} else {
headers['Content-Type'] = 'application/json';
}
Future<Response> retry(APIException exception) async {
if (currentRetryTime > 0) {
currentRetryTime -= 1;
debugPrint('will retry after 3 seconds...');
await Future.delayed(const Duration(seconds: 3));
return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers);
} else {
throw exception;
}
}
try {
return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers);
} on SocketException catch (e) {
return await retry(APIException(APIException.OTHER, arguments: e));
} on HttpException catch (e) {
return await retry(APIException(APIException.OTHER, arguments: e));
} on TimeoutException catch (e) {
throw APIException(APIException.TIMEOUT, arguments: e);
} on ClientException catch (e) {
return await retry(APIException(APIException.OTHER, arguments: e));
}
}
Future<Response> deleteJsonForResponse<T>(String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0}) async {
int currentRetryTime = retryTimes;
if (headers == null) {
headers = {'Content-Type': 'application/json'};
} else {
headers['Content-Type'] = 'application/json';
}
Future<Response> retry(APIException exception) async {
if (currentRetryTime > 0) {
currentRetryTime -= 1;
debugPrint('will retry after 3 seconds...');
await Future.delayed(const Duration(seconds: 3));
return await _deleteForResponse(url, token: token, queryParameters: queryParameters, headers: headers);
} else {
throw exception;
}
}
try {
return await _deleteForResponse(url, token: token, queryParameters: queryParameters, headers: headers);
} on SocketException catch (e) {
return await retry(APIException(APIException.OTHER, arguments: e));
} on HttpException catch (e) {
return await retry(APIException(APIException.OTHER, arguments: e));
} on TimeoutException catch (e) {
throw APIException(APIException.TIMEOUT, arguments: e);
} on ClientException catch (e) {
return await retry(APIException(APIException.OTHER, arguments: e));
}
}
Future<Response> _getForResponse(String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers}) async {
var defaultHeaders = <String, String>{};
if (token != null) {
defaultHeaders['Authorization'] = 'Bearer $token';
}
if (headers != null && headers.isNotEmpty) {
defaultHeaders.addAll(headers);
}
if (queryParameters != null) {
var queryString = Uri(queryParameters: queryParameters).query;
url = '$url?$queryString';
}
var response = await _get(Uri.parse(url), headers: defaultHeaders).timeout(const Duration(seconds: 60));
if (response.statusCode >= 200 && response.statusCode < 300) {
return response;
} else {
throw _throwAPIException(response);
}
}
Future<Response> _get(url, {Map<String, String>? headers}) => _withClient((client) => client.get(url, headers: headers));
bool _certificateCheck(X509Certificate cert, String host, int port) => true;
Future<T> _withClient<T>(Future<T> Function(Client) fn) async {
var httpClient = HttpClient()..badCertificateCallback = _certificateCheck;
var client = IOClient(httpClient);
try {
return await fn(client);
} finally {
client.close();
}
}
Future<Response> _post(url, {Map<String, String>? headers, body, Encoding? encoding}) => _withClient((client) => client.post(url, headers: headers, body: body, encoding: encoding));
Future<Response> _put(url, {Map<String, String>? headers, body, Encoding? encoding}) => _withClient((client) => client.put(url, headers: headers, body: body, encoding: encoding));
Future<Response> _delete(url, {Map<String, String>? headers}) => _withClient((client) => client.delete(url, headers: headers));
}