Refactored error handling, configuration, and telemetry logic for improved readability, efficiency, and maintainability.
This commit is contained in:
parent
d8f91f42e5
commit
73597b9bca
|
|
@ -11,6 +11,40 @@ class AppConfig {
|
|||
required this.keycloakRedirectUrl,
|
||||
});
|
||||
|
||||
factory AppConfig.fromEnvironment() {
|
||||
const apiBaseUrl = String.fromEnvironment('API_BASE_URL', defaultValue: '');
|
||||
const useLocalAuth =
|
||||
bool.fromEnvironment('USE_LOCAL_AUTH', defaultValue: false);
|
||||
const localTenantId = String.fromEnvironment(
|
||||
'LOCAL_TENANT_ID',
|
||||
defaultValue: '11111111-1111-1111-1111-111111111111',
|
||||
);
|
||||
const localRoles =
|
||||
String.fromEnvironment('LOCAL_ROLES', defaultValue: 'CAREGIVER');
|
||||
const keycloakIssuer =
|
||||
String.fromEnvironment('KEYCLOAK_ISSUER', defaultValue: '');
|
||||
const keycloakIssuerUri =
|
||||
String.fromEnvironment('KEYCLOAK_ISSUER_URI', defaultValue: '');
|
||||
final resolvedIssuer =
|
||||
keycloakIssuer.isNotEmpty ? keycloakIssuer : keycloakIssuerUri;
|
||||
const keycloakClientId =
|
||||
String.fromEnvironment('KEYCLOAK_CLIENT_ID', defaultValue: '');
|
||||
const keycloakRedirectUrl = String.fromEnvironment(
|
||||
'KEYCLOAK_REDIRECT_URL',
|
||||
defaultValue: '',
|
||||
);
|
||||
|
||||
return AppConfig(
|
||||
apiBaseUrl: apiBaseUrl,
|
||||
useLocalAuth: useLocalAuth,
|
||||
localTenantId: localTenantId,
|
||||
localRoles: localRoles,
|
||||
keycloakIssuer: resolvedIssuer,
|
||||
keycloakClientId: keycloakClientId,
|
||||
keycloakRedirectUrl: keycloakRedirectUrl,
|
||||
);
|
||||
}
|
||||
|
||||
final String apiBaseUrl;
|
||||
final bool useLocalAuth;
|
||||
final String localTenantId;
|
||||
|
|
|
|||
|
|
@ -22,37 +22,7 @@ import '../features/telemetry/domain/token_provider.dart';
|
|||
import '../features/telemetry/presentation/telemetry_controller.dart';
|
||||
import '../features/telemetry/presentation/telemetry_state.dart';
|
||||
|
||||
final appConfigProvider = Provider<AppConfig>((ref) {
|
||||
const apiBaseUrl = String.fromEnvironment('API_BASE_URL', defaultValue: '');
|
||||
const useLocalAuth =
|
||||
bool.fromEnvironment('USE_LOCAL_AUTH', defaultValue: false);
|
||||
const localTenantId = String.fromEnvironment(
|
||||
'LOCAL_TENANT_ID',
|
||||
defaultValue: '11111111-1111-1111-1111-111111111111',
|
||||
);
|
||||
const localRoles =
|
||||
String.fromEnvironment('LOCAL_ROLES', defaultValue: 'CAREGIVER');
|
||||
const keycloakIssuer = String.fromEnvironment('KEYCLOAK_ISSUER', defaultValue: '');
|
||||
const keycloakIssuerUri =
|
||||
String.fromEnvironment('KEYCLOAK_ISSUER_URI', defaultValue: '');
|
||||
final resolvedIssuer =
|
||||
keycloakIssuer.isNotEmpty ? keycloakIssuer : keycloakIssuerUri;
|
||||
const keycloakClientId =
|
||||
String.fromEnvironment('KEYCLOAK_CLIENT_ID', defaultValue: '');
|
||||
const keycloakRedirectUrl = String.fromEnvironment(
|
||||
'KEYCLOAK_REDIRECT_URL',
|
||||
defaultValue: '',
|
||||
);
|
||||
return AppConfig(
|
||||
apiBaseUrl: apiBaseUrl,
|
||||
useLocalAuth: useLocalAuth,
|
||||
localTenantId: localTenantId,
|
||||
localRoles: localRoles,
|
||||
keycloakIssuer: resolvedIssuer,
|
||||
keycloakClientId: keycloakClientId,
|
||||
keycloakRedirectUrl: keycloakRedirectUrl,
|
||||
);
|
||||
});
|
||||
final appConfigProvider = Provider<AppConfig>((ref) => AppConfig.fromEnvironment());
|
||||
|
||||
final secureStorageProvider = Provider<FlutterSecureStorage>((ref) {
|
||||
return const FlutterSecureStorage();
|
||||
|
|
@ -85,9 +55,11 @@ final dioProvider = Provider<Dio>((ref) {
|
|||
if (config.useLocalAuth) {
|
||||
final session = await localDataSource.readLocalSession();
|
||||
if (session != null) {
|
||||
options.headers['X-Local-Email'] = session.email;
|
||||
options.headers['X-Local-Roles'] = session.roles;
|
||||
options.headers['X-Tenant-Id'] = session.tenantId;
|
||||
options.headers.addAll({
|
||||
'X-Local-Email': session.email,
|
||||
'X-Local-Roles': session.roles,
|
||||
'X-Tenant-Id': session.tenantId,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
final token = await localDataSource.readToken();
|
||||
|
|
@ -98,21 +70,18 @@ final dioProvider = Provider<Dio>((ref) {
|
|||
handler.next(options);
|
||||
},
|
||||
onError: (error, handler) async {
|
||||
if (config.useLocalAuth) {
|
||||
handler.next(error);
|
||||
return;
|
||||
}
|
||||
final response = error.response;
|
||||
final requestOptions = error.requestOptions;
|
||||
if (response?.statusCode != 401 || requestOptions.extra['retried'] == true) {
|
||||
handler.next(error);
|
||||
return;
|
||||
}
|
||||
final refreshToken = (await localDataSource.readToken())?.refreshToken;
|
||||
if (refreshToken == null || refreshToken.isEmpty) {
|
||||
handler.next(error);
|
||||
return;
|
||||
|
||||
if (config.useLocalAuth ||
|
||||
response?.statusCode != 401 ||
|
||||
requestOptions.extra['retried'] == true ||
|
||||
refreshToken == null ||
|
||||
refreshToken.isEmpty) {
|
||||
return handler.next(error);
|
||||
}
|
||||
|
||||
try {
|
||||
final newToken =
|
||||
await authRemoteDataSource.refreshToken(refreshToken: refreshToken);
|
||||
|
|
|
|||
|
|
@ -40,9 +40,15 @@ class AuthLocalDataSource {
|
|||
}
|
||||
|
||||
Future<LocalAuthSession?> readLocalSession() async {
|
||||
final email = await _storage.read(key: _localEmailKey);
|
||||
final roles = await _storage.read(key: _localRolesKey);
|
||||
final tenantId = await _storage.read(key: _localTenantKey);
|
||||
final results = await Future.wait([
|
||||
_storage.read(key: _localEmailKey),
|
||||
_storage.read(key: _localRolesKey),
|
||||
_storage.read(key: _localTenantKey),
|
||||
]);
|
||||
final email = results[0];
|
||||
final roles = results[1];
|
||||
final tenantId = results[2];
|
||||
|
||||
if (email == null || tenantId == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -54,11 +60,13 @@ class AuthLocalDataSource {
|
|||
}
|
||||
|
||||
Future<void> clear() async {
|
||||
await _storage.delete(key: _accessTokenKey);
|
||||
await _storage.delete(key: _refreshTokenKey);
|
||||
await _storage.delete(key: _localEmailKey);
|
||||
await _storage.delete(key: _localRolesKey);
|
||||
await _storage.delete(key: _localTenantKey);
|
||||
await Future.wait([
|
||||
_storage.delete(key: _accessTokenKey),
|
||||
_storage.delete(key: _refreshTokenKey),
|
||||
_storage.delete(key: _localEmailKey),
|
||||
_storage.delete(key: _localRolesKey),
|
||||
_storage.delete(key: _localTenantKey),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -76,9 +76,8 @@ class AuthController extends Notifier<AuthState> {
|
|||
|
||||
String _friendlyError(Object error) {
|
||||
final message = error.toString();
|
||||
if (message.startsWith('Exception: ')) {
|
||||
return message.substring('Exception: '.length);
|
||||
}
|
||||
return message;
|
||||
return message.startsWith('Exception: ')
|
||||
? message.substring('Exception: '.length)
|
||||
: message;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,13 +13,9 @@ class HomePage extends ConsumerStatefulWidget {
|
|||
}
|
||||
|
||||
class _HomePageState extends ConsumerState<HomePage> {
|
||||
late final ProviderSubscription<TelemetryState> _telemetrySubscription;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_telemetrySubscription =
|
||||
ref.listenManual<TelemetryState>(telemetryControllerProvider, (previous, next) {
|
||||
Widget build(BuildContext context) {
|
||||
ref.listen<TelemetryState>(telemetryControllerProvider, (previous, next) {
|
||||
if (previous?.lastOutcome == next.lastOutcome ||
|
||||
next.lastOutcome == null ||
|
||||
!mounted) {
|
||||
|
|
@ -37,16 +33,7 @@ class _HomePageState extends ConsumerState<HomePage> {
|
|||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_telemetrySubscription.close();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final telemetryState = ref.watch(telemetryControllerProvider);
|
||||
|
||||
|
|
|
|||
|
|
@ -28,17 +28,14 @@ class TelemetryServiceImpl implements TelemetryService {
|
|||
}
|
||||
|
||||
try {
|
||||
final payload = _payloadBuilder.build();
|
||||
final response = await _remoteDataSource.sendTestTelemetry(
|
||||
accessToken: accessToken,
|
||||
payload: payload,
|
||||
payload: _payloadBuilder.build(),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return;
|
||||
if (response.statusCode != 200 && response.statusCode != 201) {
|
||||
throw _failureFromStatus(response.statusCode);
|
||||
}
|
||||
|
||||
throw _failureFromStatus(response.statusCode);
|
||||
} on HttpClientException catch (error) {
|
||||
throw _failureFromHttp(error);
|
||||
} on TelemetryFailure {
|
||||
|
|
@ -53,19 +50,16 @@ class TelemetryServiceImpl implements TelemetryService {
|
|||
}
|
||||
|
||||
TelemetryFailure _failureFromStatus(int statusCode) {
|
||||
if (statusCode == 401 || statusCode == 403) {
|
||||
return TelemetryFailure(
|
||||
'Not authorized',
|
||||
type: TelemetryFailureType.unauthorized,
|
||||
statusCode: statusCode,
|
||||
debugMessage: 'Telemetry request unauthorized ($statusCode)',
|
||||
);
|
||||
}
|
||||
final isUnauthorized = statusCode == 401 || statusCode == 403;
|
||||
return TelemetryFailure(
|
||||
'Telemetry request failed',
|
||||
type: TelemetryFailureType.server,
|
||||
isUnauthorized ? 'Not authorized' : 'Telemetry request failed',
|
||||
type: isUnauthorized
|
||||
? TelemetryFailureType.unauthorized
|
||||
: TelemetryFailureType.server,
|
||||
statusCode: statusCode,
|
||||
debugMessage: 'Telemetry request failed with status $statusCode',
|
||||
debugMessage: isUnauthorized
|
||||
? 'Telemetry request unauthorized ($statusCode)'
|
||||
: 'Telemetry request failed with status $statusCode',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue