Skip to content

feat: 'keys' method to list all cache keys #164

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/src/json_cache.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:collection';

/// Represents cached data in json format.
///
///> Cache is a hardware or software component that stores data so that future
Expand Down Expand Up @@ -25,4 +27,9 @@ abstract class JsonCache {
///
/// Returns `true` if there is cached data at [key]; `false` otherwise.
Future<bool> contains(String key);

/// Lists all keys.
///
/// Returns an **unmodifiable** list of cache keys without duplicates.
Future<UnmodifiableListView<String>> keys();
}
2 changes: 1 addition & 1 deletion lib/src/json_cache_exception.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class JsonCacheException<T extends Object> implements Exception {

/// The original exception that indicated the failure of the caching
/// operation.
final Exception? exception;
final Object? exception;

/// Returns [extra] along with the original exception message.
@override
Expand Down
7 changes: 7 additions & 0 deletions lib/src/json_cache_fake.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:collection';

import 'package:json_cache/json_cache.dart';

/// In-memory cache without synchronization.
Expand Down Expand Up @@ -50,4 +52,9 @@ class JsonCacheFake implements JsonCache {
Future<bool> contains(String key) async {
return _memory.containsKey(key);
}

@override
Future<UnmodifiableListView<String>> keys() async {
return UnmodifiableListView(_memory.keys);
}
}
8 changes: 7 additions & 1 deletion lib/src/json_cache_flutter_secure_storage.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'dart:collection';
import 'dart:convert';

import 'package:flutter_secure_storage/flutter_secure_storage.dart';
Expand Down Expand Up @@ -39,6 +40,11 @@ class JsonCacheFlutterSecureStorage implements JsonCache {

@override
Future<bool> contains(String key) async {
return _storage.containsKey(key: key);
return await _storage.containsKey(key: key);
}

@override
Future<UnmodifiableListView<String>> keys() async {
return UnmodifiableListView((await _storage.readAll()).keys);
}
}
6 changes: 6 additions & 0 deletions lib/src/json_cache_hive.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'dart:collection';
import 'dart:convert';

import 'package:hive/hive.dart';
Expand Down Expand Up @@ -37,4 +38,9 @@ class JsonCacheHive implements JsonCache {
Future<bool> contains(String key) async {
return _box.containsKey(key);
}

@override
Future<UnmodifiableListView<String>> keys() async {
return UnmodifiableListView(_box.keys.map((k) => k as String));
}
}
7 changes: 7 additions & 0 deletions lib/src/json_cache_hollow.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:collection';

import 'package:json_cache/json_cache.dart';

/// Hollow (empty) [JsonCache] — It is intended to serve as a placeholder for
Expand Down Expand Up @@ -38,4 +40,9 @@ class JsonCacheHollow implements JsonCache {
/// Always returns `false`.
@override
Future<bool> contains(String key) async => false;

/// An empty list of keys.
@override
Future<UnmodifiableListView<String>> keys() async =>
UnmodifiableListView(const []);
}
12 changes: 12 additions & 0 deletions lib/src/json_cache_local_storage.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'dart:collection';
import 'dart:convert';

import 'package:json_cache/json_cache.dart';
Expand Down Expand Up @@ -44,4 +45,15 @@ final class JsonCacheLocalStorage implements JsonCache {

@override
Future<bool> contains(String key) async => _storage.getItem(key) != null;

@override
Future<UnmodifiableListView<String>> keys() async {
return UnmodifiableListView(
List.generate(
_storage.length,
(int i) => _storage.key(i)!,
growable: false,
),
);
}
}
12 changes: 8 additions & 4 deletions lib/src/json_cache_mem.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:collection';

import 'package:json_cache/json_cache.dart';
import 'package:mutex/mutex.dart';
Expand Down Expand Up @@ -93,8 +94,6 @@ class JsonCacheMem implements JsonCache {
static final _shrMutex = ReadWriteMutex();

/// Frees up storage space in both the level2 cache and in-memory cache.
///
/// Throws [JsonCacheException] to indicate operation failure.
@override
Future<void> clear() async {
await _mutex.protectWrite(() async {
Expand All @@ -105,8 +104,6 @@ class JsonCacheMem implements JsonCache {

/// Updates the data located at [key] in both the _level 2_ cache and
/// in-memory cache.
///
/// Throws [JsonCacheException] to indicate operation failure.
@override
Future<void> refresh(String key, Map<String, dynamic> data) async {
// ATTENTION: It is safer to copy the content of [data] before calling an
Expand Down Expand Up @@ -160,4 +157,11 @@ class JsonCacheMem implements JsonCache {
return foundInMemory ? foundInMemory : await _level2.contains(key);
});
}

@override
Future<UnmodifiableListView<String>> keys() {
return _mutex.protectRead(() async {
return UnmodifiableListView(_memory.keys);
});
}
}
10 changes: 10 additions & 0 deletions lib/src/json_cache_safe_local_storage.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// ignore_for_file: avoid_dynamic_calls

import 'dart:collection';

import 'package:json_cache/json_cache.dart';
import 'package:safe_local_storage/safe_local_storage.dart';

Expand Down Expand Up @@ -43,6 +45,14 @@ class JsonCacheSafeLocalStorage implements JsonCache {
return (await _cachedData)[key] as Map<String, dynamic>?;
}

@override
Future<UnmodifiableListView<String>> keys() async {
final data = await _cachedData;
return UnmodifiableListView(
data.keys.map((k) => k as String),
);
}

/// Gets the cached data stored in the local storage file.
Future<Map<dynamic, dynamic>> get _cachedData async {
return await _localStorage.read() as Map<dynamic, dynamic>;
Expand Down
6 changes: 6 additions & 0 deletions lib/src/json_cache_shared_preferences.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'dart:collection';
import 'dart:convert';

import 'package:json_cache/json_cache.dart';
Expand Down Expand Up @@ -47,4 +48,9 @@ class JsonCacheSharedPreferences implements JsonCache {
Future<bool> contains(String key) async {
return _sharedPreferences.containsKey(key);
}

@override
Future<UnmodifiableListView<String>> keys() async {
return UnmodifiableListView(_sharedPreferences.getKeys());
}
}
17 changes: 17 additions & 0 deletions lib/src/json_cache_try.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:collection';

import 'package:json_cache/json_cache.dart';

/// A [JsonCache] decorator that provides improved information about
Expand Down Expand Up @@ -103,4 +105,19 @@ class JsonCacheTry implements JsonCache {
);
}
}

@override
Future<UnmodifiableListView<String>> keys() async {
try {
return await _wrapped.keys();
} on Exception catch (ex, st) {
Error.throwWithStackTrace(
JsonCacheException(
extra: "Error retreiving the cache keys.",
exception: ex,
),
st,
);
}
}
}
6 changes: 6 additions & 0 deletions lib/src/json_cache_wrap.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:collection';

import 'package:json_cache/json_cache.dart';

/// Decorator Envelope of [JsonCache].
Expand Down Expand Up @@ -30,4 +32,8 @@ abstract class JsonCacheWrap implements JsonCache {
/// Forwards to the [JsonCache.contains] of its wrapped instance.
@override
Future<bool> contains(String key) => _wrapped.contains(key);

/// Forwards to the [JsonCache.keys] of its wrapped instance.
@override
Future<UnmodifiableListView<String>> keys() => _wrapped.keys();
}
11 changes: 8 additions & 3 deletions test/flutter_secure_storage_mock.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,14 @@ class FlutterSecureStorageMock implements FlutterSecureStorage {
WebOptions? webOptions,
MacOsOptions? mOptions,
WindowsOptions? wOptions,
}) {
// TODO: implement readAll
throw UnimplementedError();
}) async {
// final jsonObj = await _fakeCache.value(key);
final keys = await _fakeCache.keys();
final data = <String, String>{};
for (final key in keys) {
data[key] = json.encode(await _fakeCache.value(key));
}
return data;
}

