Skip to content

Commit

Permalink
Refactor Auth and create common Celest interface
Browse files Browse the repository at this point in the history
  • Loading branch information
dnys1 committed Mar 8, 2024
1 parent 2726410 commit 5455826
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 89 deletions.
13 changes: 8 additions & 5 deletions packages/celest_auth/example/celest/lib/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ library; // ignore_for_file: no_leading_underscores_for_library_prefixes

import 'dart:io' as _$io;

import 'package:celest_core/celest_core.dart';
import 'package:celest_core/src/util/globals.dart';
import 'package:http/http.dart' as _$http;

Expand All @@ -25,21 +26,19 @@ enum CelestEnvironment {
};
}

class Celest {
class Celest with CelestBase {
var _initialized = false;

late CelestEnvironment _currentEnvironment;

@override
late _$http.Client httpClient = _$http.Client();

late Uri _baseUri;

final _functions = CelestFunctions();

late final CelestAuth _auth = CelestAuth(
baseUri: _baseUri,
httpClient: httpClient,
);
late final CelestAuth _auth = CelestAuth(this);

T _checkInitialized<T>(T Function() value) {
if (!_initialized) {
Expand All @@ -52,13 +51,17 @@ class Celest {
CelestEnvironment get currentEnvironment =>
_checkInitialized(() => _currentEnvironment);

@override
Uri get baseUri => _checkInitialized(() => _baseUri);

CelestFunctions get functions => _checkInitialized(() => _functions);

CelestAuth get auth => _checkInitialized(() => _auth);

void init({CelestEnvironment environment = CelestEnvironment.local}) {
if (environment != _currentEnvironment) {
_auth.signOut();
}
_currentEnvironment = environment;
_baseUri = environment.baseUri;
_auth.init();
Expand Down
26 changes: 4 additions & 22 deletions packages/celest_auth/example/celest/lib/src/client/auth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,10 @@ library; // ignore_for_file: no_leading_underscores_for_library_prefixes

import 'package:celest_auth/src/auth.dart' as _$auth;
import 'package:celest_auth/src/flows/email_flow.dart' as _$email_flow;
import 'package:celest_auth/src/state/auth_state.dart' as _$auth_state;
import 'package:http/http.dart' as _$http;
import 'package:celest_core/celest_core.dart';

class CelestAuth implements _$auth.Auth {
CelestAuth({
required Uri baseUri,
required _$http.Client httpClient,
}) : _hub = _$auth.AuthImpl(
baseUri: baseUri,
httpClient: httpClient,
);
extension type CelestAuth._(_$auth.AuthImpl _hub) implements _$auth.Auth {
CelestAuth(CelestBase celest) : _hub = _$auth.AuthImpl(celest);

final _$auth.AuthImpl _hub;

late final _$email_flow.Email email = _$email_flow.Email(_hub);

@override
void init() => _hub.init();

@override
_$auth_state.AuthState get authState => _hub.authState;

@override
Stream<_$auth_state.AuthState> get authStateChanges => _hub.authStateChanges;
_$email_flow.Email get email => _$email_flow.Email(_hub);
}
54 changes: 32 additions & 22 deletions packages/celest_auth/lib/src/auth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,33 @@ import 'dart:async';
import 'package:celest_auth/src/flows/auth_flow.dart';
import 'package:celest_auth/src/platform/auth_platform.dart';
import 'package:celest_auth/src/state/auth_state.dart';
// ignore: implementation_imports
import 'package:celest_core/celest_core.dart';
import 'package:celest_core/src/auth/auth_client.dart';
// ignore: implementation_imports
import 'package:celest_core/src/storage/local/local_storage.dart';
// ignore: implementation_imports
import 'package:celest_core/src/storage/secure/secure_storage.dart';
import 'package:http/http.dart' as http;
import 'package:stream_transform/stream_transform.dart';

/// Coordinates and delegates to the various [AuthFlow] to orchestrate
/// authentication activities.
///
/// Generated Celest clients extend this class and mix in the various
/// [AuthFlow]s supported by the backend.
abstract interface class Auth {
/// Initializes Celest Auth.
/// **NOTE**: Must be called before any other getters or methods are accessed.
///
/// Must be called before any other getters or methods are accessed.
void init();
/// Initializes Celest Auth, returning the initial [AuthState].
Future<AuthState> init();

AuthState get authState;
Stream<AuthState> get authStateChanges;

/// Signs out the current user, if any.
void signOut();
}

final class AuthImpl implements Auth {
AuthImpl({
required this.baseUri,
required this.httpClient,
AuthImpl(
this.celest, {
LocalStorage? localStorage,
SecureStorage? secureStorage,
}) : localStorage = localStorage ?? LocalStorage(),
Expand All @@ -41,20 +41,27 @@ final class AuthImpl implements Auth {
const Unauthenticated(); // TODO(dnys1): const AuthInitializing();

@override
Stream<AuthState> get authStateChanges => _authStateController.stream;
Stream<AuthState> get authStateChanges =>
_authStateController.stream.startWith(_authState);

final StreamController<AuthState> _authStateController =
StreamController.broadcast(sync: true);
StreamController.broadcast();

late final StreamSubscription<AuthState> _authStateSubscription;
StreamSubscription<AuthFlowState>? _authFlowSubscription;

@override
void init() {
_authStateSubscription =
authStateChanges.listen((state) => _authState = state);
Future<AuthState> init() {
return _init ??= Future.sync(() {
_authStateSubscription =
_authStateController.stream.listen((state) => _authState = state);
// TODO(dnys1): Proper init
return authStateChanges.first;
});
}

Future<AuthState>? _init;

Future<StreamSink<FlowState>>
requestFlow<FlowState extends AuthFlowState>() async {
switch (_authState) {
Expand All @@ -70,9 +77,10 @@ final class AuthImpl implements Auth {
'User is already authenticated. Sign out before continuing.',
);
case Unauthenticated() || NeedsReauthentication():
await _authFlowSubscription?.cancel();
unawaited(_authFlowSubscription?.cancel());
final previousState = _authState;
final controller = StreamController<FlowState>(
sync: true,
onCancel: () => _authStateController.add(previousState),
);
_authFlowSubscription = controller.stream.listen(
Expand All @@ -87,16 +95,18 @@ final class AuthImpl implements Auth {
}
}

final Uri baseUri;
final http.Client httpClient;
@override
void signOut() {
localStorage.delete('userId');
secureStorage.delete('cork');
_authStateController.add(const Unauthenticated());
}

final CelestBase celest;
final LocalStorage localStorage;
final SecureStorage secureStorage;

late final AuthClient protocol = AuthClient(
baseUri: baseUri,
httpClient: httpClient,
);
late final AuthClient protocol = AuthClient(celest);
late final AuthPlatform platform = AuthPlatform(protocol: protocol);

Future<void> close() async {
Expand Down
6 changes: 1 addition & 5 deletions packages/celest_auth/lib/src/flows/email_flow.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@ import 'package:celest_core/src/auth/auth_protocol.dart';
import 'package:celest_core/src/auth/otp/otp_types.dart';
import 'package:state_notifier/state_notifier.dart';

final class Email {
Email(this._hub);

final AuthImpl _hub;

extension type Email(AuthImpl _hub) {
Future<EmailSignUpNeedsVerification> signUp({
required String email,
}) async {
Expand Down
1 change: 1 addition & 0 deletions packages/celest_auth/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dependencies:
shelf: ^1.4.1
shelf_router: ^1.1.4
state_notifier: ^1.0.0
stream_transform: ^2.1.0
web: ^0.5.0

dev_dependencies:
Expand Down
3 changes: 3 additions & 0 deletions packages/celest_core/lib/celest_core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ library;
export 'src/auth/auth_exception.dart';
export 'src/auth/user.dart';

/// Base
export 'src/base/celest_base.dart';

/// Exceptions
export 'src/exception/celest_exception.dart';
export 'src/exception/cloud_exception.dart';
Expand Down
39 changes: 8 additions & 31 deletions packages/celest_core/lib/src/auth/auth_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,24 @@ import 'package:celest_core/src/auth/auth_protocol.dart';
import 'package:celest_core/src/auth/otp/otp_types.dart';
import 'package:celest_core/src/auth/passkeys/passkey_types.dart';
import 'package:celest_core/src/base/base_protocol.dart';
import 'package:http/http.dart' as http;

final class AuthClient implements AuthProtocol {
AuthClient({
required this.baseUri,
http.Client? httpClient,
}) : _client = httpClient ?? http.Client();
AuthClient(this.celest);

final http.Client _client;
final Uri baseUri;
final CelestBase celest;

@override
late final PasskeyClient passkeys = PasskeyClient(
baseUri: baseUri,
httpClient: _client,
);
late final PasskeyClient passkeys = PasskeyClient(celest);

@override
late final EmailClient email = EmailClient(
baseUri: baseUri,
httpClient: _client,
);
late final EmailClient email = EmailClient(celest);
}

final class PasskeyClient with BaseProtocol implements PasskeyProtocol {
PasskeyClient({
required this.baseUri,
required this.httpClient,
});
PasskeyClient(this.celest);

@override
final http.Client httpClient;

@override
final Uri baseUri;
final CelestBase celest;

@override
Future<PasskeyOptions> authenticate({
Expand All @@ -63,16 +46,10 @@ final class PasskeyClient with BaseProtocol implements PasskeyProtocol {
}

final class EmailClient with BaseProtocol implements EmailProtocol {
EmailClient({
required this.baseUri,
required this.httpClient,
});

@override
final http.Client httpClient;
EmailClient(this.celest);

@override
final Uri baseUri;
final CelestBase celest;

@override
Future<OtpParameters> sendOtp({
Expand Down
8 changes: 4 additions & 4 deletions packages/celest_core/lib/src/base/base_protocol.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import 'dart:convert';

import 'package:celest_core/src/base/celest_base.dart';
import 'package:http/http.dart' as http;

mixin BaseProtocol {
http.Client get httpClient;
Uri get baseUri;
CelestBase get celest;

Future<Map<String, Object?>> postJson(
String path,
Map<String, Object?> json,
) async {
final uri = baseUri.resolve(path);
final resp = await httpClient.post(
final uri = celest.baseUri.resolve(path);
final resp = await celest.httpClient.post(
uri,
body: jsonEncode(json),
headers: {
Expand Down
6 changes: 6 additions & 0 deletions packages/celest_core/lib/src/base/celest_base.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import 'package:http/http.dart' as http;

abstract mixin class CelestBase {
http.Client get httpClient;
Uri get baseUri;
}

0 comments on commit 5455826

Please sign in to comment.