From 188d1577c6827f5de7a9f4b9594d43cbb63f3cbe Mon Sep 17 00:00:00 2001 From: Hui Zhao Date: Thu, 1 Jun 2023 09:57:51 -0700 Subject: [PATCH] fix(datastore): Flutter: fail to serialize list of custom type values --- .../sqlite/SQLiteModelFieldTypeConverter.java | 16 ++- .../SQLiteModelFieldTypeConverterTest.java | 107 ++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteModelFieldTypeConverter.java b/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteModelFieldTypeConverter.java index 42b70a839c..ef2fd6e8df 100644 --- a/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteModelFieldTypeConverter.java +++ b/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteModelFieldTypeConverter.java @@ -45,7 +45,9 @@ import java.time.OffsetTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -316,7 +318,18 @@ public Object convertValueFromTarget(Model model, ModelField field) throws DataS // We don't want to store entire SerializedCustomType along with schema into // Database but only its value. if (field.isCustomType() && fieldValue != null) { - fieldValue = ((SerializedCustomType) fieldValue).getFlatSerializedData(); + if (field.isArray()) { + @SuppressWarnings("unchecked") + List listOfItems = (List) fieldValue; + List> listOfValues = new ArrayList<>(); + + for (SerializedCustomType item : listOfItems) { + listOfValues.add(item.getFlatSerializedData()); + } + fieldValue = listOfValues; + } else { + fieldValue = ((SerializedCustomType) fieldValue).getFlatSerializedData(); + } } } else { fieldValue = ModelHelper.getValue(model, field); @@ -324,6 +337,7 @@ public Object convertValueFromTarget(Model model, ModelField field) throws DataS if (fieldValue == null) { return null; } + final JavaFieldType javaFieldType = TypeConverter.getJavaFieldType(field); return convertRawValueToTarget(fieldValue, javaFieldType, gson); } diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/storage/sqlite/SQLiteModelFieldTypeConverterTest.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/storage/sqlite/SQLiteModelFieldTypeConverterTest.java index 16673d3a4b..b760e3868c 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/storage/sqlite/SQLiteModelFieldTypeConverterTest.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/storage/sqlite/SQLiteModelFieldTypeConverterTest.java @@ -16,7 +16,16 @@ package com.amplifyframework.datastore.storage.sqlite; import com.amplifyframework.AmplifyException; +import com.amplifyframework.core.model.CustomTypeField; +import com.amplifyframework.core.model.CustomTypeSchema; +import com.amplifyframework.core.model.Model; +import com.amplifyframework.core.model.ModelField; +import com.amplifyframework.core.model.ModelSchema; +import com.amplifyframework.core.model.SchemaRegistry; +import com.amplifyframework.core.model.SerializedCustomType; +import com.amplifyframework.core.model.SerializedModel; import com.amplifyframework.core.model.types.JavaFieldType; +import com.amplifyframework.datastore.DataStoreException; import com.amplifyframework.util.GsonFactory; import com.amplifyframework.util.UserAgent; @@ -24,7 +33,9 @@ import org.junit.Before; import org.junit.Test; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import static org.junit.Assert.assertEquals; @@ -100,4 +111,100 @@ public void testConvertRawValueToTargetDateTimeFlutter() throws AmplifyException final String expected = "2020-01-01T16:00:00.050020000"; assertEquals(expected, actual); } + + /** + * Test the convertValueFromTarget getting serialized value from a CustomType field in Flutter user cases. + * @throws DataStoreException Not expected. + */ + @Test + public void testConvertFromCustomTypeTargetValueFlutter() throws DataStoreException { + SerializedModel model = getTestSerializedModel(); + ModelField testField = ModelField.builder() + .name("customTypeField") + .javaClassForValue(Map.class) + .targetType("DummyCustomType") + .isRequired(true) + .isCustomType(true) + .build(); + + SQLiteModelFieldTypeConverter converter = new SQLiteModelFieldTypeConverter( + ModelSchema.builder().name("DummySchemaNotUsedInTest").modelType(Model.Type.USER).build(), + SchemaRegistry.instance(), + new Gson() + ); + + Object result = converter.convertValueFromTarget(model, testField); + assertEquals(result, "{\"phone\":\"4155555555\",\"countryCode\":\"+1\"}"); + } + + /** + * Test the convertValueFromTarget getting serialized value from a list of CustomType field in Flutter user cases. + * @throws DataStoreException Not expected. + */ + @Test + public void testConvertFromListOfCustomTypeTargetValuesFlutter() throws DataStoreException { + SerializedModel model = getTestSerializedModel(); + ModelField testField = ModelField.builder() + .name("listCustomTypeField") + .javaClassForValue(List.class) + .targetType("DummyCustomType") + .isRequired(true) + .isCustomType(true) + .isArray(true) + .build(); + + SQLiteModelFieldTypeConverter converter = new SQLiteModelFieldTypeConverter( + ModelSchema.builder().name("DummySchemaNotUsedInTest").modelType(Model.Type.USER).build(), + SchemaRegistry.instance(), + new Gson() + ); + + Object result = converter.convertValueFromTarget(model, testField); + assertEquals(result, "[{\"phone\":\"4155555555\",\"countryCode\":\"+1\"},{\"phone\":\"4155555555\"," + + "\"countryCode\":\"+1\"}]"); + } + + private SerializedModel getTestSerializedModel() { + Map serializedCustomTypeData = new HashMap<>(); + serializedCustomTypeData.put("phone", "4155555555"); + serializedCustomTypeData.put("countryCode", "+1"); + + Map customTypeFields = new HashMap<>(); + customTypeFields.put("phone", CustomTypeField.builder() + .name("phone") + .targetType("String") + .javaClassForValue(String.class) + .build()); + customTypeFields.put("countryCode", CustomTypeField.builder() + .name("countryCode") + .targetType("String") + .javaClassForValue(String.class) + .build()); + + SerializedCustomType testCustomType = SerializedCustomType.builder() + .serializedData(serializedCustomTypeData) + .customTypeSchema(CustomTypeSchema.builder() + .name("Phone") + .pluralName("Phones") + .fields(customTypeFields) + .build()) + .build(); + + List testCustomTypeList = new ArrayList<>(); + testCustomTypeList.add(testCustomType); + testCustomTypeList.add(testCustomType); + + Map serializedModelData = new HashMap<>(); + serializedModelData.put("id", "dummy-id"); + serializedModelData.put("customTypeField", testCustomType); + serializedModelData.put("listCustomTypeField", testCustomTypeList); + + return SerializedModel.builder() + .modelSchema(ModelSchema.builder() + .name("DummySchemaNotUsed") + .modelClass(SerializedModel.class) + .build()) + .serializedData(serializedModelData) + .build(); + } }