feat: Refactor repositories to utilize DataConnectService and remove FirebaseAuth dependency

This commit is contained in:
Achintha Isuru
2026-02-16 16:54:20 -05:00
parent ef58ca83be
commit 17423c5d66
8 changed files with 69 additions and 116 deletions

View File

@@ -1,6 +1,5 @@
import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_core/core.dart'; import 'package:krow_core/core.dart';
import 'package:krow_data_connect/krow_data_connect.dart';
import 'data/repositories_impl/attire_repository_impl.dart'; import 'data/repositories_impl/attire_repository_impl.dart';
import 'domain/repositories/attire_repository.dart'; import 'domain/repositories/attire_repository.dart';
@@ -14,10 +13,8 @@ class StaffAttireModule extends Module {
@override @override
void binds(Injector i) { void binds(Injector i) {
// Repository // Repository
i.addLazySingleton<AttireRepository>( i.addLazySingleton<AttireRepository>(AttireRepositoryImpl.new);
() => AttireRepositoryImpl(ExampleConnector.instance),
);
// Use Cases // Use Cases
i.addLazySingleton(GetAttireOptionsUseCase.new); i.addLazySingleton(GetAttireOptionsUseCase.new);
i.addLazySingleton(SaveAttireUseCase.new); i.addLazySingleton(SaveAttireUseCase.new);

View File

@@ -6,24 +6,30 @@ import '../../domain/repositories/attire_repository.dart';
/// Implementation of [AttireRepository]. /// Implementation of [AttireRepository].
/// ///
/// Delegates data access to [ExampleConnector] from `data_connect`. /// Delegates data access to [DataConnectService].
class AttireRepositoryImpl implements AttireRepository { class AttireRepositoryImpl implements AttireRepository {
/// The Data Connect connector instance. /// The Data Connect service.
final ExampleConnector _connector; final DataConnectService _service;
/// Creates an [AttireRepositoryImpl]. /// Creates an [AttireRepositoryImpl].
AttireRepositoryImpl(this._connector); AttireRepositoryImpl({DataConnectService? service})
: _service = service ?? DataConnectService.instance;
@override @override
Future<List<AttireItem>> getAttireOptions() async { Future<List<AttireItem>> getAttireOptions() async {
final QueryResult<ListAttireOptionsData, void> result = await _connector.listAttireOptions().execute(); return _service.run(() async {
return result.data.attireOptions.map((ListAttireOptionsAttireOptions e) => AttireItem( final QueryResult<ListAttireOptionsData, void> result =
id: e.itemId, await _service.connector.listAttireOptions().execute();
label: e.label, return result.data.attireOptions
iconName: e.icon, .map((ListAttireOptionsAttireOptions e) => AttireItem(
imageUrl: e.imageUrl, id: e.itemId,
isMandatory: e.isMandatory ?? false, label: e.label,
)).toList(); iconName: e.icon,
imageUrl: e.imageUrl,
isMandatory: e.isMandatory ?? false,
))
.toList();
});
} }
@override @override

View File

@@ -1,4 +1,3 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:krow_data_connect/krow_data_connect.dart' as dc; import 'package:krow_data_connect/krow_data_connect.dart' as dc;
import 'package:krow_domain/krow_domain.dart'; import 'package:krow_domain/krow_domain.dart';
import '../../domain/repositories/emergency_contact_repository_interface.dart'; import '../../domain/repositories/emergency_contact_repository_interface.dart';
@@ -7,38 +6,19 @@ import '../../domain/repositories/emergency_contact_repository_interface.dart';
/// ///
/// This repository delegates data operations to Firebase Data Connect. /// This repository delegates data operations to Firebase Data Connect.
class EmergencyContactRepositoryImpl class EmergencyContactRepositoryImpl
with dc.DataErrorHandler
implements EmergencyContactRepositoryInterface { implements EmergencyContactRepositoryInterface {
final dc.ExampleConnector _dataConnect; final dc.DataConnectService _service;
final FirebaseAuth _firebaseAuth;
/// Creates an [EmergencyContactRepositoryImpl]. /// Creates an [EmergencyContactRepositoryImpl].
EmergencyContactRepositoryImpl({ EmergencyContactRepositoryImpl({
required dc.ExampleConnector dataConnect, dc.DataConnectService? service,
required FirebaseAuth firebaseAuth, }) : _service = service ?? dc.DataConnectService.instance;
}) : _dataConnect = dataConnect,
_firebaseAuth = firebaseAuth;
Future<String> _getStaffId() async {
final user = _firebaseAuth.currentUser;
if (user == null) {
throw const NotAuthenticatedException(
technicalMessage: 'User not authenticated');
}
final result =
await _dataConnect.getStaffByUserId(userId: user.uid).execute();
if (result.data.staffs.isEmpty) {
throw const ServerException(technicalMessage: 'Staff profile not found');
}
return result.data.staffs.first.id;
}
@override @override
Future<List<EmergencyContact>> getContacts() async { Future<List<EmergencyContact>> getContacts() async {
return executeProtected(() async { return _service.run(() async {
final staffId = await _getStaffId(); final staffId = await _service.getStaffId();
final result = await _dataConnect final result = await _service.connector
.getEmergencyContactsByStaffId(staffId: staffId) .getEmergencyContactsByStaffId(staffId: staffId)
.execute(); .execute();
@@ -55,11 +35,11 @@ class EmergencyContactRepositoryImpl
@override @override
Future<void> saveContacts(List<EmergencyContact> contacts) async { Future<void> saveContacts(List<EmergencyContact> contacts) async {
return executeProtected(() async { return _service.run(() async {
final staffId = await _getStaffId(); final staffId = await _service.getStaffId();
// 1. Get existing to delete // 1. Get existing to delete
final existingResult = await _dataConnect final existingResult = await _service.connector
.getEmergencyContactsByStaffId(staffId: staffId) .getEmergencyContactsByStaffId(staffId: staffId)
.execute(); .execute();
final existingIds = final existingIds =
@@ -67,7 +47,7 @@ class EmergencyContactRepositoryImpl
// 2. Delete all existing // 2. Delete all existing
await Future.wait(existingIds.map( await Future.wait(existingIds.map(
(id) => _dataConnect.deleteEmergencyContact(id: id).execute())); (id) => _service.connector.deleteEmergencyContact(id: id).execute()));
// 3. Create new // 3. Create new
await Future.wait(contacts.map((contact) { await Future.wait(contacts.map((contact) {
@@ -87,7 +67,7 @@ class EmergencyContactRepositoryImpl
break; break;
} }
return _dataConnect return _service.connector
.createEmergencyContact( .createEmergencyContact(
name: contact.name, name: contact.name,
phone: contact.phone, phone: contact.phone,

View File

@@ -1,6 +1,5 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_data_connect/krow_data_connect.dart';
import 'data/repositories/emergency_contact_repository_impl.dart'; import 'data/repositories/emergency_contact_repository_impl.dart';
import 'domain/repositories/emergency_contact_repository_interface.dart'; import 'domain/repositories/emergency_contact_repository_interface.dart';
import 'domain/usecases/get_emergency_contacts_usecase.dart'; import 'domain/usecases/get_emergency_contacts_usecase.dart';
@@ -13,10 +12,7 @@ class StaffEmergencyContactModule extends Module {
void binds(Injector i) { void binds(Injector i) {
// Repository // Repository
i.addLazySingleton<EmergencyContactRepositoryInterface>( i.addLazySingleton<EmergencyContactRepositoryInterface>(
() => EmergencyContactRepositoryImpl( EmergencyContactRepositoryImpl.new,
dataConnect: ExampleConnector.instance,
firebaseAuth: FirebaseAuth.instance,
),
); );
// UseCases // UseCases

View File

@@ -1,42 +1,31 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:krow_data_connect/krow_data_connect.dart' as dc; import 'package:krow_data_connect/krow_data_connect.dart' as dc;
import '../../domain/repositories/experience_repository_interface.dart';
import 'package:krow_domain/krow_domain.dart'; import 'package:krow_domain/krow_domain.dart';
import '../../domain/repositories/experience_repository_interface.dart';
/// Implementation of [ExperienceRepositoryInterface] that delegates to Data Connect. /// Implementation of [ExperienceRepositoryInterface] that delegates to Data Connect.
class ExperienceRepositoryImpl class ExperienceRepositoryImpl implements ExperienceRepositoryInterface {
with dc.DataErrorHandler final dc.DataConnectService _service;
implements ExperienceRepositoryInterface {
final dc.ExampleConnector _dataConnect;
// ignore: unused_field
final FirebaseAuth _firebaseAuth;
/// Creates a [ExperienceRepositoryImpl] using Data Connect and Auth. /// Creates a [ExperienceRepositoryImpl] using Data Connect Service.
ExperienceRepositoryImpl({ ExperienceRepositoryImpl({
required dc.ExampleConnector dataConnect, dc.DataConnectService? service,
required FirebaseAuth firebaseAuth, }) : _service = service ?? dc.DataConnectService.instance;
}) : _dataConnect = dataConnect,
_firebaseAuth = firebaseAuth;
Future<dc.GetStaffByUserIdStaffs> _getStaff() async { Future<dc.GetStaffByIdStaff> _getStaff() async {
final user = _firebaseAuth.currentUser; final staffId = await _service.getStaffId();
if (user == null) {
throw const NotAuthenticatedException(
technicalMessage: 'User not authenticated');
}
final result = final result =
await _dataConnect.getStaffByUserId(userId: user.uid).execute(); await _service.connector.getStaffById(id: staffId).execute();
if (result.data.staffs.isEmpty) { if (result.data.staff == null) {
throw const ServerException(technicalMessage: 'Staff profile not found'); throw const ServerException(technicalMessage: 'Staff profile not found');
} }
return result.data.staffs.first; return result.data.staff!;
} }
@override @override
Future<List<String>> getIndustries() async { Future<List<String>> getIndustries() async {
return executeProtected(() async { return _service.run(() async {
final staff = await _getStaff(); final staff = await _getStaff();
return staff.industries ?? []; return staff.industries ?? [];
}); });
@@ -44,7 +33,7 @@ class ExperienceRepositoryImpl
@override @override
Future<List<String>> getSkills() async { Future<List<String>> getSkills() async {
return executeProtected(() async { return _service.run(() async {
final staff = await _getStaff(); final staff = await _getStaff();
return staff.skills ?? []; return staff.skills ?? [];
}); });
@@ -55,9 +44,9 @@ class ExperienceRepositoryImpl
List<String> industries, List<String> industries,
List<String> skills, List<String> skills,
) async { ) async {
return executeProtected(() async { return _service.run(() async {
final staff = await _getStaff(); final staff = await _getStaff();
await _dataConnect await _service.connector
.updateStaff(id: staff.id) .updateStaff(id: staff.id)
.industries(industries) .industries(industries)
.skills(skills) .skills(skills)

View File

@@ -1,6 +1,5 @@
library staff_profile_experience; library staff_profile_experience;
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_data_connect/krow_data_connect.dart'; import 'package:krow_data_connect/krow_data_connect.dart';
@@ -22,10 +21,7 @@ class StaffProfileExperienceModule extends Module {
void binds(Injector i) { void binds(Injector i) {
// Repository // Repository
i.addLazySingleton<ExperienceRepositoryInterface>( i.addLazySingleton<ExperienceRepositoryInterface>(
() => ExperienceRepositoryImpl( ExperienceRepositoryImpl.new,
dataConnect: ExampleConnector.instance,
firebaseAuth: FirebaseAuth.instance,
),
); );
// UseCases // UseCases

View File

@@ -1,4 +1,3 @@
import 'package:firebase_auth/firebase_auth.dart' as firebase_auth;
import 'package:firebase_data_connect/firebase_data_connect.dart'; import 'package:firebase_data_connect/firebase_data_connect.dart';
import 'package:krow_data_connect/krow_data_connect.dart'; import 'package:krow_data_connect/krow_data_connect.dart';
import 'package:krow_domain/krow_domain.dart'; import 'package:krow_domain/krow_domain.dart';
@@ -14,32 +13,24 @@ import '../../domain/repositories/personal_info_repository_interface.dart';
/// - Mapping between data_connect DTOs and domain entities /// - Mapping between data_connect DTOs and domain entities
/// - Containing no business logic /// - Containing no business logic
class PersonalInfoRepositoryImpl class PersonalInfoRepositoryImpl
with DataErrorHandler
implements PersonalInfoRepositoryInterface { implements PersonalInfoRepositoryInterface {
/// Creates a [PersonalInfoRepositoryImpl]. /// Creates a [PersonalInfoRepositoryImpl].
/// ///
/// Requires the Firebase Data Connect connector instance and Firebase Auth. /// Requires the Firebase Data Connect service.
PersonalInfoRepositoryImpl({ PersonalInfoRepositoryImpl({
required ExampleConnector dataConnect, DataConnectService? service,
required firebase_auth.FirebaseAuth firebaseAuth, }) : _service = service ?? DataConnectService.instance;
}) : _dataConnect = dataConnect,
_firebaseAuth = firebaseAuth; final DataConnectService _service;
final ExampleConnector _dataConnect;
final firebase_auth.FirebaseAuth _firebaseAuth;
@override @override
Future<Staff> getStaffProfile() async { Future<Staff> getStaffProfile() async {
return executeProtected(() async { return _service.run(() async {
final firebase_auth.User? user = _firebaseAuth.currentUser; final String uid = _service.auth.currentUser!.uid;
if (user == null) {
throw NotAuthenticatedException(
technicalMessage: 'User not authenticated');
}
// Query staff data from Firebase Data Connect // Query staff data from Firebase Data Connect
final QueryResult<GetStaffByUserIdData, GetStaffByUserIdVariables> result = final QueryResult<GetStaffByUserIdData, GetStaffByUserIdVariables> result =
await _dataConnect.getStaffByUserId(userId: user.uid).execute(); await _service.connector.getStaffByUserId(userId: uid).execute();
if (result.data.staffs.isEmpty) { if (result.data.staffs.isEmpty) {
throw const ServerException(technicalMessage: 'Staff profile not found'); throw const ServerException(technicalMessage: 'Staff profile not found');
@@ -53,10 +44,12 @@ class PersonalInfoRepositoryImpl
} }
@override @override
Future<Staff> updateStaffProfile({required String staffId, required Map<String, dynamic> data}) async { Future<Staff> updateStaffProfile(
return executeProtected(() async { {required String staffId, required Map<String, dynamic> data}) async {
return _service.run(() async {
// Start building the update mutation // Start building the update mutation
UpdateStaffVariablesBuilder updateBuilder = _dataConnect.updateStaff(id: staffId); UpdateStaffVariablesBuilder updateBuilder =
_service.connector.updateStaff(id: staffId);
// Apply updates from map if present // Apply updates from map if present
if (data.containsKey('name')) { if (data.containsKey('name')) {
@@ -72,8 +65,9 @@ class PersonalInfoRepositoryImpl
updateBuilder = updateBuilder.photoUrl(data['avatar'] as String?); updateBuilder = updateBuilder.photoUrl(data['avatar'] as String?);
} }
if (data.containsKey('preferredLocations')) { if (data.containsKey('preferredLocations')) {
// After schema update and SDK regeneration, preferredLocations accepts List<String> // After schema update and SDK regeneration, preferredLocations accepts List<String>
updateBuilder = updateBuilder.preferredLocations(data['preferredLocations'] as List<String>); updateBuilder = updateBuilder.preferredLocations(
data['preferredLocations'] as List<String>);
} }
// Execute the update // Execute the update
@@ -81,7 +75,8 @@ class PersonalInfoRepositoryImpl
await updateBuilder.execute(); await updateBuilder.execute();
if (result.data.staff_update == null) { if (result.data.staff_update == null) {
throw const ServerException(technicalMessage: 'Failed to update staff profile'); throw const ServerException(
technicalMessage: 'Failed to update staff profile');
} }
// Fetch the updated staff profile to return complete entity // Fetch the updated staff profile to return complete entity

View File

@@ -1,7 +1,5 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_data_connect/krow_data_connect.dart';
import 'data/repositories/personal_info_repository_impl.dart'; import 'data/repositories/personal_info_repository_impl.dart';
import 'domain/repositories/personal_info_repository_interface.dart'; import 'domain/repositories/personal_info_repository_interface.dart';
@@ -25,11 +23,7 @@ class StaffProfileInfoModule extends Module {
void binds(Injector i) { void binds(Injector i) {
// Repository // Repository
i.addLazySingleton<PersonalInfoRepositoryInterface>( i.addLazySingleton<PersonalInfoRepositoryInterface>(
() => PersonalInfoRepositoryImpl( PersonalInfoRepositoryImpl.new);
dataConnect: ExampleConnector.instance,
firebaseAuth: FirebaseAuth.instance,
),
);
// Use Cases - delegate business logic to repository // Use Cases - delegate business logic to repository
i.addLazySingleton<GetPersonalInfoUseCase>( i.addLazySingleton<GetPersonalInfoUseCase>(