feat(apps): add auth repository
This commit is contained in:
@@ -0,0 +1,55 @@
|
|||||||
|
import 'package:social_app/core/api/api_client.dart';
|
||||||
|
import 'models/signup_request.dart';
|
||||||
|
import 'models/login_request.dart';
|
||||||
|
import 'models/auth_response.dart';
|
||||||
|
|
||||||
|
class AuthApi {
|
||||||
|
final ApiClient _client;
|
||||||
|
static const _prefix = '/v1/auth';
|
||||||
|
|
||||||
|
AuthApi(this._client);
|
||||||
|
|
||||||
|
Future<SignupStartResponse> signupStart(SignupStartRequest request) async {
|
||||||
|
final response = await _client.post(
|
||||||
|
'$_prefix/signup/start',
|
||||||
|
data: request.toJson(),
|
||||||
|
);
|
||||||
|
return SignupStartResponse.fromJson(response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<AuthResponse> signupVerify(SignupVerifyRequest request) async {
|
||||||
|
final response = await _client.post(
|
||||||
|
'$_prefix/signup/verify',
|
||||||
|
data: request.toJson(),
|
||||||
|
);
|
||||||
|
return AuthResponse.fromJson(response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SignupStartResponse> signupResend(SignupResendRequest request) async {
|
||||||
|
final response = await _client.post(
|
||||||
|
'$_prefix/signup/resend',
|
||||||
|
data: request.toJson(),
|
||||||
|
);
|
||||||
|
return SignupStartResponse.fromJson(response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<AuthResponse> login(LoginRequest request) async {
|
||||||
|
final response = await _client.post(
|
||||||
|
'$_prefix/login',
|
||||||
|
data: request.toJson(),
|
||||||
|
);
|
||||||
|
return AuthResponse.fromJson(response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<AuthResponse> refresh(RefreshRequest request) async {
|
||||||
|
final response = await _client.post(
|
||||||
|
'$_prefix/refresh',
|
||||||
|
data: request.toJson(),
|
||||||
|
);
|
||||||
|
return AuthResponse.fromJson(response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> logout(LogoutRequest request) async {
|
||||||
|
await _client.post('$_prefix/logout', data: request.toJson());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import 'package:social_app/features/auth/data/models/signup_request.dart';
|
||||||
|
import 'package:social_app/features/auth/data/models/login_request.dart';
|
||||||
|
import 'package:social_app/features/auth/data/models/auth_response.dart';
|
||||||
|
|
||||||
|
abstract class AuthRepository {
|
||||||
|
Future<SignupStartResponse> signupStart(SignupStartRequest request);
|
||||||
|
Future<AuthResponse> signupVerify(SignupVerifyRequest request);
|
||||||
|
Future<SignupStartResponse> signupResend(SignupResendRequest request);
|
||||||
|
Future<AuthResponse> login(LoginRequest request);
|
||||||
|
Future<AuthResponse> refresh(String refreshToken);
|
||||||
|
Future<void> logout();
|
||||||
|
Future<String?> getAccessToken();
|
||||||
|
Future<String?> getRefreshToken();
|
||||||
|
Future<bool> isAuthenticated();
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import 'package:social_app/core/storage/token_storage.dart';
|
||||||
|
import 'auth_api.dart';
|
||||||
|
import 'auth_repository.dart';
|
||||||
|
import 'models/signup_request.dart';
|
||||||
|
import 'models/login_request.dart';
|
||||||
|
import 'models/auth_response.dart';
|
||||||
|
|
||||||
|
class AuthRepositoryImpl implements AuthRepository {
|
||||||
|
final AuthApi _api;
|
||||||
|
final TokenStorage _tokenStorage;
|
||||||
|
|
||||||
|
AuthRepositoryImpl({required AuthApi api, required TokenStorage tokenStorage})
|
||||||
|
: _api = api,
|
||||||
|
_tokenStorage = tokenStorage;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<SignupStartResponse> signupStart(SignupStartRequest request) {
|
||||||
|
return _api.signupStart(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<AuthResponse> signupVerify(SignupVerifyRequest request) async {
|
||||||
|
final response = await _api.signupVerify(request);
|
||||||
|
await _tokenStorage.saveTokens(
|
||||||
|
access: response.accessToken,
|
||||||
|
refresh: response.refreshToken,
|
||||||
|
);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<SignupStartResponse> signupResend(SignupResendRequest request) {
|
||||||
|
return _api.signupResend(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<AuthResponse> login(LoginRequest request) async {
|
||||||
|
final response = await _api.login(request);
|
||||||
|
await _tokenStorage.saveTokens(
|
||||||
|
access: response.accessToken,
|
||||||
|
refresh: response.refreshToken,
|
||||||
|
);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<AuthResponse> refresh(String refreshToken) async {
|
||||||
|
final response = await _api.refresh(
|
||||||
|
RefreshRequest(refreshToken: refreshToken),
|
||||||
|
);
|
||||||
|
await _tokenStorage.saveTokens(
|
||||||
|
access: response.accessToken,
|
||||||
|
refresh: response.refreshToken,
|
||||||
|
);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> logout() async {
|
||||||
|
final refreshToken = await _tokenStorage.getRefreshToken();
|
||||||
|
if (refreshToken != null) {
|
||||||
|
await _api.logout(LogoutRequest(refreshToken: refreshToken));
|
||||||
|
}
|
||||||
|
await _tokenStorage.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> getAccessToken() => _tokenStorage.getAccessToken();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> getRefreshToken() => _tokenStorage.getRefreshToken();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> isAuthenticated() async {
|
||||||
|
final token = await _tokenStorage.getAccessToken();
|
||||||
|
return token != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:social_app/features/auth/data/auth_repository.dart';
|
||||||
|
import 'package:social_app/features/auth/data/auth_repository_impl.dart';
|
||||||
|
import 'package:social_app/features/auth/data/models/signup_request.dart';
|
||||||
|
import 'package:social_app/features/auth/data/models/login_request.dart';
|
||||||
|
import 'package:social_app/features/auth/data/models/auth_response.dart';
|
||||||
|
import 'package:social_app/core/storage/token_storage.dart';
|
||||||
|
|
||||||
|
class MockTokenStorage extends Mock implements TokenStorage {}
|
||||||
|
|
||||||
|
class FakeSignupStartRequest extends Fake implements SignupStartRequest {}
|
||||||
|
|
||||||
|
class FakeLoginRequest extends Fake implements LoginRequest {}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late AuthRepository repository;
|
||||||
|
late MockTokenStorage mockStorage;
|
||||||
|
|
||||||
|
setUpAll(() {
|
||||||
|
registerFallbackValue(FakeSignupStartRequest());
|
||||||
|
registerFallbackValue(FakeLoginRequest());
|
||||||
|
});
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
mockStorage = MockTokenStorage();
|
||||||
|
});
|
||||||
|
|
||||||
|
group('AuthRepository', () {
|
||||||
|
test('signupStart returns SignupStartResponse', () async {
|
||||||
|
final repo = _MockAuthRepository();
|
||||||
|
when(() => repo.signupStart(any())).thenAnswer(
|
||||||
|
(_) async => const SignupStartResponse(
|
||||||
|
status: 'pending_verification',
|
||||||
|
email: 'test@example.com',
|
||||||
|
message: 'Verification code sent',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final result = await repo.signupStart(
|
||||||
|
const SignupStartRequest(
|
||||||
|
username: 'testuser',
|
||||||
|
email: 'test@example.com',
|
||||||
|
password: 'password123',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.status, 'pending_verification');
|
||||||
|
expect(result.email, 'test@example.com');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('login returns AuthResponse and saves tokens', () async {
|
||||||
|
final repo = _MockAuthRepository();
|
||||||
|
when(() => repo.login(any())).thenAnswer(
|
||||||
|
(_) async => AuthResponse(
|
||||||
|
accessToken: 'access_token',
|
||||||
|
refreshToken: 'refresh_token',
|
||||||
|
expiresIn: 3600,
|
||||||
|
tokenType: 'bearer',
|
||||||
|
user: const AuthUser(id: '123', email: 'test@example.com'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final result = await repo.login(
|
||||||
|
const LoginRequest(email: 'test@example.com', password: 'password123'),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.accessToken, 'access_token');
|
||||||
|
expect(result.user.email, 'test@example.com');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MockAuthRepository extends Mock implements AuthRepository {}
|
||||||
Reference in New Issue
Block a user