@override
Expand Down
15 changes: 9 additions & 6 deletions test/json_cache_fake_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import 'package:json_cache/json_cache.dart';
void main() {
group('JsonCacheFake', () {
const profKey = 'profile';
const profData = <String, dynamic>{'id': 1, 'name': 'John Due'};
const prefKey = 'preferences';
const profData = <String, dynamic>{'id': 1, 'name': 'John Due'};
const prefData = <String, dynamic>{
'theme': 'dark',
'notifications': {'enabled': true},
Expand Down Expand Up @@ -52,11 +52,6 @@ void main() {
});

test('contains', () async {
final profData = <String, dynamic>{'id': 1, 'name': 'John Due'};
final prefData = <String, dynamic>{
'theme': 'dark',
'notifications': {'enabled': true},
};
final fake = JsonCacheFake();
// update data
await fake.refresh(profKey, profData);
Expand All @@ -70,6 +65,14 @@ void main() {
expect(await fake.contains(prefKey), false);
expect(await fake.contains('a key'), false);
});
test('keys', () async {
final fake = JsonCacheFake();
// update data
await fake.refresh(profKey, profData);
await fake.refresh(prefKey, prefData);

expect(await fake.keys(), [profKey, prefKey]);
});
group('remove', () {
test('default ctor', () async {
final JsonCacheFake fakeCache = JsonCacheFake();
Expand Down
33 changes: 17 additions & 16 deletions test/json_cache_flutter_secure_storage_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ import 'flutter_secure_storage_mock.dart';

void main() {
group('JsonCacheFlutterSecureStorage', () {
const profKey = 'profile';
const prefKey = 'preferences';
const profData = <String, dynamic>{'id': 1, 'name': 'John Due'};
const prefData = <String, dynamic>{
'theme': 'dark',
'notifications': {'enabled': true},
};
test('clear, value, refresh', () async {
final secStorageMock = FlutterSecureStorageMock();
final JsonCacheFlutterSecureStorage flutterSecureCache =
JsonCacheFlutterSecureStorage(secStorageMock);
const profKey = 'profile';
const profData = <String, Object>{'id': 1, 'name': 'John Due'};
await flutterSecureCache.refresh(profKey, profData);
expect(secStorageMock.writeInvokations, 1);

Expand All @@ -27,13 +32,6 @@ void main() {
});

test('contains', () async {
const profKey = 'profile';
const prefKey = 'preferences';
final profData = <String, dynamic>{'id': 1, 'name': 'John Due'};
final prefData = <String, dynamic>{
'theme': 'dark',
'notifications': {'enabled': true},
};
final secStorageMock = FlutterSecureStorageMock();
final JsonCacheFlutterSecureStorage flutterSecureCache =
JsonCacheFlutterSecureStorage(secStorageMock);
Expand All @@ -52,17 +50,20 @@ void main() {
await flutterSecureCache.remove(prefKey);
expect(await flutterSecureCache.contains(prefKey), false);
});
test('keys', () async {
final secStorageMock = FlutterSecureStorageMock();
final JsonCacheFlutterSecureStorage flutterSecureCache =
JsonCacheFlutterSecureStorage(secStorageMock);
// update data
await flutterSecureCache.refresh(profKey, profData);
await flutterSecureCache.refresh(prefKey, prefData);

expect(await flutterSecureCache.keys(), [profKey, prefKey]);
});
test('remove', () async {
final secStorageMock = FlutterSecureStorageMock();
final JsonCacheFlutterSecureStorage flutterSecureCache =
JsonCacheFlutterSecureStorage(secStorageMock);
const profKey = 'profile';
const prefKey = 'preferences';
final profData = <String, Object>{'id': 1, 'name': 'John Due'};
final prefData = <String, Object>{
'theme': 'dark',
'notifications': {'enabled': true},
};
await flutterSecureCache.refresh(profKey, profData);
await flutterSecureCache.refresh(prefKey, prefData);
expect(secStorageMock.writeInvokations, 2);
Expand Down
33 changes: 17 additions & 16 deletions test/json_cache_hive_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@ void main() {
await tearDownTestHive();
});
group('JsonCacheHive', () {
const profKey = 'profile';
const prefKey = 'preferences';
const profData = <String, dynamic>{'id': 1, 'name': 'John Due'};
const prefData = <String, dynamic>{
'theme': 'dark',
'notifications': {'enabled': true},
};
test('clear, value, refresh', () async {
final box = await Hive.openBox<String>('test-clear-value-refresh');
const profKey = 'profile';
const profData = <String, Object>{'id': 1, 'name': 'John Due'};
final JsonCacheHive hiveCache = JsonCacheHive(box);
await hiveCache.refresh(profKey, profData);
var prof = await hiveCache.value(profKey);
Expand All @@ -25,13 +30,6 @@ void main() {
});

test('contains', () async {
const profKey = 'profile';
const prefKey = 'preferences';
final profData = <String, dynamic>{'id': 1, 'name': 'John Due'};
final prefData = <String, dynamic>{
'theme': 'dark',
'notifications': {'enabled': true},
};
final box = await Hive.openBox<String>('test-contains-method');
final JsonCacheHive hiveCache = JsonCacheHive(box);
// update data
Expand All @@ -49,14 +47,17 @@ void main() {
await hiveCache.remove(prefKey);
expect(await hiveCache.contains(prefKey), false);
});

test('keys', () async {
final box = await Hive.openBox<String>('test-contains-method');
final JsonCacheHive hiveCache = JsonCacheHive(box);
// update data
await hiveCache.refresh(profKey, profData);
await hiveCache.refresh(prefKey, prefData);

expect(await hiveCache.keys(), [prefKey, profKey]);
});
test('remove', () async {
const profKey = 'profile';
const prefKey = 'preferences';
final profData = <String, Object>{'id': 1, 'name': 'John Due'};
final prefData = <String, Object>{
'theme': 'dark',
'notifications': {'enabled': true},
};
final box = await Hive.openBox<String>('test-remove');
final hiveCache = JsonCacheHive(box);
await hiveCache.refresh(profKey, profData);
Expand Down
Loading