Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(aws_common,3.0): Make AWSResult sealed #3012

Merged
merged 3 commits into from
May 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> extends AWSResult<T, AuthException> {
/// 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<T extends Object?> = AWSResult<T, AuthException>;

/// A successful result to an Auth operation.
typedef AuthSuccessResult<T extends Object?>
= AWSSuccessResult<T, AuthException>;

/// A failed result to an Auth operation.
typedef AuthErrorResult<T extends Object?> = AWSErrorResult<T, AuthException>;
Original file line number Diff line number Diff line change
Expand Up @@ -203,22 +203,24 @@ class AmplifyAuthService
@override
Future<bool> 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,
},
};
});
}

Expand Down
156 changes: 114 additions & 42 deletions packages/aws_common/lib/src/result/aws_result.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<T, E extends Exception>
with AWSEquatable<AWSResult<T, E>>, AWSDebuggable {
sealed class AWSResult<V extends Object?, E extends Exception>
with AWSDebuggable, AWSSerializable<Map<String, Object?>> {
/// 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<V, E>;

/// 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<V, E>;

/// 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;
Comment on lines +38 to +41
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given V extends Object? I'm not sure if we need this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value getter throws for error results whereas this returns null

}

/// {@template aws_common.aws_success_result}
/// A successful [AWSResult].
///
/// For successful results, [value] is guaranteed to not throw.
/// {@endtemplate}
final class AWSSuccessResult<V extends Object?, E extends Exception>
extends AWSResult<V, E> with AWSEquatable<AWSSuccessResult<V, E>> {
/// {@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<Object?> 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<String, Object?> 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<V extends Object?, E extends Exception>
extends AWSResult<V, E> with AWSEquatable<AWSErrorResult<V, E>> {
/// {@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<Object?> get props => [
_value,
exception,
type,
];
V? get valueOrNull => null;

@override
AWSResultType get type => AWSResultType.error;

@override
List<Object?> 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<String, Object?> toJson() => {
'exception': switch (exception) {
final AWSSerializable serializable => serializable.toJson(),
_ => exception.toString(),
},
'stackTrace': stackTrace?.toString(),
};
}
15 changes: 6 additions & 9 deletions packages/aws_common/lib/src/util/debuggable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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',
};
}
Loading