diff --git a/packages/auth/amplify_auth_cognito_dart/lib/src/model/auth_result.dart b/packages/auth/amplify_auth_cognito_dart/lib/src/model/auth_result.dart index 88a908d5ca..454fde14f5 100644 --- a/packages/auth/amplify_auth_cognito_dart/lib/src/model/auth_result.dart +++ b/packages/auth/amplify_auth_cognito_dart/lib/src/model/auth_result.dart @@ -3,13 +3,12 @@ import 'package:amplify_core/amplify_core.dart'; -/// {@template amplify_auth_cognito.model.auth_result} /// The result of an Auth operation. -/// {@endtemplate} -class AuthResult extends AWSResult { - /// Creates a failed Auth result. - const AuthResult.error(super.exception, [super.stackTrace]) : super.error(); - - /// Creates a successful Auth result. - const AuthResult.success(super.value) : super.success(); -} +typedef AuthResult = AWSResult; + +/// A successful result to an Auth operation. +typedef AuthSuccessResult + = AWSSuccessResult; + +/// A failed result to an Auth operation. +typedef AuthErrorResult = AWSErrorResult; diff --git a/packages/authenticator/amplify_authenticator/lib/src/services/amplify_auth_service.dart b/packages/authenticator/amplify_authenticator/lib/src/services/amplify_auth_service.dart index a91971dfa7..da2c66b56c 100644 --- a/packages/authenticator/amplify_authenticator/lib/src/services/amplify_auth_service.dart +++ b/packages/authenticator/amplify_authenticator/lib/src/services/amplify_auth_service.dart @@ -203,22 +203,24 @@ class AmplifyAuthService @override Future isValidSession() async { return _withUserAgent(() async { - final res = await Amplify.Auth.fetchAuthSession() as CognitoAuthSession; - try { + final session = + await Amplify.Auth.fetchAuthSession() as CognitoAuthSession; + final CognitoAuthSession(:userPoolTokensResult) = session; + return switch (userPoolTokensResult) { // If tokens can be retrieved without an exception, return true. - res.userPoolTokensResult.value; - return true; - } on SignedOutException { - return false; - } on NetworkException { - // NetworkException indicates that access and/or id tokens have expired - // and cannot be refreshed due to a network error. In this case the user - // should be treated as authenticated to allow for offline use cases. - return true; - } on Exception { - // Any other exception should be thrown to be handled appropriately. - rethrow; - } + AuthSuccessResult _ => true, + AuthErrorResult(:final exception) => switch (exception) { + SignedOutException _ => false, + + // NetworkException indicates that access and/or id tokens have expired + // and cannot be refreshed due to a network error. In this case the user + // should be treated as authenticated to allow for offline use cases. + NetworkException _ => true, + + // Any other exception should be thrown to be handled appropriately. + _ => throw exception, + }, + }; }); } diff --git a/packages/aws_common/lib/src/result/aws_result.dart b/packages/aws_common/lib/src/result/aws_result.dart index 6359b3140d..d53705d255 100644 --- a/packages/aws_common/lib/src/result/aws_result.dart +++ b/packages/aws_common/lib/src/result/aws_result.dart @@ -10,69 +10,141 @@ import 'package:aws_common/aws_common.dart'; /// throw an exception if an exception occurred. See [exception] for more /// details. /// {@endtemplate} -class AWSResult - with AWSEquatable>, AWSDebuggable { +sealed class AWSResult + with AWSDebuggable, AWSSerializable> { /// Creates a failed result. - const AWSResult.error(E this.exception, [this.stackTrace]) - : type = AWSResultType.error, - _value = null; + const factory AWSResult.error(E exception, [StackTrace? stackTrace]) = + AWSErrorResult; /// Creates a successful result. - const AWSResult.success(T value) - : _value = value, - type = AWSResultType.success, - exception = null, - stackTrace = null; + const factory AWSResult.success(V value) = AWSSuccessResult; - /// The value of the result, or null. - final T? _value; + const AWSResult._(); /// The exception that occurred while attempting to retrieve the value. - final E? exception; + E? get exception; /// The original stack trace of [exception], if provided. - final StackTrace? stackTrace; + StackTrace? get stackTrace; /// Indicates if the result was a success. - final AWSResultType type; + @Deprecated('Use pattern matching instead') + AWSResultType get type; /// The value of the result, if the result was successful. /// /// If an exception was thrown while retrieving the value, this will throw. /// See [exception] for more details. - T get value { - switch (type) { - case AWSResultType.success: - // value will be non-null since it is required in AWSResult.success. - return _value!; - case AWSResultType.error: - if (stackTrace != null) { - // TODO(dnys1): Chain, instead, so that the current stack trace can - /// provide context to the original - } - // ignore: only_throw_errors - throw exception!; - } - } + V get value; /// The value of the result, or null if there was an error retrieving it. - T? get valueOrNull { - switch (type) { - case AWSResultType.success: - // value will be non-null since it is required in AWSResult.success. - return _value!; - case AWSResultType.error: - return null; + V? get valueOrNull; +} + +/// {@template aws_common.aws_success_result} +/// A successful [AWSResult]. +/// +/// For successful results, [value] is guaranteed to not throw. +/// {@endtemplate} +final class AWSSuccessResult + extends AWSResult with AWSEquatable> { + /// {@macro aws_common.aws_success_result} + const AWSSuccessResult(this.value) : super._(); + + @override + final V value; + + @override + V get valueOrNull => value; + + @override + AWSResultType get type => AWSResultType.success; + + @override + E? get exception => null; + + @override + StackTrace? get stackTrace => null; + + @override + List get props => [value]; + + @override + String get runtimeTypeName { + final typeName = StringBuffer('AWSSuccessResult<') + ..write( + switch (value) { + AWSDebuggable(:final runtimeTypeName) => runtimeTypeName, + _ => V, + }, + ) + ..write(', $E>'); + return typeName.toString(); + } + + @override + Map toJson() => { + 'value': switch (value) { + final AWSSerializable serializable => serializable.toJson(), + _ => value.toString(), + }, + }; +} + +/// {@template aws_common.aws_error_result} +/// A failed [AWSResult]. +/// +/// For failed results, [value] will always throw and [exception] is guaranteed +/// to not be `null`. +/// {@endtemplate} +final class AWSErrorResult + extends AWSResult with AWSEquatable> { + /// {@macro aws_common.aws_error_result} + const AWSErrorResult(this.exception, [this.stackTrace]) : super._(); + + @override + final E exception; + + @override + final StackTrace? stackTrace; + + @override + V get value { + if (stackTrace != null) { + // TODO(dnys1): Chain, instead, so that the current stack trace can + /// provide context to the original } + throw exception; } @override - List get props => [ - _value, - exception, - type, - ]; + V? get valueOrNull => null; + + @override + AWSResultType get type => AWSResultType.error; + + @override + List get props => [exception, stackTrace]; + + @override + String get runtimeTypeName { + final typeName = StringBuffer('AWSErrorResult<$V, ') + ..write( + switch (exception) { + AWSDebuggable(:final runtimeTypeName) => runtimeTypeName, + _ => E, + }, + ) + ..write('>'); + return typeName.toString(); + } @override - String get runtimeTypeName => 'AWSResult'; + Map toJson() => { + 'exception': switch (exception) { + final AWSSerializable serializable => serializable.toJson(), + _ => exception.toString(), + }, + 'stackTrace': stackTrace?.toString(), + }; } diff --git a/packages/aws_common/lib/src/util/debuggable.dart b/packages/aws_common/lib/src/util/debuggable.dart index cd1ba4a6a3..2532327d5f 100644 --- a/packages/aws_common/lib/src/util/debuggable.dart +++ b/packages/aws_common/lib/src/util/debuggable.dart @@ -16,13 +16,10 @@ mixin AWSDebuggable on Object { String get runtimeTypeName; @override - String toString() { - if (this is AWSSerializable) { - return '$runtimeTypeName ${prettyPrintJson((this as AWSSerializable).toJson())}'; - } - if (this is AWSEquatable) { - return '$runtimeTypeName ${(this as AWSEquatable).props}'; - } - return 'Instance of $runtimeTypeName'; - } + String toString() => switch (this) { + AWSSerializable(:final toJson) => + '$runtimeTypeName ${prettyPrintJson(toJson())}', + AWSEquatable(:final props) => '$runtimeTypeName $props', + _ => 'Instance of $runtimeTypeName', + }; }