mirror of
https://github.com/hanskokx/arcane_framework.git
synced 2026-05-14 02:19:08 +02:00
v1.0.5
- Added the ability to use a generic type for the login method in ArcaneAuthenticationService - Added the ability to reset the ArcaneAuthenticationService, which will unregister the current interface and clear the authentication state - Removed unused testing tooling (e.g., `@visibleForTesting`) from the codebase Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
+17
-10
@@ -1,27 +1,34 @@
|
|||||||
|
## 1.0.5
|
||||||
|
|
||||||
|
- Added the ability to use a generic type for the login method in ArcaneAuthenticationService
|
||||||
|
- Added the ability to reset the ArcaneAuthenticationService, which will unregister the current interface and clear the authentication state
|
||||||
|
- Removed unused testing tooling (e.g., `@visibleForTesting`) from the codebase
|
||||||
|
- Migration guide: Remove usages of `setMocked` in your tests
|
||||||
|
|
||||||
## 1.0.4
|
## 1.0.4
|
||||||
|
|
||||||
* Resolved an issue with authentication using the ArcaneAuthenticationService when logging in with an email and password
|
- Resolved an issue with authentication using the ArcaneAuthenticationService when logging in with an email and password
|
||||||
|
|
||||||
## 1.0.3+1
|
## 1.0.3+1
|
||||||
|
|
||||||
* Added example project
|
- Added example project
|
||||||
|
|
||||||
## 1.0.3
|
## 1.0.3
|
||||||
|
|
||||||
* Added the ability to switch back to the normal environment from the debug environment in ArcaneEnvironment
|
- Added the ability to switch back to the normal environment from the debug environment in ArcaneEnvironment
|
||||||
* (breaking) Made the optional `onLoggedOut` callback a Future instead of a void function in ArcaneAuthenticationService
|
- (breaking) Made the optional `onLoggedOut` callback a Future instead of a void function in ArcaneAuthenticationService
|
||||||
* Added additional error handling to the login method in ArcaneAuthenticationService
|
- Added additional error handling to the login method in ArcaneAuthenticationService
|
||||||
* Added support for following the system's theme in ArcaneTheme
|
- Added support for following the system's theme in ArcaneTheme
|
||||||
* Removed the BuildContext parameter from the `switchTheme` method in ArcaneTheme
|
- Removed the BuildContext parameter from the `switchTheme` method in ArcaneTheme
|
||||||
|
|
||||||
## 1.0.2
|
## 1.0.2
|
||||||
|
|
||||||
* Migrated ArcaneAuthenticationService's isSignedIn to a ValueListenable
|
- Migrated ArcaneAuthenticationService's isSignedIn to a ValueListenable
|
||||||
|
|
||||||
## 1.0.1+1
|
## 1.0.1+1
|
||||||
|
|
||||||
* Removed ID and secure storage services to improve platform compatibility
|
- Removed ID and secure storage services to improve platform compatibility
|
||||||
|
|
||||||
## 1.0.0
|
## 1.0.0
|
||||||
|
|
||||||
* Initial release
|
- Initial release
|
||||||
|
|||||||
@@ -45,6 +45,22 @@ class DebugAuthInterface implements ArcaneAuthInterface {
|
|||||||
return Result.ok(null);
|
return Result.ok(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<void, String>> login<T>({
|
||||||
|
T? input,
|
||||||
|
Future<void> Function()? onLoggedIn,
|
||||||
|
}) async {
|
||||||
|
final bool alreadyLoggedIn = await isSignedIn;
|
||||||
|
|
||||||
|
if (alreadyLoggedIn) return Result.ok(null);
|
||||||
|
|
||||||
|
Arcane.log("Logging: $input");
|
||||||
|
|
||||||
|
_isSignedIn = true;
|
||||||
|
|
||||||
|
return Result.ok(null);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Result<String, String>> resendVerificationCode(String email) async {
|
Future<Result<String, String>> resendVerificationCode(String email) async {
|
||||||
Arcane.log("Re-sending verification code to $email");
|
Arcane.log("Re-sending verification code to $email");
|
||||||
|
|||||||
@@ -51,6 +51,27 @@ abstract class ArcaneAuthInterface {
|
|||||||
required String password,
|
required String password,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Logs the user in using an optional, generic `T` type of input.
|
||||||
|
/// This login method is a generic method that can be used to login with any
|
||||||
|
/// type of input. It is useful for login methods that do not require an email
|
||||||
|
/// and password. Any type of input can be passed in, and it will be handled
|
||||||
|
/// by the implementation of the method wihin the specific authentication
|
||||||
|
/// service.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```dart
|
||||||
|
/// await authInterface.login<Map<String, String>>(
|
||||||
|
/// input: {
|
||||||
|
/// "username": "hello@world.com",
|
||||||
|
/// "password": "password",
|
||||||
|
/// },
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
Future<Result<void, String>> login<T>({
|
||||||
|
T? input,
|
||||||
|
Future<void> Function()? onLoggedIn,
|
||||||
|
});
|
||||||
|
|
||||||
/// Re-sends a verification code to the user's email address.
|
/// Re-sends a verification code to the user's email address.
|
||||||
///
|
///
|
||||||
/// This method is typically used when the user hasn't received or has lost their initial
|
/// This method is typically used when the user hasn't received or has lost their initial
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ part "authentication_interface.dart";
|
|||||||
class ArcaneAuthenticationService extends ArcaneService {
|
class ArcaneAuthenticationService extends ArcaneService {
|
||||||
ArcaneAuthenticationService._internal();
|
ArcaneAuthenticationService._internal();
|
||||||
|
|
||||||
static bool _mocked = false;
|
|
||||||
static final ArcaneAuthenticationService _instance =
|
static final ArcaneAuthenticationService _instance =
|
||||||
ArcaneAuthenticationService._internal();
|
ArcaneAuthenticationService._internal();
|
||||||
|
|
||||||
@@ -32,11 +31,17 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
/// - `debug`: Debug mode has been enabled, enabling development features.
|
/// - `debug`: Debug mode has been enabled, enabling development features.
|
||||||
AuthenticationStatus get status => _status;
|
AuthenticationStatus get status => _status;
|
||||||
|
|
||||||
static late ArcaneAuthInterface _authInterface;
|
static ArcaneAuthInterface? _authInterface;
|
||||||
|
|
||||||
/// Provides direct access to the registered `ArcaneAuthInterface`, if one has
|
/// Provides direct access to the registered `ArcaneAuthInterface`, if one has
|
||||||
/// been registered.
|
/// been registered.
|
||||||
ArcaneAuthInterface get authInterface => _authInterface;
|
ArcaneAuthInterface get authInterface {
|
||||||
|
assert(
|
||||||
|
_authInterface != null,
|
||||||
|
"No ArcaneAuthInterface has been registered",
|
||||||
|
);
|
||||||
|
return _authInterface!;
|
||||||
|
}
|
||||||
|
|
||||||
/// A shortcut to `status != AuthenticationStatus.unauthenticated`.
|
/// A shortcut to `status != AuthenticationStatus.unauthenticated`.
|
||||||
bool get isAuthenticated => status != AuthenticationStatus.unauthenticated;
|
bool get isAuthenticated => status != AuthenticationStatus.unauthenticated;
|
||||||
@@ -53,8 +58,20 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
/// provides one.
|
/// provides one.
|
||||||
Future<String?> get refreshToken => authInterface.refreshToken;
|
Future<String?> get refreshToken => authInterface.refreshToken;
|
||||||
|
|
||||||
|
/// Removes any registered `ArcaneAuthInterface` and resets all values to
|
||||||
|
/// default.
|
||||||
|
Future<void> reset() async {
|
||||||
|
_authInterface = null;
|
||||||
|
_status = AuthenticationStatus.unauthenticated;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
/// Registers an `ArcaneAuthInterface` within the `ArcaneAuthenticationService`.
|
/// Registers an `ArcaneAuthInterface` within the `ArcaneAuthenticationService`.
|
||||||
Future<void> registerInterface(ArcaneAuthInterface authInterface) async {
|
Future<void> registerInterface(ArcaneAuthInterface authInterface) async {
|
||||||
|
if (_authInterface != null) {
|
||||||
|
throw Exception("ArcaneAuthInterface has already been registered");
|
||||||
|
}
|
||||||
|
|
||||||
_authInterface = authInterface;
|
_authInterface = authInterface;
|
||||||
await authInterface.init();
|
await authInterface.init();
|
||||||
}
|
}
|
||||||
@@ -66,8 +83,6 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
Future<void> Function()? onDebugModeSet,
|
Future<void> Function()? onDebugModeSet,
|
||||||
}) async {
|
}) async {
|
||||||
if (_mocked) return;
|
|
||||||
|
|
||||||
ArcaneEnvironment? environment;
|
ArcaneEnvironment? environment;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -98,8 +113,6 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
Future<void> Function()? onDebugModeUnset,
|
Future<void> Function()? onDebugModeUnset,
|
||||||
}) async {
|
}) async {
|
||||||
if (_mocked) return;
|
|
||||||
|
|
||||||
ArcaneEnvironment? environment;
|
ArcaneEnvironment? environment;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -125,21 +138,15 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
|
|
||||||
/// Sets `status` to `AuthenticationStatus.authenticated`.
|
/// Sets `status` to `AuthenticationStatus.authenticated`.
|
||||||
void setAuthenticated() {
|
void setAuthenticated() {
|
||||||
if (_mocked) return;
|
|
||||||
|
|
||||||
_setStatus(AuthenticationStatus.authenticated);
|
_setStatus(AuthenticationStatus.authenticated);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets `status` to `AuthenticationStatus.unauthenticated`.
|
/// Sets `status` to `AuthenticationStatus.unauthenticated`.
|
||||||
void setUnauthenticated() {
|
void setUnauthenticated() {
|
||||||
if (_mocked) return;
|
|
||||||
|
|
||||||
_setStatus(AuthenticationStatus.unauthenticated);
|
_setStatus(AuthenticationStatus.unauthenticated);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setStatus(AuthenticationStatus newStatus) {
|
void _setStatus(AuthenticationStatus newStatus) {
|
||||||
if (_mocked) return;
|
|
||||||
|
|
||||||
_status = newStatus;
|
_status = newStatus;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
@@ -147,9 +154,13 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
/// Logs the current user out. Upon successful logout, `status` will be set to
|
/// Logs the current user out. Upon successful logout, `status` will be set to
|
||||||
/// `AuthenticationStatus.unauthenticated`.
|
/// `AuthenticationStatus.unauthenticated`.
|
||||||
Future<void> logOut({Future<void> Function()? onLoggedOut}) async {
|
Future<void> logOut({Future<void> Function()? onLoggedOut}) async {
|
||||||
if (_mocked) return;
|
|
||||||
if (!isAuthenticated) return;
|
if (!isAuthenticated) return;
|
||||||
|
|
||||||
|
assert(
|
||||||
|
_authInterface != null,
|
||||||
|
"No ArcaneAuthInterface has been registered",
|
||||||
|
);
|
||||||
|
|
||||||
final Result<void, String> loggedOut = await authInterface.logout();
|
final Result<void, String> loggedOut = await authInterface.logout();
|
||||||
|
|
||||||
await loggedOut.fold(
|
await loggedOut.fold(
|
||||||
@@ -174,8 +185,6 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
required String password,
|
required String password,
|
||||||
Future<void> Function()? onLoggedIn,
|
Future<void> Function()? onLoggedIn,
|
||||||
}) async {
|
}) async {
|
||||||
if (_mocked) return Result.ok(null);
|
|
||||||
|
|
||||||
if (status != AuthenticationStatus.unauthenticated) {
|
if (status != AuthenticationStatus.unauthenticated) {
|
||||||
return Result.error("Cannot sign in. Status is already ${status.name}.");
|
return Result.error("Cannot sign in. Status is already ${status.name}.");
|
||||||
}
|
}
|
||||||
@@ -194,6 +203,27 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Logs the user in using an optional, generic `T` type of input.
|
||||||
|
Future<Result<void, String>> login<T>({
|
||||||
|
T? input,
|
||||||
|
Future<void> Function()? onLoggedIn,
|
||||||
|
}) async {
|
||||||
|
if (_authInterface == null) {
|
||||||
|
return Result.error("No ArcaneAuthInterface has been registered");
|
||||||
|
}
|
||||||
|
|
||||||
|
final Result<void, String> result = await authInterface.login(
|
||||||
|
input: input,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.isSuccess) {
|
||||||
|
setAuthenticated();
|
||||||
|
if (onLoggedIn != null) await onLoggedIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempts to register a new account using the provided `email` and
|
/// Attempts to register a new account using the provided `email` and
|
||||||
/// `password`. Upon success, returns a `SignUpStep` indicating the next step
|
/// `password`. Upon success, returns a `SignUpStep` indicating the next step
|
||||||
/// in the signup process as a `SignUpStep`.
|
/// in the signup process as a `SignUpStep`.
|
||||||
@@ -201,7 +231,10 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
required String email,
|
required String email,
|
||||||
required String password,
|
required String password,
|
||||||
}) async {
|
}) async {
|
||||||
if (_mocked) return Result.ok(SignUpStep.done);
|
if (_authInterface == null) {
|
||||||
|
return Result.error("No ArcaneAuthInterface has been registered");
|
||||||
|
}
|
||||||
|
|
||||||
final Result<SignUpStep, String> result = await authInterface.signup(
|
final Result<SignUpStep, String> result = await authInterface.signup(
|
||||||
email: email,
|
email: email,
|
||||||
password: password,
|
password: password,
|
||||||
@@ -216,7 +249,10 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
required String email,
|
required String email,
|
||||||
required String confirmationCode,
|
required String confirmationCode,
|
||||||
}) async {
|
}) async {
|
||||||
if (_mocked) return Result.ok(false);
|
if (_authInterface == null) {
|
||||||
|
return Result.error("No ArcaneAuthInterface has been registered");
|
||||||
|
}
|
||||||
|
|
||||||
final Result<bool, String> result = await authInterface.confirmSignup(
|
final Result<bool, String> result = await authInterface.confirmSignup(
|
||||||
username: email,
|
username: email,
|
||||||
confirmationCode: confirmationCode,
|
confirmationCode: confirmationCode,
|
||||||
@@ -228,7 +264,10 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
/// Re-sends a verification code to be used when confirming the user's
|
/// Re-sends a verification code to be used when confirming the user's
|
||||||
/// registration.
|
/// registration.
|
||||||
Future<Result<String, String>> resendVerificationCode(String email) async {
|
Future<Result<String, String>> resendVerificationCode(String email) async {
|
||||||
if (_mocked) return Result.ok("");
|
if (_authInterface == null) {
|
||||||
|
return Result.error("No ArcaneAuthInterface has been registered");
|
||||||
|
}
|
||||||
|
|
||||||
return authInterface.resendVerificationCode(email);
|
return authInterface.resendVerificationCode(email);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +282,10 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
String? newPassword,
|
String? newPassword,
|
||||||
String? confirmationCode,
|
String? confirmationCode,
|
||||||
}) async {
|
}) async {
|
||||||
if (_mocked) return Result.ok(false);
|
if (_authInterface == null) {
|
||||||
|
return Result.error("No ArcaneAuthInterface has been registered");
|
||||||
|
}
|
||||||
|
|
||||||
final Result<bool, String> result = await authInterface.resetPassword(
|
final Result<bool, String> result = await authInterface.resetPassword(
|
||||||
email: email,
|
email: email,
|
||||||
newPassword: newPassword,
|
newPassword: newPassword,
|
||||||
@@ -252,12 +294,4 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marks the service as mocked for testing purposes.
|
|
||||||
///
|
|
||||||
/// If the service is mocked, no method will be executed.
|
|
||||||
@visibleForTesting
|
|
||||||
static void setMocked() {
|
|
||||||
_mocked = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import "package:arcane_framework/arcane_framework.dart";
|
import "package:arcane_framework/arcane_framework.dart";
|
||||||
import "package:flutter/foundation.dart";
|
|
||||||
|
|
||||||
part "feature_flags_extensions.dart";
|
part "feature_flags_extensions.dart";
|
||||||
|
|
||||||
@@ -41,15 +40,6 @@ class ArcaneFeatureFlags extends ArcaneService {
|
|||||||
/// to access the instance.
|
/// to access the instance.
|
||||||
static bool get initialized => I._initialized;
|
static bool get initialized => I._initialized;
|
||||||
|
|
||||||
bool _mocked = false;
|
|
||||||
|
|
||||||
/// Marks the feature flags as mocked for testing purposes.
|
|
||||||
///
|
|
||||||
/// When the feature flags are mocked, they bypass certain initializations, making
|
|
||||||
/// them easier to work with in unit tests.
|
|
||||||
@visibleForTesting
|
|
||||||
void setMocked() => _mocked = true;
|
|
||||||
|
|
||||||
/// Checks if a specific [feature] is enabled.
|
/// Checks if a specific [feature] is enabled.
|
||||||
///
|
///
|
||||||
/// Returns `true` if the [feature] is in the list of enabled features, otherwise returns `false`.
|
/// Returns `true` if the [feature] is in the list of enabled features, otherwise returns `false`.
|
||||||
@@ -125,8 +115,6 @@ class ArcaneFeatureFlags extends ArcaneService {
|
|||||||
/// It is called automatically when enabling or disabling features if they haven't
|
/// It is called automatically when enabling or disabling features if they haven't
|
||||||
/// already been initialized.
|
/// already been initialized.
|
||||||
void _init() {
|
void _init() {
|
||||||
if (_mocked) return;
|
|
||||||
|
|
||||||
_enabledFeatures.clear();
|
_enabledFeatures.clear();
|
||||||
|
|
||||||
I._initialized = true;
|
I._initialized = true;
|
||||||
|
|||||||
@@ -35,22 +35,12 @@ class ArcaneLogger {
|
|||||||
/// Whether the logger has been initialized.
|
/// Whether the logger has been initialized.
|
||||||
bool get initialized => I._initialized;
|
bool get initialized => I._initialized;
|
||||||
|
|
||||||
/// Marks the logger as mocked for testing purposes.
|
|
||||||
///
|
|
||||||
/// If the logger is mocked, platform-specific features (such as tracking
|
|
||||||
/// status) will not be initialized.
|
|
||||||
@visibleForTesting
|
|
||||||
void setMocked() => _mocked = true;
|
|
||||||
bool _mocked = false;
|
|
||||||
|
|
||||||
/// Initializes the logger.
|
/// Initializes the logger.
|
||||||
///
|
///
|
||||||
/// Sets up error handling for both Flutter and platform-specific errors.
|
/// Sets up error handling for both Flutter and platform-specific errors.
|
||||||
/// Also, retrieves the tracking authorization status if running on iOS or
|
/// Also, retrieves the tracking authorization status if running on iOS or
|
||||||
/// macOS.
|
/// macOS.
|
||||||
Future<void> _init() async {
|
Future<void> _init() async {
|
||||||
if (_mocked) return;
|
|
||||||
|
|
||||||
additionalMetadata.clear();
|
additionalMetadata.clear();
|
||||||
|
|
||||||
// Handles unhandled Flutter errors by logging them.
|
// Handles unhandled Flutter errors by logging them.
|
||||||
@@ -146,8 +136,6 @@ class ArcaneLogger {
|
|||||||
StackTrace? stackTrace,
|
StackTrace? stackTrace,
|
||||||
Map<String, String>? metadata,
|
Map<String, String>? metadata,
|
||||||
}) {
|
}) {
|
||||||
if (I._mocked) return;
|
|
||||||
|
|
||||||
if (!I._initialized) {
|
if (!I._initialized) {
|
||||||
throw Exception("ArcaneLogger has not yet been initialized.");
|
throw Exception("ArcaneLogger has not yet been initialized.");
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
name: arcane_framework
|
name: arcane_framework
|
||||||
description: "Agnostic Reusable Component Architecture for New Ecosystems: a modern framework for bootstrapping new applications"
|
description: "Agnostic Reusable Component Architecture for New Ecosystems: a modern framework for bootstrapping new applications"
|
||||||
version: 1.0.4
|
version: 1.0.5
|
||||||
repository: https://github.com/hanskokx/arcane_framework
|
repository: https://github.com/hanskokx/arcane_framework
|
||||||
issue_tracker: https://github.com/hanskokx/arcane_framework/issues
|
issue_tracker: https://github.com/hanskokx/arcane_framework/issues
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user