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.
333 lines
13 KiB
Dart
333 lines
13 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:developer';
|
|
import 'dart:io';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:http/http.dart';
|
|
import 'package:http/io_client.dart';
|
|
import 'package:mc_common_app/classes/app_state.dart';
|
|
import 'package:mc_common_app/classes/consts.dart';
|
|
import 'package:mc_common_app/config/routes.dart';
|
|
import 'package:mc_common_app/exceptions/api_exception.dart';
|
|
import 'package:mc_common_app/main.dart';
|
|
import 'package:mc_common_app/models/user_models/refresh_token.dart';
|
|
import 'package:mc_common_app/models/user_models/user.dart';
|
|
import 'package:mc_common_app/utils/shared_prefrence.dart';
|
|
import 'package:mc_common_app/utils/utils.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);
|
|
}
|
|
|
|
static APIException throwAPIException(Response response) {
|
|
switch (response.statusCode) {
|
|
case 400:
|
|
APIError? apiError;
|
|
if (response.body != null && response.body.isNotEmpty) {
|
|
var jsonError = jsonDecode(response.body);
|
|
apiError = APIError(jsonError['errorCode'], jsonError['errorMessage']);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
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.BAD_RESPONSE_FORMAT, 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") {
|
|
String mToken = await updateUserToken();
|
|
return await _postForResponse(url, requestBody, token: mToken, 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) {
|
|
BuildContext? context = navigatorKey.currentContext;
|
|
if (context != null) {
|
|
NoInternetDialog.show(context);
|
|
}
|
|
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.BAD_RESPONSE_FORMAT, 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.BAD_RESPONSE_FORMAT, 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") {
|
|
String mToken = await updateUserToken();
|
|
isFirstCall = false;
|
|
return await _getForResponse(url, token: mToken, 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) {
|
|
final context = navigatorKey.currentContext;
|
|
if (context != null) {
|
|
NoInternetDialog.show(context);
|
|
}
|
|
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));
|
|
|
|
// Future<RefreshToken> refreshTokenAPI(String token, String refreshToken) async {
|
|
// var postParams = {"token": token, "refreshToken": refreshToken};
|
|
// // String t = AppState().getUser.data!.accessToken ?? "";
|
|
// return await postJsonForObject((json) => RefreshToken.fromJson(json), ApiConsts.RefreshToken, postParams);
|
|
// }
|
|
|
|
// //TODO: needs to solve later this.
|
|
// String updateUserToken() {
|
|
// return "";
|
|
// }
|
|
|
|
Future<RefreshToken> refreshTokenAPI(String token, String refreshToken) async {
|
|
var postParams = {"token": token, "refreshToken": refreshToken};
|
|
// String t = AppState().getUser.data!.accessToken ?? "";
|
|
return await postJsonForObject((json) => RefreshToken.fromJson(json), ApiConsts.refreshToken, postParams);
|
|
}
|
|
|
|
Future<String> updateUserToken() async {
|
|
String token = await SharedPrefManager.getUserToken();
|
|
String refreshToken = await SharedPrefManager.getRefreshToken();
|
|
RefreshToken refresh = await refreshTokenAPI(token, refreshToken);
|
|
SharedPrefManager.setUserToken(refresh.data!.accessToken ?? "");
|
|
SharedPrefManager.setRefreshToken(refresh.data!.refreshToken ?? "");
|
|
String mdata = await SharedPrefManager.getData();
|
|
UserInfo info = UserInfo.fromJson(jsonDecode(mdata));
|
|
User user = User();
|
|
user.data = UserData(accessToken: refresh.data!.accessToken ?? "", refreshToken: refresh.data!.refreshToken ?? "", userInfo: info);
|
|
AppState().setUser = user;
|
|
return refresh.data!.accessToken ?? "";
|
|
}
|
|
}
|