Commit 1ed6020

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2024-04-03 20:14:37
implement events for repositories
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent 881aed2
health_data_store/lib/src/repositories/blood_pressure_repository_impl.dart
@@ -1,3 +1,5 @@
+import 'dart:async';
+
 import 'package:health_data_store/src/database_helper.dart';
 import 'package:health_data_store/src/database_manager.dart';
 import 'package:health_data_store/src/extensions/datetime_seconds.dart';
@@ -15,11 +17,14 @@ class BloodPressureRepositoryImpl extends BloodPressureRepository {
   /// Create [BloodPressureRecord] repository.
   BloodPressureRepositoryImpl(this._db);
 
+  final _controller = StreamController.broadcast(); // TODO: test all streams
+
   /// The [DatabaseManager] managed database
   final Database _db;
   
   @override
   Future<void> add(BloodPressureRecord record) async{
+    _controller.add(null);
     assert(record.sys != null || record.dia != null || record.pul != null,
      "Adding records that don't contain values(sys,dia,pul) can't be accessed"
      'and should therefore not be added to the repository.');
@@ -81,6 +86,7 @@ class BloodPressureRepositoryImpl extends BloodPressureRepository {
 
   @override
   Future<void> remove(BloodPressureRecord value) => _db.transaction((txn) async{
+    _controller.add(null);
     String query = 'SELECT t.entryID FROM Timestamps AS t ';
     if (value.sys != null)
       query += 'LEFT JOIN Systolic AS s ON t.entryID = s.entryID ';
@@ -120,4 +126,7 @@ class BloodPressureRepositoryImpl extends BloodPressureRepository {
     return Pressure.kPa(value);
   }
 
+  @override
+  Stream subscribe() => _controller.stream;
+
 }
health_data_store/lib/src/repositories/medicine_intake_repository_impl.dart
@@ -1,3 +1,5 @@
+import 'dart:async';
+
 import 'package:health_data_store/src/database_helper.dart';
 import 'package:health_data_store/src/database_manager.dart';
 import 'package:health_data_store/src/extensions/datetime_seconds.dart';
@@ -16,11 +18,14 @@ class MedicineIntakeRepositoryImpl extends MedicineIntakeRepository {
   /// Create a repository for medicine intakes.
   MedicineIntakeRepositoryImpl(this._db);
 
+  final _controller = StreamController.broadcast();
+
   /// The [DatabaseManager] managed database.
   final Database _db;
 
   @override
   Future<void> add(MedicineIntake intake) => _db.transaction((txn) async {
+    _controller.add(null);
     // obtain medicine id
     final medIDRes = await txn.query('Medicine',
       columns: ['medID'],
@@ -82,33 +87,39 @@ class MedicineIntakeRepositoryImpl extends MedicineIntakeRepository {
   }
 
   @override
-  Future<void> remove(MedicineIntake intake) => _db.rawDelete(
-    'DELETE FROM Intake WHERE entryID IN ('
-      'SELECT entryID FROM Timestamps '
-      'WHERE timestampUnixS = ?'
-    ') AND dosis = ? '
-    'AND medID IN ('
-      'SELECT medID FROM Medicine '
-      'WHERE designation = ?'
-      'AND color '
-        + ((intake.medicine.color != null) ? '= ?' : 'IS NULL') +
-      ' AND defaultDose '
-        + ((intake.medicine.dosis != null) ? '= ?' : 'IS NULL') +
-    ')',
-    [
-      intake.time.secondsSinceEpoch,
-      intake.dosis.mg,
-      intake.medicine.designation,
-      if (intake.medicine.color != null)
-        intake.medicine.color,
-      if (intake.medicine.dosis != null)
-        intake.medicine.dosis?.mg,
-    ]
-  );
+  Future<void> remove(MedicineIntake intake) {
+    _controller.add(null);
+    return _db.rawDelete(
+      'DELETE FROM Intake WHERE entryID IN ('
+        'SELECT entryID FROM Timestamps '
+        'WHERE timestampUnixS = ?'
+      ') AND dosis = ? '
+      'AND medID IN ('
+        'SELECT medID FROM Medicine '
+        'WHERE designation = ?'
+        'AND color '
+          + ((intake.medicine.color != null) ? '= ?' : 'IS NULL') +
+        ' AND defaultDose '
+          + ((intake.medicine.dosis != null) ? '= ?' : 'IS NULL') +
+      ')',
+      [
+        intake.time.secondsSinceEpoch,
+        intake.dosis.mg,
+        intake.medicine.designation,
+        if (intake.medicine.color != null)
+          intake.medicine.color,
+        if (intake.medicine.dosis != null)
+          intake.medicine.dosis?.mg,
+      ]
+    );
+  }
 
   Weight? _decode(Object? value) {
     if (value is! double) return null;
     return Weight.mg(value);
   }
 
+  @override
+  Stream subscribe() => _controller.stream;
+
 }
health_data_store/lib/src/repositories/note_repository_impl.dart
@@ -1,3 +1,5 @@
+import 'dart:async';
+
 import 'package:health_data_store/src/database_helper.dart';
 import 'package:health_data_store/src/database_manager.dart';
 import 'package:health_data_store/src/extensions/datetime_seconds.dart';
@@ -12,11 +14,14 @@ class NoteRepositoryImpl extends NoteRepository {
   /// Create a repository for notes.
   NoteRepositoryImpl(this._db);
 
+  final _controller = StreamController.broadcast();
+
   /// The [DatabaseManager] managed database.
   final Database _db;
 
   @override
   Future<void> add(Note note) async {
+    _controller.add(null);
     if (note.note == null && note.color == null) {
       assert(false);
       return;
@@ -60,7 +65,9 @@ class NoteRepositoryImpl extends NoteRepository {
   }
 
   @override
-  Future<void> remove(Note value) => _db.rawDelete(
+  Future<void> remove(Note value) {
+    _controller.add(null);
+    return _db.rawDelete(
     'DELETE FROM Notes WHERE entryID IN ('
       'SELECT entryID FROM Timestamps '
       'WHERE timestampUnixS = ?'
@@ -74,5 +81,8 @@ class NoteRepositoryImpl extends NoteRepository {
    if (value.color != null)
      value.color,
   ]);
+  }
 
+  @override
+  Stream subscribe() => _controller.stream;
 }
health_data_store/lib/src/repositories/repository.dart
@@ -1,3 +1,5 @@
+import 'dart:async';
+
 import 'package:health_data_store/src/database_manager.dart';
 import 'package:health_data_store/src/types/date_range.dart';
 
@@ -20,4 +22,7 @@ abstract class Repository<T> {
 
   /// Inclusively returns all values in the specified [range].
   Future<List<T>> get(DateRange range);
+
+  /// Stream that emits events everytime the data changes.
+  Stream subscribe();
 }
health_data_store/test/src/repositories/blood_pressure_repository_test.dart
@@ -1,3 +1,5 @@
+import 'dart:async';
+
 import 'package:health_data_store/src/repositories/blood_pressure_repository_impl.dart';
 import 'package:health_data_store/src/types/date_range.dart';
 import 'package:test/test.dart';
@@ -144,4 +146,23 @@ void main() {
     ));
     expect(values, isEmpty);
   });
+  test('should emit stream events on changes', () async {
+    final db = await mockDBManager();
+    addTearDown(db.close);
+
+    int notifyCount = 0;
+
+    final repo = BloodPressureRepositoryImpl(db.db);
+    unawaited(repo.subscribe().forEach((_) => notifyCount++));
+    final r = mockRecord(time: 10000, sys: 456, dia: 457, pul: 458);
+    expect(notifyCount, 0);
+    await repo.add(r);
+    expect(notifyCount, 1);
+    await repo.add(mockRecord(time: 20000, sys: 123));
+    expect(notifyCount, 2);
+    await repo.add(mockRecord(time: 30000, sys: 788, pul: 789));
+    expect(notifyCount, 3);
+    await repo.remove(r);
+    expect(notifyCount, 4);
+  });
 }
health_data_store/test/src/repositories/medicine_intake_repository_test.dart
@@ -1,3 +1,5 @@
+import 'dart:async';
+
 import 'package:health_data_store/src/repositories/medicine_intake_repository_impl.dart';
 import 'package:health_data_store/src/repositories/medicine_repository_impl.dart';
 import 'package:health_data_store/src/types/date_range.dart';
@@ -116,4 +118,24 @@ void main() {
     ));
     expect(values, isEmpty);
   });
+  test('should emit stream events on changes', () async {
+    final db = await mockDBManager();
+    addTearDown(db.close);
+    final medRepo = MedicineRepositoryImpl(db.db);
+    final med = mockMedicine();
+    await medRepo.add(med);
+
+    int notifyCount = 0;
+
+    final repo = MedicineIntakeRepositoryImpl(db.db);
+    unawaited(repo.subscribe().forEach((_) => notifyCount++));
+    final i = mockIntake(med);
+    expect(notifyCount, 0);
+    await repo.add(i);
+    expect(notifyCount, 1);
+    await repo.add(mockIntake(med, dosis: 12345));
+    expect(notifyCount, 2);
+    await repo.remove(i);
+    expect(notifyCount, 3);
+  });
 }
health_data_store/test/src/repositories/note_repository_test.dart
@@ -1,3 +1,4 @@
+import 'dart:async';
 
 import 'package:health_data_store/src/repositories/blood_pressure_repository_impl.dart';
 import 'package:health_data_store/src/repositories/note_repository_impl.dart';
@@ -77,4 +78,22 @@ void main() {
     ));
     expect(values, isEmpty);
   });
+
+  test('should emit stream events on changes', () async {
+    final db = await mockDBManager();
+    addTearDown(db.close);
+
+    int notifyCount = 0;
+
+    final repo = NoteRepositoryImpl(db.db);
+    unawaited(repo.subscribe().forEach((_) => notifyCount++));
+    final n = mockNote(time: 1000, note: 'asd');
+    expect(notifyCount, 0);
+    await repo.add(n);
+    expect(notifyCount, 1);
+    await repo.add(mockNote(time: 50000, color: 0x00FF00));
+    expect(notifyCount, 2);
+    await repo.remove(n);
+    expect(notifyCount, 3);
+  });
 }