diff --git a/packages/amplify_core/android/src/main/kotlin/com/amazonaws/amplify/amplify_core/exception/ExceptionUtil.kt b/packages/amplify_core/android/src/main/kotlin/com/amazonaws/amplify/amplify_core/exception/ExceptionUtil.kt index a69a27981e..2ba69f7da6 100644 --- a/packages/amplify_core/android/src/main/kotlin/com/amazonaws/amplify/amplify_core/exception/ExceptionUtil.kt +++ b/packages/amplify_core/android/src/main/kotlin/com/amazonaws/amplify/amplify_core/exception/ExceptionUtil.kt @@ -60,6 +60,11 @@ class ExceptionUtil { } } + @JvmStatic + fun createSerializedUnrecognizedError(@NonNull e: Exception): Map { + return createSerializedError(ExceptionMessages.missingExceptionMessage, null, e.toString()) + } + @JvmStatic fun createSerializedError(message: String, recoverySuggestion: String?, cause: String?): Map { diff --git a/packages/amplify_datastore/android/src/main/kotlin/com/amazonaws/amplify/amplify_datastore/AmplifyDataStorePlugin.kt b/packages/amplify_datastore/android/src/main/kotlin/com/amazonaws/amplify/amplify_datastore/AmplifyDataStorePlugin.kt index 013f323aba..02c5da4772 100644 --- a/packages/amplify_datastore/android/src/main/kotlin/com/amazonaws/amplify/amplify_datastore/AmplifyDataStorePlugin.kt +++ b/packages/amplify_datastore/android/src/main/kotlin/com/amazonaws/amplify/amplify_datastore/AmplifyDataStorePlugin.kt @@ -14,11 +14,15 @@ */ package com.amazonaws.amplify.amplify_datastore + import android.os.Handler import android.os.Looper import androidx.annotation.NonNull import androidx.annotation.VisibleForTesting -import com.amazonaws.amplify.amplify_datastore.types.FlutterDataStoreFailureMessage +import com.amazonaws.amplify.amplify_core.exception.ExceptionMessages +import com.amazonaws.amplify.amplify_core.exception.ExceptionUtil.Companion.createSerializedError +import com.amazonaws.amplify.amplify_core.exception.ExceptionUtil.Companion.createSerializedUnrecognizedError +import com.amazonaws.amplify.amplify_core.exception.ExceptionUtil.Companion.postExceptionToFlutterChannel import com.amazonaws.amplify.amplify_datastore.types.model.FlutterModelSchema import com.amazonaws.amplify.amplify_datastore.types.model.FlutterSerializedModel import com.amazonaws.amplify.amplify_datastore.types.model.FlutterSubscriptionEvent @@ -40,7 +44,6 @@ import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result -import kotlin.collections.HashMap /** AmplifyDataStorePlugin */ class AmplifyDataStorePlugin : FlutterPlugin, MethodCallHandler { @@ -63,7 +66,8 @@ class AmplifyDataStorePlugin : FlutterPlugin, MethodCallHandler { } @VisibleForTesting - constructor(eventHandler: DataStoreObserveEventStreamHandler, hubEventHandler: DataStoreHubEventStreamHandler) { + constructor(eventHandler: DataStoreObserveEventStreamHandler, + hubEventHandler: DataStoreHubEventStreamHandler) { dataStoreObserveEventStreamHandler = eventHandler dataStoreHubEventStreamHandler = hubEventHandler } @@ -71,14 +75,14 @@ class AmplifyDataStorePlugin : FlutterPlugin, MethodCallHandler { override fun onAttachedToEngine( @NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { channel = MethodChannel(flutterPluginBinding.binaryMessenger, - "com.amazonaws.amplify/datastore") + "com.amazonaws.amplify/datastore") channel.setMethodCallHandler(this) eventchannel = EventChannel(flutterPluginBinding.binaryMessenger, - "com.amazonaws.amplify/datastore_observe_events") + "com.amazonaws.amplify/datastore_observe_events") eventchannel.setStreamHandler(dataStoreObserveEventStreamHandler) - + hubEventChannel = EventChannel(flutterPluginBinding.binaryMessenger, - "com.amazonaws.amplify/datastore_hub_events") + "com.amazonaws.amplify/datastore_hub_events") hubEventChannel.setStreamHandler(dataStoreHubEventStreamHandler) LOG.info("Initiated DataStore plugin") } @@ -86,14 +90,15 @@ class AmplifyDataStorePlugin : FlutterPlugin, MethodCallHandler { override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { var data: Map = HashMap() try { - if(call.arguments != null) { + if (call.arguments != null) { data = checkArguments(call.arguments) as HashMap } } catch (e: Exception) { - postFlutterError( - result, - FlutterDataStoreFailureMessage.ERROR_CASTING_INPUT_IN_PLATFORM_CODE.toString(), - createErrorMap(e)) + handler.post { + postExceptionToFlutterChannel(result, "DataStoreException", + createSerializedUnrecognizedError(e)) + } + return } when (call.method) { "query" -> onQuery(result, data) @@ -110,11 +115,13 @@ class AmplifyDataStorePlugin : FlutterPlugin, MethodCallHandler { if (!request.containsKey("modelSchemas") || !request.containsKey( "modelProviderVersion") || request["modelSchemas"] !is List<*>) { - postFlutterError( - flutterResult, - FlutterDataStoreFailureMessage.ERROR_CASTING_INPUT_IN_PLATFORM_CODE.toString(), - createErrorMap(Exception( - FlutterDataStoreFailureMessage.AMPLIFY_REQUEST_MALFORMED.toString()))) + handler.post { + postExceptionToFlutterChannel(flutterResult, "DataStoreException", + createSerializedError(ExceptionMessages.missingExceptionMessage, + ExceptionMessages.missingRecoverySuggestion, + "Received invalid request from Dart, modelSchemas and/or modelProviderVersion" + + " are not available. Request: " + request.toString())) + } return } @@ -145,17 +152,11 @@ class AmplifyDataStorePlugin : FlutterPlugin, MethodCallHandler { try { modelName = request["modelName"] as String queryOptions = QueryOptionsBuilder.fromSerializedMap(request) - } catch (e: ClassCastException) { - postFlutterError( - flutterResult, - FlutterDataStoreFailureMessage.ERROR_CASTING_INPUT_IN_PLATFORM_CODE.toString(), - createErrorMap(e)) - return } catch (e: Exception) { - postFlutterError( - flutterResult, - FlutterDataStoreFailureMessage.AMPLIFY_REQUEST_MALFORMED.toString(), - createErrorMap(e)) + handler.post { + postExceptionToFlutterChannel(flutterResult, "DataStoreException", + createSerializedUnrecognizedError(e)) + } return } @@ -172,24 +173,19 @@ class AmplifyDataStorePlugin : FlutterPlugin, MethodCallHandler { LOG.debug("Number of items received " + results.size) handler.post { flutterResult.success(results) } - } catch (e: ClassCastException) { - postFlutterError( - flutterResult, - FlutterDataStoreFailureMessage.ERROR_CASTING_INPUT_IN_PLATFORM_CODE.toString(), - createErrorMap(e)) } catch (e: Exception) { - postFlutterError( - flutterResult, - FlutterDataStoreFailureMessage.AMPLIFY_REQUEST_MALFORMED.toString(), - createErrorMap(e)) + handler.post { + postExceptionToFlutterChannel(flutterResult, "DataStoreException", + createSerializedUnrecognizedError(e)) + } } }, { LOG.error("Query operation failed.", it) - postFlutterError( - flutterResult, - FlutterDataStoreFailureMessage.AMPLIFY_DATASTORE_QUERY_FAILED.toString(), - createErrorMap(it)) + handler.post { + postExceptionToFlutterChannel(flutterResult, "DataStoreException", + createSerializedError(it)) + } } ) } @@ -197,16 +193,17 @@ class AmplifyDataStorePlugin : FlutterPlugin, MethodCallHandler { @VisibleForTesting fun onDelete(flutterResult: Result, request: Map) { val modelName: String - val serializedModelData: Map + val serializedModelData: Map try { modelName = request["modelName"] as String - serializedModelData = deserializeNestedModels(request["serializedModel"].safeCastToMap()!!) + serializedModelData = + deserializeNestedModels(request["serializedModel"].safeCastToMap()!!) } catch (e: Exception) { - postFlutterError( - flutterResult, - FlutterDataStoreFailureMessage.AMPLIFY_REQUEST_MALFORMED.toString(), - createErrorMap(e)) + handler.post { + postExceptionToFlutterChannel(flutterResult, "DataStoreException", + createSerializedUnrecognizedError(e)) + } return } @@ -219,38 +216,39 @@ class AmplifyDataStorePlugin : FlutterPlugin, MethodCallHandler { .build() plugin.delete( - instance, - { - LOG.info("Deleted item: " + it.item().toString()) - handler.post { flutterResult.success(null) } - }, - { - LOG.error("Delete operation failed.", it) - if (it is DataStoreException && it.localizedMessage == "Wanted to delete one row, but deleted 0 rows.") { + instance, + { + LOG.info("Deleted item: " + it.item().toString()) handler.post { flutterResult.success(null) } - } else { - postFlutterError( - flutterResult, - FlutterDataStoreFailureMessage.AMPLIFY_DATASTORE_DELETE_FAILED.toString(), - createErrorMap(it)) + }, + { + LOG.error("Delete operation failed.", it) + if (it.localizedMessage == "Wanted to delete one row, but deleted 0 rows.") { + handler.post { flutterResult.success(null) } + } else { + handler.post { + postExceptionToFlutterChannel(flutterResult, "DataStoreException", + createSerializedError(it)) + } + } } - } ) } @VisibleForTesting fun onSave(flutterResult: Result, request: Map) { val modelName: String - val serializedModelData: Map + val serializedModelData: Map try { modelName = request["modelName"] as String - serializedModelData = deserializeNestedModels(request["serializedModel"].safeCastToMap()!!) + serializedModelData = + deserializeNestedModels(request["serializedModel"].safeCastToMap()!!) } catch (e: Exception) { - postFlutterError( - flutterResult, - FlutterDataStoreFailureMessage.AMPLIFY_REQUEST_MALFORMED.toString(), - createErrorMap(e)) + handler.post { + postExceptionToFlutterChannel(flutterResult, "DataStoreException", + createSerializedUnrecognizedError(e)) + } return } @@ -267,20 +265,20 @@ class AmplifyDataStorePlugin : FlutterPlugin, MethodCallHandler { plugin.save( serializedModel, predicate, - Consumer{ + Consumer { LOG.info("Saved item: " + it.item().toString()) handler.post { flutterResult.success(null) } }, Consumer { LOG.error("Save operation failed", it) - postFlutterError( - flutterResult, - FlutterDataStoreFailureMessage.AMPLIFY_DATASTORE_SAVE_FAILED.toString(), - createErrorMap(it)) + handler.post { + postExceptionToFlutterChannel(flutterResult, "DataStoreException", + createSerializedError(it)) + } } ) } - + fun onClear(flutterResult: Result) { val plugin = Amplify.DataStore.getPlugin("awsDataStorePlugin") as AWSDataStorePlugin @@ -291,10 +289,10 @@ class AmplifyDataStorePlugin : FlutterPlugin, MethodCallHandler { }, { LOG.error("Failed to clear store with error: ", it) - postFlutterError( - flutterResult, - FlutterDataStoreFailureMessage.AMPLIFY_DATASTORE_CLEAR_FAILED.toString(), - createErrorMap(it)) + handler.post { + postExceptionToFlutterChannel(flutterResult, "DataStoreException", + createSerializedError(it)) + } } ) } @@ -310,16 +308,15 @@ class AmplifyDataStorePlugin : FlutterPlugin, MethodCallHandler { { event -> LOG.debug("Received event: $event") if (event.item() is SerializedModel) { - dataStoreObserveEventStreamHandler?.sendEvent(FlutterSubscriptionEvent( + dataStoreObserveEventStreamHandler.sendEvent(FlutterSubscriptionEvent( serializedModel = event.item() as SerializedModel, eventType = event.type().name.toLowerCase()).toMap()) } }, { failure: DataStoreException -> LOG.error("Received an error", failure) - dataStoreObserveEventStreamHandler?.error("AmplifyException", - FlutterDataStoreFailureMessage.AMPLIFY_DATASTORE_OBSERVE_EVENT_FAILURE.toString(), - createErrorMap(failure)) + dataStoreObserveEventStreamHandler.error("DataStoreException", + createSerializedError(failure)) }, { LOG.info("Observation complete.") } ) @@ -337,40 +334,15 @@ class AmplifyDataStorePlugin : FlutterPlugin, MethodCallHandler { return args.safeCastToMap()!! } - protected fun createErrorMap(@NonNull error: Exception): Map { - var errorMap = HashMap() - - var localizedError = "" - var recoverySuggestion = "" - if (error is DataStoreException) { - recoverySuggestion = error.recoverySuggestion - } - if (error.localizedMessage != null) { - localizedError = error.localizedMessage - } - errorMap.put("PLATFORM_EXCEPTIONS", mapOf( - "platform" to "Android", - "localizedErrorMessage" to localizedError, - "recoverySuggestion" to recoverySuggestion, - "errorString" to error.toString() - )) - return errorMap - } - - private fun postFlutterError(flutterResult: Result, msg: String, errorMap: Map) { - handler.post { flutterResult.error("AmplifyException", msg, errorMap) } - } - @VisibleForTesting fun deserializeNestedModels(serializedModelData: Map): Map { return serializedModelData.mapValues { - if(it.value is Map<*, *>) { + if (it.value is Map<*, *>) { SerializedModel.builder() .serializedData(deserializeNestedModels(it.value as HashMap)) .modelSchema(null) .build() as Any - } - else + } else it.value } } diff --git a/packages/amplify_datastore/android/src/main/kotlin/com/amazonaws/amplify/amplify_datastore/DataStoreObserveEventStreamHandler.kt b/packages/amplify_datastore/android/src/main/kotlin/com/amazonaws/amplify/amplify_datastore/DataStoreObserveEventStreamHandler.kt index 7daf219038..6f16cc5370 100644 --- a/packages/amplify_datastore/android/src/main/kotlin/com/amazonaws/amplify/amplify_datastore/DataStoreObserveEventStreamHandler.kt +++ b/packages/amplify_datastore/android/src/main/kotlin/com/amazonaws/amplify/amplify_datastore/DataStoreObserveEventStreamHandler.kt @@ -17,6 +17,7 @@ package com.amazonaws.amplify.amplify_datastore import android.os.Handler import android.os.Looper +import com.amazonaws.amplify.amplify_core.exception.ExceptionMessages import com.amplifyframework.datastore.appsync.SerializedModel import io.flutter.plugin.common.EventChannel @@ -35,8 +36,8 @@ class DataStoreObserveEventStreamHandler : EventChannel.StreamHandler { eventSink?.endOfStream() } - fun error(errorCode: String, localizedMessage: String?, details: Any?) { - handler.post { eventSink?.error(errorCode, localizedMessage, details) } + fun error(errorCode: String, details: Any?) { + handler.post { eventSink?.error(errorCode, ExceptionMessages.defaultFallbackExceptionMessage, details) } } override fun onCancel(p0: Any?) { diff --git a/packages/amplify_datastore/android/src/main/kotlin/com/amazonaws/amplify/amplify_datastore/types/FlutterDataStoreFailureMessage.kt b/packages/amplify_datastore/android/src/main/kotlin/com/amazonaws/amplify/amplify_datastore/types/FlutterDataStoreFailureMessage.kt deleted file mode 100644 index 3b6083dd42..0000000000 --- a/packages/amplify_datastore/android/src/main/kotlin/com/amazonaws/amplify/amplify_datastore/types/FlutterDataStoreFailureMessage.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package com.amazonaws.amplify.amplify_datastore.types - -enum class FlutterDataStoreFailureMessage { - ERROR_CASTING_INPUT_IN_PLATFORM_CODE, - AMPLIFY_REQUEST_MALFORMED, - AMPLIFY_DATASTORE_QUERY_FAILED, - AMPLIFY_DATASTORE_DELETE_FAILED, - AMPLIFY_DATASTORE_SAVE_FAILED, - AMPLIFY_DATASTORE_CLEAR_FAILED, - AMPLIFY_DATASTORE_OBSERVE_EVENT_FAILURE, - AMPLIFY_DATASTORE_HUB_EVENT_FAILURE -} diff --git a/packages/amplify_datastore/android/src/test/kotlin/com/amazonaws/amplify/amplify_datastore/AmplifyDataStorePluginTest.kt b/packages/amplify_datastore/android/src/test/kotlin/com/amazonaws/amplify/amplify_datastore/AmplifyDataStorePluginTest.kt index e5baa189af..61397f7c49 100644 --- a/packages/amplify_datastore/android/src/test/kotlin/com/amazonaws/amplify/amplify_datastore/AmplifyDataStorePluginTest.kt +++ b/packages/amplify_datastore/android/src/test/kotlin/com/amazonaws/amplify/amplify_datastore/AmplifyDataStorePluginTest.kt @@ -15,9 +15,8 @@ package com.amazonaws.amplify.amplify_datastore -import com.amazonaws.amplify.amplify_datastore.types.FlutterDataStoreFailureMessage +import com.amazonaws.amplify.amplify_core.exception.ExceptionMessages import com.amazonaws.amplify.amplify_datastore.types.model.FlutterSerializedModel -import com.amazonaws.amplify.amplify_datastore.types.query.QueryPredicateBuilder import com.amplifyframework.core.Action import com.amplifyframework.core.Amplify import com.amplifyframework.core.Consumer @@ -32,23 +31,28 @@ import com.amplifyframework.core.model.query.predicate.QueryPredicate import com.amplifyframework.core.model.query.predicate.QueryPredicateOperation.not import com.amplifyframework.core.model.query.predicate.QueryPredicates import com.amplifyframework.core.model.temporal.Temporal -import com.amplifyframework.datastore.* +import com.amplifyframework.datastore.AWSDataStorePlugin +import com.amplifyframework.datastore.DataStoreCategory +import com.amplifyframework.datastore.DataStoreException +import com.amplifyframework.datastore.DataStoreItemChange import com.amplifyframework.datastore.appsync.SerializedModel -import com.amplifyframework.hub.AWSHubPlugin import io.flutter.plugin.common.MethodChannel +import java.lang.reflect.Field +import java.lang.reflect.Modifier import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyString -import org.mockito.ArgumentMatchers.matches -import org.mockito.Mockito.* +import org.mockito.Mockito.`when` +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.mock +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoInteractions import org.mockito.invocation.InvocationOnMock import org.robolectric.RobolectricTestRunner -import java.lang.reflect.Field -import java.lang.reflect.Modifier @RunWith(RobolectricTestRunner::class) class AmplifyDataStorePluginTest { @@ -63,6 +67,8 @@ class AmplifyDataStorePluginTest { mock(DataStoreObserveEventStreamHandler::class.java) private val mockHubHandler: DataStoreHubEventStreamHandler = mock(DataStoreHubEventStreamHandler::class.java) + private val dataStoreException = + DataStoreException("Some useful exception message", "Some useful recovery message") @Before fun setup() { @@ -83,7 +89,8 @@ class AmplifyDataStorePluginTest { .serializedData( mapOf("id" to "43036c6b-8044-4309-bddc-262b6c686026", "title" to "Title 2", - "created" to Temporal.DateTime("2020-02-20T20:20:20-08:00"))) + "created" to Temporal.DateTime( + "2020-02-20T20:20:20-08:00"))) .modelSchema(modelSchema) .build() ) @@ -94,7 +101,7 @@ class AmplifyDataStorePluginTest { @Test fun test_query_success_result() { doAnswer { invocation: InvocationOnMock -> - assertEquals("Post",invocation.arguments[0]) + assertEquals("Post", invocation.arguments[0]) assertEquals(Where.matchesAll(), invocation.arguments[1]) (invocation.arguments[2] as Consumer>).accept( amplifySuccessResults.iterator()) @@ -114,10 +121,11 @@ class AmplifyDataStorePluginTest { @Test fun test_query_with_predicates_success_zero_result() { - val queryOptions = Where.matches(field("post.id").eq("123").or(field("rating").ge(4).and(not( - field("created").eq("2020-02-20T20:20:20-08:00"))))) - .paginated(Page.startingAt(2).withLimit(8)) - .sorted(field("post.id").ascending(), field("created").descending()) + val queryOptions = + Where.matches(field("post.id").eq("123").or(field("rating").ge(4).and(not( + field("created").eq("2020-02-20T20:20:20-08:00"))))) + .paginated(Page.startingAt(2).withLimit(8)) + .sorted(field("post.id").ascending(), field("created").descending()) doAnswer { invocation: InvocationOnMock -> assertEquals("Post", invocation.arguments[0]) @@ -139,14 +147,12 @@ class AmplifyDataStorePluginTest { @Test fun test_query_api_error() { - val dataStoreException = DataStoreException("AmplifyException", DataStoreException.REPORT_BUG_TO_AWS_SUGGESTION) - val testRequest: HashMap = (readMapFromFile("query_api", "request/only_model_name.json", HashMap::class.java) as HashMap) doAnswer { invocation: InvocationOnMock -> - assertEquals("Post",invocation.arguments[0]) + assertEquals("Post", invocation.arguments[0]) assertEquals(Where.matchesAll(), invocation.arguments[1]) (invocation.arguments[3] as Consumer).accept( dataStoreException) @@ -160,14 +166,11 @@ class AmplifyDataStorePluginTest { flutterPlugin.onQuery(mockResult, testRequest) verify(mockResult, times(1)).error( - "AmplifyException", - FlutterDataStoreFailureMessage.AMPLIFY_DATASTORE_QUERY_FAILED.toString(), - mapOf("PLATFORM_EXCEPTIONS" to - mapOf( - "platform" to "Android", - "localizedErrorMessage" to "AmplifyException", - "recoverySuggestion" to DataStoreException.REPORT_BUG_TO_AWS_SUGGESTION, - "errorString" to dataStoreException.toString())) + "DataStoreException", + ExceptionMessages.defaultFallbackExceptionMessage, + mapOf( + "message" to "Some useful exception message", + "recoverySuggestion" to "Some useful recovery message") ) } @@ -179,10 +182,12 @@ class AmplifyDataStorePluginTest { verifyNoInteractions(mockAmplifyDataStorePlugin) verify(mockResult, times(1)).error( - matches("AmplifyException"), - matches(FlutterDataStoreFailureMessage.ERROR_CASTING_INPUT_IN_PLATFORM_CODE.toString()), - //TODO: Improve MalformedRequestError handling to be more specific - any() + "DataStoreException", + ExceptionMessages.defaultFallbackExceptionMessage, + mapOf( + "message" to ExceptionMessages.missingExceptionMessage, + "recoverySuggestion" to ExceptionMessages.missingRecoverySuggestion, + "underlyingException" to "kotlin.TypeCastException: null cannot be cast to non-null type kotlin.String") ) } @@ -192,7 +197,8 @@ class AmplifyDataStorePluginTest { "request/instance_no_predicate.json", HashMap::class.java) as HashMap) - val serializedModelData: HashMap = testRequest["serializedModel"] as HashMap + val serializedModelData: HashMap = + testRequest["serializedModel"] as HashMap val serializedModel = SerializedModel.builder() .serializedData(serializedModelData) @@ -224,13 +230,13 @@ class AmplifyDataStorePluginTest { @Test fun test_delete_api_error() { - val dataStoreException = DataStoreException("AmplifyException", DataStoreException.REPORT_BUG_TO_AWS_SUGGESTION) val testRequest: HashMap = (readMapFromFile("delete_api", "request/instance_no_predicate.json", HashMap::class.java) as HashMap) - val serializedModelData: HashMap = testRequest["serializedModel"] as HashMap + val serializedModelData: HashMap = + testRequest["serializedModel"] as HashMap val serializedModel = SerializedModel.builder() .serializedData(serializedModelData) @@ -238,10 +244,10 @@ class AmplifyDataStorePluginTest { .build() doAnswer { invocation: InvocationOnMock -> - assertEquals(serializedModel, invocation.arguments[0]) - (invocation.arguments[2] as Consumer).accept( - dataStoreException) - null as Void? + assertEquals(serializedModel, invocation.arguments[0]) + (invocation.arguments[2] as Consumer).accept( + dataStoreException) + null as Void? }.`when`(mockAmplifyDataStorePlugin).delete( any(), any< @@ -251,14 +257,11 @@ class AmplifyDataStorePluginTest { flutterPlugin.onDelete(mockResult, testRequest) verify(mockResult, times(1)).error( - "AmplifyException", - FlutterDataStoreFailureMessage.AMPLIFY_DATASTORE_DELETE_FAILED.toString(), - mapOf("PLATFORM_EXCEPTIONS" to - mapOf( - "platform" to "Android", - "localizedErrorMessage" to "AmplifyException", - "recoverySuggestion" to DataStoreException.REPORT_BUG_TO_AWS_SUGGESTION, - "errorString" to dataStoreException.toString())) + "DataStoreException", + ExceptionMessages.defaultFallbackExceptionMessage, + mapOf( + "message" to "Some useful exception message", + "recoverySuggestion" to "Some useful recovery message") ) } @@ -270,10 +273,12 @@ class AmplifyDataStorePluginTest { verifyNoInteractions(mockAmplifyDataStorePlugin) verify(mockResult, times(1)).error( - matches("AmplifyException"), - matches(FlutterDataStoreFailureMessage.AMPLIFY_REQUEST_MALFORMED.toString()), - //TODO: Improve MalformedRequestError handling to be more specific - any() + "DataStoreException", + ExceptionMessages.defaultFallbackExceptionMessage, + mapOf( + "message" to ExceptionMessages.missingExceptionMessage, + "recoverySuggestion" to ExceptionMessages.missingRecoverySuggestion, + "underlyingException" to "kotlin.TypeCastException: null cannot be cast to non-null type kotlin.String") ) } @@ -283,7 +288,8 @@ class AmplifyDataStorePluginTest { "request/instance_without_predicate.json", HashMap::class.java) as HashMap) - val serializedModelData: HashMap = testRequest["serializedModel"] as HashMap + val serializedModelData: HashMap = + testRequest["serializedModel"] as HashMap val serializedModel = SerializedModel.builder() .serializedData(serializedModelData) @@ -317,13 +323,13 @@ class AmplifyDataStorePluginTest { @Test fun test_save_api_error() { - val dataStoreException = DataStoreException("AmplifyException", DataStoreException.REPORT_BUG_TO_AWS_SUGGESTION) val testRequest: HashMap = (readMapFromFile("save_api", "request/instance_without_predicate.json", HashMap::class.java) as HashMap) - val serializedModelData: HashMap = testRequest["serializedModel"] as HashMap + val serializedModelData: HashMap = + testRequest["serializedModel"] as HashMap val serializedModel = SerializedModel.builder() .serializedData(serializedModelData) @@ -345,14 +351,11 @@ class AmplifyDataStorePluginTest { flutterPlugin.onSave(mockResult, testRequest) verify(mockResult, times(1)).error( - "AmplifyException", - FlutterDataStoreFailureMessage.AMPLIFY_DATASTORE_SAVE_FAILED.toString(), - mapOf("PLATFORM_EXCEPTIONS" to - mapOf( - "platform" to "Android", - "localizedErrorMessage" to "AmplifyException", - "recoverySuggestion" to DataStoreException.REPORT_BUG_TO_AWS_SUGGESTION, - "errorString" to dataStoreException.toString())) + "DataStoreException", + ExceptionMessages.defaultFallbackExceptionMessage, + mapOf( + "message" to "Some useful exception message", + "recoverySuggestion" to "Some useful recovery message") ) } @@ -364,22 +367,25 @@ class AmplifyDataStorePluginTest { verifyNoInteractions(mockAmplifyDataStorePlugin) verify(mockResult, times(1)).error( - matches("AmplifyException"), - matches(FlutterDataStoreFailureMessage.AMPLIFY_REQUEST_MALFORMED.toString()), - //TODO: Improve MalformedRequestError handling to be more specific - any() + "DataStoreException", + ExceptionMessages.defaultFallbackExceptionMessage, + mapOf( + "message" to ExceptionMessages.missingExceptionMessage, + "recoverySuggestion" to ExceptionMessages.missingRecoverySuggestion, + "underlyingException" to "kotlin.TypeCastException: null cannot be cast to non-null type kotlin.String") ) } @Test fun test_observe_success_event() { - flutterPlugin = AmplifyDataStorePlugin(eventHandler = mockStreamHandler, hubEventHandler = mockHubHandler) + flutterPlugin = AmplifyDataStorePlugin(eventHandler = mockStreamHandler, + hubEventHandler = mockHubHandler) val eventData: HashMap = (readMapFromFile("observe_api", - "post_type_success_event.json", - HashMap::class.java) as HashMap) + "post_type_success_event.json", + HashMap::class.java) as HashMap) val modelData = mapOf("id" to "43036c6b-8044-4309-bddc-262b6c686026", - "title" to "Title 2", - "created" to Temporal.DateTime("2020-02-20T20:20:20-08:00")) + "title" to "Title 2", + "created" to Temporal.DateTime("2020-02-20T20:20:20-08:00")) val instance = SerializedModel.builder() .serializedData(modelData) .modelSchema(modelSchema) @@ -409,9 +415,8 @@ class AmplifyDataStorePluginTest { @Test fun test_observe_error_event() { - flutterPlugin = AmplifyDataStorePlugin(eventHandler = mockStreamHandler, hubEventHandler = mockHubHandler) - val dataStoreException = DataStoreException("AmplifyException", - DataStoreException.REPORT_BUG_TO_AWS_SUGGESTION) + flutterPlugin = AmplifyDataStorePlugin(eventHandler = mockStreamHandler, + hubEventHandler = mockHubHandler) doAnswer { invocation: InvocationOnMock -> (invocation.arguments[2] as Consumer).accept( @@ -426,15 +431,10 @@ class AmplifyDataStorePluginTest { verify(mockResult, times(1)).success(true) verify(mockStreamHandler, times(1)).error( - "AmplifyException", - FlutterDataStoreFailureMessage.AMPLIFY_DATASTORE_OBSERVE_EVENT_FAILURE.toString(), + "DataStoreException", mapOf( - "PLATFORM_EXCEPTIONS" to - mapOf( - "platform" to "Android", - "localizedErrorMessage" to "AmplifyException", - "recoverySuggestion" to DataStoreException.REPORT_BUG_TO_AWS_SUGGESTION, - "errorString" to dataStoreException.toString())) + "message" to "Some useful exception message", + "recoverySuggestion" to "Some useful recovery message") ) } @@ -443,7 +443,8 @@ class AmplifyDataStorePluginTest { doAnswer { invocation: InvocationOnMock -> (invocation.arguments[0] as Action).call() null as Void? - }.`when`(mockAmplifyDataStorePlugin).clear(any(), any>()) + }.`when`(mockAmplifyDataStorePlugin) + .clear(any(), any>()) flutterPlugin.onClear(mockResult) @@ -452,25 +453,22 @@ class AmplifyDataStorePluginTest { @Test fun test_clear_error() { - var dataStoreException = DataStoreException("AmplifyException", DataStoreException.REPORT_BUG_TO_AWS_SUGGESTION) doAnswer { invocation: InvocationOnMock -> (invocation.arguments[1] as Consumer).accept( dataStoreException) null as Void? - }.`when`(mockAmplifyDataStorePlugin).clear(any(), any>()) + }.`when`(mockAmplifyDataStorePlugin) + .clear(any(), any>()) flutterPlugin.onClear(mockResult) verify(mockResult, times(1)).error( - "AmplifyException", - FlutterDataStoreFailureMessage.AMPLIFY_DATASTORE_CLEAR_FAILED.toString(), - mapOf("PLATFORM_EXCEPTIONS" to - mapOf( - "platform" to "Android", - "localizedErrorMessage" to "AmplifyException", - "recoverySuggestion" to DataStoreException.REPORT_BUG_TO_AWS_SUGGESTION, - "errorString" to dataStoreException.toString())) + "DataStoreException", + ExceptionMessages.defaultFallbackExceptionMessage, + mapOf( + "message" to "Some useful exception message", + "recoverySuggestion" to "Some useful recovery message") ) } diff --git a/packages/amplify_datastore/example/ios/unit_tests/DataStorePluginUnitTests.swift b/packages/amplify_datastore/example/ios/unit_tests/DataStorePluginUnitTests.swift index b673ede0bc..6d2ab4a8e5 100644 --- a/packages/amplify_datastore/example/ios/unit_tests/DataStorePluginUnitTests.swift +++ b/packages/amplify_datastore/example/ios/unit_tests/DataStorePluginUnitTests.swift @@ -16,6 +16,7 @@ import XCTest import Amplify import Combine +import amplify_core @testable import AmplifyPlugins @testable import amplify_datastore @@ -68,7 +69,7 @@ class DataStorePluginUnitTests: XCTestCase { XCTAssertEqual( QueryPaginationInput.page(2, limit: 8), paginationInput) - + // Return from the mock completion(.success(amplifySuccessResults as! [M])) } @@ -99,7 +100,7 @@ class DataStorePluginUnitTests: XCTestCase { }) } - func test_query_failure_called_with_no_query_parameters() throws { + func test_query_called_with_no_query_parameters_failed_with_invalid_condition() throws { class MockDataStoreBridge: DataStoreBridge { override func onQuery(_ modelType: M.Type, @@ -127,13 +128,11 @@ class DataStorePluginUnitTests: XCTestCase { flutterResult: { (results) -> Void in if let exception = results as? FlutterError { // Result #1 (Any/AnyObject is not equatable so we iterate over fields we know) - XCTAssertEqual("AmplifyException", exception.code) - XCTAssertEqual(FlutterDataStoreErrorMessage.QUERY_FAILED.rawValue, exception.message) + XCTAssertEqual("DataStoreException", exception.code) + XCTAssertEqual(ErrorMessages.defaultFallbackErrorMessage, exception.message) let errorMap: [String: Any] = exception.details as! [String : Any] - XCTAssertEqual("test error", errorMap["invalidCondition"] as? String) - XCTAssertEqual( - ["platform": "iOS", "localizedErrorMessage": "test error", "recoverySuggestion": "test recovery suggestion"], - errorMap["PLATFORM_EXCEPTIONS"] as? [String: String]) + XCTAssertEqual("test error", errorMap["message"] as? String) + XCTAssertEqual("test recovery suggestion", errorMap["recoverySuggestion"] as? String) } else { XCTFail() } @@ -144,9 +143,9 @@ class DataStorePluginUnitTests: XCTestCase { class MockDataStoreBridge: DataStoreBridge { override func onDelete( - serializedModel: FlutterSerializedModel, - modelSchema: ModelSchema, - completion: @escaping DataStoreCallback + serializedModel: FlutterSerializedModel, + modelSchema: ModelSchema, + completion: @escaping DataStoreCallback ) throws { // Validations that we called the native library correctly XCTAssertEqual(testSchema.name, modelSchema.name) @@ -167,17 +166,17 @@ class DataStorePluginUnitTests: XCTestCase { }) } - func test_delete_error_result() throws { - + func test_delete_failed_with_invalid_internal_operation() throws { + class MockDataStoreBridge: DataStoreBridge { override func onDelete( - serializedModel: FlutterSerializedModel, - modelSchema: ModelSchema, - completion: @escaping DataStoreCallback) throws { + serializedModel: FlutterSerializedModel, + modelSchema: ModelSchema, + completion: @escaping DataStoreCallback) throws { // Validations that we called the native library correctly XCTAssertEqual(testSchema.name, modelSchema.name) // Return from the mock - completion(.failure(causedBy: DataStoreError.unknown("test error", "test recovery suggestion", nil))) + completion(.failure(causedBy: DataStoreError.internalOperation("test error", "test recovery suggestion", nil))) } } @@ -188,13 +187,11 @@ class DataStorePluginUnitTests: XCTestCase { args: try readJsonMap(filePath: "instance_no_predicate") as [String: Any], flutterResult: { (results) -> Void in if let exception = results as? FlutterError { - XCTAssertEqual("AmplifyException", exception.code) - XCTAssertEqual(FlutterDataStoreErrorMessage.DELETE_FAILED.rawValue, exception.message) + XCTAssertEqual("DataStoreException", exception.code) + XCTAssertEqual(ErrorMessages.defaultFallbackErrorMessage, exception.message) let errorMap: [String: Any] = exception.details as! [String : Any] - XCTAssertEqual("test error", errorMap["unknown"] as? String) - XCTAssertEqual( - ["platform": "iOS", "localizedErrorMessage": "test error", "recoverySuggestion": "test recovery suggestion"], - errorMap["PLATFORM_EXCEPTIONS"] as? [String: String]) + XCTAssertEqual("test error", errorMap["message"] as? String) + XCTAssertEqual("test recovery suggestion", errorMap["recoverySuggestion"] as? String) } else { XCTFail() } @@ -303,15 +300,11 @@ class DataStorePluginUnitTests: XCTestCase { class MockStreamHandler: DataStoreObserveEventStreamHandler { override func sendError(flutterError: FlutterError) { eventSentExp?.fulfill() - print(flutterError) - XCTAssertEqual("AmplifyException", flutterError.code) - XCTAssertEqual("AMPLIFY_DATASTORE_OBSERVE_EVENT_FAILURE", flutterError.message) + XCTAssertEqual("DataStoreException", flutterError.code) + XCTAssertEqual(ErrorMessages.defaultFallbackErrorMessage, flutterError.message) let errorMap: [String: Any] = flutterError.details as! [String : Any] - XCTAssertEqual("This is test error", errorMap["unknown"] as? String) - XCTAssertEqual( - ["platform": "iOS", "localizedErrorMessage": "This is test error", - "recoverySuggestion": "And a test recovery suggestion"], - errorMap["PLATFORM_EXCEPTIONS"] as? [String: String]) + XCTAssertEqual("This is test error", errorMap["message"] as? String) + XCTAssertEqual("And a test recovery suggestion", errorMap["recoverySuggestion"] as? String) } } @@ -351,10 +344,10 @@ class DataStorePluginUnitTests: XCTestCase { pluginUnderTest.onClear( flutterResult: {(result) in XCTAssertNil(result) - }) + }) } - func test_clear_failure() throws { + func test_clear_failure_with_unknown_error() throws { class MockDataStoreBridge: DataStoreBridge { override func onClear( @@ -370,17 +363,15 @@ class DataStorePluginUnitTests: XCTestCase { pluginUnderTest.onClear( flutterResult: { (result) -> Void in if let exception = result as? FlutterError { - XCTAssertEqual("AmplifyException", exception.code) - XCTAssertEqual(FlutterDataStoreErrorMessage.CLEAR_FAILED.rawValue, exception.message) + XCTAssertEqual("DataStoreException", exception.code) + XCTAssertEqual(ErrorMessages.defaultFallbackErrorMessage, exception.message) let errorMap: [String: Any] = exception.details as! [String : Any] - XCTAssertEqual("test error", errorMap["unknown"] as? String) - XCTAssertEqual( - ["platform": "iOS", "localizedErrorMessage": "test error", "recoverySuggestion": "test recovery suggestion"], - errorMap["PLATFORM_EXCEPTIONS"] as? [String: String]) + XCTAssertEqual("test error", errorMap["message"] as? String) + XCTAssertEqual("test recovery suggestion", errorMap["recoverySuggestion"] as? String) } else { XCTFail() } - }) + }) } func test_save_success_without_predicate() throws { @@ -411,15 +402,15 @@ class DataStorePluginUnitTests: XCTestCase { }) } - func test_save_with_api_error() throws { + func test_save_failed_with_unkown_error() throws { let testArgs = try readJsonMap(filePath: "instance_without_predicate") as [String: Any] class MockDataStoreBridge: DataStoreBridge { override func onSave( - serializedModel: M, - modelSchema: ModelSchema, - when predicate: QueryPredicate? = nil, - completion: @escaping DataStoreCallback) throws { + serializedModel: M, + modelSchema: ModelSchema, + when predicate: QueryPredicate? = nil, + completion: @escaping DataStoreCallback) throws { // Validations that we called the native library correctly XCTAssertEqual("9fc5fab4-37ff-4566-97e5-19c5d58a4c22", serializedModel.id) XCTAssertEqual(testSchema.name, modelSchema.name) @@ -436,13 +427,11 @@ class DataStorePluginUnitTests: XCTestCase { args: testArgs, flutterResult: { (results) -> Void in if let exception = results as? FlutterError { - XCTAssertEqual("AmplifyException", exception.code) - XCTAssertEqual(FlutterDataStoreErrorMessage.SAVE_FAILED.rawValue, exception.message) + XCTAssertEqual("DataStoreException", exception.code) + XCTAssertEqual(ErrorMessages.defaultFallbackErrorMessage, exception.message) let errorMap: [String: Any] = exception.details as! [String : Any] - XCTAssertEqual("test error", errorMap["unknown"] as? String) - XCTAssertEqual( - ["platform": "iOS", "localizedErrorMessage": "test error", "recoverySuggestion": "test recovery suggestion"], - errorMap["PLATFORM_EXCEPTIONS"] as? [String: String]) + XCTAssertEqual("test error", errorMap["message"] as? String) + XCTAssertEqual("test recovery suggestion", errorMap["recoverySuggestion"] as? String) } else { XCTFail() } @@ -451,35 +440,19 @@ class DataStorePluginUnitTests: XCTestCase { func test_save_with_malformed_error() throws { - class MockDataStoreBridge: DataStoreBridge { - override func onSave( - serializedModel: M, - modelSchema: ModelSchema, - when predicate: QueryPredicate? = nil, - completion: @escaping DataStoreCallback) throws { - // Validations that we called the native library correctly - XCTAssertEqual("9fc5fab4-37ff-4566-97e5-19c5d58a4c22", serializedModel.id) - XCTAssertEqual(testSchema.name, modelSchema.name) - XCTAssertNil(predicate) - // Return from the mock - completion(.success(serializedModel)) - } - } - - let dataStoreBridge: MockDataStoreBridge = MockDataStoreBridge() - pluginUnderTest = SwiftAmplifyDataStorePlugin(bridge: dataStoreBridge, flutterModelRegistration: flutterModelSchemaRegistration) + pluginUnderTest = SwiftAmplifyDataStorePlugin( + bridge: DataStoreBridge(), + flutterModelRegistration: flutterModelSchemaRegistration) pluginUnderTest.onSave( args: [:], flutterResult: { (results) -> Void in if let exception = results as? FlutterError { - XCTAssertEqual("AmplifyException", exception.code) - XCTAssertEqual(FlutterDataStoreErrorMessage.MALFORMED.rawValue, exception.message) + XCTAssertEqual("DataStoreException", exception.code) + XCTAssertEqual(ErrorMessages.defaultFallbackErrorMessage, exception.message) let errorMap: [String: Any] = exception.details as! [String : Any] - XCTAssertEqual("The modelName was not passed in the arguments", errorMap["decodingError"] as? String) - XCTAssertEqual( - ["platform": "iOS", "localizedErrorMessage": "The modelName was not passed in the arguments", "recoverySuggestion": "The request should include the modelName of type String"], - errorMap["PLATFORM_EXCEPTIONS"] as? [String: String]) + XCTAssertEqual("The modelName was not passed in the arguments", errorMap["message"] as? String) + XCTAssertEqual("The request should include the modelName of type String", errorMap["recoverySuggestion"] as? String) } else { XCTFail() } diff --git a/packages/amplify_datastore/ios/Classes/FlutterDataStoreErrorHandler.swift b/packages/amplify_datastore/ios/Classes/FlutterDataStoreErrorHandler.swift index 7a4fa4d062..561a800be3 100644 --- a/packages/amplify_datastore/ios/Classes/FlutterDataStoreErrorHandler.swift +++ b/packages/amplify_datastore/ios/Classes/FlutterDataStoreErrorHandler.swift @@ -16,82 +16,29 @@ import Foundation import Amplify import AmplifyPlugins +import amplify_core class FlutterDataStoreErrorHandler { - static func handleDataStoreError(error: DataStoreError, flutterResult: FlutterResult, msg: String) { - flutterResult(convertToFlutterError(error: error, msg: msg)) - } - - static func convertToFlutterError(error: DataStoreError, msg: String) -> FlutterError { - if case .internalOperation(let localizedError, let recoverySuggestion, let error) = error { - let errorCode = error != nil ? "\(error!)" : "unknown" - return formatError(errorCode: errorCode, - msg: msg, - localizedError: localizedError, - recoverySuggestion: recoverySuggestion) - - } - if case .configuration(let localizedError, let recoverySuggestion, let error) = error { - let errorCode = error != nil ? "\(error!)" : "configuration" - return formatError(errorCode: errorCode, - msg: msg, - localizedError: localizedError, - recoverySuggestion: recoverySuggestion) - - } - if case .invalidCondition(let localizedError, let recoverySuggestion, let error) = error { - let errorCode = error != nil ? "\(error!)" : "invalidCondition" - return formatError(errorCode: errorCode, - msg: msg, - localizedError: localizedError, - recoverySuggestion: recoverySuggestion) - - } - if case .decodingError(let localizedError, let recoverySuggestion) = error { - let errorCode = "decodingError" - return formatError(errorCode: errorCode, - msg: msg, - localizedError: localizedError, - recoverySuggestion: recoverySuggestion) - - } - if case .unknown(let localizedError, let recoverySuggestion, let error) = error { - let errorCode = error != nil ? "\(error!)" : "unknown" - return formatError(errorCode: errorCode, - msg: msg, - localizedError: localizedError, - recoverySuggestion: recoverySuggestion) - - } - return formatError(errorCode: "unknown", - msg: msg, - localizedError: "unknown error", - recoverySuggestion: "We don't have a recovery suggestion for this error right now.") - } - - static func platformExceptions(localizedError: String, recoverySuggestion: String) -> [String: String] { - var platformDict: [String: String] = [:] - platformDict["platform"] = "iOS" - platformDict["localizedErrorMessage"] = localizedError - platformDict["recoverySuggestion"] = recoverySuggestion - return platformDict + static func handleDataStoreError(error: DataStoreError, flutterResult: FlutterResult) { + ErrorUtil.postErrorToFlutterChannel(result: flutterResult, + errorCode: "DataStoreException", + details: FlutterDataStoreErrorHandler.createSerializedError(error: error)) } - static func formatError(errorCode: String, - msg: String, - localizedError: String, - recoverySuggestion: String) -> FlutterError { - var errorMap: [String: Any] = [errorCode: localizedError] - errorMap["PLATFORM_EXCEPTIONS"] = - platformExceptions(localizedError: localizedError, recoverySuggestion: recoverySuggestion) - return createFlutterError(msg: msg, errorMap: errorMap) + static func createSerializedError(error: DataStoreError) -> Dictionary { + return createSerializedError(message: error.errorDescription, + recoverySuggestion: error.recoverySuggestion, + underlyingError: error.underlyingError?.localizedDescription) } - static func createFlutterError(msg: String, errorMap: [String: Any]) -> FlutterError { - return FlutterError( - code: "AmplifyException", - message: msg, - details: errorMap) + static func createSerializedError(message: String, + recoverySuggestion: String?, + underlyingError: String?) -> Dictionary { + var serializedException: Dictionary = [:] + serializedException["message"] = message + serializedException["recoverySuggestion"] = recoverySuggestion + serializedException["underlyingException"] = underlyingError + return serializedException } } diff --git a/packages/amplify_datastore/ios/Classes/SwiftAmplifyDataStorePlugin.swift b/packages/amplify_datastore/ios/Classes/SwiftAmplifyDataStorePlugin.swift index 5513cf52b0..abe3a353d8 100644 --- a/packages/amplify_datastore/ios/Classes/SwiftAmplifyDataStorePlugin.swift +++ b/packages/amplify_datastore/ios/Classes/SwiftAmplifyDataStorePlugin.swift @@ -19,6 +19,7 @@ import Amplify import AmplifyPlugins import AWSCore import Combine +import amplify_core public class SwiftAmplifyDataStorePlugin: NSObject, FlutterPlugin { @@ -54,9 +55,8 @@ public class SwiftAmplifyDataStorePlugin: NSObject, FlutterPlugin { try arguments = checkArguments(args: call.arguments as Any) } } catch { - result(FlutterDataStoreErrorHandler.createFlutterError( - msg: FlutterDataStoreErrorMessage.MALFORMED.rawValue, - errorMap: ["UNKNOWN": "\(error.localizedDescription).\nAn unrecognized error has occurred. See logs for details." ])) + FlutterDataStoreErrorHandler.handleDataStoreError(error: DataStoreError(error: error), + flutterResult: result) return } @@ -94,7 +94,7 @@ public class SwiftAmplifyDataStorePlugin: NSObject, FlutterPlugin { modelSchemas.forEach { (modelSchema) in flutterModelRegistration.addModelSchema(modelName: modelSchema.name, modelSchema: modelSchema) } - + self.dataStoreHubEventStreamHandler?.registerModelsForHub(flutterModels: flutterModelRegistration) let dataStorePlugin = AWSDataStorePlugin(modelRegistration: flutterModelRegistration) @@ -103,9 +103,11 @@ public class SwiftAmplifyDataStorePlugin: NSObject, FlutterPlugin { print("Amplify configured with DataStore plugin") result(true) } catch ModelSchemaError.parse(let className, let fieldName, let desiredType){ - result(FlutterDataStoreErrorHandler.createFlutterError( - msg: FlutterDataStoreErrorMessage.MALFORMED.rawValue, - errorMap: ["MALFORMED_REQUEST": "Invalid modelSchema " + className + "-" + fieldName + " cannot be cast to " + desiredType ])) + FlutterDataStoreErrorHandler.handleDataStoreError( + error: DataStoreError.decodingError( + "Invalid modelSchema " + className + "-" + fieldName + " cannot be cast to " + desiredType, + ErrorMessages.missingRecoverySuggestion), + flutterResult: result) return } catch { @@ -125,16 +127,15 @@ public class SwiftAmplifyDataStorePlugin: NSObject, FlutterPlugin { let querySortInput = try QuerySortBuilder.fromSerializedList(args["querySort"] as? [[String: Any]]) let queryPagination = QueryPaginationBuilder.fromSerializedMap(args["queryPagination"] as? [String: Any]) try bridge.onQuery(FlutterSerializedModel.self, - modelSchema: modelSchema, - where: queryPredicates, - sort: querySortInput, - paginate: queryPagination) { (result) in + modelSchema: modelSchema, + where: queryPredicates, + sort: querySortInput, + paginate: queryPagination) { (result) in switch result { case .failure(let error): print("Query API failed. Error = \(error)") FlutterDataStoreErrorHandler.handleDataStoreError(error: error, - flutterResult: flutterResult, - msg: FlutterDataStoreErrorMessage.QUERY_FAILED.rawValue) + flutterResult: flutterResult) case .success(let res): let serializedResults = res.map { (queryResult) -> [String: Any] in return queryResult.toMap(modelSchema: modelSchema) @@ -148,15 +149,12 @@ public class SwiftAmplifyDataStorePlugin: NSObject, FlutterPlugin { print("Failed to parse query arguments with \(error)") FlutterDataStoreErrorHandler.handleDataStoreError( error: error, - flutterResult: flutterResult, - msg: FlutterDataStoreErrorMessage.MALFORMED.rawValue - ) + flutterResult: flutterResult) } catch { print("An unexpected error occured when parsing query arguments: \(error)") - flutterResult(FlutterDataStoreErrorHandler.createFlutterError( - msg: FlutterDataStoreErrorMessage.MALFORMED.rawValue, - errorMap: ["UNKNOWN": "\(error.localizedDescription).\nAn unrecognized error has occurred. See logs for details." ])) + FlutterDataStoreErrorHandler.handleDataStoreError(error: DataStoreError(error: error), + flutterResult: flutterResult) } } @@ -179,9 +177,7 @@ public class SwiftAmplifyDataStorePlugin: NSObject, FlutterPlugin { print("Save API failed. Error: \(error)") FlutterDataStoreErrorHandler.handleDataStoreError( error: error, - flutterResult: flutterResult, - msg: FlutterDataStoreErrorMessage.SAVE_FAILED.rawValue - ) + flutterResult: flutterResult) case .success(let model): print("Successfully saved model: \(model)") flutterResult(nil) @@ -192,15 +188,12 @@ public class SwiftAmplifyDataStorePlugin: NSObject, FlutterPlugin { print("Failed to parse save arguments with \(error)") FlutterDataStoreErrorHandler.handleDataStoreError( error: error, - flutterResult: flutterResult, - msg: FlutterDataStoreErrorMessage.MALFORMED.rawValue - ) + flutterResult: flutterResult) } catch { print("An unexpected error occured when parsing save arguments: \(error)") - flutterResult(FlutterDataStoreErrorHandler.createFlutterError( - msg: FlutterDataStoreErrorMessage.MALFORMED.rawValue, - errorMap: ["UNKNOWN": "\(error.localizedDescription).\nAn unrecognized error has occurred. See logs for details." ])) + FlutterDataStoreErrorHandler.handleDataStoreError(error: DataStoreError(error: error), + flutterResult: flutterResult) } } @@ -216,15 +209,14 @@ public class SwiftAmplifyDataStorePlugin: NSObject, FlutterPlugin { try bridge.onDelete( serializedModel: serializedModel, modelSchema: modelSchema) { (result) in - switch result { - case .failure(let error): - print("Delete API failed. Error = \(error)") - FlutterDataStoreErrorHandler.handleDataStoreError(error: error, - flutterResult: flutterResult, - msg: FlutterDataStoreErrorMessage.DELETE_FAILED.rawValue) - case .success(): - flutterResult(nil) - } + switch result { + case .failure(let error): + print("Delete API failed. Error = \(error)") + FlutterDataStoreErrorHandler.handleDataStoreError(error: error, + flutterResult: flutterResult) + case .success(): + flutterResult(nil) + } } } @@ -232,27 +224,23 @@ public class SwiftAmplifyDataStorePlugin: NSObject, FlutterPlugin { print("Failed to parse delete arguments with \(error)") FlutterDataStoreErrorHandler.handleDataStoreError( error: error, - flutterResult: flutterResult, - msg: FlutterDataStoreErrorMessage.MALFORMED.rawValue - ) + flutterResult: flutterResult) } catch { print("An unexpected error occured when parsing delete arguments: \(error)") - flutterResult(FlutterDataStoreErrorHandler.createFlutterError( - msg: FlutterDataStoreErrorMessage.MALFORMED.rawValue, - errorMap: ["UNKNOWN": "\(error.localizedDescription).\nAn unrecognized error has occurred. See logs for details." ])) + FlutterDataStoreErrorHandler.handleDataStoreError(error: DataStoreError(error: error), + flutterResult: flutterResult) return } - } public func onSetupObserve(flutterResult: @escaping FlutterResult) { do { observeSubscription = try observeSubscription ?? bridge.onObserve().sink { if case let .failure(error) = $0 { - let flutterError = FlutterDataStoreErrorHandler.convertToFlutterError( - error: error, - msg: FlutterDataStoreErrorMessage.OBSERVE_EVENT_FAILURE.rawValue) + let flutterError = FlutterError(code: "DataStoreException", + message: ErrorMessages.defaultFallbackErrorMessage, + details: FlutterDataStoreErrorHandler.createSerializedError(error: error)) self.dataStoreObserveEventStreamHandler?.sendError(flutterError: flutterError) } } receiveValue: { (mutationEvent) in @@ -289,9 +277,7 @@ public class SwiftAmplifyDataStorePlugin: NSObject, FlutterPlugin { print("Clear API failed. Error: \(error)") FlutterDataStoreErrorHandler.handleDataStoreError( error: error, - flutterResult: flutterResult, - msg: FlutterDataStoreErrorMessage.CLEAR_FAILED.rawValue - ) + flutterResult: flutterResult) case .success(): print("Successfully cleared the store") flutterResult(nil) @@ -300,9 +286,8 @@ public class SwiftAmplifyDataStorePlugin: NSObject, FlutterPlugin { } catch { print("An unexpected error occured: \(error)") - flutterResult(FlutterDataStoreErrorHandler.createFlutterError( - msg: FlutterDataStoreErrorMessage.UNEXPECTED_ERROR.rawValue, - errorMap: ["UNKNOWN": "\(error.localizedDescription).\nAn unexpected error has occurred. See logs for details." ])) + FlutterDataStoreErrorHandler.handleDataStoreError(error: DataStoreError(error: error), + flutterResult: flutterResult) } } diff --git a/packages/amplify_datastore/ios/amplify_datastore.podspec b/packages/amplify_datastore/ios/amplify_datastore.podspec index d15c9d545a..d22bf96173 100644 --- a/packages/amplify_datastore/ios/amplify_datastore.podspec +++ b/packages/amplify_datastore/ios/amplify_datastore.podspec @@ -17,6 +17,7 @@ The DataStore module for Amplify Flutter. s.dependency 'Flutter' s.dependency 'Amplify' s.dependency 'AmplifyPlugins/AWSDataStorePlugin' + s.dependency 'amplify_core' s.platform = :ios, '13.0' # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported. diff --git a/packages/amplify_datastore/lib/amplify_datastore.dart b/packages/amplify_datastore/lib/amplify_datastore.dart index 69037c608d..5e45d68450 100644 --- a/packages/amplify_datastore/lib/amplify_datastore.dart +++ b/packages/amplify_datastore/lib/amplify_datastore.dart @@ -48,7 +48,9 @@ class AmplifyDataStore extends DataStorePluginInterface { ModelProviderInterface provider = modelProvider == null ? this.modelProvider : modelProvider; if (provider == null || provider.modelSchemas.isEmpty) { - throw ArgumentError("Need to provide at least one modelSchema"); + throw DataStoreException('No modelProvider or modelSchemas found', + recoverySuggestion: + 'Pass in a modelProvider instance while instantiating DataStorePlugin'); } streamWrapper.registerModelsForHub(provider); return _instance.configureModelProvider(modelProvider: modelProvider); diff --git a/packages/amplify_datastore/lib/method_channel_datastore.dart b/packages/amplify_datastore/lib/method_channel_datastore.dart index efb3c552ac..0f948a4704 100644 --- a/packages/amplify_datastore/lib/method_channel_datastore.dart +++ b/packages/amplify_datastore/lib/method_channel_datastore.dart @@ -15,6 +15,7 @@ import 'dart:collection'; +import 'package:amplify_core/types/index.dart'; import 'package:amplify_datastore/amplify_datastore.dart'; import 'package:flutter/services.dart'; import 'package:amplify_datastore_plugin_interface/amplify_datastore_plugin_interface.dart'; @@ -37,7 +38,7 @@ class AmplifyDataStoreMethodChannel extends AmplifyDataStore { 'modelProviderVersion': modelProvider.version }); } on PlatformException catch (e) { - throw _formatError(e); + throw _deserializeException(e); } } @@ -70,12 +71,14 @@ class AmplifyDataStoreMethodChannel extends AmplifyDataStore { serializedResult["serializedData"]))) .toList(); } on PlatformException catch (e) { - throw _formatError(e); - } on TypeError { - throw DataStoreError.init( - cause: "ERROR_FORMATTING_PLATFORM_CHANNEL_RESPONSE", - errorMap: new LinkedHashMap.from( - {"errorMessage": "Failed to deserialize query API results"})); + 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()); } } @@ -87,7 +90,7 @@ class AmplifyDataStoreMethodChannel extends AmplifyDataStore { 'serializedModel': model.toJson(), }); } on PlatformException catch (e) { - throw _formatError(e); + throw _deserializeException(e); } } @@ -100,7 +103,7 @@ class AmplifyDataStoreMethodChannel extends AmplifyDataStore { }; await _channel.invokeMethod('save', methodChannelSaveInput); } on PlatformException catch (e) { - throw _formatError(e); + throw _deserializeException(e); } } @@ -134,7 +137,7 @@ class AmplifyDataStoreMethodChannel extends AmplifyDataStore { try { await _channel.invokeMethod('clear'); } on PlatformException catch (e) { - throw _formatError(e); + throw _deserializeException(e); } } @@ -144,9 +147,16 @@ class AmplifyDataStoreMethodChannel extends AmplifyDataStore { return serializedItem["modelName"] as String; } - DataStoreError _formatError(PlatformException e) { - LinkedHashMap eMap = new LinkedHashMap(); - e.details.forEach((k, v) => {eMap.putIfAbsent(k, () => v)}); - return DataStoreError.init(cause: e.message, errorMap: eMap); + DataStoreException _deserializeException(PlatformException e) { + if (e.code == 'DataStoreException') { + throw DataStoreException.fromMap(Map.from(e.details)); + } else { + // This shouldn't happen. All exceptions coming from platform for + // amplify_datastore should have a known code. Throw an unknown error. + throw DataStoreException(AmplifyExceptionMessages.missingExceptionMessage, + recoverySuggestion: + AmplifyExceptionMessages.missingRecoverySuggestion, + underlyingException: e.toString()); + } } } diff --git a/packages/amplify_datastore/test/amplify_datastore_clear_test.dart b/packages/amplify_datastore/test/amplify_datastore_clear_test.dart index b3136b7a68..6edc36ac1f 100644 --- a/packages/amplify_datastore/test/amplify_datastore_clear_test.dart +++ b/packages/amplify_datastore/test/amplify_datastore_clear_test.dart @@ -13,6 +13,7 @@ * permissions and limitations under the License. */ +import 'package:amplify_core/types/index.dart'; import 'package:amplify_datastore/amplify_datastore.dart'; import 'package:amplify_datastore_plugin_interface/amplify_datastore_plugin_interface.dart'; import 'package:flutter/services.dart'; @@ -39,35 +40,48 @@ void main() { }); test( - 'A PlatformException for a failed API call results in the corresponding DataStoreError', + 'A PlatformException for a failed API call results in the corresponding DataStoreException', () async { dataStoreChannel.setMockMethodCallHandler((MethodCall methodCall) async { - throw PlatformException( - code: 'AMPLIFY_EXCEPTION', - message: 'AMPLIFY_DATASTORE_CLEAR_FAILED', - details: {}); + 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((error) => error.cause, - 'error message', 'AMPLIFY_DATASTORE_CLEAR_FAILED'))); + 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 the corresponding DataStoreError', + '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' + }); dataStoreChannel.setMockMethodCallHandler((MethodCall methodCall) async { - throw PlatformException( - code: 'AMPLIFY_EXCEPTION', - message: 'An unrecognized message', - details: {}); + throw platformException; }); expect( () => dataStore.clear(), - throwsA(isA().having( - (error) => error.cause, - 'error message', - 'UNRECOGNIZED_DATASTORE_ERROR', - ))); + throwsA(isA() + .having((exception) => exception.message, 'message', + AmplifyExceptionMessages.missingExceptionMessage) + .having( + (exception) => exception.recoverySuggestion, + 'recoverySuggestion', + AmplifyExceptionMessages.missingRecoverySuggestion) + .having((exception) => exception.underlyingException, + 'underlyingException', platformException.toString()))); }); } diff --git a/packages/amplify_datastore/test/amplify_datastore_delete_test.dart b/packages/amplify_datastore/test/amplify_datastore_delete_test.dart index 80e53eaae2..23502fcbca 100644 --- a/packages/amplify_datastore/test/amplify_datastore_delete_test.dart +++ b/packages/amplify_datastore/test/amplify_datastore_delete_test.dart @@ -36,7 +36,7 @@ void main() { dataStoreChannel.setMockMethodCallHandler(null); }); - test('delete with a valid model executes without an error ', () async { + 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']; @@ -51,37 +51,24 @@ void main() { }); test( - 'A PlatformException for a failed API call results in the corresponding DataStoreError', + 'A PlatformException for a failed API call results in the corresponding DataStoreException', () async { dataStoreChannel.setMockMethodCallHandler((MethodCall methodCall) async { - throw PlatformException( - code: 'AMPLIFY_EXCEPTION', - message: 'AMPLIFY_DATASTORE_DELETE_FAILED', - details: {}); + throw PlatformException(code: 'DataStoreException', details: { + 'message': 'Delete failed for whatever known reason', + 'recoverySuggestion': 'some insightful suggestion', + 'underlyingException': 'Act of God' + }); }); expect( () => dataStore.delete(Post( title: 'test title', id: '4281dfba-96c8-4a38-9a8e-35c7e893ea47')), - throwsA(isA().having((error) => error.cause, - 'error message', 'AMPLIFY_DATASTORE_DELETE_FAILED'))); - }); - - test( - 'An unrecognized PlatformException results in the corresponding DataStoreError', - () async { - dataStoreChannel.setMockMethodCallHandler((MethodCall methodCall) async { - throw PlatformException( - code: 'AMPLIFY_EXCEPTION', - message: 'An unrecognized message', - details: {}); - }); - expect( - () => dataStore.delete(Post( - title: 'test title', id: '4281dfba-96c8-4a38-9a8e-35c7e893ea47')), - throwsA(isA().having( - (error) => error.cause, - 'error message', - 'UNRECOGNIZED_DATASTORE_ERROR', - ))); + 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_query_test.dart b/packages/amplify_datastore/test/amplify_datastore_query_test.dart index 239eb2f6de..db1e476036 100644 --- a/packages/amplify_datastore/test/amplify_datastore_query_test.dart +++ b/packages/amplify_datastore/test/amplify_datastore_query_test.dart @@ -13,6 +13,7 @@ * permissions and limitations under the License. */ +import 'package:amplify_core/types/index.dart'; import 'package:amplify_datastore/amplify_datastore.dart'; import 'package:amplify_datastore_plugin_interface/amplify_datastore_plugin_interface.dart'; import 'package:flutter/services.dart'; @@ -120,29 +121,22 @@ void main() { test('method channel throws a known PlatformException', () async { dataStoreChannel.setMockMethodCallHandler((MethodCall methodCall) async { if (methodCall.method == "query") { - throw PlatformException( - code: "AMPLIFY_EXCEPTION", - message: "AMPLIFY_REQUEST_MALFORMED", - details: {}); + throw PlatformException(code: 'DataStoreException', details: { + 'message': 'Query failed for whatever known reason', + 'recoverySuggestion': 'some insightful suggestion', + 'underlyingException': 'Act of God' + }); } }); expect( () => dataStore.query(Post.classType), - throwsA(isA().having((error) => error.cause, - "error message", "AMPLIFY_REQUEST_MALFORMED"))); - }); - - test('method channel throws an unknown PlatformException', () async { - dataStoreChannel.setMockMethodCallHandler((MethodCall methodCall) async { - if (methodCall.method == "query") { - throw PlatformException( - code: "AMPLIFY_EXCEPTION", message: "Some Random", details: {}); - } - }); - expect( - () => dataStore.query(Post.classType), - throwsA(isA().having((error) => error.cause, - "error message", "UNRECOGNIZED_DATASTORE_ERROR"))); + 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'))); }); test('method channel returns results something that cannot be parsed', @@ -154,7 +148,20 @@ void main() { }); expect( () => dataStore.query(Post.classType), - throwsA(isA().having((error) => error.cause, - "error message", "ERROR_FORMATTING_PLATFORM_CHANNEL_RESPONSE"))); + throwsA(isA() + .having( + (exception) => exception.message, + 'message', + "An unrecognized exception has happened while Serialization/de-serialization." + + " Please see underlyingException for more details.", + ) + .having( + (exception) => exception.recoverySuggestion, + 'recoverySuggestion', + AmplifyExceptionMessages.missingRecoverySuggestion) + .having( + (exception) => exception.underlyingException, + 'underlyingException', + 'type \'String\' is not a subtype of type \'List?\' in type cast'))); }); } diff --git a/packages/amplify_datastore/test/amplify_datastore_save_test.dart b/packages/amplify_datastore/test/amplify_datastore_save_test.dart index d210127e24..84a4a67176 100644 --- a/packages/amplify_datastore/test/amplify_datastore_save_test.dart +++ b/packages/amplify_datastore/test/amplify_datastore_save_test.dart @@ -56,59 +56,23 @@ void main() { 'A PlatformException for malformed request results in the corresponding DataStoreError', () async { dataStoreChannel.setMockMethodCallHandler((MethodCall methodCall) async { - throw PlatformException( - code: "AMPLIFY_EXCEPTION", - message: "AMPLIFY_REQUEST_MALFORMED", - details: {}); + 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, )), - throwsA(isA().having( - (error) => error.cause, - "error message", - "AMPLIFY_REQUEST_MALFORMED", - ))); - }); - - test( - 'A PlatformException for a failed API call results in the corresponding DataStoreError', - () async { - dataStoreChannel.setMockMethodCallHandler((MethodCall methodCall) async { - throw PlatformException( - code: "AMPLIFY_EXCEPTION", - message: "AMPLIFY_DATASTORE_SAVE_FAILED", - details: {}); - }); - expect( - () => dataStore.save(Post( - title: 'Test Post', - rating: 10, - )), - throwsA(isA().having( - (error) => error.cause, - "error message", - "AMPLIFY_DATASTORE_SAVE_FAILED", - ))); - }); - - test( - 'An unrecognized PlatformException results in the corresponding DataStoreError', - () async { - dataStoreChannel.setMockMethodCallHandler((MethodCall methodCall) async { - throw PlatformException( - code: "AMPLIFY_EXCEPTION", - message: "An unrecognized message", - details: {}); - }); - expect( - () => dataStore.save(Post( - title: 'Test Post', - rating: 10, - )), - throwsA(isA().having((error) => error.cause, - "error message", "UNRECOGNIZED_DATASTORE_ERROR"))); + 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_plugin_interface/lib/amplify_datastore_plugin_interface.dart b/packages/amplify_datastore_plugin_interface/lib/amplify_datastore_plugin_interface.dart index 2c024719c4..a7a81b3547 100644 --- a/packages/amplify_datastore_plugin_interface/lib/amplify_datastore_plugin_interface.dart +++ b/packages/amplify_datastore_plugin_interface/lib/amplify_datastore_plugin_interface.dart @@ -41,8 +41,7 @@ export 'src/types/models/auth_rule.dart'; //export 'src/types/temporal/time.dart'; export 'src/types/temporal/datetime_parse.dart'; export 'src/types/utils/parsers.dart'; -export 'src/Errors/datastore_error.dart'; -export 'src/Errors/datastore_error_types.dart'; +export 'src/types/exception/DataStoreException.dart'; export 'src/types/models/subscription_event.dart'; abstract class DataStorePluginInterface extends AmplifyPluginInterface { diff --git a/packages/amplify_datastore_plugin_interface/lib/src/Errors/datastore_error.dart b/packages/amplify_datastore_plugin_interface/lib/src/Errors/datastore_error.dart deleted file mode 100644 index b2d3e9d868..0000000000 --- a/packages/amplify_datastore_plugin_interface/lib/src/Errors/datastore_error.dart +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -import 'dart:collection'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; -import './datastore_error_types.dart'; - -class DataStoreError implements Exception { - String cause; - List exceptionList = []; - DataStoreError.init( - {@required cause, @required LinkedHashMap errorMap}) { - this.cause = DataStoreErrorTypes.contains(cause) - ? cause - : "UNRECOGNIZED_DATASTORE_ERROR"; - errorMap.forEach((k, v) => - {exceptionList.add(DataStoreException(exception: k, detail: v))}); - } -} - -class DataStoreException { - String exception; - dynamic detail; - DataStoreException({@required this.exception, this.detail}); -} diff --git a/packages/amplify_datastore_plugin_interface/lib/src/Errors/datastore_error_types.dart b/packages/amplify_datastore_plugin_interface/lib/src/Errors/datastore_error_types.dart deleted file mode 100644 index 781043c29b..0000000000 --- a/packages/amplify_datastore_plugin_interface/lib/src/Errors/datastore_error_types.dart +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -var DataStoreErrorTypes = [ - 'AMPLIFY_REQUEST_MALFORMED', - 'ERROR_CASTING_INPUT_IN_PLATFORM_CODE', - 'AMPLIFY_DATASTORE_QUERY_FAILED', - 'AMPLIFY_DATASTORE_SAVE_FAILED', - 'AMPLIFY_DATASTORE_CLEAR_FAILED', - 'AMPLIFY_DATASTORE_DELETE_FAILED', - 'ERROR_FORMATTING_PLATFORM_CHANNEL_RESPONSE', - 'MODEL_NOT_FOUND_IN_MODEL_PROVIDER' - 'ERROR_FORMATTING_PLATFORM_CHANNEL_RESPONSE' -]; diff --git a/packages/amplify_datastore_plugin_interface/lib/src/types/exception/DataStoreException.dart b/packages/amplify_datastore_plugin_interface/lib/src/types/exception/DataStoreException.dart new file mode 100644 index 0000000000..ddcd2788c8 --- /dev/null +++ b/packages/amplify_datastore_plugin_interface/lib/src/types/exception/DataStoreException.dart @@ -0,0 +1,39 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import 'package:amplify_core/types/exception/AmplifyException.dart'; + +/// Exception thrown from DataStore Category +class DataStoreException extends AmplifyException { + /// Named constructor + const DataStoreException(String message, + {String recoverySuggestion, String underlyingException}) + : super(message, + recoverySuggestion: recoverySuggestion, + underlyingException: underlyingException); + + /// Constructor for down casting an AmplifyException to this exception + DataStoreException._private(AmplifyException exception) + : super(exception.message, + recoverySuggestion: exception.recoverySuggestion, + underlyingException: exception.underlyingException); + + /// Instantiates and return a new `DataStoreException` from the + /// serialized exception data + static DataStoreException fromMap(Map serializedException) { + return DataStoreException._private( + AmplifyException.fromMap(serializedException)); + } +} diff --git a/packages/amplify_datastore_plugin_interface/lib/src/types/query/query_field.dart b/packages/amplify_datastore_plugin_interface/lib/src/types/query/query_field.dart index 92ea19565a..0961d1a219 100644 --- a/packages/amplify_datastore_plugin_interface/lib/src/types/query/query_field.dart +++ b/packages/amplify_datastore_plugin_interface/lib/src/types/query/query_field.dart @@ -19,8 +19,6 @@ import 'package:amplify_datastore_plugin_interface/src/types/models/model_field_ import 'package:flutter/foundation.dart'; import '../temporal/datetime_parse.dart'; -import '../utils/parsers.dart'; - part 'query_field_operators.dart'; part 'query_pagination.dart'; part 'query_predicate.dart';