From 2cd1a8effc32c718ac0bc4e15e6e97061070f4e7 Mon Sep 17 00:00:00 2001 From: Jason Dean Lessenich Date: Tue, 27 Feb 2024 14:09:01 +0100 Subject: [PATCH 1/7] Implemented Currency Routes & Caching --- lib/restrr.dart | 1 + lib/src/cache/batch_cache_view.dart | 13 +++ lib/src/cache/cache_view.dart | 15 +++ lib/src/entities/currency.dart | 32 +++++++ lib/src/entities/restrr_entity.dart | 5 + lib/src/entities/user.dart | 5 +- lib/src/entity_builder.dart | 12 +++ lib/src/requests/route.dart | 8 +- lib/src/requests/route_definitions.dart | 10 ++ lib/src/restrr_base.dart | 121 ++++++++++++++++++++++-- lib/src/service/currency_service.dart | 69 ++++++++++++++ 11 files changed, 276 insertions(+), 15 deletions(-) create mode 100644 lib/src/cache/batch_cache_view.dart create mode 100644 lib/src/cache/cache_view.dart create mode 100644 lib/src/entities/currency.dart create mode 100644 lib/src/service/currency_service.dart diff --git a/lib/restrr.dart b/lib/restrr.dart index 9ef135c..6f56c48 100644 --- a/lib/restrr.dart +++ b/lib/restrr.dart @@ -5,6 +5,7 @@ export 'src/entity_builder.dart'; export 'src/restrr_base.dart'; /* [ /src/entities ] */ +export 'src/entities/currency.dart'; export 'src/entities/health_response.dart'; export 'src/entities/restrr_entity.dart'; export 'src/entities/user.dart'; diff --git a/lib/src/cache/batch_cache_view.dart b/lib/src/cache/batch_cache_view.dart new file mode 100644 index 0000000..f93e4c4 --- /dev/null +++ b/lib/src/cache/batch_cache_view.dart @@ -0,0 +1,13 @@ +import '../../restrr.dart'; + +class RestrrEntityBatchCacheView { + List? _lastSnapshot; + + List? get() => _lastSnapshot; + + void update(List value) => _lastSnapshot = value; + + void clear() => _lastSnapshot = null; + + bool get hasSnapshot => _lastSnapshot != null; +} \ No newline at end of file diff --git a/lib/src/cache/cache_view.dart b/lib/src/cache/cache_view.dart new file mode 100644 index 0000000..6fa2b22 --- /dev/null +++ b/lib/src/cache/cache_view.dart @@ -0,0 +1,15 @@ +import 'package:restrr/restrr.dart'; + +class RestrrEntityCacheView { + final Map _cache = {}; + + T? get(int id) => _cache[id]; + + T add(T value) => _cache[value.id] = value; + + T? remove(int id) => _cache.remove(id); + + void clear() => _cache.clear(); + + bool contains(int id) => _cache.containsKey(id); +} diff --git a/lib/src/entities/currency.dart b/lib/src/entities/currency.dart new file mode 100644 index 0000000..c1c5d0d --- /dev/null +++ b/lib/src/entities/currency.dart @@ -0,0 +1,32 @@ +import 'package:restrr/restrr.dart'; + +abstract class Currency implements RestrrEntity { + String get name; + String get symbol; + String get isoCode; + int get decimalPlaces; + int get user; +} + +class CurrencyImpl extends RestrrEntityImpl implements Currency { + @override + final String name; + @override + final String symbol; + @override + final String isoCode; + @override + final int decimalPlaces; + @override + final int user; + + const CurrencyImpl({ + required super.api, + required super.id, + required this.name, + required this.symbol, + required this.isoCode, + required this.decimalPlaces, + required this.user, + }); +} diff --git a/lib/src/entities/restrr_entity.dart b/lib/src/entities/restrr_entity.dart index f568523..9e9cd54 100644 --- a/lib/src/entities/restrr_entity.dart +++ b/lib/src/entities/restrr_entity.dart @@ -5,13 +5,18 @@ import '../../restrr.dart'; abstract class RestrrEntity { /// A reference to the Restrr instance. Restrr get api; + + int get id; } class RestrrEntityImpl implements RestrrEntity { @override final Restrr api; + @override + final int id; const RestrrEntityImpl({ required this.api, + required this.id, }); } diff --git a/lib/src/entities/user.dart b/lib/src/entities/user.dart index 6aae6f1..8937aad 100644 --- a/lib/src/entities/user.dart +++ b/lib/src/entities/user.dart @@ -1,7 +1,6 @@ import '../../restrr.dart'; abstract class User extends RestrrEntity { - int get id; String get username; String? get email; String? get displayName; @@ -14,8 +13,6 @@ abstract class User extends RestrrEntity { } class UserImpl extends RestrrEntityImpl implements User { - @override - final int id; @override final String username; @override @@ -29,7 +26,7 @@ class UserImpl extends RestrrEntityImpl implements User { const UserImpl({ required super.api, - required this.id, + required super.id, required this.username, required this.email, required this.displayName, diff --git a/lib/src/entity_builder.dart b/lib/src/entity_builder.dart index f5df14c..cefebe3 100644 --- a/lib/src/entity_builder.dart +++ b/lib/src/entity_builder.dart @@ -25,4 +25,16 @@ class EntityBuilder { isAdmin: json['is_admin'], ); } + + Currency buildCurrency(Map json) { + return CurrencyImpl( + api: api, + id: json['id'], + name: json['name'], + symbol: json['symbol'], + isoCode: json['iso_code'], + decimalPlaces: json['decimal_places'], + user: json['user'], + ); + } } diff --git a/lib/src/requests/route.dart b/lib/src/requests/route.dart index c35a14d..93cd8ed 100644 --- a/lib/src/requests/route.dart +++ b/lib/src/requests/route.dart @@ -37,18 +37,18 @@ class Route { Route.patch(String path, {bool isVersioned = true}) : this._('PATCH', path, isVersioned: isVersioned); - CompiledRoute compile({List params = const []}) { + CompiledRoute compile({List params = const []}) { if (params.length != paramCount) { throw ArgumentError( 'Error compiling route [$method $path}]: Incorrect amount of parameters! Expected: $paramCount, Provided: ${params.length}'); } final Map values = {}; String compiledRoute = path; - for (String param in params) { + for (dynamic param in params) { int paramStart = compiledRoute.indexOf('{'); int paramEnd = compiledRoute.indexOf('}'); - values[compiledRoute.substring(paramStart + 1, paramEnd)] = param; - compiledRoute = compiledRoute.replaceRange(paramStart, paramEnd + 1, param); + values[compiledRoute.substring(paramStart + 1, paramEnd)] = param.toString(); + compiledRoute = compiledRoute.replaceRange(paramStart, paramEnd + 1, param.toString()); } return CompiledRoute(this, compiledRoute, values); } diff --git a/lib/src/requests/route_definitions.dart b/lib/src/requests/route_definitions.dart index 9057b4d..4a0b4eb 100644 --- a/lib/src/requests/route_definitions.dart +++ b/lib/src/requests/route_definitions.dart @@ -15,3 +15,13 @@ class UserRoutes { static final Route logout = Route.delete('/user/logout'); static final Route register = Route.post('/user/register'); } + +class CurrencyRoutes { + const CurrencyRoutes._(); + + static final Route retrieveAll = Route.get('/currency'); + static final Route create = Route.post('/currency/{currencyId}'); + static final Route retrieveById = Route.get('/currency/{currencyId}'); + static final Route deleteById = Route.delete('/currency/{currencyId}'); + static final Route updateById = Route.patch('/currency/{currencyId}'); +} diff --git a/lib/src/restrr_base.dart b/lib/src/restrr_base.dart index abb4192..2ae5f9d 100644 --- a/lib/src/restrr_base.dart +++ b/lib/src/restrr_base.dart @@ -1,9 +1,12 @@ import 'package:logging/logging.dart'; +import 'package:restrr/src/cache/batch_cache_view.dart'; import 'package:restrr/src/requests/route.dart'; import 'package:restrr/src/service/api_service.dart'; +import 'package:restrr/src/service/currency_service.dart'; import 'package:restrr/src/service/user_service.dart'; import '../restrr.dart'; +import 'cache/cache_view.dart'; class RestrrOptions { final bool isWeb; @@ -59,7 +62,7 @@ class RestrrBuilder { /// Logs in with the given [username] and [password]. Future> _handleLogin(RestrrImpl apiImpl, String username, String password) async { - final RestResponse response = await apiImpl.userService.login(username, password); + final RestResponse response = await apiImpl._userService.login(username, password); if (!response.hasData) { Restrr.log.warning('Invalid credentials for user $username'); return RestrrError.invalidCredentials.toRestResponse(statusCode: response.statusCode); @@ -73,7 +76,7 @@ class RestrrBuilder { Future> _handleRegistration(RestrrImpl apiImpl, String username, String password, {String? email, String? displayName}) async { final RestResponse response = - await apiImpl.userService.register(username, password, email: email, displayName: displayName); + await apiImpl._userService.register(username, password, email: email, displayName: displayName); if (response.hasError) { Restrr.log.warning('Failed to register user $username'); return response.error?.toRestResponse(statusCode: response.statusCode) ?? RestrrError.unknown.toRestResponse(); @@ -85,7 +88,7 @@ class RestrrBuilder { /// Attempts to refresh the session with still saved credentials. Future> _handleSavedSession(RestrrImpl apiImpl) async { - final RestResponse response = await apiImpl.userService.getSelf(); + final RestResponse response = await apiImpl._userService.getSelf(); if (response.hasError) { Restrr.log.warning('Failed to refresh session'); return response.error?.toRestResponse(statusCode: response.statusCode) ?? RestrrError.unknown.toRestResponse(); @@ -108,8 +111,6 @@ abstract class Restrr { /// The currently authenticated user. User get selfUser; - Future logout(); - /// Checks whether the given [uri] is valid and the API is healthy. static Future> checkUri(Uri uri, {bool isWeb = false}) async { return RequestHandler.request( @@ -118,6 +119,23 @@ abstract class Restrr { isWeb: isWeb, routeOptions: RouteOptions(hostUri: uri)); } + + /// Retrieves the currently authenticated user. + Future retrieveSelf({bool forceRetrieve = false}); + + /// Logs out the current user. + Future logout(); + + Future?> retrieveAllCurrencies({bool forceRetrieve = false}); + + Future createCurrency( + {required String name, required String symbol, required String isoCode, required int decimalPlaces}); + + Future retrieveCurrencyById(int id, {bool forceRetrieve = false}); + + Future deleteCurrencyById(int id); + + Future updateCurrencyById(int id, {String? name, String? symbol, String? isoCode, int? decimalPlaces}); } class RestrrImpl implements Restrr { @@ -126,7 +144,17 @@ class RestrrImpl implements Restrr { @override final RouteOptions routeOptions; - late final UserService userService = UserService(api: this); + /* Services */ + + late final UserService _userService = UserService(api: this); + late final CurrencyService _currencyService = CurrencyService(api: this); + + /* Caches */ + + late final RestrrEntityCacheView _userCache = RestrrEntityCacheView(); + late final RestrrEntityCacheView _currencyCache = RestrrEntityCacheView(); + + late final RestrrEntityBatchCacheView _currencyBatchCache = RestrrEntityBatchCacheView(); RestrrImpl._({required this.options, required this.routeOptions}); @@ -136,13 +164,92 @@ class RestrrImpl implements Restrr { @override late final User selfUser; + @override + Future retrieveSelf({bool forceRetrieve = false}) async { + return _getOrRetrieveSingle( + key: selfUser.id, + cacheView: _userCache, + retrieveFunction: (api) => api._userService.getSelf(), + forceRetrieve: forceRetrieve); + } + @override Future logout() async { - final RestResponse response = await UserService(api: this).logout(); + final RestResponse response = await _userService.logout(); if (response.hasData && response.data! && !options.isWeb) { await CompiledRoute.cookieJar.deleteAll(); return true; } return false; } + + @override + Future?> retrieveAllCurrencies({bool forceRetrieve = false}) async { + return _getOrRetrieveMulti( + batchCache: _currencyBatchCache, + retrieveFunction: (api) => api._currencyService.retrieveAllCurrencies(), + ); + } + + @override + Future createCurrency( + {required String name, required String symbol, required String isoCode, required int decimalPlaces}) async { + final RestResponse response = await _currencyService.createCurrency( + name: name, symbol: symbol, isoCode: isoCode, decimalPlaces: decimalPlaces); + return response.data; + } + + @override + Future retrieveCurrencyById(int id, {bool forceRetrieve = false}) async { + return _getOrRetrieveSingle( + key: id, + cacheView: _currencyCache, + retrieveFunction: (api) => api._currencyService.retrieveCurrencyById(id), + forceRetrieve: forceRetrieve); + } + + @override + Future deleteCurrencyById(int id) async { + final RestResponse response = await _currencyService.deleteCurrencyById(id); + return response.hasData && response.data!; + } + + @override + Future updateCurrencyById(int id, + {String? name, String? symbol, String? isoCode, int? decimalPlaces}) async { + final RestResponse response = await _currencyService.updateCurrencyById(id, + name: name, symbol: symbol, isoCode: isoCode, decimalPlaces: decimalPlaces); + return response.data; + } + + Future _getOrRetrieveSingle( + {required int key, + required RestrrEntityCacheView cacheView, + required Future> Function(RestrrImpl) retrieveFunction, + bool forceRetrieve = false}) async { + if (!forceRetrieve && cacheView.contains(key)) { + return cacheView.get(key)!; + } + final RestResponse response = await retrieveFunction.call(this); + if (response.hasData) { + return cacheView.add(response.data!); + } + return null; + } + + Future?> _getOrRetrieveMulti( + {required RestrrEntityBatchCacheView batchCache, + required Future>> Function(RestrrImpl) retrieveFunction, + bool forceRetrieve = false}) async { + if (!forceRetrieve && batchCache.hasSnapshot) { + return batchCache.get()!; + } + final RestResponse> response = await retrieveFunction.call(this); + if (response.hasData) { + final List remote = response.data!; + batchCache.update(remote); + return remote; + } + return null; + } } diff --git a/lib/src/service/currency_service.dart b/lib/src/service/currency_service.dart new file mode 100644 index 0000000..7d61814 --- /dev/null +++ b/lib/src/service/currency_service.dart @@ -0,0 +1,69 @@ +import 'package:restrr/restrr.dart'; + +import 'api_service.dart'; + +class CurrencyService extends ApiService { + const CurrencyService({required super.api}); + + Future>> retrieveAllCurrencies() async { + return multiRequest( + route: CurrencyRoutes.retrieveAll.compile(), + mapper: (json) => api.entityBuilder.buildCurrency(json), + errorMap: { + 401: RestrrError.notSignedIn, + }); + } + + Future> createCurrency( + {required String name, required String symbol, required String isoCode, required int decimalPlaces}) async { + return request( + route: CurrencyRoutes.create.compile(), + mapper: (json) => api.entityBuilder.buildCurrency(json), + body: { + 'name': name, + 'symbol': symbol, + 'iso_code': isoCode, + 'decimal_places': decimalPlaces, + }, + errorMap: { + 401: RestrrError.notSignedIn, + }); + } + + Future> retrieveCurrencyById(int id) async { + return request( + route: CurrencyRoutes.retrieveById.compile(params: [id]), + mapper: (json) => api.entityBuilder.buildCurrency(json), + errorMap: { + 401: RestrrError.notSignedIn, + 404: RestrrError.notFound, + }); + } + + Future> deleteCurrencyById(int id) async { + return noResponseRequest(route: CurrencyRoutes.deleteById.compile(params: [id]), errorMap: { + 401: RestrrError.notSignedIn, + 404: RestrrError.notFound, + }); + } + + Future> updateCurrencyById(int id, + {String? name, String? symbol, String? isoCode, int? decimalPlaces}) async { + if (name == null && symbol == null && isoCode == null && decimalPlaces == null) { + throw ArgumentError('At least one field must be set'); + } + return request( + route: CurrencyRoutes.updateById.compile(params: [id]), + mapper: (json) => api.entityBuilder.buildCurrency(json), + body: { + if (name != null) 'name': name, + if (symbol != null) 'symbol': symbol, + if (isoCode != null) 'iso_code': isoCode, + if (decimalPlaces != null) 'decimal_places': decimalPlaces, + }, + errorMap: { + 401: RestrrError.notSignedIn, + 404: RestrrError.notFound, + }); + } +} From 2e3d2d5be1b66932902f79e48e1b6f7d3977406c Mon Sep 17 00:00:00 2001 From: Jason Dean Lessenich Date: Tue, 27 Feb 2024 14:13:23 +0100 Subject: [PATCH 2/7] Added ID (int) typedef --- lib/src/cache/cache_view.dart | 8 ++++---- lib/src/entities/restrr_entity.dart | 6 ++++-- lib/src/restrr_base.dart | 14 +++++++------- lib/src/service/currency_service.dart | 6 +++--- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/lib/src/cache/cache_view.dart b/lib/src/cache/cache_view.dart index 6fa2b22..6886121 100644 --- a/lib/src/cache/cache_view.dart +++ b/lib/src/cache/cache_view.dart @@ -1,15 +1,15 @@ import 'package:restrr/restrr.dart'; class RestrrEntityCacheView { - final Map _cache = {}; + final Map _cache = {}; - T? get(int id) => _cache[id]; + T? get(ID id) => _cache[id]; T add(T value) => _cache[value.id] = value; - T? remove(int id) => _cache.remove(id); + T? remove(ID id) => _cache.remove(id); void clear() => _cache.clear(); - bool contains(int id) => _cache.containsKey(id); + bool contains(ID id) => _cache.containsKey(id); } diff --git a/lib/src/entities/restrr_entity.dart b/lib/src/entities/restrr_entity.dart index 9e9cd54..caeda28 100644 --- a/lib/src/entities/restrr_entity.dart +++ b/lib/src/entities/restrr_entity.dart @@ -1,19 +1,21 @@ import '../../restrr.dart'; +typedef ID = int; + /// The base class for all Restrr entities. /// This simply provides a reference to the Restrr instance. abstract class RestrrEntity { /// A reference to the Restrr instance. Restrr get api; - int get id; + ID get id; } class RestrrEntityImpl implements RestrrEntity { @override final Restrr api; @override - final int id; + final ID id; const RestrrEntityImpl({ required this.api, diff --git a/lib/src/restrr_base.dart b/lib/src/restrr_base.dart index 2ae5f9d..8afa11a 100644 --- a/lib/src/restrr_base.dart +++ b/lib/src/restrr_base.dart @@ -131,11 +131,11 @@ abstract class Restrr { Future createCurrency( {required String name, required String symbol, required String isoCode, required int decimalPlaces}); - Future retrieveCurrencyById(int id, {bool forceRetrieve = false}); + Future retrieveCurrencyById(ID id, {bool forceRetrieve = false}); - Future deleteCurrencyById(int id); + Future deleteCurrencyById(ID id); - Future updateCurrencyById(int id, {String? name, String? symbol, String? isoCode, int? decimalPlaces}); + Future updateCurrencyById(ID id, {String? name, String? symbol, String? isoCode, int? decimalPlaces}); } class RestrrImpl implements Restrr { @@ -200,7 +200,7 @@ class RestrrImpl implements Restrr { } @override - Future retrieveCurrencyById(int id, {bool forceRetrieve = false}) async { + Future retrieveCurrencyById(ID id, {bool forceRetrieve = false}) async { return _getOrRetrieveSingle( key: id, cacheView: _currencyCache, @@ -209,13 +209,13 @@ class RestrrImpl implements Restrr { } @override - Future deleteCurrencyById(int id) async { + Future deleteCurrencyById(ID id) async { final RestResponse response = await _currencyService.deleteCurrencyById(id); return response.hasData && response.data!; } @override - Future updateCurrencyById(int id, + Future updateCurrencyById(ID id, {String? name, String? symbol, String? isoCode, int? decimalPlaces}) async { final RestResponse response = await _currencyService.updateCurrencyById(id, name: name, symbol: symbol, isoCode: isoCode, decimalPlaces: decimalPlaces); @@ -223,7 +223,7 @@ class RestrrImpl implements Restrr { } Future _getOrRetrieveSingle( - {required int key, + {required ID key, required RestrrEntityCacheView cacheView, required Future> Function(RestrrImpl) retrieveFunction, bool forceRetrieve = false}) async { diff --git a/lib/src/service/currency_service.dart b/lib/src/service/currency_service.dart index 7d61814..a89626b 100644 --- a/lib/src/service/currency_service.dart +++ b/lib/src/service/currency_service.dart @@ -30,7 +30,7 @@ class CurrencyService extends ApiService { }); } - Future> retrieveCurrencyById(int id) async { + Future> retrieveCurrencyById(ID id) async { return request( route: CurrencyRoutes.retrieveById.compile(params: [id]), mapper: (json) => api.entityBuilder.buildCurrency(json), @@ -40,14 +40,14 @@ class CurrencyService extends ApiService { }); } - Future> deleteCurrencyById(int id) async { + Future> deleteCurrencyById(ID id) async { return noResponseRequest(route: CurrencyRoutes.deleteById.compile(params: [id]), errorMap: { 401: RestrrError.notSignedIn, 404: RestrrError.notFound, }); } - Future> updateCurrencyById(int id, + Future> updateCurrencyById(ID id, {String? name, String? symbol, String? isoCode, int? decimalPlaces}) async { if (name == null && symbol == null && isoCode == null && decimalPlaces == null) { throw ArgumentError('At least one field must be set'); From 1ab111e86783c653f8414e00d5cadd98a44c7d04 Mon Sep 17 00:00:00 2001 From: Jason Dean Lessenich Date: Tue, 27 Feb 2024 14:51:18 +0100 Subject: [PATCH 3/7] Made `Currency#user` nullable --- lib/src/entities/currency.dart | 4 ++-- test/restrr_entity_test.dart | 23 ++++++++++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/lib/src/entities/currency.dart b/lib/src/entities/currency.dart index c1c5d0d..21907f6 100644 --- a/lib/src/entities/currency.dart +++ b/lib/src/entities/currency.dart @@ -5,7 +5,7 @@ abstract class Currency implements RestrrEntity { String get symbol; String get isoCode; int get decimalPlaces; - int get user; + int? get user; } class CurrencyImpl extends RestrrEntityImpl implements Currency { @@ -18,7 +18,7 @@ class CurrencyImpl extends RestrrEntityImpl implements Currency { @override final int decimalPlaces; @override - final int user; + final int? user; const CurrencyImpl({ required super.api, diff --git a/test/restrr_entity_test.dart b/test/restrr_entity_test.dart index ed4f353..5a5c47f 100644 --- a/test/restrr_entity_test.dart +++ b/test/restrr_entity_test.dart @@ -24,6 +24,17 @@ const String userJson = ''' } '''; +const String currencyJson = ''' +{ + "id": 1, + "name": "US Dollar", + "symbol": "\$", + "iso_code": "USD", + "decimal_places": 2, + "user": 1 +} +'''; + void main() { late Restrr api; @@ -40,7 +51,7 @@ void main() { expect(healthResponse.details, null); }); - test('.buildUser', () { + test('.buildUser', () async { final User user = api.entityBuilder.buildUser(jsonDecode(userJson)); expect(user.id, 1); expect(user.username, 'admin'); @@ -48,5 +59,15 @@ void main() { expect(user.createdAt, DateTime.parse('+002024-02-17T20:48:43.391176000Z')); expect(user.isAdmin, true); }); + + test('.buildCurrency', () { + final Currency currency = api.entityBuilder.buildCurrency(jsonDecode(currencyJson)); + expect(currency.id, 1); + expect(currency.name, 'US Dollar'); + expect(currency.symbol, '\$'); + expect(currency.isoCode, 'USD'); + expect(currency.decimalPlaces, 2); + expect(currency.user, 1); + }); }); } From da669bf0ac58904fc7f045f6dcd4f37d2735c46d Mon Sep 17 00:00:00 2001 From: Jason Dean Lessenich Date: Tue, 27 Feb 2024 14:58:57 +0100 Subject: [PATCH 4/7] Added `Currency#isCustom` & `Currency#isCreatedBy` --- lib/src/entities/currency.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/src/entities/currency.dart b/lib/src/entities/currency.dart index 21907f6..89c9d5d 100644 --- a/lib/src/entities/currency.dart +++ b/lib/src/entities/currency.dart @@ -6,6 +6,10 @@ abstract class Currency implements RestrrEntity { String get isoCode; int get decimalPlaces; int? get user; + + bool get isCustom; + + bool isCreatedBy(User user); } class CurrencyImpl extends RestrrEntityImpl implements Currency { @@ -29,4 +33,10 @@ class CurrencyImpl extends RestrrEntityImpl implements Currency { required this.decimalPlaces, required this.user, }); + + @override + bool get isCustom => user != null; + + @override + bool isCreatedBy(User user) => this.user == user.id; } From 2564b079578b1b902af2ba8cdf97f92333b1793c Mon Sep 17 00:00:00 2001 From: Jason Dean Lessenich Date: Tue, 27 Feb 2024 15:03:01 +0100 Subject: [PATCH 5/7] Updated CHANGELOG.md & bumped version --- CHANGELOG.md | 10 ++++++++++ pubspec.yaml | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51e3eb1..42463cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## 0.5 +- Added `Currency` +- Added `Restrr#retrieveAllCurrencies` +- Added `Restrr#createCurrency` +- Added `Restrr#retrieveCurrencyById` +- Added `Restrr#deleteCurrencyById` +- Added `Restrr#updateCurrencyById` +- Added `Restrr#retrieveSelf` +- Implemented (Batch)CacheViews (some retrieve methods now have a `forceRetrieve` parameter) + ## 0.4.2 - Fixed missing `isWeb` in `RestrrBuilder#create` diff --git a/pubspec.yaml b/pubspec.yaml index e1652bc..401ab90 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: restrr description: Dart package which allows to communicate with the financrr REST API. -version: 0.4.2 +version: 0.5.0 repository: https://github.com/financrr/restrr environment: From f326684b9f3de402aef75edd1910042a172e2df3 Mon Sep 17 00:00:00 2001 From: Jason Dean Lessenich Date: Tue, 27 Feb 2024 15:06:31 +0100 Subject: [PATCH 6/7] Moved single-entity-caching to EntityBuilder --- lib/src/cache/cache_view.dart | 2 +- lib/src/entity_builder.dart | 8 ++++++-- lib/src/restrr_base.dart | 13 +++++-------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/src/cache/cache_view.dart b/lib/src/cache/cache_view.dart index 6886121..c2d7efb 100644 --- a/lib/src/cache/cache_view.dart +++ b/lib/src/cache/cache_view.dart @@ -5,7 +5,7 @@ class RestrrEntityCacheView { T? get(ID id) => _cache[id]; - T add(T value) => _cache[value.id] = value; + T cache(T value) => _cache[value.id] = value; T? remove(ID id) => _cache.remove(id); diff --git a/lib/src/entity_builder.dart b/lib/src/entity_builder.dart index cefebe3..f6431eb 100644 --- a/lib/src/entity_builder.dart +++ b/lib/src/entity_builder.dart @@ -15,7 +15,7 @@ class EntityBuilder { } User buildUser(Map json) { - return UserImpl( + final UserImpl user = UserImpl( api: api, id: json['id'], username: json['username'], @@ -24,10 +24,12 @@ class EntityBuilder { createdAt: DateTime.parse(json['created_at']), isAdmin: json['is_admin'], ); + api.userCache.cache(user); + return user; } Currency buildCurrency(Map json) { - return CurrencyImpl( + final CurrencyImpl currency = CurrencyImpl( api: api, id: json['id'], name: json['name'], @@ -36,5 +38,7 @@ class EntityBuilder { decimalPlaces: json['decimal_places'], user: json['user'], ); + api.currencyCache.cache(currency); + return currency; } } diff --git a/lib/src/restrr_base.dart b/lib/src/restrr_base.dart index 8afa11a..a44cf83 100644 --- a/lib/src/restrr_base.dart +++ b/lib/src/restrr_base.dart @@ -151,8 +151,8 @@ class RestrrImpl implements Restrr { /* Caches */ - late final RestrrEntityCacheView _userCache = RestrrEntityCacheView(); - late final RestrrEntityCacheView _currencyCache = RestrrEntityCacheView(); + late final RestrrEntityCacheView userCache = RestrrEntityCacheView(); + late final RestrrEntityCacheView currencyCache = RestrrEntityCacheView(); late final RestrrEntityBatchCacheView _currencyBatchCache = RestrrEntityBatchCacheView(); @@ -168,7 +168,7 @@ class RestrrImpl implements Restrr { Future retrieveSelf({bool forceRetrieve = false}) async { return _getOrRetrieveSingle( key: selfUser.id, - cacheView: _userCache, + cacheView: userCache, retrieveFunction: (api) => api._userService.getSelf(), forceRetrieve: forceRetrieve); } @@ -203,7 +203,7 @@ class RestrrImpl implements Restrr { Future retrieveCurrencyById(ID id, {bool forceRetrieve = false}) async { return _getOrRetrieveSingle( key: id, - cacheView: _currencyCache, + cacheView: currencyCache, retrieveFunction: (api) => api._currencyService.retrieveCurrencyById(id), forceRetrieve: forceRetrieve); } @@ -231,10 +231,7 @@ class RestrrImpl implements Restrr { return cacheView.get(key)!; } final RestResponse response = await retrieveFunction.call(this); - if (response.hasData) { - return cacheView.add(response.data!); - } - return null; + return response.hasData ? response.data : null; } Future?> _getOrRetrieveMulti( From 61c1dfb57838127b66a7d7c3165bf0d266423363 Mon Sep 17 00:00:00 2001 From: Jason Dean Lessenich Date: Tue, 27 Feb 2024 15:07:17 +0100 Subject: [PATCH 7/7] Use cache as return value --- lib/src/entity_builder.dart | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/src/entity_builder.dart b/lib/src/entity_builder.dart index f6431eb..275adb5 100644 --- a/lib/src/entity_builder.dart +++ b/lib/src/entity_builder.dart @@ -24,8 +24,7 @@ class EntityBuilder { createdAt: DateTime.parse(json['created_at']), isAdmin: json['is_admin'], ); - api.userCache.cache(user); - return user; + return api.userCache.cache(user); } Currency buildCurrency(Map json) { @@ -38,7 +37,6 @@ class EntityBuilder { decimalPlaces: json['decimal_places'], user: json['user'], ); - api.currencyCache.cache(currency); - return currency; + return api.currencyCache.cache(currency); } }