|
|
|
|
@ -3,6 +3,7 @@ import 'dart:convert';
|
|
|
|
|
import 'dart:developer';
|
|
|
|
|
import 'dart:io';
|
|
|
|
|
|
|
|
|
|
import 'package:clarity_flutter/clarity_flutter.dart';
|
|
|
|
|
import 'package:easy_localization/easy_localization.dart';
|
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
@ -264,6 +265,11 @@ class ApiClientImp implements ApiClient {
|
|
|
|
|
final message = e.message.contains('Connection reset by peer') ? LocaleKeys.networkConnectionReset.tr() : LocaleKeys.networkErrorMessage.tr();
|
|
|
|
|
onFailure(message, -1, failureType: ConnectivityFailure(message));
|
|
|
|
|
_analytics.errorTracking.log(endPoint, error: "SocketException: $e");
|
|
|
|
|
|
|
|
|
|
Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim()));
|
|
|
|
|
Clarity.setCustomTag('api_err_request', _sanitizeRequestBody(requestBody));
|
|
|
|
|
Clarity.sendCustomEvent('api_failure');
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
} on http.ClientException catch (e) {
|
|
|
|
|
final message = e.message.contains('Connection reset by peer') ? LocaleKeys.networkConnectionReset.tr() : LocaleKeys.networkErrorMessage.tr();
|
|
|
|
|
@ -274,6 +280,10 @@ class ApiClientImp implements ApiClient {
|
|
|
|
|
final message = LocaleKeys.networkTimeout.tr();
|
|
|
|
|
onFailure(message, -1, failureType: ConnectivityFailure(message));
|
|
|
|
|
_analytics.errorTracking.log(endPoint, error: "TimeoutException");
|
|
|
|
|
|
|
|
|
|
Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim()));
|
|
|
|
|
Clarity.setCustomTag('api_err_request', _sanitizeRequestBody(requestBody));
|
|
|
|
|
Clarity.sendCustomEvent('api_timeout_failure');
|
|
|
|
|
return;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
final message = LocaleKeys.networkUnknownError.tr();
|
|
|
|
|
@ -287,6 +297,11 @@ class ApiClientImp implements ApiClient {
|
|
|
|
|
if (statusCode < 200 || statusCode >= 400) {
|
|
|
|
|
onFailure(LocaleKeys.networkErrorWhileFetching.tr(), statusCode, failureType: StatusCodeFailure(LocaleKeys.networkErrorWhileFetching.tr()));
|
|
|
|
|
logApiEndpointError(endPoint, LocaleKeys.networkErrorWhileFetching.tr(), statusCode);
|
|
|
|
|
|
|
|
|
|
Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim()));
|
|
|
|
|
Clarity.setCustomTag('api_err_request', _sanitizeRequestBody(requestBody));
|
|
|
|
|
Clarity.setCustomTag('api_err_status', statusCode.toString());
|
|
|
|
|
Clarity.sendCustomEvent('api_status_code_failure');
|
|
|
|
|
} else {
|
|
|
|
|
var parsed = json.decode(utf8.decode(response.bodyBytes));
|
|
|
|
|
if (isAllowAny) {
|
|
|
|
|
@ -388,6 +403,11 @@ class ApiClientImp implements ApiClient {
|
|
|
|
|
);
|
|
|
|
|
logApiEndpointError(endPoint, parsed['message'] ?? parsed['message'], statusCode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim()));
|
|
|
|
|
Clarity.setCustomTag('api_err_request', _sanitizeRequestBody(requestBody));
|
|
|
|
|
Clarity.setCustomTag('api_err_status', statusCode.toString());
|
|
|
|
|
Clarity.sendCustomEvent('api_message_status_failure');
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (parsed['SameClinicApptList'] != null) {
|
|
|
|
|
@ -666,4 +686,42 @@ class ApiClientImp implements ApiClient {
|
|
|
|
|
logApiEndpointError(String endPoint, error, code) {
|
|
|
|
|
_analytics.errorTracking.log(endPoint, error: error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String _getEndofUrl(String url) {
|
|
|
|
|
const int clarityLimit = 255;
|
|
|
|
|
if (url.length <= clarityLimit) return url;
|
|
|
|
|
|
|
|
|
|
// Extracts the final 252 characters and prepends '...'
|
|
|
|
|
return '...${url.substring(url.length - (clarityLimit - 3))}';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String _sanitizeRequestBody(String body) {
|
|
|
|
|
if (body.isEmpty) return '';
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// Handle standard JSON bodies
|
|
|
|
|
final Map<String, dynamic> data = json.decode(body);
|
|
|
|
|
|
|
|
|
|
// Case-insensitive key matching and removal
|
|
|
|
|
data.removeWhere((key, value) => key.toLowerCase() == 'sessionid' || key.toLowerCase() == 'tokenid');
|
|
|
|
|
|
|
|
|
|
return json.encode(data);
|
|
|
|
|
} catch (_) {
|
|
|
|
|
// Fallback: Handle Form URL Encoded strings or raw key-value pairs (SessionID=XYZ&TokenID=abc)
|
|
|
|
|
String modifiedBody = body;
|
|
|
|
|
final sensitiveKeys = ['SessionID', 'TokenID'];
|
|
|
|
|
|
|
|
|
|
for (var key in sensitiveKeys) {
|
|
|
|
|
// Matches "key=value" surrounded by standard query delimiters
|
|
|
|
|
final regExp = RegExp(r'' + key + r'=[^&]*&?', caseSensitive: false);
|
|
|
|
|
modifiedBody = modifiedBody.replaceAll(regExp, '');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clean up any trailing trailing ampersands
|
|
|
|
|
if (modifiedBody.endsWith('&')) {
|
|
|
|
|
modifiedBody = modifiedBody.substring(0, modifiedBody.length - 1);
|
|
|
|
|
}
|
|
|
|
|
return modifiedBody;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|