Skip to content

Update datastore error handling for android + flutter as per #314 #329

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Feb 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6e8fd19
feat(amplify_flutter): Error handling refactor
Amplifiyer Jan 20, 2021
54371b4
Minor fixes
Amplifiyer Jan 20, 2021
489941b
Android test fixes
Amplifiyer Jan 20, 2021
301d9dc
Mute iOS unit tests for now
Amplifiyer Jan 20, 2021
5a81812
Add iOS Unit test
Amplifiyer Jan 20, 2021
d16160e
Update with PR comments
Amplifiyer Jan 21, 2021
b5d6604
Fix small typo
Amplifiyer Jan 21, 2021
917144b
Try adding gradle opts to avoid android test failures
Amplifiyer Jan 21, 2021
c1e06af
Increase android build timeout to 20 mins
Amplifiyer Jan 21, 2021
e415ef7
Merge remote-tracking branch 'amplify-prod/master' into error_handlin…
Amplifiyer Jan 21, 2021
c394fb6
Reset the license language refactor
Amplifiyer Jan 22, 2021
328e8a1
Update the gradle options for android builds
Amplifiyer Jan 22, 2021
c83361b
Another attempt at fixing gradle options related to deamon
Amplifiyer Jan 22, 2021
865f046
Merge branch 'master' of github.com:aws-amplify/amplify-flutter into …
Amplifiyer Jan 26, 2021
4615d7a
Update gitignore to ignore .last_build_id files
Amplifiyer Jan 26, 2021
741a628
Update new Auth API to conform to refactor changes
Amplifiyer Jan 26, 2021
130f19e
chore(amplify_datastore): Update error handling for android + flutter…
Amplifiyer Jan 26, 2021
20f142c
chore(amplify_datastore): Add new exception for datastore
Amplifiyer Jan 26, 2021
ab0170f
Add error handling for iOS platform
Amplifiyer Jan 27, 2021
c4bfb09
PR Updates
Amplifiyer Feb 2, 2021
37b4393
Merge remote-tracking branch 'amplify-prod/master' into datastore_err…
Amplifiyer Feb 2, 2021
8913874
Merge branch 'master' into datastore_error_handling
Amplifiyer Feb 2, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ class ExceptionUtil {
}
}

@JvmStatic
fun createSerializedUnrecognizedError(@NonNull e: Exception): Map<String, Any?> {
return createSerializedError(ExceptionMessages.missingExceptionMessage, null, e.toString())
}

@JvmStatic
fun createSerializedError(message: String, recoverySuggestion: String?,
cause: String?): Map<String, String?> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -63,37 +66,39 @@ class AmplifyDataStorePlugin : FlutterPlugin, MethodCallHandler {
}

@VisibleForTesting
constructor(eventHandler: DataStoreObserveEventStreamHandler, hubEventHandler: DataStoreHubEventStreamHandler) {
constructor(eventHandler: DataStoreObserveEventStreamHandler,
hubEventHandler: DataStoreHubEventStreamHandler) {
dataStoreObserveEventStreamHandler = eventHandler
dataStoreHubEventStreamHandler = hubEventHandler
}

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")
}

override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
var data: Map<String, Any> = HashMap()
try {
if(call.arguments != null) {
if (call.arguments != null) {
data = checkArguments(call.arguments) as HashMap<String, Any>
}
} 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)
Expand All @@ -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
}

Expand Down Expand Up @@ -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
}

Expand All @@ -172,41 +173,37 @@ 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))
}
}
)
}

@VisibleForTesting
fun onDelete(flutterResult: Result, request: Map<String, Any>) {
val modelName: String
val serializedModelData: Map<String, Any>
val serializedModelData: Map<String, Any>

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
}

Expand All @@ -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<String, Any>) {
val modelName: String
val serializedModelData: Map<String, Any>
val serializedModelData: Map<String, Any>

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
}

Expand All @@ -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

Expand All @@ -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))
}
}
)
}
Expand All @@ -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.") }
)
Expand All @@ -337,40 +334,15 @@ class AmplifyDataStorePlugin : FlutterPlugin, MethodCallHandler {
return args.safeCastToMap()!!
}

protected fun createErrorMap(@NonNull error: Exception): Map<String, Any> {
var errorMap = HashMap<String, Any>()

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<String, Any>) {
handler.post { flutterResult.error("AmplifyException", msg, errorMap) }
}

@VisibleForTesting
fun deserializeNestedModels(serializedModelData: Map<String, Any>): Map<String, Any> {
return serializedModelData.mapValues {
if(it.value is Map<*, *>) {
if (it.value is Map<*, *>) {
SerializedModel.builder()
.serializedData(deserializeNestedModels(it.value as HashMap<String, Any>))
.modelSchema(null)
.build() as Any
}
else
} else
it.value
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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?) {
Expand Down

This file was deleted.

Loading