From 32d406d797fbb0bec43ba1153210377b41041de9 Mon Sep 17 00:00:00 2001 From: Jordan Nelson Date: Fri, 7 Jun 2024 14:03:14 -0400 Subject: [PATCH] chore: add amplify_lints to datastore --- .../amplify_datastore/analysis_options.yaml | 7 + .../lib/amplify_datastore.dart | 71 ++++--- .../amplify_datastore_stream_controller.dart | 183 ++++++++-------- .../lib/src/datastore_plugin_options.dart | 2 +- .../lib/src/method_channel_datastore.dart | 160 +++++++------- .../lib/src/stream_utils/throttle.dart | 65 +++--- .../lib/src/types/observe_query_executor.dart | 34 ++- packages/amplify_datastore/pubspec.yaml | 1 + .../test/amplify_datastore_clear_test.dart | 91 +++++--- ...y_datastore_custom_error_handler_test.dart | 47 +++-- .../test/amplify_datastore_delete_test.dart | 79 ++++--- .../test/amplify_datastore_observe_test.dart | 41 ++-- .../test/amplify_datastore_query_test.dart | 147 +++++++------ .../test/amplify_datastore_save_test.dart | 72 ++++--- ...lify_datastore_stream_controller_test.dart | 195 ++++++++---------- .../test/amplify_datastore_test.dart | 64 +++--- .../test/outbox_mutation_event_test.dart | 24 +-- .../test/query_pagination_test.dart | 26 ++- .../test/query_predicate_test.dart | 192 +++++++++-------- .../test/query_sort_test.dart | 65 +++--- 20 files changed, 872 insertions(+), 694 deletions(-) create mode 100644 packages/amplify_datastore/analysis_options.yaml diff --git a/packages/amplify_datastore/analysis_options.yaml b/packages/amplify_datastore/analysis_options.yaml new file mode 100644 index 0000000000..f91162e6e6 --- /dev/null +++ b/packages/amplify_datastore/analysis_options.yaml @@ -0,0 +1,7 @@ +include: package:amplify_lints/library.yaml + +analyzer: + exclude: + - lib/**/*.g.dart + - lib/src/sdk/src/** + - lib/src/native_auth_plugin.dart diff --git a/packages/amplify_datastore/lib/amplify_datastore.dart b/packages/amplify_datastore/lib/amplify_datastore.dart index 685165c76a..e989129596 100644 --- a/packages/amplify_datastore/lib/amplify_datastore.dart +++ b/packages/amplify_datastore/lib/amplify_datastore.dart @@ -41,9 +41,10 @@ class AmplifyDataStore extends DataStorePluginInterface @protected AmplifyDataStore.emptyConstructor() : super.emptyConstructor(); - static AmplifyDataStore _instance = AmplifyDataStoreMethodChannel(); + static final AmplifyDataStore _instance = AmplifyDataStoreMethodChannel(); static DataStoreStreamController streamWrapper = DataStoreStreamController(); + @override StreamController get streamController { return streamWrapper.datastoreStreamController; } @@ -161,7 +162,7 @@ class AmplifyDataStore extends DataStorePluginInterface @override Future configureDataStore({ ModelProviderInterface? modelProvider, - Function(AmplifyException)? errorHandler, + void Function(AmplifyException)? errorHandler, DataStoreConflictHandler? conflictHandler, List? syncExpressions, int? syncInterval, @@ -169,11 +170,13 @@ class AmplifyDataStore extends DataStorePluginInterface int? syncPageSize, AuthModeStrategy authModeStrategy = AuthModeStrategy.defaultStrategy, }) async { - ModelProviderInterface provider = modelProvider ?? this.modelProvider!; + final provider = modelProvider ?? this.modelProvider!; if (provider.modelSchemas.isEmpty) { - throw DataStoreException('No modelProvider or modelSchemas found', - recoverySuggestion: - 'Pass in a modelProvider instance while instantiating DataStorePlugin'); + throw const DataStoreException( + 'No modelProvider or modelSchemas found', + recoverySuggestion: + 'Pass in a modelProvider instance while instantiating DataStorePlugin', + ); } streamWrapper.registerModelsForHub(provider); return _instance.configureDataStore( @@ -189,12 +192,18 @@ class AmplifyDataStore extends DataStorePluginInterface } @override - Future> query(ModelType modelType, - {QueryPredicate? where, - QueryPagination? pagination, - List? sortBy}) async { - return _instance.query(modelType, - where: where, pagination: pagination, sortBy: sortBy); + Future> query( + ModelType modelType, { + QueryPredicate? where, + QueryPagination? pagination, + List? sortBy, + }) async { + return _instance.query( + modelType, + where: where, + pagination: pagination, + sortBy: sortBy, + ); } @override @@ -208,8 +217,10 @@ class AmplifyDataStore extends DataStorePluginInterface } @override - Stream> observe(ModelType modelType, - {QueryPredicate? where}) { + Stream> observe( + ModelType modelType, { + QueryPredicate? where, + }) { return _instance.observe(modelType, where: where); } @@ -259,26 +270,38 @@ class _NativeAmplifyAuthCognito // authSession cannot be typed properly without depending on amplify_auth_cognito final authSession = await Amplify.Auth.fetchAuthSession() as dynamic; final nativeAuthSession = NativeAuthSession( - isSignedIn: authSession.isSignedIn, - userSub: authSession.userSubResult.valueOrNull, - identityId: authSession.identityIdResult.valueOrNull, + // ignore: avoid_dynamic_calls + isSignedIn: authSession.isSignedIn as bool, + // ignore: avoid_dynamic_calls + userSub: authSession.userSubResult.valueOrNull as String?, + // ignore: avoid_dynamic_calls + identityId: authSession.identityIdResult.valueOrNull as String?, ); + // ignore: avoid_dynamic_calls final userPoolTokens = authSession.userPoolTokensResult.valueOrNull; if (userPoolTokens != null) { nativeAuthSession.userPoolTokens = NativeUserPoolTokens( - accessToken: userPoolTokens.accessToken.raw, - refreshToken: userPoolTokens.refreshToken, - idToken: userPoolTokens.idToken.raw, + // ignore: avoid_dynamic_calls + accessToken: userPoolTokens.accessToken.raw as String, + // ignore: avoid_dynamic_calls + refreshToken: userPoolTokens.refreshToken as String, + // ignore: avoid_dynamic_calls + idToken: userPoolTokens.idToken.raw as String, ); } + // ignore: avoid_dynamic_calls final awsCredentials = authSession.credentialsResult.valueOrNull; if (awsCredentials != null) { nativeAuthSession.awsCredentials = NativeAWSCredentials( - accessKeyId: awsCredentials.accessKeyId, - secretAccessKey: awsCredentials.secretAccessKey, - sessionToken: awsCredentials.sessionToken, + // ignore: avoid_dynamic_calls + accessKeyId: awsCredentials.accessKeyId as String, + // ignore: avoid_dynamic_calls + secretAccessKey: awsCredentials.secretAccessKey as String, + // ignore: avoid_dynamic_calls + sessionToken: awsCredentials.sessionToken as String, expirationIso8601Utc: - awsCredentials.expiration?.toUtc().toIso8601String(), + // ignore: avoid_dynamic_calls + awsCredentials.expiration?.toUtc().toIso8601String() as String?, ); } return nativeAuthSession; diff --git a/packages/amplify_datastore/lib/src/amplify_datastore_stream_controller.dart b/packages/amplify_datastore/lib/src/amplify_datastore_stream_controller.dart index e1d9397a59..e97bd7e5a7 100644 --- a/packages/amplify_datastore/lib/src/amplify_datastore_stream_controller.dart +++ b/packages/amplify_datastore/lib/src/amplify_datastore_stream_controller.dart @@ -8,7 +8,7 @@ import 'package:flutter/services.dart'; const channel = EventChannel('com.amazonaws.amplify/datastore_hub_events'); late ModelProviderInterface modelProvider; -StreamSubscription? eventStream; +StreamSubscription>? eventStream; class DataStoreStreamController { StreamController get datastoreStreamController { @@ -26,102 +26,107 @@ StreamController _controller = onCancel: _onCancel, ); -_onListen() { - if (eventStream == null) { - eventStream = channel - .receiveBroadcastStream(1) - .cast>() - .listen((event) { - final map = event.cast(); - final eventName = map['eventName'] as String; - switch (eventName) { - case 'ready': - { - _rebroadcast(DataStoreHubEventType.ready); - } +void _onListen() { + eventStream ??= channel + .receiveBroadcastStream(1) + .cast>() + .listen((event) { + final map = event.cast(); + final eventName = map['eventName'] as String; + switch (eventName) { + case 'ready': + { + _rebroadcast(DataStoreHubEventType.ready); + } + case 'networkStatus': + { + _rebroadcast( + DataStoreHubEventType.networkStatus, + payload: NetworkStatusEvent(map), + ); + } + case 'subscriptionsEstablished': + { + _rebroadcast(DataStoreHubEventType.subscriptionsEstablished); + } + case 'syncQueriesStarted': + { + _rebroadcast( + DataStoreHubEventType.syncQueriesStarted, + payload: SyncQueriesStartedEvent(map), + ); + } + case 'modelSynced': + { + _rebroadcast( + DataStoreHubEventType.modelSynced, + payload: ModelSyncedEvent(map), + ); + } + case 'syncQueriesReady': + { + _rebroadcast(DataStoreHubEventType.syncQueriesReady); + } + case 'outboxMutationEnqueued': + { + _rebroadcast( + DataStoreHubEventType.outboxMutationEnqueued, + payload: OutboxMutationEvent(map, modelProvider, eventName), + ); + } + case 'outboxMutationProcessed': + { + _rebroadcast( + DataStoreHubEventType.outboxMutationProcessed, + payload: OutboxMutationEvent(map, modelProvider, eventName), + ); + } + case 'outboxMutationFailed': + { + _rebroadcast( + DataStoreHubEventType.outboxMutationFailed, + payload: OutboxMutationEvent(map, modelProvider, eventName), + ); + } + case 'outboxStatus': + { + _rebroadcast( + DataStoreHubEventType.outboxStatus, + payload: OutboxStatusEvent(map), + ); + } + // event name in amplify-android + case 'subscriptionDataProcessed': + // event name in amplify-ios + case 'syncReceived': + { + _rebroadcast( + DataStoreHubEventType.subscriptionDataProcessed, + payload: SubscriptionDataProcessedEvent( + map, + modelProvider, + ), + ); break; - case 'networkStatus': - { - _rebroadcast(DataStoreHubEventType.networkStatus, - payload: NetworkStatusEvent(map)); - } - break; - case 'subscriptionsEstablished': - { - _rebroadcast(DataStoreHubEventType.subscriptionsEstablished); - } - break; - case 'syncQueriesStarted': - { - _rebroadcast(DataStoreHubEventType.syncQueriesStarted, - payload: SyncQueriesStartedEvent(map)); - } - break; - case 'modelSynced': - { - _rebroadcast(DataStoreHubEventType.modelSynced, - payload: ModelSyncedEvent(map)); - } - break; - case 'syncQueriesReady': - { - _rebroadcast(DataStoreHubEventType.syncQueriesReady); - } - break; - case 'outboxMutationEnqueued': - { - _rebroadcast(DataStoreHubEventType.outboxMutationEnqueued, - payload: OutboxMutationEvent(map, modelProvider, eventName)); - } - break; - case 'outboxMutationProcessed': - { - _rebroadcast(DataStoreHubEventType.outboxMutationProcessed, - payload: OutboxMutationEvent(map, modelProvider, eventName)); - } - break; - case 'outboxMutationFailed': - { - _rebroadcast(DataStoreHubEventType.outboxMutationFailed, - payload: OutboxMutationEvent(map, modelProvider, eventName)); - } - break; - case 'outboxStatus': - { - _rebroadcast(DataStoreHubEventType.outboxStatus, - payload: OutboxStatusEvent(map)); - } - break; - // event name in amplify-android - case 'subscriptionDataProcessed': - // event name in amplify-ios - case 'syncReceived': - { - _rebroadcast( - DataStoreHubEventType.subscriptionDataProcessed, - payload: SubscriptionDataProcessedEvent( - map, - modelProvider, - ), - ); - break; - } - default: - break; - } - }); - } + } + default: + break; + } + }); } -_rebroadcast(DataStoreHubEventType type, {DataStoreHubEventPayload? payload}) { +void _rebroadcast( + DataStoreHubEventType type, { + DataStoreHubEventPayload? payload, +}) { try { _controller.add(DataStoreHubEvent(type.name, type: type, payload: payload)); - } catch (e) { - print(e); + } on Exception catch (e) { + safePrint(e); } } -_onCancel() { +void _onCancel() { if (!_controller.hasListener) { eventStream?.cancel(); eventStream = null; diff --git a/packages/amplify_datastore/lib/src/datastore_plugin_options.dart b/packages/amplify_datastore/lib/src/datastore_plugin_options.dart index 368a9aaa53..070b6f4942 100644 --- a/packages/amplify_datastore/lib/src/datastore_plugin_options.dart +++ b/packages/amplify_datastore/lib/src/datastore_plugin_options.dart @@ -20,7 +20,7 @@ class DataStorePluginOptions { /// The custom error handler function that receives an [AmplifyException] /// object when DataStore encounters an unhandled error. - final Function(AmplifyException)? errorHandler; + final void Function(AmplifyException)? errorHandler; /// The custom conflict handler function that receives an [ConflictData] /// object when DataStore encounters a data conflict. diff --git a/packages/amplify_datastore/lib/src/method_channel_datastore.dart b/packages/amplify_datastore/lib/src/method_channel_datastore.dart index 735cc87b3a..945f2711b7 100644 --- a/packages/amplify_datastore/lib/src/method_channel_datastore.dart +++ b/packages/amplify_datastore/lib/src/method_channel_datastore.dart @@ -8,30 +8,31 @@ import 'package:flutter/services.dart'; const MethodChannel _channel = MethodChannel('com.amazonaws.amplify/datastore'); +/// A callback for handling model conflicts. typedef ConflictHandler = ConflictResolutionDecision Function(ConflictData); /// An implementation of [AmplifyDataStore] that uses method channels. class AmplifyDataStoreMethodChannel extends AmplifyDataStore { - dynamic _allModelsStreamFromMethodChannel; + /// Internal use constructor + AmplifyDataStoreMethodChannel() : super.emptyConstructor(); + Stream>? _allModelsStreamFromMethodChannel; List? _syncExpressions; - ObserveQueryExecutor _observeQueryExecutor = ObserveQueryExecutor( + final ObserveQueryExecutor _observeQueryExecutor = ObserveQueryExecutor( dataStoreEventStream: AmplifyDataStore.streamWrapper.datastoreStreamController.stream, ); - /// Internal use constructor - AmplifyDataStoreMethodChannel() : super.emptyConstructor(); - // Receives calls from Native Future _methodCallHandler(MethodCall call) async { switch (call.method) { case 'resolveQueryPredicate': - String? id = call.arguments; - if (id == null) { + final id = call.arguments; + if (id! is String) { throw ArgumentError( - 'resolveQueryPredicate must be called with an id'); + 'resolveQueryPredicate must be called with an id', + ); } return _syncExpressions! .firstWhere((syncExpression) => syncExpression.id == id) @@ -39,31 +40,33 @@ class AmplifyDataStoreMethodChannel extends AmplifyDataStore { .serializeAsMap(); case 'errorHandler': - if (errorHandler == null) - throw StateError("Native calling non existent ErrorHandler in Dart"); + if (errorHandler == null) { + throw StateError('Native calling non existent ErrorHandler in Dart'); + } - Map arguments = - Map.from(call.arguments); + final arguments = + Map.from(call.arguments as Map); errorHandler!(_deserializeExceptionFromMap(arguments)); - break; case 'conflictHandler': - if (conflictHandler == null) + if (conflictHandler == null) { throw StateError( - "Native calling non existent ConflictHandler in Dart"); + 'Native calling non existent ConflictHandler in Dart', + ); + } - Map arguments = - (call.arguments as Map).cast(); + final arguments = (call.arguments as Map).cast(); - final modelName = arguments["__modelName"] as String; + final modelName = arguments['__modelName'] as String; final modelType = modelProvider!.getModelTypeByModelName(modelName); - ConflictData conflictData = ConflictData.fromJson( - modelType, - (arguments["local"] as Map).cast(), - (arguments["remote"] as Map).cast()); + final conflictData = ConflictData.fromJson( + modelType, + (arguments['local'] as Map).cast(), + (arguments['remote'] as Map).cast(), + ); - ConflictResolutionDecision decision = conflictHandler!(conflictData); + final decision = conflictHandler!(conflictData); return decision.toJson(); default: throw UnimplementedError('${call.method} has not been implemented.'); @@ -76,7 +79,7 @@ class AmplifyDataStoreMethodChannel extends AmplifyDataStore { @override Future configureDataStore({ ModelProviderInterface? modelProvider, - Function(AmplifyException)? errorHandler, + void Function(AmplifyException)? errorHandler, DataStoreConflictHandler? conflictHandler, List? syncExpressions, int? syncInterval, @@ -111,11 +114,12 @@ class AmplifyDataStoreMethodChannel extends AmplifyDataStore { 'authModeStrategy': authModeStrategy.rawValue, }); } on PlatformException catch (e) { - if (e.code == "AmplifyAlreadyConfiguredException") { - throw AmplifyAlreadyConfiguredException( - AmplifyExceptionMessages.alreadyConfiguredDefaultMessage, - recoverySuggestion: - AmplifyExceptionMessages.alreadyConfiguredDefaultSuggestion); + if (e.code == 'AmplifyAlreadyConfiguredException') { + throw const AmplifyAlreadyConfiguredException( + AmplifyExceptionMessages.alreadyConfiguredDefaultMessage, + recoverySuggestion: + AmplifyExceptionMessages.alreadyConfiguredDefaultSuggestion, + ); } else { throw _deserializeException(e); } @@ -123,35 +127,42 @@ class AmplifyDataStoreMethodChannel extends AmplifyDataStore { } @override - Future> query(ModelType modelType, - {QueryPredicate? where, - QueryPagination? pagination, - List? sortBy}) async { + Future> query( + ModelType modelType, { + QueryPredicate? where, + QueryPagination? pagination, + List? sortBy, + }) async { try { await _setUpObserveIfNeeded(); - final List>? serializedResults = - await (_channel.invokeListMethod('query', { + final serializedResults = await (_channel + .invokeListMethod>('query', { 'modelName': modelType.modelName(), 'queryPredicate': where?.serializeAsMap(), 'queryPagination': pagination?.serializeAsMap(), - 'querySort': sortBy?.map((element) => element.serializeAsMap()).toList() + 'querySort': + sortBy?.map((element) => element.serializeAsMap()).toList(), })); - if (serializedResults == null) - throw DataStoreException( - AmplifyExceptionMessages.nullReturnedFromMethodChannel); + if (serializedResults == null) { + throw const DataStoreException( + AmplifyExceptionMessages.nullReturnedFromMethodChannel, + ); + } return serializedResults - .map((serializedResult) => modelType - .fromJson(new Map.from(serializedResult))) + .map( + (serializedResult) => + modelType.fromJson(Map.from(serializedResult)), + ) .toList(); } on PlatformException catch (e) { throw _deserializeException(e); } on TypeError catch (e) { throw DataStoreException( - "An unrecognized exception has happened while Serialization/de-serialization." + - " Please see underlyingException for more details.", - recoverySuggestion: - AmplifyExceptionMessages.missingRecoverySuggestion, - underlyingException: e.toString()); + 'An unrecognized exception has happened while Serialization/de-serialization.' + ' Please see underlyingException for more details.', + recoverySuggestion: AmplifyExceptionMessages.missingRecoverySuggestion, + underlyingException: e.toString(), + ); } } @@ -159,7 +170,7 @@ class AmplifyDataStoreMethodChannel extends AmplifyDataStore { Future delete(T model, {QueryPredicate? where}) async { try { await _setUpObserveIfNeeded(); - var methodChannelDeleteInput = { + final methodChannelDeleteInput = { 'modelName': model.getInstanceType().modelName(), if (where != null) 'queryPredicate': where.serializeAsMap(), 'serializedModel': model.toJson(), @@ -174,7 +185,7 @@ class AmplifyDataStoreMethodChannel extends AmplifyDataStore { Future save(T model, {QueryPredicate? where}) async { try { await _setUpObserveIfNeeded(); - var methodChannelSaveInput = { + final methodChannelSaveInput = { 'modelName': model.getInstanceType().modelName(), if (where != null) 'queryPredicate': where.serializeAsMap(), 'serializedModel': model.toJson(), @@ -186,21 +197,23 @@ class AmplifyDataStoreMethodChannel extends AmplifyDataStore { } @override - Stream> observe(ModelType modelType, - {QueryPredicate? where}) async* { + Stream> observe( + ModelType modelType, { + QueryPredicate? where, + }) async* { await _setUpObserveIfNeeded(); // Step #1. Open the event channel if it's not already open. Note // that there is only one event channel for all observe calls for all models - const _eventChannel = + const eventChannel = EventChannel('com.amazonaws.amplify/datastore_observe_events'); _allModelsStreamFromMethodChannel = _allModelsStreamFromMethodChannel ?? - _eventChannel.receiveBroadcastStream(0); + eventChannel.receiveBroadcastStream(0); // Step #2. Apply client side filtering on the stream. // Currently only modelType filtering is supported. - Stream filteredStream = - _allModelsStreamFromMethodChannel.where((event) { + final filteredStream = + _allModelsStreamFromMethodChannel!.where((Map event) { //TODO: errors are not model specific. Should we pass all errors to users return _getModelNameFromEvent(event) == modelType.modelName(); }); @@ -259,42 +272,49 @@ class AmplifyDataStoreMethodChannel extends AmplifyDataStore { } String _getModelNameFromEvent(Map serializedEvent) { - Map serializedItem = - Map.from(serializedEvent["item"]); - return serializedItem["__modelName"] as String; + final serializedItem = Map.from( + serializedEvent['item'] as Map, + ); + return serializedItem['__modelName'] as String; } AmplifyException _deserializeExceptionFromMap(Map e) { if (e['errorCode'] == 'DataStoreException') { - return DataStoreException.fromMap(Map.from(e['details'])); + return DataStoreException.fromMap( + Map.from(e['details'] as Map), + ); } else if (e['errorCode'] == 'AmplifyAlreadyConfiguredException') { return AmplifyAlreadyConfiguredException.fromMap( - Map.from(e['details'])); + Map.from(e['details'] as Map), + ); } else { // This shouldn't happen. All exceptions coming from platform for // amplify_datastore should have a known code. Throw an unknown error. return DataStoreException( - AmplifyExceptionMessages.missingExceptionMessage, - recoverySuggestion: - AmplifyExceptionMessages.missingRecoverySuggestion, - underlyingException: e.toString()); + AmplifyExceptionMessages.missingExceptionMessage, + recoverySuggestion: AmplifyExceptionMessages.missingRecoverySuggestion, + underlyingException: e.toString(), + ); } } AmplifyException _deserializeException(PlatformException e) { if (e.code == 'DataStoreException') { - return DataStoreException.fromMap(Map.from(e.details)); + return DataStoreException.fromMap( + Map.from(e.details as Map), + ); } else if (e.code == 'AmplifyAlreadyConfiguredException') { return AmplifyAlreadyConfiguredException.fromMap( - Map.from(e.details)); + Map.from(e.details as Map), + ); } else { // This shouldn't happen. All exceptions coming from platform for // amplify_datastore should have a known code. Throw an unknown error. return DataStoreException( - AmplifyExceptionMessages.missingExceptionMessage, - recoverySuggestion: - AmplifyExceptionMessages.missingRecoverySuggestion, - underlyingException: e.toString()); + AmplifyExceptionMessages.missingExceptionMessage, + recoverySuggestion: AmplifyExceptionMessages.missingRecoverySuggestion, + underlyingException: e.toString(), + ); } } @@ -303,6 +323,6 @@ class AmplifyDataStoreMethodChannel extends AmplifyDataStore { /// `observe` APIs. If already set up, this channel call resolves /// immediately. Future _setUpObserveIfNeeded() { - return _channel.invokeMethod('setUpObserve', {}); + return _channel.invokeMethod('setUpObserve', {}); } } diff --git a/packages/amplify_datastore/lib/src/stream_utils/throttle.dart b/packages/amplify_datastore/lib/src/stream_utils/throttle.dart index 1be661de11..8702b0b935 100644 --- a/packages/amplify_datastore/lib/src/stream_utils/throttle.dart +++ b/packages/amplify_datastore/lib/src/stream_utils/throttle.dart @@ -12,7 +12,8 @@ extension Throttle on Stream { /// emits one event every time the [throttleCount] is reached /// or the [duration] since the last event has been reached /// - /// If [until] is supplied, the throttling stops once the condition is met + /// If [throttleIf] is supplied, events will be throttled while the condition + /// is true. /// /// Note: This is intended for use in observeQuery and is not intended /// to be exposed as part of the public API @@ -34,44 +35,44 @@ extension Throttle on Stream { // number of items that have emitted from the source stream since // the last event was emitted - int _count = 0; + var count = 0; // cached data & sink during throttling // if the timer expires, the cached sink will be // used to emit the cached data - T? _data; - Sink? _sink; + T? dataCache; + Sink? sinkCache; // timer for throttling by time - Timer? _timer; + Timer? timer; - bool _hasEmittedFirstEvent = false; + var hasEmittedFirstEvent = false; - bool timerHasExpired() => _timer != null && !_timer!.isActive; + bool timerHasExpired() => timer != null && !timer!.isActive; bool throttleCountReached() => - throttleCount != null && _count == throttleCount - 1; + throttleCount != null && count == throttleCount - 1; void resetTimer(void Function() callback) { if (duration == null) return; - _timer?.cancel(); - _timer = Timer(duration, () { + timer?.cancel(); + timer = Timer(duration, () { callback(); }); } - void emitData(T data, Sink sink) { - _hasEmittedFirstEvent = true; + void emitData(T data, Sink sink) { + hasEmittedFirstEvent = true; // clear cached data & sink - _data = null; - _sink = null; + dataCache = null; + sinkCache = null; // reset the count and timer - _count = 0; + count = 0; resetTimer(() { - if (_data != null && _sink != null) { - emitData(_data!, _sink!); + if (dataCache != null && sinkCache != null) { + emitData(dataCache as T, sinkCache!); } }); @@ -79,31 +80,33 @@ extension Throttle on Stream { sink.add(data); } - void throttleData(T data, Sink sink) { + void throttleData(T data, Sink sink) { // cache data & sink - _data = data; - _sink = sink; + dataCache = data; + sinkCache = sink; // increment counter - _count++; + count++; } bool shouldEmitData(T data) { - bool throttle = throttleIf == null || throttleIf(data); - if (_hasEmittedFirstEvent && throttle) { + final throttle = throttleIf == null || throttleIf(data); + if (hasEmittedFirstEvent && throttle) { return timerHasExpired() || throttleCountReached(); } return true; } - return this.transform( - StreamTransformer.fromHandlers(handleData: (data, sink) { - if (shouldEmitData(data)) { - emitData(data, sink); - } else { - throttleData(data, sink); - } - }), + return transform( + StreamTransformer.fromHandlers( + handleData: (data, sink) { + if (shouldEmitData(data)) { + emitData(data, sink); + } else { + throttleData(data, sink); + } + }, + ), ); } } diff --git a/packages/amplify_datastore/lib/src/types/observe_query_executor.dart b/packages/amplify_datastore/lib/src/types/observe_query_executor.dart index acd6591dce..1b96994adb 100644 --- a/packages/amplify_datastore/lib/src/types/observe_query_executor.dart +++ b/packages/amplify_datastore/lib/src/types/observe_query_executor.dart @@ -17,12 +17,11 @@ typedef Query = Future> Function( }); typedef Observe = Stream> Function( - ModelType modelType); + ModelType modelType, +); /// A class for handling observeQuery operations class ObserveQueryExecutor { - final Stream dataStoreEventStream; - ObserveQueryExecutor({ required this.dataStoreEventStream, }) : _dataStoreHubEventStream = dataStoreEventStream @@ -31,6 +30,7 @@ class ObserveQueryExecutor { .asBroadcastStream() { _initModelSyncCache(); } + final Stream dataStoreEventStream; /// A stream of ModelSyncEvents final Stream _dataStoreHubEventStream; @@ -53,9 +53,9 @@ class ObserveQueryExecutor { // cached subscription events that occur prior to the // initial query completing - List> subscriptionEvents = []; + final subscriptionEvents = >[]; - Stream> syncStatusStream = _isModelSyncedStream(modelType) + final syncStatusStream = _isModelSyncedStream(modelType) .skipWhile((element) => querySnapshot == null) .where((event) => event != querySnapshot!.isSynced) .map>((value) { @@ -63,7 +63,7 @@ class ObserveQueryExecutor { return querySnapshot!; }); - Stream> observeStream = observe(modelType) + final observeStream = observe(modelType) .map?>((event) { // cache subscription events until the initial query is returned if (querySnapshot == null) { @@ -72,7 +72,7 @@ class ObserveQueryExecutor { } // apply the most recent event to the cached QuerySnapshot - var updatedQuerySnapshot = querySnapshot!.withSubscriptionEvent( + final updatedQuerySnapshot = querySnapshot!.withSubscriptionEvent( event: event, ); @@ -82,19 +82,18 @@ class ObserveQueryExecutor { } // otherwise, update the cached QuerySnapshot and return it - querySnapshot = updatedQuerySnapshot; - return querySnapshot; + return querySnapshot = updatedQuerySnapshot; }) // filter out null values .where((event) => event != null) .cast>(); - Future> queryFuture = query( + final queryFuture = query( modelType, where: where, sortBy: sortBy, ).then((value) { - // create & cache the intitial QuerySnapshot + // create & cache the initial QuerySnapshot querySnapshot = QuerySnapshot( items: value, isSynced: _isModelSyncComplete(modelType), @@ -103,7 +102,7 @@ class ObserveQueryExecutor { ); // apply any cached subscription events - for (var event in subscriptionEvents) { + for (final event in subscriptionEvents) { querySnapshot = querySnapshot!.withSubscriptionEvent(event: event); } @@ -134,7 +133,7 @@ class ObserveQueryExecutor { } _ModelSyncStatus _getModelSyncStatus(ModelType type) { - _ModelSyncStatus? status = _modelSyncCache[type.modelName()]; + final status = _modelSyncCache[type.modelName()]; return status ?? _ModelSyncStatus.none; } @@ -147,16 +146,13 @@ class ObserveQueryExecutor { } void _initModelSyncCache() { - this - ._dataStoreHubEventStream - .map((event) => event.payload) - .listen((payload) { + _dataStoreHubEventStream.map((event) => event.payload).listen((payload) { if (payload is ModelSyncedEvent) { _modelSyncCache[payload.modelName] = _ModelSyncStatus.complete; } else if (payload is SyncQueriesStartedEvent) { - payload.models.forEach((model) { + for (final model in payload.models) { _modelSyncCache[model] = _ModelSyncStatus.started; - }); + } } }); } diff --git a/packages/amplify_datastore/pubspec.yaml b/packages/amplify_datastore/pubspec.yaml index df8ead82e3..fa7015fad4 100644 --- a/packages/amplify_datastore/pubspec.yaml +++ b/packages/amplify_datastore/pubspec.yaml @@ -22,6 +22,7 @@ dependencies: dev_dependencies: amplify_test: path: ../test/amplify_test + amplify_lints: ">=3.1.0 <3.2.0" flutter_test: sdk: flutter fake_async: ^1.2.0 diff --git a/packages/amplify_datastore/test/amplify_datastore_clear_test.dart b/packages/amplify_datastore/test/amplify_datastore_clear_test.dart index 8a53355256..84ac68bd5e 100644 --- a/packages/amplify_datastore/test/amplify_datastore_clear_test.dart +++ b/packages/amplify_datastore/test/amplify_datastore_clear_test.dart @@ -8,11 +8,9 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { - const MethodChannel dataStoreChannel = - MethodChannel('com.amazonaws.amplify/datastore'); + const dataStoreChannel = MethodChannel('com.amazonaws.amplify/datastore'); - AmplifyDataStore dataStore = - AmplifyDataStore(modelProvider: ModelProvider.instance); + final dataStore = AmplifyDataStore(modelProvider: ModelProvider.instance); final binding = TestWidgetsFlutterBinding.ensureInitialized(); @@ -30,7 +28,7 @@ void main() { return null; }, ); - Future clearFuture = dataStore.clear(); + final clearFuture = dataStore.clear(); expect(clearFuture, completes); }); @@ -40,33 +38,50 @@ void main() { binding.defaultBinaryMessenger.setMockMethodCallHandler( dataStoreChannel, (MethodCall methodCall) async { - throw PlatformException(code: 'DataStoreException', details: { - 'message': 'Clear failed for whatever known reason', - 'recoverySuggestion': 'some insightful suggestion', - 'underlyingException': 'Act of God' - }); + throw PlatformException( + code: 'DataStoreException', + details: { + 'message': 'Clear failed for whatever known reason', + 'recoverySuggestion': 'some insightful suggestion', + 'underlyingException': 'Act of God', + }, + ); }, ); expect( - () => dataStore.clear(), - throwsA(isA() - .having((exception) => exception.message, 'message', - 'Clear failed for whatever known reason') - .having((exception) => exception.recoverySuggestion, - 'recoverySuggestion', 'some insightful suggestion') - .having((exception) => exception.underlyingException, - 'underlyingException', 'Act of God'))); + dataStore.clear, + throwsA( + isA() + .having( + (exception) => exception.message, + 'message', + 'Clear failed for whatever known reason', + ) + .having( + (exception) => exception.recoverySuggestion, + 'recoverySuggestion', + 'some insightful suggestion', + ) + .having( + (exception) => exception.underlyingException, + 'underlyingException', + 'Act of God', + ), + ), + ); }); test( 'An unrecognized PlatformException results in a generic DataStoreException', () async { - var platformException = - PlatformException(code: 'BadExceptionCode', details: { - 'message': 'Clear failed for whatever known reason', - 'recoverySuggestion': 'some insightful suggestion', - 'underlyingException': 'Act of God' - }); + final platformException = PlatformException( + code: 'BadExceptionCode', + details: { + 'message': 'Clear failed for whatever known reason', + 'recoverySuggestion': 'some insightful suggestion', + 'underlyingException': 'Act of God', + }, + ); binding.defaultBinaryMessenger.setMockMethodCallHandler( dataStoreChannel, (MethodCall methodCall) async { @@ -74,15 +89,25 @@ void main() { }, ); expect( - () => dataStore.clear(), - throwsA(isA() - .having((exception) => exception.message, 'message', - AmplifyExceptionMessages.missingExceptionMessage) + dataStore.clear, + throwsA( + isA() + .having( + (exception) => exception.message, + 'message', + AmplifyExceptionMessages.missingExceptionMessage, + ) .having( - (exception) => exception.recoverySuggestion, - 'recoverySuggestion', - AmplifyExceptionMessages.missingRecoverySuggestion) - .having((exception) => exception.underlyingException, - 'underlyingException', platformException.toString()))); + (exception) => exception.recoverySuggestion, + 'recoverySuggestion', + AmplifyExceptionMessages.missingRecoverySuggestion, + ) + .having( + (exception) => exception.underlyingException, + 'underlyingException', + platformException.toString(), + ), + ), + ); }); } diff --git a/packages/amplify_datastore/test/amplify_datastore_custom_error_handler_test.dart b/packages/amplify_datastore/test/amplify_datastore_custom_error_handler_test.dart index dd50b6b746..914be81b79 100644 --- a/packages/amplify_datastore/test/amplify_datastore_custom_error_handler_test.dart +++ b/packages/amplify_datastore/test/amplify_datastore_custom_error_handler_test.dart @@ -24,8 +24,7 @@ extension MockMethodChannel on MethodChannel { } void main() { - const MethodChannel dataStoreChannel = - MethodChannel('com.amazonaws.amplify/datastore'); + const dataStoreChannel = MethodChannel('com.amazonaws.amplify/datastore'); AmplifyException? receivedException; @@ -38,45 +37,49 @@ void main() { return null; }, ); - AmplifyDataStore dataStore = AmplifyDataStore( - modelProvider: ModelProvider.instance, - options: DataStorePluginOptions( - errorHandler: (exception) => {receivedException = exception})); + final dataStore = AmplifyDataStore( + modelProvider: ModelProvider.instance, + options: DataStorePluginOptions( + errorHandler: (exception) => {receivedException = exception}, + ), + ); return dataStore.configureDataStore(); }); test( 'DataStoreException from MethodChannel is properly serialized and called', () async { - await dataStoreChannel.invokeMockMethod("errorHandler", { + await dataStoreChannel.invokeMockMethod('errorHandler', { 'errorCode': 'DataStoreException', 'errorMessage': 'ErrorMessage', 'details': { 'message': 'message', 'recoverySuggestion': 'recoverySuggestion', - 'underlyingException': 'underlyingException' - } + 'underlyingException': 'underlyingException', + }, }); expect( - receivedException, - DataStoreException.fromMap({ - 'message': 'message', - 'recoverySuggestion': 'recoverySuggestion', - 'underlyingException': 'underlyingException' - })); + receivedException, + DataStoreException.fromMap({ + 'message': 'message', + 'recoverySuggestion': 'recoverySuggestion', + 'underlyingException': 'underlyingException', + }), + ); }); test( 'Unknown DataStoreException from MethodChannel is properly serialized and called', () async { await dataStoreChannel - .invokeMockMethod("errorHandler", {'badErrorFormat': 'badErrorFormat'}); + .invokeMockMethod('errorHandler', {'badErrorFormat': 'badErrorFormat'}); expect( - receivedException, - DataStoreException(AmplifyExceptionMessages.missingExceptionMessage, - recoverySuggestion: - AmplifyExceptionMessages.missingRecoverySuggestion, - underlyingException: - {'badErrorFormat': 'badErrorFormat'}.toString())); + receivedException, + DataStoreException( + AmplifyExceptionMessages.missingExceptionMessage, + recoverySuggestion: AmplifyExceptionMessages.missingRecoverySuggestion, + underlyingException: {'badErrorFormat': 'badErrorFormat'}.toString(), + ), + ); }); } diff --git a/packages/amplify_datastore/test/amplify_datastore_delete_test.dart b/packages/amplify_datastore/test/amplify_datastore_delete_test.dart index 3c9f7d3b7f..d7a8e6e5f7 100644 --- a/packages/amplify_datastore/test/amplify_datastore_delete_test.dart +++ b/packages/amplify_datastore/test/amplify_datastore_delete_test.dart @@ -8,11 +8,9 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { - const MethodChannel dataStoreChannel = - MethodChannel('com.amazonaws.amplify/datastore'); + const dataStoreChannel = MethodChannel('com.amazonaws.amplify/datastore'); - AmplifyDataStore dataStore = - AmplifyDataStore(modelProvider: ModelProvider.instance); + final dataStore = AmplifyDataStore(modelProvider: ModelProvider.instance); final binding = TestWidgetsFlutterBinding.ensureInitialized(); @@ -26,21 +24,23 @@ void main() { }); test('delete with a valid model executes without an exception ', () async { - var json = - await getJsonFromFile('delete_api/request/instance_no_predicate.json'); - var model = json['serializedModel']; + final json = + await getJsonFromFile('delete_api/request/instance_no_predicate.json') + as Map; + final model = json['serializedModel'] as Map; binding.defaultBinaryMessenger.setMockMethodCallHandler( dataStoreChannel, (MethodCall methodCall) async { return null; }, ); - Post instance = Post( - title: model['title'], - rating: model['rating'], - created: TemporalDateTime.fromString(model['created']), - id: model['id']); - Future deleteFuture = dataStore.delete(instance); + final instance = Post( + title: model['title'] as String, + rating: model['rating'] as int, + created: TemporalDateTime.fromString(model['created'] as String), + id: model['id'] as String?, + ); + final deleteFuture = dataStore.delete(instance); expect(deleteFuture, completes); }); @@ -50,26 +50,43 @@ void main() { binding.defaultBinaryMessenger.setMockMethodCallHandler( dataStoreChannel, (MethodCall methodCall) async { - throw PlatformException(code: 'DataStoreException', details: { - 'message': 'Delete failed for whatever known reason', - 'recoverySuggestion': 'some insightful suggestion', - 'underlyingException': 'Act of God' - }); + throw PlatformException( + code: 'DataStoreException', + details: { + 'message': 'Delete failed for whatever known reason', + 'recoverySuggestion': 'some insightful suggestion', + 'underlyingException': 'Act of God', + }, + ); }, ); expect( - () => dataStore.delete(Post( - id: '4281dfba-96c8-4a38-9a8e-35c7e893ea47', - title: 'test title', - rating: 0, - created: - TemporalDateTime.fromString("2021-11-09T18:53:12.183540Z"))), - throwsA(isA() - .having((exception) => exception.message, 'message', - 'Delete failed for whatever known reason') - .having((exception) => exception.recoverySuggestion, - 'recoverySuggestion', 'some insightful suggestion') - .having((exception) => exception.underlyingException, - 'underlyingException', 'Act of God'))); + () => dataStore.delete( + Post( + id: '4281dfba-96c8-4a38-9a8e-35c7e893ea47', + title: 'test title', + rating: 0, + created: TemporalDateTime.fromString('2021-11-09T18:53:12.183540Z'), + ), + ), + throwsA( + isA() + .having( + (exception) => exception.message, + 'message', + 'Delete failed for whatever known reason', + ) + .having( + (exception) => exception.recoverySuggestion, + 'recoverySuggestion', + 'some insightful suggestion', + ) + .having( + (exception) => exception.underlyingException, + 'underlyingException', + 'Act of God', + ), + ), + ); }); } diff --git a/packages/amplify_datastore/test/amplify_datastore_observe_test.dart b/packages/amplify_datastore/test/amplify_datastore_observe_test.dart index 2cc83f58f9..663badf63b 100644 --- a/packages/amplify_datastore/test/amplify_datastore_observe_test.dart +++ b/packages/amplify_datastore/test/amplify_datastore_observe_test.dart @@ -8,16 +8,14 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { - const MethodChannel dataStoreChannel = - MethodChannel('com.amazonaws.amplify/datastore'); + const dataStoreChannel = MethodChannel('com.amazonaws.amplify/datastore'); // event channels are backed by method channels. However // event channels cannot be mocked like method channels. - const MethodChannel eventChannel = + const eventChannel = MethodChannel('com.amazonaws.amplify/datastore_observe_events'); - AmplifyDataStore dataStore = - AmplifyDataStore(modelProvider: ModelProvider.instance); + final dataStore = AmplifyDataStore(modelProvider: ModelProvider.instance); final binding = TestWidgetsFlutterBinding.ensureInitialized(); @@ -34,18 +32,18 @@ void main() { binding.defaultBinaryMessenger.setMockMethodCallHandler( dataStoreChannel, (MethodCall methodCall) async { - expect("setUpObserve", methodCall.method); + expect('setUpObserve', methodCall.method); return null; }, ); - var json = + final json = await getJsonFromFile('observe_api/post_type_success_event.json'); binding.defaultBinaryMessenger.setMockMethodCallHandler( dataStoreChannel, (MethodCall methodCall) async { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + await TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .handlePlatformMessage( - "com.amazonaws.amplify/datastore_observe_events", + 'com.amazonaws.amplify/datastore_observe_events', const StandardMethodCodec().encodeSuccessEnvelope(json), (ByteData? data) {}, ); @@ -54,13 +52,14 @@ void main() { ); dataStore.observe(Post.classType).listen((event) { expect( - event.item, - Post( - id: "43036c6b-8044-4309-bddc-262b6c686026", - title: "Title 2", - rating: 0, - created: - TemporalDateTime.fromString("2020-02-20T20:20:20-08:00"))); + event.item, + Post( + id: '43036c6b-8044-4309-bddc-262b6c686026', + title: 'Title 2', + rating: 0, + created: TemporalDateTime.fromString('2020-02-20T20:20:20-08:00'), + ), + ); expect(event.eventType, EventType.create); }); }); @@ -69,18 +68,18 @@ void main() { binding.defaultBinaryMessenger.setMockMethodCallHandler( dataStoreChannel, (MethodCall methodCall) async { - expect("setUpObserve", methodCall.method); + expect('setUpObserve', methodCall.method); return null; }, ); - var json = + final json = await getJsonFromFile('observe_api/blog_type_success_event.json'); binding.defaultBinaryMessenger.setMockMethodCallHandler( eventChannel, (MethodCall methodCall) async { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + await TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .handlePlatformMessage( - "com.amazonaws.amplify/datastore_observe_events", + 'com.amazonaws.amplify/datastore_observe_events', const StandardMethodCodec().encodeSuccessEnvelope(json), (ByteData? data) {}, ); @@ -88,7 +87,7 @@ void main() { }, ); dataStore.observe(Post.classType).listen((event) { - fail("No message should ever be received"); + fail('No message should ever be received'); }); }); } diff --git a/packages/amplify_datastore/test/amplify_datastore_query_test.dart b/packages/amplify_datastore/test/amplify_datastore_query_test.dart index 2217b4370b..fd81c49da9 100644 --- a/packages/amplify_datastore/test/amplify_datastore_query_test.dart +++ b/packages/amplify_datastore/test/amplify_datastore_query_test.dart @@ -8,11 +8,9 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { - const MethodChannel dataStoreChannel = - MethodChannel('com.amazonaws.amplify/datastore'); + const dataStoreChannel = MethodChannel('com.amazonaws.amplify/datastore'); - AmplifyDataStore dataStore = - AmplifyDataStore(modelProvider: ModelProvider.instance); + final dataStore = AmplifyDataStore(modelProvider: ModelProvider.instance); final binding = TestWidgetsFlutterBinding.ensureInitialized(); @@ -27,66 +25,72 @@ void main() { binding.defaultBinaryMessenger.setMockMethodCallHandler( dataStoreChannel, (MethodCall methodCall) async { - if (methodCall.method == "query") { + if (methodCall.method == 'query') { return getJsonFromFile('query_api/response/nested_results.json'); } return null; }, ); - List comments = await dataStore.query(Comment.classType); + final comments = await dataStore.query(Comment.classType); expect(comments.length, 1); expect( - comments[0], - Comment( - id: '39c3c0e6-8726-436e-8cdf-bff38e9a62da', - content: 'Loving Amplify Datastore!', - post: Post( - id: 'e50ffa8f-783b-4780-89b4-27043ffc35be', - title: "some title", - rating: 10, - created: TemporalDateTime.fromString("2020-11-25T01:28:49Z")), - )); + comments[0], + Comment( + id: '39c3c0e6-8726-436e-8cdf-bff38e9a62da', + content: 'Loving Amplify Datastore!', + post: Post( + id: 'e50ffa8f-783b-4780-89b4-27043ffc35be', + title: 'some title', + rating: 10, + created: TemporalDateTime.fromString('2020-11-25T01:28:49Z'), + ), + ), + ); }); test('query returns 2 sucessful results', () async { binding.defaultBinaryMessenger.setMockMethodCallHandler( dataStoreChannel, (MethodCall methodCall) async { - if (methodCall.method == "query") { + if (methodCall.method == 'query') { return getJsonFromFile('query_api/response/2_results.json'); } return null; }, ); - List posts = await dataStore.query(Post.classType); + final posts = await dataStore.query(Post.classType); expect(posts.length, 2); expect( - posts[0], - Post( - id: '4281dfba-96c8-4a38-9a8e-35c7e893ea47', - created: TemporalDateTime.fromString("2020-02-20T20:20:20+03:50"), - rating: 4, - title: 'Title 1')); + posts[0], + Post( + id: '4281dfba-96c8-4a38-9a8e-35c7e893ea47', + created: TemporalDateTime.fromString('2020-02-20T20:20:20+03:50'), + rating: 4, + title: 'Title 1', + ), + ); expect( - posts[1], - Post( - id: '43036c6b-8044-4309-bddc-262b6c686026', - created: TemporalDateTime.fromString("2020-02-20T20:20:20-08:00"), - rating: 6, - title: 'Title 2')); + posts[1], + Post( + id: '43036c6b-8044-4309-bddc-262b6c686026', + created: TemporalDateTime.fromString('2020-02-20T20:20:20-08:00'), + rating: 6, + title: 'Title 2', + ), + ); }); test('query returns 0 sucessful results', () async { binding.defaultBinaryMessenger.setMockMethodCallHandler( dataStoreChannel, (MethodCall methodCall) async { - if (methodCall.method == "query") { + if (methodCall.method == 'query') { return []; } return null; }, ); - List posts = await dataStore.query(Post.classType); + final posts = await dataStore.query(Post.classType); expect(posts.length, 0); }); @@ -96,15 +100,17 @@ void main() { binding.defaultBinaryMessenger.setMockMethodCallHandler( dataStoreChannel, (MethodCall methodCall) async { - if (methodCall.method == "query") { - expect(methodCall.arguments, - await getJsonFromFile('query_api/request/only_model_name.json')); + if (methodCall.method == 'query') { + expect( + methodCall.arguments, + await getJsonFromFile('query_api/request/only_model_name.json'), + ); return []; } return null; }, ); - List posts = await dataStore.query(Post.classType); + final posts = await dataStore.query(Post.classType); expect(posts.length, 0); }); @@ -113,22 +119,28 @@ void main() { binding.defaultBinaryMessenger.setMockMethodCallHandler( dataStoreChannel, (MethodCall methodCall) async { - if (methodCall.method == "query") { + if (methodCall.method == 'query') { expect( - methodCall.arguments, - await getJsonFromFile( - 'query_api/request/model_name_with_all_query_parameters.json')); + methodCall.arguments, + await getJsonFromFile( + 'query_api/request/model_name_with_all_query_parameters.json', + ), + ); return []; } return null; }, ); - List posts = await dataStore.query(Post.classType, - where: Post.ID.eq("123").or(Post.RATING - .ge(4) - .and(not(Post.CREATED.eq("2020-02-20T20:20:20-08:00")))), - sortBy: [Post.ID.ascending(), Post.CREATED.descending()], - pagination: QueryPagination(page: 2, limit: 8)); + final posts = await dataStore.query( + Post.classType, + where: Post.ID.eq('123').or( + Post.RATING + .ge(4) + .and(not(Post.CREATED.eq('2020-02-20T20:20:20-08:00'))), + ), + sortBy: [Post.ID.ascending(), Post.CREATED.descending()], + pagination: const QueryPagination(page: 2, limit: 8), + ); expect(posts.length, 0); }); @@ -136,24 +148,39 @@ void main() { binding.defaultBinaryMessenger.setMockMethodCallHandler( dataStoreChannel, (MethodCall methodCall) async { - if (methodCall.method == "query") { - throw PlatformException(code: 'DataStoreException', details: { - 'message': 'Query failed for whatever known reason', - 'recoverySuggestion': 'some insightful suggestion', - 'underlyingException': 'Act of God' - }); + if (methodCall.method == 'query') { + throw PlatformException( + code: 'DataStoreException', + details: { + 'message': 'Query failed for whatever known reason', + 'recoverySuggestion': 'some insightful suggestion', + 'underlyingException': 'Act of God', + }, + ); } return null; }, ); expect( - () => dataStore.query(Post.classType), - throwsA(isA() - .having((exception) => exception.message, 'message', - 'Query failed for whatever known reason') - .having((exception) => exception.recoverySuggestion, - 'recoverySuggestion', 'some insightful suggestion') - .having((exception) => exception.underlyingException, - 'underlyingException', 'Act of God'))); + () => dataStore.query(Post.classType), + throwsA( + isA() + .having( + (exception) => exception.message, + 'message', + 'Query failed for whatever known reason', + ) + .having( + (exception) => exception.recoverySuggestion, + 'recoverySuggestion', + 'some insightful suggestion', + ) + .having( + (exception) => exception.underlyingException, + 'underlyingException', + 'Act of God', + ), + ), + ); }); } diff --git a/packages/amplify_datastore/test/amplify_datastore_save_test.dart b/packages/amplify_datastore/test/amplify_datastore_save_test.dart index 5b602efc1c..1cad1ce03c 100644 --- a/packages/amplify_datastore/test/amplify_datastore_save_test.dart +++ b/packages/amplify_datastore/test/amplify_datastore_save_test.dart @@ -8,11 +8,9 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { - const MethodChannel dataStoreChannel = - MethodChannel('com.amazonaws.amplify/datastore'); + const dataStoreChannel = MethodChannel('com.amazonaws.amplify/datastore'); - AmplifyDataStore dataStore = - AmplifyDataStore(modelProvider: ModelProvider.instance); + final dataStore = AmplifyDataStore(modelProvider: ModelProvider.instance); final binding = TestWidgetsFlutterBinding.ensureInitialized(); @@ -25,21 +23,23 @@ void main() { binding.defaultBinaryMessenger.setMockMethodCallHandler( dataStoreChannel, (MethodCall methodCall) async { - if (methodCall.method == "save") { + if (methodCall.method == 'save') { expect( methodCall.arguments, await getJsonFromFile( - 'save_api/request/instance_without_predicate.json'), + 'save_api/request/instance_without_predicate.json', + ), ); } return null; }, ); - Post post = Post( - id: '9fc5fab4-37ff-4566-97e5-19c5d58a4c22', - title: 'New Post being saved', - rating: 10, - created: TemporalDateTime.fromString('2020-11-12T03:15:48+03:18')); + final post = Post( + id: '9fc5fab4-37ff-4566-97e5-19c5d58a4c22', + title: 'New Post being saved', + rating: 10, + created: TemporalDateTime.fromString('2020-11-12T03:15:48+03:18'), + ); await dataStore.save(post); }); @@ -49,24 +49,42 @@ void main() { binding.defaultBinaryMessenger.setMockMethodCallHandler( dataStoreChannel, (MethodCall methodCall) async { - throw PlatformException(code: 'DataStoreException', details: { - 'message': 'Save failed for whatever known reason', - 'recoverySuggestion': 'some insightful suggestion', - 'underlyingException': 'Act of God' - }); + throw PlatformException( + code: 'DataStoreException', + details: { + 'message': 'Save failed for whatever known reason', + 'recoverySuggestion': 'some insightful suggestion', + 'underlyingException': 'Act of God', + }, + ); }, ); expect( - () => dataStore.save(Post( - title: 'Test Post', - rating: 10, - created: TemporalDateTime.fromString("2020-02-20T20:20:20-08:00"))), - throwsA(isA() - .having((exception) => exception.message, 'message', - 'Save failed for whatever known reason') - .having((exception) => exception.recoverySuggestion, - 'recoverySuggestion', 'some insightful suggestion') - .having((exception) => exception.underlyingException, - 'underlyingException', 'Act of God'))); + () => dataStore.save( + Post( + title: 'Test Post', + rating: 10, + created: TemporalDateTime.fromString('2020-02-20T20:20:20-08:00'), + ), + ), + throwsA( + isA() + .having( + (exception) => exception.message, + 'message', + 'Save failed for whatever known reason', + ) + .having( + (exception) => exception.recoverySuggestion, + 'recoverySuggestion', + 'some insightful suggestion', + ) + .having( + (exception) => exception.underlyingException, + 'underlyingException', + 'Act of God', + ), + ), + ); }); } diff --git a/packages/amplify_datastore/test/amplify_datastore_stream_controller_test.dart b/packages/amplify_datastore/test/amplify_datastore_stream_controller_test.dart index a1d151fbd2..bdba98f18b 100644 --- a/packages/amplify_datastore/test/amplify_datastore_stream_controller_test.dart +++ b/packages/amplify_datastore/test/amplify_datastore_stream_controller_test.dart @@ -3,7 +3,6 @@ // ignore_for_file: deprecated_member_use -import 'dart:async'; import 'dart:convert'; import 'package:amplify_core/amplify_core.dart'; @@ -14,15 +13,13 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { - const MethodChannel datastoreChannel = - MethodChannel('com.amazonaws.amplify/datastore'); - const MethodChannel coreChannel = MethodChannel('com.amazonaws.amplify/core'); - const String channelName = "com.amazonaws.amplify/datastore_hub_events"; - - DataStoreStreamController controller = DataStoreStreamController(); - controller.registerModelsForHub(ModelProvider.instance); - StreamController dataStoreStreamController = - controller.datastoreStreamController; + const datastoreChannel = MethodChannel('com.amazonaws.amplify/datastore'); + const coreChannel = MethodChannel('com.amazonaws.amplify/core'); + const channelName = 'com.amazonaws.amplify/datastore_hub_events'; + + final controller = DataStoreStreamController() + ..registerModelsForHub(ModelProvider.instance); + final dataStoreStreamController = controller.datastoreStreamController; TestWidgetsFlutterBinding.ensureInitialized(); setUpAll(() async { @@ -39,9 +36,9 @@ void main() { .setMockMessageHandler(channelName, null); }); - tearDownAll(() => dataStoreStreamController.close()); + tearDownAll(dataStoreStreamController.close); - handler(event) { + void handler(ByteData? event) { ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( channelName, event, @@ -50,7 +47,7 @@ void main() { } test('Can receive Ready Event', () async { - var json = await getJsonFromFile('hub/readyEvent.json'); + final json = await getJsonFromFile('hub/readyEvent.json'); void emitEvent(ByteData event) { handler(event); } @@ -63,20 +60,19 @@ void main() { }, ); - List events = []; - StreamSubscription sub = dataStoreStreamController.stream.listen((event) { - events.add(event); - }); + final events = []; + final sub = dataStoreStreamController.stream.listen(events.add); await Future.delayed(Duration.zero); - sub.cancel(); + await sub.cancel(); - expect(events.last, isInstanceOf()); - expect(events.last.eventName, equals("ready")); + expect(events.last, isInstanceOf()); + expect(events.last.eventName, equals('ready')); }); test('Can receive SubscriptionsEstablished Event', () async { - var json = await getJsonFromFile('hub/subscriptionsEstablishedEvent.json'); + final json = + await getJsonFromFile('hub/subscriptionsEstablishedEvent.json'); void emitEvent(ByteData event) { handler(event); } @@ -89,20 +85,18 @@ void main() { }, ); - List events = []; - StreamSubscription sub = dataStoreStreamController.stream.listen((event) { - events.add(event); - }); + final events = []; + final sub = dataStoreStreamController.stream.listen(events.add); await Future.delayed(Duration.zero); - sub.cancel(); + await sub.cancel(); - expect(events.last, isInstanceOf()); - expect(events.last.eventName, equals("subscriptionsEstablished")); + expect(events.last, isInstanceOf()); + expect(events.last.eventName, equals('subscriptionsEstablished')); }); test('Can receive SyncQueriesReady Event', () async { - var json = await getJsonFromFile('hub/syncQueriesReadyEvent.json'); + final json = await getJsonFromFile('hub/syncQueriesReadyEvent.json'); void emitEvent(ByteData event) { handler(event); } @@ -115,20 +109,18 @@ void main() { }, ); - List events = []; - StreamSubscription sub = dataStoreStreamController.stream.listen((event) { - events.add(event); - }); + final events = []; + final sub = dataStoreStreamController.stream.listen(events.add); await Future.delayed(Duration.zero); - sub.cancel(); + await sub.cancel(); - expect(events.last, isInstanceOf()); - expect(events.last.eventName, equals("syncQueriesReady")); + expect(events.last, isInstanceOf()); + expect(events.last.eventName, equals('syncQueriesReady')); }); test('Can receive NetworkStatus Event', () async { - var json = await getJsonFromFile('hub/networkStatusEvent.json'); + final json = await getJsonFromFile('hub/networkStatusEvent.json'); void emitEvent(ByteData event) { handler(event); } @@ -141,21 +133,19 @@ void main() { }, ); - List events = []; - StreamSubscription sub = dataStoreStreamController.stream.listen((event) { - events.add(event); - }); + final events = []; + final sub = dataStoreStreamController.stream.listen(events.add); await Future.delayed(Duration.zero); - sub.cancel(); + await sub.cancel(); - NetworkStatusEvent payload = events.last.payload as NetworkStatusEvent; - expect(events.last, isInstanceOf()); + final payload = events.last.payload as NetworkStatusEvent; + expect(events.last, isInstanceOf()); expect(payload.active, equals(true)); }); test('Can receive ModelSynced Event', () async { - var json = await getJsonFromFile('hub/modelSyncedEvent.json'); + final json = await getJsonFromFile('hub/modelSyncedEvent.json'); void emitEvent(ByteData event) { handler(event); } @@ -168,25 +158,24 @@ void main() { }, ); - List events = []; - StreamSubscription sub = dataStoreStreamController.stream.listen((event) { - events.add(event); - }); + final events = []; + final sub = dataStoreStreamController.stream.listen(events.add); await Future.delayed(Duration.zero); - sub.cancel(); - ModelSyncedEvent payload = events.last.payload as ModelSyncedEvent; - expect(events.last, isInstanceOf()); + await sub.cancel(); + final payload = events.last.payload as ModelSyncedEvent; + expect(events.last, isInstanceOf()); expect(payload.added, 1); expect(payload.deleted, 0); expect(payload.isDeltaSync, true); expect(payload.isFullSync, false); expect(payload.updated, 0); - expect(payload.modelName, "Post"); + expect(payload.modelName, 'Post'); }); test('Can receive OutboxMutation (Enqueued) Event', () async { - var json = await getJsonFromFile('hub/outboxMutationEnqueuedEvent.json'); + final json = await getJsonFromFile('hub/outboxMutationEnqueuedEvent.json') + as Map; void emitEvent(ByteData event) { handler(event); } @@ -199,29 +188,31 @@ void main() { }, ); - List events = []; - StreamSubscription sub = dataStoreStreamController.stream.listen((event) { - events.add(event); - }); + final events = []; + final sub = dataStoreStreamController.stream.listen(events.add); await Future.delayed(Duration.zero); - sub.cancel(); - - OutboxMutationEvent payload = events.last.payload as OutboxMutationEvent; - TemporalDateTime parsedDate = - TemporalDateTime.fromString(json["element"]["model"]["created"]); - expect(events.last, isInstanceOf()); - expect(payload.modelName, "Post"); + await sub.cancel(); + + final payload = events.last.payload as OutboxMutationEvent; + final element = json['element'] as Map; + final model = element['model'] as Map; + final parsedDate = TemporalDateTime.fromString(model['created'] as String); + expect(events.last, isInstanceOf()); + expect(payload.modelName, 'Post'); expect(payload.element, isInstanceOf()); expect(payload.element.model, isInstanceOf()); - expect(payload.element.model.modelIdentifier.serializeAsString(), - equals("43036c6b-8044-4309-bddc-262b6c686026")); - expect((payload.element.model as Post).title, equals("Title 1")); + expect( + payload.element.model.modelIdentifier.serializeAsString(), + equals('43036c6b-8044-4309-bddc-262b6c686026'), + ); + expect((payload.element.model as Post).title, equals('Title 1')); expect((payload.element.model as Post).created, equals(parsedDate)); }); test('Can receive OutboxMutation (Processed) Event', () async { - var json = await getJsonFromFile('hub/outboxMutationProcessedEvent.json'); + final json = await getJsonFromFile('hub/outboxMutationProcessedEvent.json') + as Map; void emitEvent(ByteData event) { handler(event); } @@ -234,27 +225,27 @@ void main() { }, ); - List events = []; - StreamSubscription sub = dataStoreStreamController.stream.listen((event) { - events.add(event); - }); + final events = []; + final sub = dataStoreStreamController.stream.listen(events.add); await Future.delayed(Duration.zero); - sub.cancel(); - - OutboxMutationEvent payload = events.last.payload as OutboxMutationEvent; - HubEventElementWithMetadata element = - payload.element as HubEventElementWithMetadata; - TemporalDateTime parsedDate = - TemporalDateTime.fromString(json["element"]["model"]["created"]); - expect(events.last, isInstanceOf()); - expect(payload.modelName, "Post"); + await sub.cancel(); + + final payload = events.last.payload as OutboxMutationEvent; + final element = payload.element as HubEventElementWithMetadata; + final elementJson = json['element'] as Map; + final model = elementJson['model'] as Map; + final parsedDate = TemporalDateTime.fromString(model['created'] as String); + expect(events.last, isInstanceOf()); + expect(payload.modelName, 'Post'); expect(element, isInstanceOf()); expect(element, isInstanceOf()); expect(element.model, isInstanceOf()); - expect(element.model.modelIdentifier.serializeAsString(), - equals("43036c6b-8044-4309-bddc-262b6c686026")); - expect((element.model as Post).title, equals("Title 1")); + expect( + element.model.modelIdentifier.serializeAsString(), + equals('43036c6b-8044-4309-bddc-262b6c686026'), + ); + expect((element.model as Post).title, equals('Title 1')); expect((element.model as Post).created, equals(parsedDate)); expect(element.deleted, equals(false)); expect(element.version, equals(1)); @@ -262,7 +253,7 @@ void main() { }); test('Can receive OutboxStatus Event', () async { - var json = await getJsonFromFile('hub/outboxStatusEvent.json'); + final json = await getJsonFromFile('hub/outboxStatusEvent.json'); void emitEvent(ByteData event) { handler(event); } @@ -275,22 +266,21 @@ void main() { }, ); - List events = []; - StreamSubscription sub = dataStoreStreamController.stream.listen((event) { - events.add(event); - }); + final events = []; + final sub = dataStoreStreamController.stream.listen(events.add); await Future.delayed(Duration.zero); - sub.cancel(); + await sub.cancel(); - OutboxStatusEvent payload = events.last.payload as OutboxStatusEvent; - expect(events.last, isInstanceOf()); + final payload = events.last.payload as OutboxStatusEvent; + expect(events.last, isInstanceOf()); expect(payload.isEmpty, equals(true)); }); test('Can receive SyncQueriesStarted Event', () async { - var json = await getJsonFromFile('hub/syncQueriesStartedEvent.json'); - json["models"] = jsonEncode(json["models"]); + final json = await getJsonFromFile('hub/syncQueriesStartedEvent.json') + as Map; + json['models'] = jsonEncode(json['models']); void emitEvent(ByteData event) { handler(event); } @@ -303,18 +293,15 @@ void main() { }, ); - List events = []; - StreamSubscription sub = dataStoreStreamController.stream.listen((event) { - events.add(event); - }); + final events = []; + final sub = dataStoreStreamController.stream.listen(events.add); await Future.delayed(Duration.zero); - sub.cancel(); + await sub.cancel(); - SyncQueriesStartedEvent payload = - events.last.payload as SyncQueriesStartedEvent; - expect(events.last, isInstanceOf()); + final payload = events.last.payload as SyncQueriesStartedEvent; + expect(events.last, isInstanceOf()); expect(payload.models.length, equals(1)); - expect(payload.models.first, equals("Post")); + expect(payload.models.first, equals('Post')); }); } diff --git a/packages/amplify_datastore/test/amplify_datastore_test.dart b/packages/amplify_datastore/test/amplify_datastore_test.dart index ed6bbae0f5..30af8e24a1 100644 --- a/packages/amplify_datastore/test/amplify_datastore_test.dart +++ b/packages/amplify_datastore/test/amplify_datastore_test.dart @@ -12,19 +12,20 @@ void main() { const mockSyncMaxRecords = 60000; const mockSyncPagesize = 1000; - const MethodChannel dataStoreChannel = - MethodChannel('com.amazonaws.amplify/datastore'); + const dataStoreChannel = MethodChannel('com.amazonaws.amplify/datastore'); - AmplifyDataStore dataStore = AmplifyDataStore( - modelProvider: ModelProvider.instance, - options: DataStorePluginOptions( - syncExpressions: [ - DataStoreSyncExpression(Blog.classType, () => Blog.NAME.eq('foo')), - DataStoreSyncExpression(Post.classType, () => Post.TITLE.eq('bar')) - ], - syncInterval: mockSyncInterval, - syncMaxRecords: mockSyncMaxRecords, - syncPageSize: mockSyncPagesize)); + final dataStore = AmplifyDataStore( + modelProvider: ModelProvider.instance, + options: DataStorePluginOptions( + syncExpressions: [ + DataStoreSyncExpression(Blog.classType, () => Blog.NAME.eq('foo')), + DataStoreSyncExpression(Post.classType, () => Post.TITLE.eq('bar')), + ], + syncInterval: mockSyncInterval, + syncMaxRecords: mockSyncMaxRecords, + syncPageSize: mockSyncPagesize, + ), + ); final binding = TestWidgetsFlutterBinding.ensureInitialized(); @@ -35,30 +36,35 @@ void main() { test('DataStore custom configuration should be passed via MethodChannel', () async { - var expectedBlogExpression = + final expectedBlogExpression = await getJsonFromFile('sync_expressions/blog_name.json'); - var expectedPostExpression = + final expectedPostExpression = await getJsonFromFile('sync_expressions/post_title.json'); binding.defaultBinaryMessenger.setMockMethodCallHandler( dataStoreChannel, (MethodCall methodCall) async { - if (methodCall.method == "configureDataStore") { - final modelSchemas = methodCall.arguments['modelSchemas']; - final syncExpressions = methodCall.arguments['syncExpressions']; - final syncInterval = methodCall.arguments['syncInterval']; - final syncMaxRecords = methodCall.arguments['syncMaxRecords']; - final syncPageSize = methodCall.arguments['syncPageSize']; + if (methodCall.method == 'configureDataStore') { + final arguments = methodCall.arguments as Map; + final modelSchemas = arguments['modelSchemas']; + final syncExpressions = arguments['syncExpressions'] as List; + final syncInterval = arguments['syncInterval']; + final syncMaxRecords = arguments['syncMaxRecords']; + final syncPageSize = arguments['syncPageSize']; expect( - modelSchemas, - ModelProvider.instance.modelSchemas - .map((schema) => schema.toMap()) - .toList()); - expect(syncExpressions.map((expression) { - // Ignore generated ID - (expression as Map).remove("id"); - return expression; - }), [expectedBlogExpression, expectedPostExpression]); + modelSchemas, + ModelProvider.instance.modelSchemas + .map((schema) => schema.toMap()) + .toList(), + ); + expect( + syncExpressions.map((expression) { + // Ignore generated ID + (expression as Map).remove('id'); + return expression; + }), + [expectedBlogExpression, expectedPostExpression], + ); expect(syncInterval, mockSyncInterval); expect(syncMaxRecords, mockSyncMaxRecords); expect(syncPageSize, mockSyncPagesize); diff --git a/packages/amplify_datastore/test/outbox_mutation_event_test.dart b/packages/amplify_datastore/test/outbox_mutation_event_test.dart index a3247762b2..fd79f6e0d5 100644 --- a/packages/amplify_datastore/test/outbox_mutation_event_test.dart +++ b/packages/amplify_datastore/test/outbox_mutation_event_test.dart @@ -7,32 +7,32 @@ import 'package:amplify_test/test_models/ModelProvider.dart'; import 'package:flutter_test/flutter_test.dart'; void main() async { - var modelProvider = ModelProvider(); - var outboxMutationEnqueuedEventJson = + final modelProvider = ModelProvider(); + final outboxMutationEnqueuedEventJson = await getJsonFromFile('hub/outboxMutationEnqueuedEvent.json') as Map; - var outboxMutationEnqueuedEvent = OutboxMutationEvent( + final outboxMutationEnqueuedEvent = OutboxMutationEvent( outboxMutationEnqueuedEventJson, modelProvider, 'outboxMutationEnqueued', ); - var outboxMutationProcessedEventJson = + final outboxMutationProcessedEventJson = await getJsonFromFile('hub/outboxMutationProcessedEvent.json') as Map; - var outboxMutationProcessedEvent = OutboxMutationEvent( + final outboxMutationProcessedEvent = OutboxMutationEvent( outboxMutationProcessedEventJson, modelProvider, 'outboxMutationProcessed', ); - var expectedPost = Post( + final expectedPost = Post( id: '43036c6b-8044-4309-bddc-262b6c686026', title: 'Title 1', rating: 5, created: TemporalDateTime.fromString('2020-02-20T20:20:20-08:00'), ); - var expectedProcessedHubEvent = HubEventElementWithMetadata( + final expectedProcessedHubEvent = HubEventElementWithMetadata( expectedPost, version: 1, lastChangedAt: 1624492860, @@ -41,7 +41,7 @@ void main() async { group('OutboxMutationEvent', () { group('outboxMutationEnqueued', () { - var enqueuedHubEventElement = outboxMutationEnqueuedEvent.element; + final enqueuedHubEventElement = outboxMutationEnqueuedEvent.element; test('is HubEventElement', () { expect( @@ -55,7 +55,7 @@ void main() async { }); test('fromMap', () { - var post = enqueuedHubEventElement.model as Post; + final post = enqueuedHubEventElement.model as Post; expect(post, expectedPost); }); }); @@ -70,7 +70,7 @@ void main() async { group('fromMap', () { test('all fields', () { - var processedHubEventElement = outboxMutationProcessedEvent.element + final processedHubEventElement = outboxMutationProcessedEvent.element as HubEventElementWithMetadata; expect( processedHubEventElement.model, @@ -91,7 +91,7 @@ void main() async { }); test('_deleted = null', () { - var outboxMutationProcessedEvent = OutboxMutationEvent( + final outboxMutationProcessedEvent = OutboxMutationEvent( { ...outboxMutationProcessedEventJson, '_deleted': null, @@ -99,7 +99,7 @@ void main() async { modelProvider, 'outboxMutationProcessed', ); - var processedHubEventElement = outboxMutationProcessedEvent.element + final processedHubEventElement = outboxMutationProcessedEvent.element as HubEventElementWithMetadata; expect(processedHubEventElement.deleted, false); }); diff --git a/packages/amplify_datastore/test/query_pagination_test.dart b/packages/amplify_datastore/test/query_pagination_test.dart index a7aa531989..11d3d27159 100644 --- a/packages/amplify_datastore/test/query_pagination_test.dart +++ b/packages/amplify_datastore/test/query_pagination_test.dart @@ -11,28 +11,34 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); dynamic getJsonFromFile(String path) async { - path = 'resources/query_pagination/' + path; - String jsonString = ''; + path = 'resources/query_pagination/$path'; + var jsonString = ''; try { jsonString = await File(path).readAsString(); - } catch (e) { - jsonString = await File('test/' + path).readAsString(); + } on Exception { + jsonString = await File('test/$path').readAsString(); } return jsonDecode(jsonString); } test('when providing custom page and limit', () async { - expect(QueryPagination(page: 3, limit: 200).serializeAsMap(), - await getJsonFromFile('custom_page_and_limit.json')); + expect( + const QueryPagination(page: 3, limit: 200).serializeAsMap(), + await getJsonFromFile('custom_page_and_limit.json'), + ); }); test('when only need first page', () async { - expect(QueryPagination.firstPage().serializeAsMap(), - await getJsonFromFile('first_page.json')); + expect( + const QueryPagination.firstPage().serializeAsMap(), + await getJsonFromFile('first_page.json'), + ); }); test('when only need first result', () async { - expect(QueryPagination.firstResult().serializeAsMap(), - await getJsonFromFile('first_result.json')); + expect( + const QueryPagination.firstResult().serializeAsMap(), + await getJsonFromFile('first_result.json'), + ); }); } diff --git a/packages/amplify_datastore/test/query_predicate_test.dart b/packages/amplify_datastore/test/query_predicate_test.dart index 7f0ae9c07b..79bd95bfe8 100644 --- a/packages/amplify_datastore/test/query_predicate_test.dart +++ b/packages/amplify_datastore/test/query_predicate_test.dart @@ -13,24 +13,26 @@ void main() { group('query predicate serialization', () { dynamic getJsonFromFile(String path) async { - path = 'resources/query_predicate/' + path; - String jsonString = ''; + path = 'resources/query_predicate/$path'; + var jsonString = ''; try { jsonString = await File(path).readAsString(); - } catch (e) { - jsonString = await File('test/' + path).readAsString(); + } on Exception { + jsonString = await File('test/$path').readAsString(); } return jsonDecode(jsonString); } test('when id not equals', () async { - expect(Post.ID.ne("123").serializeAsMap(), - await getJsonFromFile('id_not_equals.json')); + expect( + Post.ID.ne('123').serializeAsMap(), + await getJsonFromFile('id_not_equals.json'), + ); }); test('bad model id field naming backwards compatibility', () async { - QueryPredicate testPredicateWithBadIdFiledNaming = - QueryField(fieldName: 'blog.id').ne('123'); + final QueryPredicate testPredicateWithBadIdFiledNaming = + const QueryField(fieldName: 'blog.id').ne('123'); expect( testPredicateWithBadIdFiledNaming.serializeAsMap(), await getJsonFromFile('id_not_equals.json'), @@ -38,82 +40,102 @@ void main() { }); test('when rating greater or equal', () async { - expect(Post.RATING.ge(4).serializeAsMap(), - await getJsonFromFile('rating_greater_or_equal.json')); - expect((Post.RATING >= 4).serializeAsMap(), - await getJsonFromFile('rating_greater_or_equal.json')); + expect( + Post.RATING.ge(4).serializeAsMap(), + await getJsonFromFile('rating_greater_or_equal.json'), + ); + expect( + (Post.RATING >= 4).serializeAsMap(), + await getJsonFromFile('rating_greater_or_equal.json'), + ); }); - test('when rating less or equal AND \(id contains or title begins with\)', + test('when rating less or equal AND (id contains or title begins with)', () async { - QueryPredicate testPredicate = Post.RATING + final QueryPredicate testPredicate = Post.RATING .le(4) - .and(Post.ID.contains("abc").or(Post.TITLE.beginsWith("def"))); + .and(Post.ID.contains('abc').or(Post.TITLE.beginsWith('def'))); - expect(testPredicate.serializeAsMap(), - await getJsonFromFile('complex_nested.json')); + expect( + testPredicate.serializeAsMap(), + await getJsonFromFile('complex_nested.json'), + ); }); test( 'when rating between AND id contains AND title begins_with AND created equals', () async { - QueryPredicate testPredicate = Post.RATING + final QueryPredicate testPredicate = Post.RATING .between(1, 4) - .and(Post.ID.contains("abc")) - .and(Post.TITLE.beginsWith("def")) - .and(Post.CREATED.eq("2020-02-20T20:20:20-08:00")); + .and(Post.ID.contains('abc')) + .and(Post.TITLE.beginsWith('def')) + .and(Post.CREATED.eq('2020-02-20T20:20:20-08:00')); - expect(testPredicate.serializeAsMap(), - await getJsonFromFile('group_with_only_and.json')); + expect( + testPredicate.serializeAsMap(), + await getJsonFromFile('group_with_only_and.json'), + ); }); test('when rating lt AND id eq OR title contains', () async { - QueryPredicate testPredicate = Post.RATING + final QueryPredicate testPredicate = Post.RATING .lt(4) - .and(Post.ID.contains("abc")) - .or(Post.TITLE.contains("def")); + .and(Post.ID.contains('abc')) + .or(Post.TITLE.contains('def')); - expect(testPredicate.serializeAsMap(), - await getJsonFromFile('group_mixed_and_or.json')); + expect( + testPredicate.serializeAsMap(), + await getJsonFromFile('group_mixed_and_or.json'), + ); }); test('when rating gt but not eq', () async { - QueryPredicate testPredicate = + final QueryPredicate testPredicate = Post.RATING.gt(4).and(not(Post.RATING.eq(1))); - expect(testPredicate.serializeAsMap(), - await getJsonFromFile('mixed_with_not.json')); + expect( + testPredicate.serializeAsMap(), + await getJsonFromFile('mixed_with_not.json'), + ); }); test('when negate complex predicate', () async { - QueryPredicate testPredicate = not(Post.RATING - .eq(1) - .and(Post.RATING.eq(4).or(Post.TITLE.contains("crap")))); + final QueryPredicate testPredicate = not( + Post.RATING + .eq(1) + .and(Post.RATING.eq(4).or(Post.TITLE.contains('crap'))), + ); - expect(testPredicate.serializeAsMap(), - await getJsonFromFile('negate_complex_predicate.json')); + expect( + testPredicate.serializeAsMap(), + await getJsonFromFile('negate_complex_predicate.json'), + ); }); test('when operands are bool and double', () async { - QueryPredicate testPredicate = + final QueryPredicate testPredicate = Post.RATING.eq(1.3).and(Post.CREATED.eq(true)); - expect(testPredicate.serializeAsMap(), - await getJsonFromFile('bool_and_double_operands.json')); + expect( + testPredicate.serializeAsMap(), + await getJsonFromFile('bool_and_double_operands.json'), + ); }); test('when value is a temporal type', () async { - QueryPredicate testPredicate = Post.CREATED.eq( + final QueryPredicate testPredicate = Post.CREATED.eq( TemporalDateTime(DateTime.utc(2020, 01, 01)), ); - expect(testPredicate.serializeAsMap(), - await getJsonFromFile('temporal_predicate.json')); + expect( + testPredicate.serializeAsMap(), + await getJsonFromFile('temporal_predicate.json'), + ); }); test('when query by model identifier with eq()', () async { final testPredicate = Inventory.MODEL_IDENTIFIER.eq( - InventoryModelIdentifier( + const InventoryModelIdentifier( productID: 'product-id', name: 'product-name', warehouseID: 'warehouse-id', @@ -127,7 +149,7 @@ void main() { test('when query by model identifier with ne()', () async { final testPredicate = Inventory.MODEL_IDENTIFIER.ne( - InventoryModelIdentifier( + const InventoryModelIdentifier( productID: 'product-id', name: 'product-name', warehouseID: 'warehouse-id', @@ -136,14 +158,16 @@ void main() { ); final serialized = testPredicate.serializeAsMap(); - expect(serialized, - await getJsonFromFile('model_identifier_not_equals.json')); + expect( + serialized, + await getJsonFromFile('model_identifier_not_equals.json'), + ); }); test('when query by model identifier with not(eq())', () async { final testPredicate = not( Inventory.MODEL_IDENTIFIER.eq( - InventoryModelIdentifier( + const InventoryModelIdentifier( productID: 'product-id', name: 'product-name', warehouseID: 'warehouse-id', @@ -153,14 +177,16 @@ void main() { ); final serialized = testPredicate.serializeAsMap(); - expect(serialized, - await getJsonFromFile('model_identifier_group_not_equals.json')); + expect( + serialized, + await getJsonFromFile('model_identifier_group_not_equals.json'), + ); }); test('when query by model identifier with not(ne())', () async { final testPredicate = not( Inventory.MODEL_IDENTIFIER.ne( - InventoryModelIdentifier( + const InventoryModelIdentifier( productID: 'product-id', name: 'product-name', warehouseID: 'warehouse-id', @@ -170,66 +196,68 @@ void main() { ); final serialized = testPredicate.serializeAsMap(); - expect(serialized, - await getJsonFromFile('model_identifier_group_equals.json')); + expect( + serialized, + await getJsonFromFile('model_identifier_group_equals.json'), + ); }); }); group('query predicate comparison', () { - Post post1 = Post( + final post1 = Post( title: 'post one', rating: 1, likeCount: 1, created: TemporalDateTime(DateTime(2020, 01, 01, 10, 30)), ); - Post post2 = Post( + final post2 = Post( title: 'post two', rating: 10, likeCount: 10, created: TemporalDateTime(DateTime(2020, 01, 01, 12, 00)), ); - Post post3 = Post( + final post3 = Post( title: 'post three', rating: 100, likeCount: 100, created: TemporalDateTime(DateTime(2021, 01, 01, 12, 00)), ); - Post post4 = Post( + final post4 = Post( title: 'post four', rating: 1000, ); - StringListTypeModel stringListTypeModel1 = StringListTypeModel( + final stringListTypeModel1 = StringListTypeModel( value: ['abc'], ); - StringListTypeModel stringListTypeModel2 = StringListTypeModel( + final stringListTypeModel2 = StringListTypeModel( value: ['abc', 'xyz'], ); - StringListTypeModel stringListTypeModel3 = StringListTypeModel( + final stringListTypeModel3 = StringListTypeModel( value: ['xyz'], ); test('equals', () { - QueryPredicate testPredicate = Post.LIKECOUNT.eq(1); + final QueryPredicate testPredicate = Post.LIKECOUNT.eq(1); expect(testPredicate.evaluate(post1), isTrue); expect(testPredicate.evaluate(post2), isFalse); expect(testPredicate.evaluate(post4), isFalse); }); test('equals (ID)', () { - QueryPredicate testPredicate = Post.ID.eq(post2.id); + final QueryPredicate testPredicate = Post.ID.eq(post2.id); expect(testPredicate.evaluate(post1), isFalse); expect(testPredicate.evaluate(post2), isTrue); expect(testPredicate.evaluate(post4), isFalse); }); test('equals (ModelIdentifier)', () { - QueryPredicate testPredicate = Post.MODEL_IDENTIFIER.eq( + final QueryPredicate testPredicate = Post.MODEL_IDENTIFIER.eq( PostModelIdentifier(id: post2.id), ); expect(testPredicate.evaluate(post1), isFalse); @@ -238,21 +266,21 @@ void main() { }); test('not equals', () { - QueryPredicate testPredicate = Post.LIKECOUNT.ne(1); + final QueryPredicate testPredicate = Post.LIKECOUNT.ne(1); expect(testPredicate.evaluate(post1), isFalse); expect(testPredicate.evaluate(post2), isTrue); expect(testPredicate.evaluate(post4), isTrue); }); test('less than', () { - QueryPredicate testPredicate = Post.LIKECOUNT.lt(5); + final QueryPredicate testPredicate = Post.LIKECOUNT.lt(5); expect(testPredicate.evaluate(post1), isTrue); expect(testPredicate.evaluate(post2), isFalse); expect(testPredicate.evaluate(post4), isFalse); }); test('less than or equal', () { - QueryPredicate testPredicate = Post.LIKECOUNT.le(10); + final QueryPredicate testPredicate = Post.LIKECOUNT.le(10); expect(testPredicate.evaluate(post1), isTrue); expect(testPredicate.evaluate(post2), isTrue); expect(testPredicate.evaluate(post3), isFalse); @@ -260,14 +288,14 @@ void main() { }); test('greater than', () { - QueryPredicate testPredicate = Post.LIKECOUNT.gt(5); + final QueryPredicate testPredicate = Post.LIKECOUNT.gt(5); expect(testPredicate.evaluate(post1), isFalse); expect(testPredicate.evaluate(post2), isTrue); expect(testPredicate.evaluate(post4), isFalse); }); test('greater than or equal', () { - QueryPredicate testPredicate = Post.LIKECOUNT.ge(10); + final QueryPredicate testPredicate = Post.LIKECOUNT.ge(10); expect(testPredicate.evaluate(post1), isFalse); expect(testPredicate.evaluate(post2), isTrue); expect(testPredicate.evaluate(post3), isTrue); @@ -275,7 +303,7 @@ void main() { }); test('between', () { - QueryPredicate testPredicate = Post.LIKECOUNT.between(5, 100); + final QueryPredicate testPredicate = Post.LIKECOUNT.between(5, 100); expect(testPredicate.evaluate(post1), isFalse); expect(testPredicate.evaluate(post2), isTrue); expect(testPredicate.evaluate(post3), isTrue); @@ -283,55 +311,57 @@ void main() { }); test('contains', () { - QueryPredicate testPredicate = Post.TITLE.contains("one"); + final QueryPredicate testPredicate = Post.TITLE.contains('one'); expect(testPredicate.evaluate(post1), isTrue); expect(testPredicate.evaluate(post2), isFalse); }); test('beginsWith', () { - QueryPredicate testPredicate = Post.TITLE.beginsWith("post o"); + final QueryPredicate testPredicate = Post.TITLE.beginsWith('post o'); expect(testPredicate.evaluate(post1), isTrue); expect(testPredicate.evaluate(post2), isFalse); }); test('and', () { - QueryPredicate testPredicate = - Post.TITLE.contains("post") & Post.RATING.lt(10); + final QueryPredicate testPredicate = + Post.TITLE.contains('post') & Post.RATING.lt(10); expect(testPredicate.evaluate(post1), isTrue); expect(testPredicate.evaluate(post2), isFalse); }); test('or', () { - QueryPredicate testPredicate = - Post.TITLE.contains("two") | Post.RATING.lt(10); + final QueryPredicate testPredicate = + Post.TITLE.contains('two') | Post.RATING.lt(10); expect(testPredicate.evaluate(post1), isTrue); expect(testPredicate.evaluate(post2), isTrue); expect(testPredicate.evaluate(post3), isFalse); }); test('not', () { - QueryPredicate testPredicate = not(Post.RATING.lt(5)); + final QueryPredicate testPredicate = not(Post.RATING.lt(5)); expect(testPredicate.evaluate(post1), isFalse); expect(testPredicate.evaluate(post2), isTrue); }); test('attributeExists', () { - QueryPredicate testPredicate = Post.LIKECOUNT.attributeExists(); + final QueryPredicate testPredicate = Post.LIKECOUNT.attributeExists(); expect(testPredicate.evaluate(post4), isFalse); expect(testPredicate.evaluate(post2), isTrue); }); test('Temporal type', () { - QueryPredicate testPredicate = Post.CREATED.lt(TemporalDateTime( - DateTime(2020, 01, 01, 12, 00), - )); + final QueryPredicate testPredicate = Post.CREATED.lt( + TemporalDateTime( + DateTime(2020, 01, 01, 12, 00), + ), + ); expect(testPredicate.evaluate(post1), isTrue); expect(testPredicate.evaluate(post2), isFalse); }); group('List type', () { test('contains', () { - QueryPredicate testPredicate = StringListTypeModel.VALUE.contains( + final QueryPredicate testPredicate = StringListTypeModel.VALUE.contains( 'abc', ); expect(testPredicate.evaluate(stringListTypeModel1), isTrue); @@ -364,5 +394,5 @@ void main() { }); }); - group("query by model identifier predicate", () {}); + group('query by model identifier predicate', () {}); } diff --git a/packages/amplify_datastore/test/query_sort_test.dart b/packages/amplify_datastore/test/query_sort_test.dart index 61362292b8..080171b5d6 100644 --- a/packages/amplify_datastore/test/query_sort_test.dart +++ b/packages/amplify_datastore/test/query_sort_test.dart @@ -16,24 +16,26 @@ void main() { tearDown(() {}); dynamic getJsonFromFile(String path) async { - path = 'resources/query_sort/' + path; - String jsonString = ''; + path = 'resources/query_sort/$path'; + var jsonString = ''; try { jsonString = await File(path).readAsString(); - } catch (e) { - jsonString = await File('test/' + path).readAsString(); + } on Exception { + jsonString = await File('test/$path').readAsString(); } return jsonDecode(jsonString); } test('when sorting by Id ascending', () async { - expect([Post.ID.ascending().serializeAsMap()], - await getJsonFromFile('sort_by_id_ascending.json')); + expect( + [Post.ID.ascending().serializeAsMap()], + await getJsonFromFile('sort_by_id_ascending.json'), + ); }); test('bad model id field naming backwards compatibility', () async { - QuerySortBy testPredicateWithBadIdFiledNaming = - QueryField(fieldName: 'blog.id').ascending(); + final testPredicateWithBadIdFiledNaming = + const QueryField(fieldName: 'blog.id').ascending(); expect( [testPredicateWithBadIdFiledNaming.serializeAsMap()], await getJsonFromFile('sort_by_id_ascending.json'), @@ -41,38 +43,41 @@ void main() { }); test('when sorting by Id ascending and then rating descending', () async { - expect([ - Post.ID.ascending().serializeAsMap(), - Post.RATING.descending().serializeAsMap() - ], await getJsonFromFile('multiple_sorting.json')); + expect( + [ + Post.ID.ascending().serializeAsMap(), + Post.RATING.descending().serializeAsMap(), + ], + await getJsonFromFile('multiple_sorting.json'), + ); }); group('compare', () { - Post post1 = Post( + final post1 = Post( id: '123e4567-e89b-12d3-a456-426614174000', title: 'post1', rating: 1, created: TemporalDateTime(DateTime(2020, 01, 01, 10, 30)), ); - Post post2 = Post( + final post2 = Post( id: '123e4567-e89b-12d3-a456-426614174001', title: 'post2', rating: 2, created: TemporalDateTime(DateTime(2020, 01, 01, 12, 30)), ); - Post post2Copy = post2.copyWith(); + final post2Copy = post2.copyWith(); - Post post3 = post1.copyWith(likeCount: 0); + final post3 = post1.copyWith(likeCount: 0); - Post post4 = post1.copyWith(likeCount: 1); + final post4 = post1.copyWith(likeCount: 1); - Post post4Copy = post4.copyWith(); + final post4Copy = post4.copyWith(); test('should compare ID fields', () { - QuerySortBy sortByAsc = Post.ID.ascending(); - QuerySortBy sortByDesc = Post.ID.descending(); + final sortByAsc = Post.ID.ascending(); + final sortByDesc = Post.ID.descending(); expect(sortByAsc.compare(post1, post2), -1); expect(sortByAsc.compare(post2, post1), 1); @@ -84,8 +89,8 @@ void main() { }); test('should compare int fields', () { - QuerySortBy sortByAsc = Post.RATING.ascending(); - QuerySortBy sortByDesc = Post.RATING.descending(); + final sortByAsc = Post.RATING.ascending(); + final sortByDesc = Post.RATING.descending(); expect(sortByAsc.compare(post1, post2), -1); expect(sortByAsc.compare(post2, post1), 1); @@ -97,8 +102,8 @@ void main() { }); test('should compare bool fields', () { - QuerySortBy sortByAsc = Post.LIKECOUNT.ascending(); - QuerySortBy sortByDesc = Post.LIKECOUNT.descending(); + final sortByAsc = Post.LIKECOUNT.ascending(); + final sortByDesc = Post.LIKECOUNT.descending(); expect(sortByAsc.compare(post3, post4), -1); expect(sortByAsc.compare(post4, post3), 1); @@ -110,8 +115,8 @@ void main() { }); test('should compare string fields', () { - QuerySortBy sortByAsc = Post.TITLE.ascending(); - QuerySortBy sortByDesc = Post.TITLE.descending(); + final sortByAsc = Post.TITLE.ascending(); + final sortByDesc = Post.TITLE.descending(); expect(sortByAsc.compare(post1, post2), -1); expect(sortByAsc.compare(post2, post1), 1); @@ -123,8 +128,8 @@ void main() { }); test('should compare date/time fields', () { - QuerySortBy sortByAsc = Post.CREATED.ascending(); - QuerySortBy sortByDesc = Post.CREATED.descending(); + final sortByAsc = Post.CREATED.ascending(); + final sortByDesc = Post.CREATED.descending(); expect(sortByAsc.compare(post1, post2), -1); expect(sortByAsc.compare(post2, post1), 1); @@ -136,8 +141,8 @@ void main() { }); test('should handle null values', () { - QuerySortBy sortByAsc = Post.LIKECOUNT.ascending(); - QuerySortBy sortByDesc = Post.LIKECOUNT.descending(); + final sortByAsc = Post.LIKECOUNT.ascending(); + final sortByDesc = Post.LIKECOUNT.descending(); expect(sortByAsc.compare(post1, post3), -1); expect(sortByAsc.compare(post1, post4), -1);