Skip to content

Commit

Permalink
feat: add storage strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
cevheri committed Nov 27, 2024
1 parent 3dae23f commit c21bb13
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 41 deletions.
113 changes: 85 additions & 28 deletions lib/configuration/local_storage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ import 'package:flutter_bloc_advance/configuration/app_logger.dart';
import 'package:get_storage/get_storage.dart';
import 'package:shared_preferences/shared_preferences.dart';

abstract class StorageStrategy {
Future<bool> save(String key, dynamic value);

Future<dynamic> read(String key);

Future<bool> remove(String key);

Future<void> clear();
}

class AppLocalStorageCached {
static final _log = AppLogger.getLogger("AppLocalStorageCached");
static late String? jwtToken;
Expand All @@ -23,42 +33,28 @@ class AppLocalStorageCached {
/// LocalStorage predefined keys
enum StorageKeys { jwtToken, roles, language, username }

// extension StorageKeysExtension on StorageKeys {
// String get name {
// switch (this) {
// case StorageKeys.jwtToken:
// return "TOKEN";
// case StorageKeys.roles:
// return "ROLES";
// case StorageKeys.language:
// return "LANGUAGE";
// case StorageKeys.username:
// return "USERNAME";
// default:
// return "";
// }
// }
// }

/// Application Local Storage
///
/// This class is used to store data locally with the help of shared preferences.
class AppLocalStorage {
class SharedPreferencesStrategy implements StorageStrategy {
static final _log = AppLogger.getLogger("AppLocalStorage");
static final AppLocalStorage _instance = AppLocalStorage._internal();

static final SharedPreferencesStrategy _instance = SharedPreferencesStrategy._internal();

SharedPreferencesStrategy._internal();

factory SharedPreferencesStrategy() {
_log.trace("Creating AppLocalStorage instance");
return _instance;
}

SharedPreferences? _prefsInstance;
@visibleForTesting
void setPreferencesInstance(SharedPreferences prefs) {
_prefsInstance = prefs;
}

factory AppLocalStorage() {
_log.trace("Creating AppLocalStorage instance");
return _instance;
}

AppLocalStorage._internal();

/// Shared Preferences private instance
Future<SharedPreferences> get _prefs async => _prefsInstance ??= await SharedPreferences.getInstance();
Expand All @@ -76,6 +72,7 @@ class AppLocalStorage {
/// <br>
///
/// throws Exception if value type is not supported
@override
Future<bool> save(String key, dynamic value) async {
_log.trace("Saving data to local storage {} {}", [key, value]);
final prefs = await _prefs;
Expand Down Expand Up @@ -112,6 +109,7 @@ class AppLocalStorage {
/// - **double**
/// - **bool**
/// - **List String**
@override
Future<dynamic> read(String key) async {
_log.trace("Reading data from local storage");
final prefs = await _prefs;
Expand All @@ -123,6 +121,7 @@ class AppLocalStorage {
/// Remove data from local storage
///
/// This method removes data from local storage. It takes a key as parameter.
@override
Future<bool> remove(String key) async {
_log.trace("Removing data from local storage");
try {
Expand All @@ -140,6 +139,7 @@ class AppLocalStorage {
/// Clear all data from local storage
///
/// This method clears all data from local storage.
@override
Future<void> clear() async {
_log.info("Clearing all data from local storage");
final prefs = await _prefs;
Expand All @@ -152,12 +152,12 @@ class AppLocalStorage {
/// Application Local Storage with GetX
///
/// This class is used to store data locally with the help of get storage.
class AppLocalStorageGetX{
class GetStorageStrategy implements StorageStrategy {
static final _log = AppLogger.getLogger("AppLocalStorageGetX");
static final AppLocalStorageGetX _instance = AppLocalStorageGetX._internal();
AppLocalStorageGetX._internal();
static final GetStorageStrategy _instance = GetStorageStrategy._internal();
GetStorageStrategy._internal();

factory AppLocalStorageGetX(){
factory GetStorageStrategy(){
_log.trace("Creating AppLocalStorageGetX instance");
return _instance;
}
Expand All @@ -173,6 +173,7 @@ class AppLocalStorageGetX{
Future<GetStorage> get _prefs async => _prefsInstance ??= GetStorage();

/// Save data to local storage <br>
@override
Future<bool> save(String key, dynamic value) async {
_log.trace("Saving data to local storage {} {}", [key, value]);
final prefs = await _prefs;
Expand All @@ -188,6 +189,7 @@ class AppLocalStorageGetX{
}

/// Get data from local storage <br>
@override
Future<dynamic> read(String key) async {
_log.trace("Reading data from local storage");
final prefs = await _prefs;
Expand All @@ -197,6 +199,7 @@ class AppLocalStorageGetX{
}

/// Remove data from local storage
@override
Future<bool> remove(String key) async {
_log.trace("Removing data from local storage");
try {
Expand All @@ -212,6 +215,7 @@ class AppLocalStorageGetX{
}

/// Clear all data from local storage
@override
Future<void> clear() async {
_log.info("Clearing all data from local storage");
final prefs = await _prefs;
Expand All @@ -220,3 +224,56 @@ class AppLocalStorageGetX{
_log.info("Cleared all data from local storage");
}
}

enum StorageType {
sharedPreferences,
getStorage
}

class AppLocalStorage {
static final _log = AppLogger.getLogger("AppLocalStorage");
static final AppLocalStorage _instance = AppLocalStorage._internal();

late StorageStrategy _strategy;

AppLocalStorage._internal() {
_log.trace("Creating AppLocalStorage instance");
_strategy = SharedPreferencesStrategy();
}

factory AppLocalStorage() => _instance;

void setStrategy(StorageType type) {
_log.trace("Setting storage strategy to {}", [type]);
switch (type) {
case StorageType.sharedPreferences:
_strategy = SharedPreferencesStrategy();
break;
case StorageType.getStorage:
_strategy = GetStorageStrategy();
break;
}
}


Future<bool> save(String key, dynamic value) async {
final result = await _strategy.save(key, value);
await AppLocalStorageCached.loadCache();
return result;
}

Future<dynamic> read(String key) async {
return await _strategy.read(key);
}

Future<bool> remove(String key) async {
final result = await _strategy.remove(key);
await AppLocalStorageCached.loadCache();
return result;
}

Future<void> clear() async {
await _strategy.clear();
await AppLocalStorageCached.loadCache();
}
}
5 changes: 4 additions & 1 deletion lib/main/main_local.mapper.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import 'package:dart_json_mapper/dart_json_mapper.dart' show JsonMapper, JsonMapperAdapter, SerializationOptions, DeserializationOptions, typeOf;
import 'package:flutter_bloc_advance/configuration/app_logger.dart' as x8 show LogFormat;
import 'package:flutter_bloc_advance/configuration/environment.dart' as x9 show Environment;
import 'package:flutter_bloc_advance/configuration/local_storage.dart' as x10 show StorageKeys;
import 'package:flutter_bloc_advance/configuration/local_storage.dart' as x10 show StorageKeys, StorageType;
import 'package:flutter_bloc_advance/data/models/authority.dart' as x2 show Authority;
import 'package:flutter_bloc_advance/data/models/change_password.dart' as x0 show PasswordChangeDTO;
import 'package:flutter_bloc_advance/data/models/city.dart' as x3 show City;
Expand Down Expand Up @@ -88,6 +88,8 @@ final mainLocalGeneratedAdapter = JsonMapperAdapter(
typeOf<Set<x9.Environment>>(): (value) => value.cast<x9.Environment>(),
typeOf<List<x10.StorageKeys>>(): (value) => value.cast<x10.StorageKeys>(),
typeOf<Set<x10.StorageKeys>>(): (value) => value.cast<x10.StorageKeys>(),
typeOf<List<x10.StorageType>>(): (value) => value.cast<x10.StorageType>(),
typeOf<Set<x10.StorageType>>(): (value) => value.cast<x10.StorageType>(),
typeOf<List<x11.AccountStatus>>(): (value) => value.cast<x11.AccountStatus>(),
typeOf<Set<x11.AccountStatus>>(): (value) => value.cast<x11.AccountStatus>(),
typeOf<List<x12.AuthorityStatus>>(): (value) => value.cast<x12.AuthorityStatus>(),
Expand All @@ -113,6 +115,7 @@ final mainLocalGeneratedAdapter = JsonMapperAdapter(
x8.LogFormat: x8.LogFormat.values,
x9.Environment: x9.Environment.values,
x10.StorageKeys: x10.StorageKeys.values,
x10.StorageType: x10.StorageType.values,
x11.AccountStatus: x11.AccountStatus.values,
x12.AuthorityStatus: x12.AuthorityStatus.values,
x13.CityStatus: x13.CityStatus.values,
Expand Down
5 changes: 4 additions & 1 deletion lib/main/main_prod.mapper.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import 'package:dart_json_mapper/dart_json_mapper.dart' show JsonMapper, JsonMapperAdapter, SerializationOptions, DeserializationOptions, typeOf;
import 'package:flutter_bloc_advance/configuration/app_logger.dart' as x8 show LogFormat;
import 'package:flutter_bloc_advance/configuration/environment.dart' as x9 show Environment;
import 'package:flutter_bloc_advance/configuration/local_storage.dart' as x10 show StorageKeys;
import 'package:flutter_bloc_advance/configuration/local_storage.dart' as x10 show StorageKeys, StorageType;
import 'package:flutter_bloc_advance/data/models/authority.dart' as x2 show Authority;
import 'package:flutter_bloc_advance/data/models/change_password.dart' as x0 show PasswordChangeDTO;
import 'package:flutter_bloc_advance/data/models/city.dart' as x3 show City;
Expand Down Expand Up @@ -88,6 +88,8 @@ final mainProdGeneratedAdapter = JsonMapperAdapter(
typeOf<Set<x9.Environment>>(): (value) => value.cast<x9.Environment>(),
typeOf<List<x10.StorageKeys>>(): (value) => value.cast<x10.StorageKeys>(),
typeOf<Set<x10.StorageKeys>>(): (value) => value.cast<x10.StorageKeys>(),
typeOf<List<x10.StorageType>>(): (value) => value.cast<x10.StorageType>(),
typeOf<Set<x10.StorageType>>(): (value) => value.cast<x10.StorageType>(),
typeOf<List<x11.AccountStatus>>(): (value) => value.cast<x11.AccountStatus>(),
typeOf<Set<x11.AccountStatus>>(): (value) => value.cast<x11.AccountStatus>(),
typeOf<List<x12.AuthorityStatus>>(): (value) => value.cast<x12.AuthorityStatus>(),
Expand All @@ -113,6 +115,7 @@ final mainProdGeneratedAdapter = JsonMapperAdapter(
x8.LogFormat: x8.LogFormat.values,
x9.Environment: x9.Environment.values,
x10.StorageKeys: x10.StorageKeys.values,
x10.StorageType: x10.StorageType.values,
x11.AccountStatus: x11.AccountStatus.values,
x12.AuthorityStatus: x12.AuthorityStatus.values,
x13.CityStatus: x13.CityStatus.values,
Expand Down
4 changes: 2 additions & 2 deletions test/conf/local_storage_getx_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import 'local_storage_getx_test.mocks.dart';
@GenerateMocks([GetStorage])
void main() {
group('AppLocalStorageGetX Tests', () {
late AppLocalStorageGetX storage;
late GetStorageStrategy storage;
late MockGetStorage mockPrefs;

setUpAll(() async {
Expand All @@ -20,7 +20,7 @@ void main() {
setUp(() {
// Given
mockPrefs = MockGetStorage();
storage = AppLocalStorageGetX();
storage = GetStorageStrategy();
storage.setPreferencesInstance(mockPrefs);
});

Expand Down
24 changes: 20 additions & 4 deletions test/conf/local_storage_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,35 @@ import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:shared_preferences/shared_preferences.dart';

import '../test_utils.dart';
import 'local_storage_test.mocks.dart';

@GenerateMocks([SharedPreferences])
void main() {
group('AppLocalStorage', () {
late AppLocalStorage localStorage;

setUpAll(() async {
await TestUtils().setupUnitTest();
});

setUp(() {
AppLogger.configure(isProduction: false, logFormat: LogFormat.simple);
localStorage = AppLocalStorage();
SharedPreferences.setMockInitialValues({});
});

test("set strategy sharedPreferences", () {
localStorage.setStrategy(StorageType.sharedPreferences);
});
test("set strategy getStorage", () {
localStorage.setStrategy(StorageType.getStorage);
});

test("set strategy sharedPreferences", () {
localStorage.setStrategy(StorageType.sharedPreferences);
});

test('save and read String value', () async {
await localStorage.save('testKey', 'testValue');
final result = await localStorage.read('testKey');
Expand Down Expand Up @@ -83,12 +99,12 @@ void main() {
});

group('remove method error handling', () {
late AppLocalStorage localStorage;
late SharedPreferencesStrategy localStorage;
late SharedPreferences mockPrefs;

setUp(() {
AppLogger.configure(isProduction: false, logFormat: LogFormat.simple);
localStorage = AppLocalStorage();
localStorage = SharedPreferencesStrategy();
mockPrefs = MockSharedPreferences();
SharedPreferences.setMockInitialValues({});
localStorage.setPreferencesInstance(mockPrefs);
Expand All @@ -106,12 +122,12 @@ void main() {
});

group('remove method error handling', () {
late AppLocalStorage localStorage;
late SharedPreferencesStrategy localStorage;
late SharedPreferences mockPrefs;

setUp(() {
AppLogger.configure(isProduction: false, logFormat: LogFormat.simple);
localStorage = AppLocalStorage();
localStorage = SharedPreferencesStrategy();
mockPrefs = MockSharedPreferences();
SharedPreferences.setMockInitialValues({});
localStorage.setPreferencesInstance(mockPrefs);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import 'package:flutter_bloc_advance/configuration/local_storage.dart' as _i5;
import 'package:flutter_bloc_advance/presentation/common_widgets/drawer/drawer_bloc/drawer_bloc.dart'
as _i2;
import 'package:mockito/mockito.dart' as _i1;
import 'package:shared_preferences/shared_preferences.dart' as _i6;

// ignore_for_file: type=lint
// ignore_for_file: avoid_redundant_argument_values
Expand Down Expand Up @@ -177,11 +176,10 @@ class MockAppLocalStorage extends _i1.Mock implements _i5.AppLocalStorage {
}

@override
void setPreferencesInstance(_i6.SharedPreferences? prefs) =>
super.noSuchMethod(
void setStrategy(_i5.StorageType? type) => super.noSuchMethod(
Invocation.method(
#setPreferencesInstance,
[prefs],
#setStrategy,
[type],
),
returnValueForMissingStub: null,
);
Expand Down

0 comments on commit c21bb13

Please sign in to comment.