Skip to content

Commit

Permalink
v0.9 — Abstracted cache & enhanced RestrrOptions accessibility (#21)
Browse files Browse the repository at this point in the history
* Abstracted cache & enhanced RestrrOptions accessibility

* Updated CHANGELOG.md & version
  • Loading branch information
jasonlessenich committed Apr 12, 2024
1 parent 8f8b701 commit 3df8b9e
Show file tree
Hide file tree
Showing 21 changed files with 142 additions and 186 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.9
- Added `EntityCacheView`
- Temporary remove page cache
- Added ability to use custom EntityCacheView implementations (See `RestrrOptions`)

## 0.8.1
- Fixed http methods of `SessionRoutes#deleteCurrent` & `SessionRoutes#deleteById`

Expand Down
3 changes: 3 additions & 0 deletions lib/restrr.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ export 'src/api/restrr.dart';
export 'src/api/restrr_builder.dart';
export 'src/api/server_info.dart';

/* [ /src/api/cache ] */
export 'src/api/cache/entity_cache_view.dart';

/* [ /src/api/entities ] */
export 'src/api/entities/currency/currency.dart';
export 'src/api/entities/currency/custom_currency.dart';
Expand Down
11 changes: 11 additions & 0 deletions lib/src/api/cache/entity_cache_view.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import 'package:restrr/restrr.dart';

abstract interface class EntityCacheView<E extends RestrrEntity<E, ID>, ID extends EntityId<E>> {
E? get(ID id);
List<E> getAll();
E add(E entity);
E? remove(ID id);
void removeWhere(bool Function(E) predicate);
bool contains(ID id);
void clear();
}
12 changes: 6 additions & 6 deletions lib/src/api/requests/request_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ class RequestHandler {
dynamic body,
String contentType = 'application/json'}) async {
try {
final Response<dynamic> response = await route.submit(
routeOptions: routeOptions, body: body, bearerToken: bearerToken, contentType: contentType);
final Response<dynamic> response =
await route.submit(routeOptions: routeOptions, body: body, bearerToken: bearerToken, contentType: contentType);
return RestResponse(data: mapper.call(response.data), statusCode: response.statusCode);
} on DioException catch (e) {
return _handleDioException(e);
Expand Down Expand Up @@ -61,8 +61,8 @@ class RequestHandler {
Map<int, RestrrError> errorMap = const {},
String contentType = 'application/json'}) async {
try {
final Response<dynamic> response = await route.submit(
routeOptions: routeOptions, body: body, bearerToken: bearerToken, contentType: contentType);
final Response<dynamic> response =
await route.submit(routeOptions: routeOptions, body: body, bearerToken: bearerToken, contentType: contentType);
return RestResponse(data: true, statusCode: response.statusCode);
} on DioException catch (e) {
return _handleDioException(e);
Expand Down Expand Up @@ -97,8 +97,8 @@ class RequestHandler {
dynamic body,
String contentType = 'application/json'}) async {
try {
final Response<dynamic> response = await route.submit(
routeOptions: routeOptions, body: body, bearerToken: bearerToken, contentType: contentType);
final Response<dynamic> response =
await route.submit(routeOptions: routeOptions, body: body, bearerToken: bearerToken, contentType: contentType);
if (response.data is! List<dynamic>) {
throw StateError('Received response is not a list!');
}
Expand Down
21 changes: 12 additions & 9 deletions lib/src/api/restrr.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ import 'package:restrr/src/internal/requests/responses/rest_response.dart';
import '../../restrr.dart';

class RestrrOptions {
final bool isWeb;
final bool disableLogging;
const RestrrOptions({this.isWeb = false, this.disableLogging = false});
bool isWeb = false;
bool disableLogging = false;

EntityCacheView<Currency, CurrencyId>? currencyCacheView;
EntityCacheView<PartialSession, PartialSessionId>? sessionCacheView;
EntityCacheView<Transaction, TransactionId>? transactionCacheView;
EntityCacheView<Account, AccountId>? accountCacheView;
EntityCacheView<User, UserId>? userCacheView;
}

class RouteOptions {
final Uri hostUri;
final int apiVersion;
const RouteOptions({required this.hostUri, this.apiVersion = -1});
Uri? hostUri;
int? apiVersion;
}

abstract class Restrr {
Expand All @@ -32,7 +36,7 @@ abstract class Restrr {
final RestResponse<ServerInfo> response = await RequestHandler.request(
route: StatusRoutes.health.compile(),
mapper: (json) => ServerInfo.fromJson(json),
routeOptions: RouteOptions(hostUri: uri),
routeOptions: RouteOptions()..hostUri = uri,
);
if (response.hasError) {
throw response.error!;
Expand Down Expand Up @@ -73,8 +77,7 @@ abstract class Restrr {

/* Currencies */

Future<Currency> createCurrency(
{required String name, required String symbol, required int decimalPlaces, String? isoCode});
Future<Currency> createCurrency({required String name, required String symbol, required int decimalPlaces, String? isoCode});

List<Currency> getCurrencies();

Expand Down
22 changes: 11 additions & 11 deletions lib/src/api/restrr_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ class RestrrBuilder {

final Uri uri;

final RestrrOptions options;
RestrrOptions options = RestrrOptions();

RestrrBuilder({required this.uri, this.options = const RestrrOptions()});
RestrrBuilder({required this.uri});

RestrrBuilder on<T extends RestrrEvent>(Type type, void Function(T) func) {
_eventMap[type] = func;
Expand Down Expand Up @@ -42,16 +42,16 @@ class RestrrBuilder {
});
}

Future<Restrr> _handleAuthProcess(
{required Future<RestResponse<PartialSession>> Function(RestrrImpl) authFunction}) async {
Future<Restrr> _handleAuthProcess({required Future<RestResponse<PartialSession>> Function(RestrrImpl) authFunction}) async {
// check if the URI is valid and the API is healthy
final ServerInfo statusResponse = await Restrr.checkUri(uri, isWeb: options.isWeb);
Restrr.log.config('Host: $uri, API v${statusResponse.apiVersion}');
// build api instance
final RestrrImpl apiImpl = RestrrImpl(
options: options,
routeOptions: RouteOptions(hostUri: uri, apiVersion: statusResponse.apiVersion),
eventMap: _eventMap);
final RestrrImpl apiImpl = RestrrImpl(eventMap: _eventMap)
..options = options
..routeOptions = (RouteOptions()
..hostUri = uri
..apiVersion = statusResponse.apiVersion);
// call auth function
final RestResponse<PartialSession> response = await authFunction(apiImpl);
if (response.hasError) {
Expand All @@ -65,15 +65,15 @@ class RestrrBuilder {
// Retrieve all accounts & currencies to make them available in the cache
try {
final List<Account> accounts =
await RequestUtils.fetchAllPaginated<Account, AccountId>(apiImpl, await apiImpl.retrieveAllAccounts(limit: 50));
await RequestUtils.fetchAllPaginated<Account, AccountId>(apiImpl, await apiImpl.retrieveAllAccounts(limit: 50));
Restrr.log.info('Cached ${accounts.length} account(s)');
} catch (e) {
Restrr.log.warning('Failed to cache accounts: $e');
}

try {
final List<Currency> currencies = await RequestUtils.fetchAllPaginated<Currency, CurrencyId>(
apiImpl, await apiImpl.retrieveAllCurrencies(limit: 50));
final List<Currency> currencies =
await RequestUtils.fetchAllPaginated<Currency, CurrencyId>(apiImpl, await apiImpl.retrieveAllCurrencies(limit: 50));
Restrr.log.info('Cached ${currencies.length} currencies');
} catch (e) {
Restrr.log.warning('Failed to cache currencies: $e');
Expand Down
19 changes: 0 additions & 19 deletions lib/src/internal/cache/batch_cache_view.dart

This file was deleted.

32 changes: 0 additions & 32 deletions lib/src/internal/cache/cache_view.dart

This file was deleted.

26 changes: 26 additions & 0 deletions lib/src/internal/cache/entity_cache_view_impl.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import 'package:restrr/restrr.dart';

class EntityCacheViewImpl<E extends RestrrEntity<E, ID>, ID extends EntityId<E>> implements EntityCacheView<E, ID> {
final Map<ID, E> _cache = {};

@override
E? get(ID id) => _cache[id];

@override
List<E> getAll() => _cache.values.toList();

@override
E add(E entity) => _cache[entity.id] = entity;

@override
E? remove(ID id) => _cache.remove(id);

@override
void removeWhere(bool Function(E p1) predicate) => _cache.removeWhere((k, v) => predicate(v));

@override
bool contains(ID id) => _cache.containsKey(id);

@override
void clear() => _cache.clear();
}
13 changes: 5 additions & 8 deletions lib/src/internal/entities/account_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ class AccountIdImpl extends IdImpl<Account> implements AccountId {
const AccountIdImpl({required super.api, required super.value});

@override
Account? get() => api.accountCache.get(value);
Account? get() => api.accountCache.get(this);

@override
Future<Account> retrieve({forceRetrieve = false}) {
return RequestUtils.getOrRetrieveSingle(
api: api,
key: this,
cacheView: api.accountCache,
compiledRoute: AccountRoutes.getById.compile(params: [value]),
Expand Down Expand Up @@ -51,14 +52,10 @@ class AccountImpl extends RestrrEntityImpl<Account, AccountId> implements Accoun

@override
Future<bool> delete() => RequestUtils.deleteSingle(
compiledRoute: AccountRoutes.deleteById.compile(params: [id.value]),
api: api,
key: id,
cacheView: api.transactionCache);
compiledRoute: AccountRoutes.deleteById.compile(params: [id.value]), api: api, key: id, cacheView: api.accountCache);

@override
Future<Account> update(
{String? name, String? description, String? iban, int? originalBalance, Id? currencyId}) async {
Future<Account> update({String? name, String? description, String? iban, int? originalBalance, Id? currencyId}) async {
if (name == null && description == null && iban == null && originalBalance == null && currencyId == null) {
throw ArgumentError('At least one field must be set');
}
Expand All @@ -81,7 +78,7 @@ class AccountImpl extends RestrrEntityImpl<Account, AccountId> implements Accoun
@override
Future<Paginated<Transaction>> retrieveAllTransactions({int page = 1, int limit = 25, bool forceRetrieve = false}) {
return RequestUtils.getOrRetrievePage(
pageCache: api.transactionPageCache,
api: api,
compiledRoute: AccountRoutes.getTransactions.compile(params: [id.value]),
page: page,
limit: limit,
Expand Down
3 changes: 2 additions & 1 deletion lib/src/internal/entities/currency/currency_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ class CurrencyIdImpl extends IdImpl<Currency> implements CurrencyId {
const CurrencyIdImpl({required super.api, required super.value});

@override
Currency? get() => api.currencyCache.get(value);
Currency? get() => api.currencyCache.get(this);

@override
Future<Currency> retrieve({forceRetrieve = false}) => RequestUtils.getOrRetrieveSingle(
api: api,
key: this,
cacheView: api.currencyCache,
compiledRoute: CurrencyRoutes.getById.compile(params: [value]),
Expand Down
5 changes: 1 addition & 4 deletions lib/src/internal/entities/currency/custom_currency_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@ class CustomCurrencyImpl extends CurrencyImpl implements CustomCurrency {

@override
Future<bool> delete() => RequestUtils.deleteSingle(
compiledRoute: CurrencyRoutes.deleteById.compile(params: [id.value]),
api: api,
key: id,
cacheView: api.transactionCache);
compiledRoute: CurrencyRoutes.deleteById.compile(params: [id.value]), api: api, key: id, cacheView: api.currencyCache);

@override
Future<Currency> update({String? name, String? symbol, String? isoCode, int? decimalPlaces}) async {
Expand Down
8 changes: 3 additions & 5 deletions lib/src/internal/entities/session/partial_session_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ class PartialSessionIdImpl extends IdImpl<PartialSession> implements PartialSess
const PartialSessionIdImpl({required super.api, required super.value});

@override
PartialSession? get() => api.sessionCache.get(value);
PartialSession? get() => api.sessionCache.get(this);

@override
Future<PartialSession> retrieve({forceRetrieve = false}) => RequestUtils.getOrRetrieveSingle(
api: api,
key: this,
cacheView: api.sessionCache,
compiledRoute: SessionRoutes.getById.compile(params: [value]),
Expand Down Expand Up @@ -39,8 +40,5 @@ class PartialSessionImpl extends RestrrEntityImpl<PartialSession, PartialSession

@override
Future<bool> delete() => RequestUtils.deleteSingle(
compiledRoute: SessionRoutes.deleteById.compile(params: [id.value]),
api: api,
key: id,
cacheView: api.transactionCache);
compiledRoute: SessionRoutes.deleteById.compile(params: [id.value]), api: api, key: id, cacheView: api.sessionCache);
}
3 changes: 2 additions & 1 deletion lib/src/internal/entities/transaction_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ class TransactionIdImpl extends IdImpl<Transaction> implements TransactionId {
const TransactionIdImpl({required super.api, required super.value});

@override
Transaction? get() => api.transactionCache.get(value);
Transaction? get() => api.transactionCache.get(this);

@override
Future<Transaction> retrieve({forceRetrieve = false}) => RequestUtils.getOrRetrieveSingle(
api: api,
key: this,
cacheView: api.transactionCache,
compiledRoute: TransactionRoutes.getById.compile(params: [value]),
Expand Down
3 changes: 2 additions & 1 deletion lib/src/internal/entities/user_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ class UserIdImpl extends IdImpl<User> implements UserId {
const UserIdImpl({required super.api, required super.value});

@override
User? get() => api.userCache.get(value);
User? get() => api.userCache.get(this);

@override
Future<User> retrieve({forceRetrieve = false}) {
return RequestUtils.getOrRetrieveSingle(
api: api,
key: this,
cacheView: api.userCache,
compiledRoute: UserRoutes.getSelf.compile(),
Expand Down
Loading

0 comments on commit 3df8b9e

Please sign in to comment.