diff --git a/packages/gotrue/lib/src/fetch.dart b/packages/gotrue/lib/src/fetch.dart index c369f55d..d8fb9c9e 100644 --- a/packages/gotrue/lib/src/fetch.dart +++ b/packages/gotrue/lib/src/fetch.dart @@ -45,27 +45,38 @@ class GotrueFetch { if (error is! Response) { throw AuthRetryableFetchException(message: error.toString()); } + final response = error; // If the status is 500 or above, it's likely a server error, // and can be retried. - if (error.statusCode >= 500) { + if (response.statusCode >= 500) { throw AuthRetryableFetchException( - message: error.body, - statusCode: error.statusCode.toString(), + message: response.body, + statusCode: response.statusCode.toString(), ); } final dynamic data; + + // Catch this case as trying to decode it will throw a misleading [FormatException] + if (response.body.isEmpty) { + throw AuthUnknownException( + message: + 'Received an empty response with status code ${response.statusCode}', + originalError: response, + ); + } try { - data = jsonDecode(error.body); + data = jsonDecode(response.body); } catch (error) { throw AuthUnknownException( - message: error.toString(), originalError: error); + message: 'Failed to decode error response', + originalError: error, + ); } - String? errorCode; - final responseApiVersion = ApiVersion.fromResponse(error); + final responseApiVersion = ApiVersion.fromResponse(response); if (responseApiVersion?.isSameOrAfter(ApiVersions.v20240101) ?? false) { errorCode = _getErrorCode(data, 'code'); @@ -85,21 +96,21 @@ class GotrueFetch { .isEmpty) { throw AuthWeakPasswordException( message: _getErrorMessage(data), - statusCode: error.statusCode.toString(), + statusCode: response.statusCode.toString(), reasons: List.from(data['weak_password']['reasons']), ); } } else if (errorCode == ErrorCode.weakPassword.code) { throw AuthWeakPasswordException( message: _getErrorMessage(data), - statusCode: error.statusCode.toString(), + statusCode: response.statusCode.toString(), reasons: List.from(data['weak_password']?['reasons'] ?? []), ); } throw AuthApiException( _getErrorMessage(data), - statusCode: error.statusCode.toString(), + statusCode: response.statusCode.toString(), code: errorCode, ); } diff --git a/packages/gotrue/lib/src/gotrue_client.dart b/packages/gotrue/lib/src/gotrue_client.dart index 96e020b3..bd967b7b 100644 --- a/packages/gotrue/lib/src/gotrue_client.dart +++ b/packages/gotrue/lib/src/gotrue_client.dart @@ -1006,8 +1006,8 @@ class GoTrueClient { return AuthResponse(session: session); } - } catch (error) { - notifyException(error); + } catch (error, stackTrace) { + notifyException(error, stackTrace); rethrow; } } diff --git a/packages/gotrue/lib/src/types/auth_exception.dart b/packages/gotrue/lib/src/types/auth_exception.dart index 683e6b2f..d6b8553d 100644 --- a/packages/gotrue/lib/src/types/auth_exception.dart +++ b/packages/gotrue/lib/src/types/auth_exception.dart @@ -1,4 +1,5 @@ import 'package:gotrue/src/types/error_code.dart'; +import 'package:http/http.dart' as http; class AuthException implements Exception { /// Human readable error message associated with the error. @@ -46,6 +47,9 @@ class AuthSessionMissingException extends AuthException { message ?? 'Auth session missing!', statusCode: '400', ); + @override + String toString() => + 'AuthSessionMissingException(message: $message, statusCode: $statusCode)'; } class AuthRetryableFetchException extends AuthException { @@ -53,17 +57,37 @@ class AuthRetryableFetchException extends AuthException { String message = 'AuthRetryableFetchException', super.statusCode, }) : super(message); + + @override + String toString() => + 'AuthRetryableFetchException(message: $message, statusCode: $statusCode)'; } class AuthApiException extends AuthException { AuthApiException(super.message, {super.statusCode, super.code}); + + @override + String toString() => + 'AuthApiException(message: $message, statusCode: $statusCode, code: $code)'; } class AuthUnknownException extends AuthException { + /// May contain a non 2xx [http.Response] object or the original thrown error. final Object originalError; - AuthUnknownException({required String message, required this.originalError}) - : super(message); + AuthUnknownException({ + required String message, + required this.originalError, + }) : super( + message, + statusCode: originalError is http.Response + ? originalError.statusCode.toString() + : null, + ); + + @override + String toString() => + 'AuthUnknownException(message: $message, originalError: $originalError, statusCode: $statusCode)'; } class AuthWeakPasswordException extends AuthException { @@ -74,4 +98,8 @@ class AuthWeakPasswordException extends AuthException { required super.statusCode, required this.reasons, }) : super(message, code: ErrorCode.weakPassword.code); + + @override + String toString() => + 'AuthWeakPasswordException(message: $message, statusCode: $statusCode, reasons: $reasons)'; } diff --git a/packages/gotrue/lib/src/types/auth_state.dart b/packages/gotrue/lib/src/types/auth_state.dart index ec703011..b06c213a 100644 --- a/packages/gotrue/lib/src/types/auth_state.dart +++ b/packages/gotrue/lib/src/types/auth_state.dart @@ -13,6 +13,6 @@ class AuthState { @override String toString() { - return 'AuthState{event: $event, session: $session, fromBroadcast: $fromBroadcast}'; + return 'AuthState(event: $event, session: $session, fromBroadcast: $fromBroadcast)'; } } diff --git a/packages/gotrue/test/client_test.dart b/packages/gotrue/test/client_test.dart index 70f892a0..a1abc8b4 100644 --- a/packages/gotrue/test/client_test.dart +++ b/packages/gotrue/test/client_test.dart @@ -484,8 +484,11 @@ void main() { try { await client.signInWithPassword(email: email1, password: password); } catch (error) { - expect(error, isA()); - expect((error as AuthException).statusCode, '420'); + expect(error, isA()); + error as AuthUnknownException; + expect(error.statusCode, '420'); + expect(error.originalError, isA()); + expect(error.message, contains('empty response')); } }); }); diff --git a/packages/gotrue/test/custom_http_client.dart b/packages/gotrue/test/custom_http_client.dart index a4ee349e..3909cf08 100644 --- a/packages/gotrue/test/custom_http_client.dart +++ b/packages/gotrue/test/custom_http_client.dart @@ -10,7 +10,7 @@ class CustomHttpClient extends BaseClient { Future send(BaseRequest request) async { //Return custom status code to check for usage of this client. return StreamedResponse( - request.finalize(), + Stream.empty(), 420, request: request, );