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.
HMG_QLine/lib/api/api_client.dart

244 lines
9.9 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart';
import 'package:http/io_client.dart';
import 'package:hmg_qline/constants/app_constants.dart';
import 'package:hmg_qline/utilities/api_exception.dart';
typedef FactoryConstructor<U> = U Function(dynamic);
abstract class ApiClient {
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});
Future<Response> postJsonForResponse<T>(String url, T jsonObject, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0});
Future<U> getJsonForObject<T, U>(FactoryConstructor<U> factoryConstructor, String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0});
Future<Response> getJsonForResponse<T>(String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0});
}
class ApiClientImp implements ApiClient {
@override
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}) async {
var headers0 = {'Accept': 'application/json'};
if (headers != null && headers.isNotEmpty) {
headers0.addAll(headers);
}
var response = await postJsonForResponse(url, jsonObject, token: token, queryParameters: queryParameters, headers: headers0, retryTimes: retryTimes);
try {
if (!kReleaseMode) {
log("statusCode:${response.statusCode}");
}
var jsonData = jsonDecode(response.body);
return factoryConstructor(jsonData);
} catch (ex) {
log(ex.toString());
log("exception:$ex");
throw APIException(APIException.badResponseFormat, arguments: ex);
}
}
@override
Future<Response> postJsonForResponse<T>(String url, T jsonObject, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0}) async {
String? requestBody;
if (jsonObject != null) {
requestBody = jsonEncode(jsonObject);
if (headers == null) {
headers = {'Content-Type': 'application/json'};
} else {
headers['Content-Type'] = 'application/json';
}
}
return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes);
}
Future<Response> _postForResponse(
String url,
requestBody, {
String? token,
Map<String, dynamic>? queryParameters,
Map<String, String>? headers,
int retryTimes = 0,
}) async {
try {
var headers0 = <String, String>{};
if (token != null) {
headers0['Authorization'] = 'Bearer $token';
}
if (headers != null && headers.isNotEmpty) {
headers0.addAll(headers);
}
if (queryParameters != null) {
var queryString = Uri(queryParameters: queryParameters).query;
url = '$url?$queryString';
}
Response response;
response = await _post(Uri.parse(url), body: requestBody, headers: headers0).timeout(const Duration(seconds: 100));
if (!kReleaseMode) {
logger.d("------URL------");
logger.i(url);
logger.d("------Payload------");
logger.i(jsonDecode(requestBody));
logger.d("------Response------");
logger.i(jsonDecode(response.body));
}
if (response.statusCode >= 200 && response.statusCode < 500) {
var jsonData = jsonDecode(response.body);
if (jsonData["StatusMessage"] != null && jsonData["StatusMessage"] == "Unauthorized user attempt to access API") {
return await _postForResponse(url, requestBody, token: '', queryParameters: queryParameters, headers: headers, retryTimes: retryTimes);
}
return response;
} else {
throw APIError.throwAPIException(response);
}
} on SocketException catch (e) {
if (retryTimes > 0) {
log('will retry after 3 seconds...');
await Future.delayed(const Duration(seconds: 3));
return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1);
} else {
throw APIException(APIException.other, arguments: e);
}
} on HttpException catch (e) {
if (retryTimes > 0) {
log('will retry after 3 seconds...');
await Future.delayed(const Duration(seconds: 3));
return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1);
} else {
throw APIException(APIException.other, arguments: e);
}
} on TimeoutException catch (e) {
throw APIException(APIException.timeout, arguments: e);
} on ClientException catch (e) {
if (retryTimes > 0) {
log('will retry after 3 seconds...');
await Future.delayed(const Duration(seconds: 3));
return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1);
} else {
throw APIException(APIException.other, arguments: e);
}
} catch (ex) {
log("exception1:$ex");
throw APIException(APIException.badResponseFormat, arguments: ex);
}
}
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));
@override
Future<U> getJsonForObject<T, U>(FactoryConstructor<U> factoryConstructor, String url,
{String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0}) async {
var headers0 = {'Accept': 'application/json'};
if (headers != null && headers.isNotEmpty) {
headers0.addAll(headers);
}
var response = await getJsonForResponse(url, token: token, queryParameters: queryParameters, headers: headers0, retryTimes: retryTimes);
try {
var jsonData = jsonDecode(response.body);
return factoryConstructor(jsonData);
} catch (ex) {
log("exception:${response.body}");
throw APIException(APIException.badResponseFormat, arguments: ex);
}
}
@override
Future<Response> getJsonForResponse<T>(String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0}) async {
if (headers == null) {
headers = {'Content-Type': 'application/json'};
} else {
headers['Content-Type'] = 'application/json';
}
return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes);
}
bool isFirstCall = true;
Future<Response> _getForResponse(String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0}) async {
try {
var headers0 = <String, String>{};
if (token != null) {
headers0['Authorization'] = 'Bearer $token';
}
if (headers != null && headers.isNotEmpty) {
headers0.addAll(headers);
}
if (queryParameters != null) {
String queryString = Uri(queryParameters: queryParameters).query;
if (isFirstCall) url = '$url?$queryString';
}
if (!kReleaseMode) {
logger.i(url);
logger.i("$queryParameters");
}
var response = await _get(Uri.parse(url), headers: headers0).timeout(const Duration(seconds: 60));
if (!kReleaseMode) {
logger.i(jsonDecode(response.body));
}
if (response.statusCode >= 200 && response.statusCode < 500) {
var jsonData = jsonDecode(response.body);
if (jsonData["StatusMessage"] != null && jsonData["StatusMessage"] == "Unauthorized user attempt to access API") {
isFirstCall = false;
return await _getForResponse(url, token: '', queryParameters: queryParameters, headers: headers, retryTimes: retryTimes);
}
return response;
} else {
throw APIError.throwAPIException(response);
}
} on SocketException catch (e) {
if (retryTimes > 0) {
log('will retry after 3 seconds...');
await Future.delayed(const Duration(seconds: 3));
return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1);
} else {
throw APIException(APIException.other, arguments: e);
}
} on HttpException catch (e) {
if (retryTimes > 0) {
log('will retry after 3 seconds...');
await Future.delayed(const Duration(seconds: 3));
return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1);
} else {
throw APIException(APIException.other, arguments: e);
}
} on TimeoutException catch (e) {
throw APIException(APIException.timeout, arguments: e);
} on ClientException catch (e) {
if (retryTimes > 0) {
await Future.delayed(const Duration(seconds: 3));
return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1);
} else {
throw APIException(APIException.other, arguments: e);
}
}
}
Future<Response> _get(url, {Map<String, String>? headers}) => _withClient((client) => client.get(url, headers: headers));
}