From 41d6bf4a9b015dafb7228c8ca2385e11d4d8ffca Mon Sep 17 00:00:00 2001 From: Michael Law <1365977+lawmicha@users.noreply.github.com> Date: Wed, 2 Nov 2022 19:38:30 -0400 Subject: [PATCH 01/33] fix(datastore): remove typename from ModelMetadata --- .../datastore/appsync/ModelMetadata.java | 24 ++++------ .../datastore/appsync/ModelWithMetadata.java | 2 +- .../appsync/ModelWithMetadataAdapter.java | 7 ++- .../appsync/ModelWithMetadataAdapterTest.java | 11 ++--- .../datastore/AWSDataStorePluginTest.java | 9 ++-- .../ConflictResolverIntegrationTest.java | 7 ++- .../datastore/MutationProcessorRetryTest.java | 7 ++- ...SyncConflictUnhandledErrorFactoryTest.java | 2 +- .../AppSyncConflictUnhandledErrorTest.java | 2 +- .../datastore/appsync/AppSyncMocking.java | 7 ++- .../datastore/appsync/AppSyncMockingTest.java | 10 ++-- .../TestModelWithMetadataInstances.java | 8 ++-- .../syncengine/ConflictResolverTest.java | 12 ++--- .../datastore/syncengine/MergerTest.java | 46 +++++++------------ .../syncengine/MutationProcessorTest.java | 9 ++-- .../syncengine/OrchestratorTest.java | 2 +- .../syncengine/SubscriptionProcessorTest.java | 2 +- .../syncengine/SyncProcessorTest.java | 3 +- .../syncengine/VersionRepositoryTest.java | 5 +- 19 files changed, 76 insertions(+), 99 deletions(-) diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java index 071129e728..b74d3e57a5 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java @@ -39,7 +39,6 @@ public final class ModelMetadata implements Model { private final @ModelField(targetType = "Boolean") Boolean _deleted; private final @ModelField(targetType = "Int") Integer _version; private final @ModelField(targetType = "AWSTimestamp") Temporal.Timestamp _lastChangedAt; - private final @ModelField(targetType = "String") String __typename; /** * Constructor for this metadata model. @@ -47,19 +46,16 @@ public final class ModelMetadata implements Model { * @param deleted Whether this object was deleted since the last sync time specified * @param version What version this object was last seen at * @param lastChangedAt When was this object last changed - * @param typename The type name of the model. */ public ModelMetadata( @NonNull String id, @Nullable Boolean deleted, @Nullable Integer version, - @Nullable Temporal.Timestamp lastChangedAt, - @Nullable String typename) { + @Nullable Temporal.Timestamp lastChangedAt) { this.id = Objects.requireNonNull(id); this._deleted = deleted; this._version = version; this._lastChangedAt = lastChangedAt; - this.__typename = typename; } /** @@ -90,21 +86,21 @@ public Integer getVersion() { } /** - * Gets the type of the model. - * @return Type of the Model. + * Gets last changed at time. + * @return last changed at time */ @Nullable - public String getTypename() { - return __typename; + public Temporal.Timestamp getLastChangedAt() { + return _lastChangedAt; } /** - * Gets last changed at time. - * @return last changed at time + * Gets the model name. + * @return modelName */ @Nullable - public Temporal.Timestamp getLastChangedAt() { - return _lastChangedAt; + public String getModelName() { + return id.split("\\|")[0]; } @Override @@ -136,7 +132,6 @@ public int hashCode() { result = 31 * result + (_deleted != null ? _deleted.hashCode() : 0); result = 31 * result + (_version != null ? _version.hashCode() : 0); result = 31 * result + (_lastChangedAt != null ? _lastChangedAt.hashCode() : 0); - result = 31 * result + (__typename != null ? __typename.hashCode() : 0); return result; } @@ -147,7 +142,6 @@ public String toString() { ", _deleted=" + _deleted + ", _version=" + _version + ", _lastChangedAt=" + _lastChangedAt + - ", __typename=" + __typename + '}'; } } diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadata.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadata.java index 7090e7cfdb..d12d15de94 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadata.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadata.java @@ -42,7 +42,7 @@ public ModelWithMetadata(@NonNull M model, @NonNull ModelMetadata syncMetadata) this.syncMetadata = new ModelMetadata(model.getModelName() + "|" + model.getPrimaryKeyString(), syncMetadata.isDeleted(), syncMetadata.getVersion(), - syncMetadata.getLastChangedAt(), syncMetadata.getTypename()); + syncMetadata.getLastChangedAt()); } /** diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java index e494c5bc98..7823852fad 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java @@ -29,6 +29,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; @@ -117,10 +118,14 @@ public JsonElement serialize( // Flatten out the fields of the model and its metadata into a flat key-value map. // To do this, serialize each individually, and then add the key/value pairs for each // object into a new container. - JsonObject serializedMetadata = (JsonObject) context.serialize(src.getSyncMetadata()); + ModelMetadata modelMetadata = src.getSyncMetadata(); + JsonObject serializedMetadata = (JsonObject) context.serialize(modelMetadata); for (Map.Entry entry : serializedMetadata.entrySet()) { result.add(entry.getKey(), entry.getValue()); } + // Additionally serialize the stored model name as the typename, mirroring the deserialization process. + result.addProperty(TYPE_NAME, modelMetadata.getModelName()); + JsonObject serializedModel = (JsonObject) context.serialize(src.getModel()); for (Map.Entry entry : serializedModel.entrySet()) { result.add(entry.getKey(), entry.getValue()); diff --git a/aws-api-appsync/src/test/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapterTest.java b/aws-api-appsync/src/test/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapterTest.java index c095560913..f2beccc0be 100644 --- a/aws-api-appsync/src/test/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapterTest.java +++ b/aws-api-appsync/src/test/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapterTest.java @@ -68,7 +68,7 @@ public void setup() { public void adapterCanSerializeMwm() throws JSONException { Temporal.Timestamp lastChangedAt = Temporal.Timestamp.now(); String modelId = UUID.randomUUID().toString(); - ModelMetadata metadata = new ModelMetadata(modelId, false, 4, lastChangedAt, "BlogOwner"); + ModelMetadata metadata = new ModelMetadata(modelId, false, 4, lastChangedAt); BlogOwner model = BlogOwner.builder() .name("Blog Owner") .build(); @@ -80,7 +80,7 @@ public void adapterCanSerializeMwm() throws JSONException { .put("_lastChangedAt", metadata.getLastChangedAt().getSecondsSinceEpoch()) .put("_deleted", metadata.isDeleted()) .put("_version", metadata.getVersion()) - .put("__typename", metadata.getTypename()) + .put("__typename", mwm.getSyncMetadata().getModelName()) .toString(); String actual = gson.toJson(mwm); JSONAssert.assertEquals(expected, actual, true); @@ -97,7 +97,7 @@ public void adapterCanDeserializeJsonIntoMwm() { .id("45a5f600-8aa8-41ac-a529-aed75036f5be") .build(); Temporal.Timestamp lastChangedAt = new Temporal.Timestamp(1594858827, TimeUnit.SECONDS); - ModelMetadata metadata = new ModelMetadata(model.getId(), false, 3, lastChangedAt, model.getModelName()); + ModelMetadata metadata = new ModelMetadata(model.getId(), false, 3, lastChangedAt); ModelWithMetadata expected = new ModelWithMetadata<>(model, metadata); // Arrange some JSON, and then try to deserialize it @@ -133,15 +133,14 @@ public void adapterCanDeserializeJsonOfSerializedModelIntoMwm() throws AmplifyEx .serializedData(postSerializedData) .build(); Temporal.Timestamp lastChangedAt = new Temporal.Timestamp(1594858827, TimeUnit.SECONDS); - ModelMetadata metadata = new ModelMetadata(model.getPrimaryKeyString(), false, 3, lastChangedAt, - model.getModelName()); + ModelMetadata metadata = new ModelMetadata(model.getPrimaryKeyString(), false, 3, lastChangedAt); ModelWithMetadata expected = new ModelWithMetadata<>(model, metadata); // Arrange some JSON, and then try to deserialize it String json = Resources.readAsString("serialized-model-with-metadata.json"); Type type = TypeMaker.getParameterizedType(ModelWithMetadata.class, SerializedModel.class); ModelWithMetadata actual = gson.fromJson(json, type); - + // Assert that the deserialized output matches out expected value Assert.assertEquals(expected, actual); } diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/AWSDataStorePluginTest.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/AWSDataStorePluginTest.java index e440bfc44d..41c82cad48 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/AWSDataStorePluginTest.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/AWSDataStorePluginTest.java @@ -231,8 +231,7 @@ public void clearStopsSyncAndDeletesDatabase() throws AmplifyException, JSONExce int indexOfResponseConsumer = 1; Consumer>> onResponse = invocation.getArgument(indexOfResponseConsumer); - ModelMetadata modelMetadata = new ModelMetadata(person1.getId(), false, 1, Temporal.Timestamp.now(), - "Person"); + ModelMetadata modelMetadata = new ModelMetadata(person1.getId(), false, 1, Temporal.Timestamp.now()); ModelWithMetadata modelWithMetadata = new ModelWithMetadata<>(person1, modelMetadata); onResponse.accept(new GraphQLResponse<>(modelWithMetadata, Collections.emptyList())); return mock(GraphQLOperation.class); @@ -256,7 +255,7 @@ public void clearStopsSyncAndDeletesDatabase() throws AmplifyException, JSONExce Consumer>> onResponse = invocation.getArgument(indexOfResponseConsumer); ModelMetadata modelMetadata = new ModelMetadata(person2.getId(), false, 1, - Temporal.Timestamp.now(), "Person"); + Temporal.Timestamp.now()); ModelWithMetadata modelWithMetadata = new ModelWithMetadata<>(person2, modelMetadata); onResponse.accept(new GraphQLResponse<>(modelWithMetadata, Collections.emptyList())); return mock(GraphQLOperation.class); @@ -325,7 +324,7 @@ public void stopStopsSyncUntilNextInteraction() throws AmplifyException, JSONExc Consumer>> onResponse = invocation.getArgument(indexOfResponseConsumer); ModelMetadata modelMetadata = new ModelMetadata(person1.getPrimaryKeyString(), false, 1, - Temporal.Timestamp.now(), "Person"); + Temporal.Timestamp.now()); ModelWithMetadata modelWithMetadata = new ModelWithMetadata<>(person1, modelMetadata); onResponse.accept(new GraphQLResponse<>(modelWithMetadata, Collections.emptyList())); return mock(GraphQLOperation.class); @@ -349,7 +348,7 @@ public void stopStopsSyncUntilNextInteraction() throws AmplifyException, JSONExc Consumer>> onResponse = invocation.getArgument(indexOfResponseConsumer); ModelMetadata modelMetadata = new ModelMetadata(person2.getPrimaryKeyString(), false, 1, - Temporal.Timestamp.now(), "Person"); + Temporal.Timestamp.now()); ModelWithMetadata modelWithMetadata = new ModelWithMetadata<>(person2, modelMetadata); onResponse.accept(new GraphQLResponse<>(modelWithMetadata, Collections.emptyList())); return mock(GraphQLOperation.class); diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java index e5ef6dfe54..037aa400e5 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java @@ -191,8 +191,7 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { int indexOfResponseConsumer = 1; Consumer>> onResponse = invocation.getArgument(indexOfResponseConsumer); - ModelMetadata modelMetadata = new ModelMetadata(person1.getId(), false, 1, Temporal.Timestamp.now(), - "Person"); + ModelMetadata modelMetadata = new ModelMetadata(person1.getId(), false, 1, Temporal.Timestamp.now()); ModelWithMetadata modelWithMetadata = new ModelWithMetadata<>(person1, modelMetadata); onResponse.accept(new GraphQLResponse<>(modelWithMetadata, Collections.emptyList())); verify(mockApiCategory, atLeast(2)).mutate(argThat(getMatcherFor(person1)), @@ -207,7 +206,7 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { doAnswer(invocation -> { int indexOfResponseConsumer = 1; ModelMetadata modelMetadata = new ModelMetadata(person1.getId(), false, 1, - Temporal.Timestamp.now(), "Person"); + Temporal.Timestamp.now()); ModelWithMetadata modelWithMetadata = new ModelWithMetadata<>(person1, modelMetadata); // Mock the API emitting an ApiEndpointStatusChangeEvent event. Consumer>>> onResponse = @@ -221,7 +220,7 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { }).doAnswer(invocation -> { int indexOfResponseConsumer = 1; Car car = Car.builder().build(); - ModelMetadata modelMetadata = new ModelMetadata(car.getId(), false, 1, Temporal.Timestamp.now(), "Person"); + ModelMetadata modelMetadata = new ModelMetadata(car.getId(), false, 1, Temporal.Timestamp.now()); ModelWithMetadata modelWithMetadata = new ModelWithMetadata<>(car, modelMetadata); Consumer>>> onResponse = invocation.getArgument(indexOfResponseConsumer); diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/MutationProcessorRetryTest.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/MutationProcessorRetryTest.java index 4e58b5ce97..62ef05359e 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/MutationProcessorRetryTest.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/MutationProcessorRetryTest.java @@ -167,8 +167,7 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { int indexOfResponseConsumer = 1; Consumer>> onResponse = invocation.getArgument(indexOfResponseConsumer); - ModelMetadata modelMetadata = new ModelMetadata(person1.getId(), false, 1, Temporal.Timestamp.now(), - "Person"); + ModelMetadata modelMetadata = new ModelMetadata(person1.getId(), false, 1, Temporal.Timestamp.now()); ModelWithMetadata modelWithMetadata = new ModelWithMetadata<>(person1, modelMetadata); onResponse.accept(new GraphQLResponse<>(modelWithMetadata, Collections.emptyList())); verify(mockApiCategory, atLeast(2)).mutate(argThat(getMatcherFor(person1)), @@ -183,7 +182,7 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { doAnswer(invocation -> { int indexOfResponseConsumer = 1; ModelMetadata modelMetadata = new ModelMetadata(person1.getId(), false, 1, - Temporal.Timestamp.now(), "Person"); + Temporal.Timestamp.now()); ModelWithMetadata modelWithMetadata = new ModelWithMetadata<>(person1, modelMetadata); // Mock the API emitting an ApiEndpointStatusChangeEvent event. Consumer>>> onResponse = @@ -197,7 +196,7 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { }).doAnswer(invocation -> { int indexOfResponseConsumer = 1; Car car = Car.builder().build(); - ModelMetadata modelMetadata = new ModelMetadata(car.getId(), false, 1, Temporal.Timestamp.now(), "Person"); + ModelMetadata modelMetadata = new ModelMetadata(car.getId(), false, 1, Temporal.Timestamp.now()); ModelWithMetadata modelWithMetadata = new ModelWithMetadata<>(car, modelMetadata); Consumer>>> onResponse = invocation.getArgument(indexOfResponseConsumer); diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncConflictUnhandledErrorFactoryTest.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncConflictUnhandledErrorFactoryTest.java index bdb416f8a2..4b7c395804 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncConflictUnhandledErrorFactoryTest.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncConflictUnhandledErrorFactoryTest.java @@ -38,7 +38,7 @@ public void createUnhandledConflictError() { .name("Blogger Tony") .build(); Temporal.Timestamp lastChangedAt = new Temporal.Timestamp(1602732606L, TimeUnit.SECONDS); - ModelMetadata metadata = new ModelMetadata(model.getPrimaryKeyString(), true, 6, lastChangedAt, "BlogOwner"); + ModelMetadata metadata = new ModelMetadata(model.getPrimaryKeyString(), true, 6, lastChangedAt); ModelWithMetadata serverData = new ModelWithMetadata<>(model, metadata); AppSyncConflictUnhandledError error = AppSyncConflictUnhandledErrorFactory.createUnhandledConflictError(serverData); diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncConflictUnhandledErrorTest.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncConflictUnhandledErrorTest.java index ecb085f533..8e3bf4975f 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncConflictUnhandledErrorTest.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncConflictUnhandledErrorTest.java @@ -124,7 +124,7 @@ public void conflictErrorExtractedIfPresent() { assertEquals( new ModelWithMetadata<>( new Note("KoolId22", "Resurecting the dataz"), - new ModelMetadata("KoolId22", true, 7, lastChangedAt, "Note") + new ModelMetadata("KoolId22", true, 7, lastChangedAt) ), conflictUnhandledError.getServerVersion() ); diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncMocking.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncMocking.java index 7122b1aa94..39aef7bb13 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncMocking.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncMocking.java @@ -204,8 +204,7 @@ public CreateConfigurator mockSuccessResponse( */ @NonNull public CreateConfigurator mockSuccessResponse(@NonNull T model) { - ModelMetadata metadata = new ModelMetadata(model.getPrimaryKeyString(), false, 1, Temporal.Timestamp.now(), - model.getModelName()); + ModelMetadata metadata = new ModelMetadata(model.getPrimaryKeyString(), false, 1, Temporal.Timestamp.now()); ModelWithMetadata modelWithMetadata = new ModelWithMetadata<>(model, metadata); return mockSuccessResponse(model, modelWithMetadata); } @@ -313,7 +312,7 @@ public UpdateConfigurator mockSuccessResponse( public UpdateConfigurator mockSuccessResponse(@NonNull T model, int version) { Temporal.Timestamp lastChangedAt = Temporal.Timestamp.now(); ModelMetadata metadata = new ModelMetadata(model.getPrimaryKeyString(), false, version + 1, - lastChangedAt, model.getModelName()); + lastChangedAt); ModelWithMetadata modelWithMetadata = new ModelWithMetadata<>(model, metadata); return mockSuccessResponse(model, version, modelWithMetadata); } @@ -426,7 +425,7 @@ public DeleteConfigurator mockSuccessResponse( public DeleteConfigurator mockSuccessResponse(@NonNull T model, int version) { Temporal.Timestamp lastChangedAt = Temporal.Timestamp.now(); ModelMetadata metadata = new ModelMetadata(model.getPrimaryKeyString(), true, version + 1, - lastChangedAt, model.getModelName()); + lastChangedAt); ModelWithMetadata modelWithMetadata = new ModelWithMetadata<>(model, metadata); return mockSuccessResponse(model, version, modelWithMetadata); } diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncMockingTest.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncMockingTest.java index 1f996decd1..cce66ee859 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncMockingTest.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncMockingTest.java @@ -170,7 +170,7 @@ public void mockErrorResponseForCreate() { public void mockSuccessResponseForUpdate() { ModelMetadata updatedMetadata = new ModelMetadata(StrawMen.TONY_MODEL.getPrimaryKeyString(), false, 2, StrawMen.JOE_METADATA - .getLastChangedAt(), StrawMen.TONY_MODEL.getModelName()); + .getLastChangedAt()); ModelWithMetadata tonyWithUpdatedMetadata = new ModelWithMetadata<>(StrawMen.TONY_MODEL, updatedMetadata); AppSyncMocking.update(appSync) @@ -218,7 +218,7 @@ public void mockErrorResponseForUpdate() { public void mockSuccessResponseForDelete() { ModelMetadata deletedMetadata = new ModelMetadata(StrawMen.TONY_MODEL.getPrimaryKeyString(), true, 2, - StrawMen.JOE_METADATA.getLastChangedAt(), StrawMen.TONY_MODEL.getModelName()); + StrawMen.JOE_METADATA.getLastChangedAt()); ModelWithMetadata tonyWithDeleteMetadata = new ModelWithMetadata<>(StrawMen.TONY_MODEL, deletedMetadata); AppSyncMocking.delete(appSync) @@ -335,8 +335,7 @@ static final class StrawMen { .name("Joe") .build(); static final ModelMetadata JOE_METADATA = - new ModelMetadata(JOE_MODEL.getPrimaryKeyString(), false, 1, Temporal.Timestamp.now(), - JOE_MODEL.getModelName()); + new ModelMetadata(JOE_MODEL.getPrimaryKeyString(), false, 1, Temporal.Timestamp.now()); static final ModelWithMetadata JOE = new ModelWithMetadata<>(JOE_MODEL, JOE_METADATA); @@ -344,8 +343,7 @@ static final class StrawMen { .name("Tony") .build(); static final ModelMetadata TONY_METADATA = - new ModelMetadata(TONY_MODEL.getPrimaryKeyString(), false, 1, Temporal.Timestamp.now(), - TONY_MODEL.getModelName()); + new ModelMetadata(TONY_MODEL.getPrimaryKeyString(), false, 1, Temporal.Timestamp.now()); static final ModelWithMetadata TONY = new ModelWithMetadata<>(TONY_MODEL, TONY_METADATA); diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/TestModelWithMetadataInstances.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/TestModelWithMetadataInstances.java index 91ca2b7b74..0eaa1efa99 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/TestModelWithMetadataInstances.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/TestModelWithMetadataInstances.java @@ -40,7 +40,7 @@ public final class TestModelWithMetadataInstances { new ModelMetadata("d5b44350-b8e9-4deb-94c2-7fe986d6a0e1", null, 3, - new Temporal.Timestamp(223344L, TimeUnit.SECONDS), "BlogOwner") + new Temporal.Timestamp(223344L, TimeUnit.SECONDS)) ); public static final ModelWithMetadata BLOGGER_ISLA = new ModelWithMetadata<>( @@ -51,7 +51,7 @@ public final class TestModelWithMetadataInstances { new ModelMetadata("c0601168-2931-4bc0-bf13-5963cd31f828", null, 11, - new Temporal.Timestamp(998877L, TimeUnit.SECONDS), "BlogOwner") + new Temporal.Timestamp(998877L, TimeUnit.SECONDS)) ); public static final ModelWithMetadata DRUM_POST = new ModelWithMetadata<>( @@ -64,7 +64,7 @@ public final class TestModelWithMetadataInstances { new ModelMetadata("83ceb757-c8c8-4b6a-bee0-a43afb53a73a", null, 5, - new Temporal.Timestamp(123123L, TimeUnit.SECONDS), "Post") + new Temporal.Timestamp(123123L, TimeUnit.SECONDS)) ); public static final ModelWithMetadata DELETED_DRUM_POST = new ModelWithMetadata<>( @@ -72,7 +72,7 @@ public final class TestModelWithMetadataInstances { new ModelMetadata("83ceb757-c8c8-4b6a-bee0-a43afb53a73a", Boolean.TRUE, 5, - new Temporal.Timestamp(123123L, TimeUnit.SECONDS), "Post") + new Temporal.Timestamp(123123L, TimeUnit.SECONDS)) ); private TestModelWithMetadataInstances() {} diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/ConflictResolverTest.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/ConflictResolverTest.java index 013cd88ce5..5b6c81a4aa 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/ConflictResolverTest.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/ConflictResolverTest.java @@ -104,7 +104,7 @@ public void conflictIsResolvedByApplyingRemoteData() throws DataStoreException { .name("Remote Susan") .build(); Temporal.Timestamp now = Temporal.Timestamp.now(); - ModelMetadata modelMetadata = new ModelMetadata(serverSusan.getId(), false, 2, now, "BlogOwner"); + ModelMetadata modelMetadata = new ModelMetadata(serverSusan.getId(), false, 2, now); ModelWithMetadata serverData = new ModelWithMetadata<>(serverSusan, modelMetadata); // Arrange a conflict error that we could hypothetically get from AppSync @@ -151,7 +151,7 @@ public void conflictIsResolvedByRetryingLocalData() throws DataStoreException { .name("Server Blogger") .build(); Temporal.Timestamp now = Temporal.Timestamp.now(); - ModelMetadata metadata = new ModelMetadata(serverModel.getId(), false, 4, now, "BlogOwner"); + ModelMetadata metadata = new ModelMetadata(serverModel.getId(), false, 4, now); ModelWithMetadata serverData = new ModelWithMetadata<>(serverModel, metadata); // Arrange a hypothetical conflict error from AppSync @@ -212,7 +212,7 @@ public void conflictIsResolvedByRetryingLocalDataWithSerializedModel() throws Am .name("Server Blogger") .build(); Temporal.Timestamp now = Temporal.Timestamp.now(); - ModelMetadata metadata = new ModelMetadata(serverModel.getId(), false, 4, now, "BlogOwner"); + ModelMetadata metadata = new ModelMetadata(serverModel.getId(), false, 4, now); ModelWithMetadata serverData = new ModelWithMetadata<>(serializedOwner, metadata); // Arrange a hypothetical conflict error from AppSync @@ -270,7 +270,7 @@ public void conflictIsResolvedByRetryingLocalDataWithFlutterSerializedModel() th .build(); Temporal.Timestamp now = Temporal.Timestamp.now(); - ModelMetadata metadata = new ModelMetadata(serverModel.getPrimaryKeyString(), false, 4, now, "BlogOwner"); + ModelMetadata metadata = new ModelMetadata(serverModel.getPrimaryKeyString(), false, 4, now); ModelWithMetadata serverData = new ModelWithMetadata<>(serializedOwner, metadata); // Arrange a hypothetical conflict error from AppSync @@ -312,7 +312,7 @@ public void conflictIsResolvedByRetryingWithCustomModel() throws DataStoreExcept .name("Remote model") .build(); Temporal.Timestamp now = Temporal.Timestamp.now(); - ModelMetadata remoteMetadata = new ModelMetadata(remoteModel.getPrimaryKeyString(), false, 4, now, "BlogOwner"); + ModelMetadata remoteMetadata = new ModelMetadata(remoteModel.getPrimaryKeyString(), false, 4, now); ModelWithMetadata remoteData = new ModelWithMetadata<>(remoteModel, remoteMetadata); // Arrange an unhandled conflict error based on the server data AppSyncConflictUnhandledError unhandledConflictError = @@ -330,7 +330,7 @@ public void conflictIsResolvedByRetryingWithCustomModel() throws DataStoreExcept // When the AppSync update API is called, return a mock response ModelMetadata metadata = new ModelMetadata(customModel.getPrimaryKeyString(), false, - remoteMetadata.getVersion(), now, "BlogOwner"); + remoteMetadata.getVersion(), now); ModelWithMetadata responseData = new ModelWithMetadata<>(customModel, metadata); AppSyncMocking.update(appSync) .mockSuccessResponse(customModel, remoteMetadata.getVersion(), responseData); diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/MergerTest.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/MergerTest.java index 6243883c3e..62cf0b5550 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/MergerTest.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/MergerTest.java @@ -92,16 +92,14 @@ public void mergeDeletionForExistingItem() throws DataStoreException, Interrupte .name("Jameson") .build(); ModelMetadata originalMetadata = - new ModelMetadata(blogOwner.getId(), false, 1, Temporal.Timestamp.now(), - blogOwner.getModelName()); + new ModelMetadata(blogOwner.getId(), false, 1, Temporal.Timestamp.now()); storageAdapter.save(blogOwner, originalMetadata); // Just to be sure, our arrangement worked, and that thing is in there, right? Good. assertEquals(Collections.singletonList(blogOwner), storageAdapter.query(BlogOwner.class)); // Act: merge a model deletion. ModelMetadata deletionMetadata = - new ModelMetadata(blogOwner.getId(), true, 2, Temporal.Timestamp.now(), - blogOwner.getModelName()); + new ModelMetadata(blogOwner.getId(), true, 2, Temporal.Timestamp.now()); TestObserver observer = merger.merge(new ModelWithMetadata<>(blogOwner, deletionMetadata)).test(); assertTrue(observer.await(REASONABLE_WAIT_TIME, TimeUnit.MILLISECONDS)); @@ -130,8 +128,7 @@ public void mergeDeletionForNotExistingItem() throws DataStoreException, Interru // Act: try to merge a deletion that refers to an item not in the store ModelMetadata deletionMetadata = - new ModelMetadata(blogOwner.getId(), true, 1, Temporal.Timestamp.now(), - blogOwner.getModelName()); + new ModelMetadata(blogOwner.getId(), true, 1, Temporal.Timestamp.now()); TestObserver observer = merger.merge(new ModelWithMetadata<>(blogOwner, deletionMetadata)).test(); assertTrue(observer.await(REASONABLE_WAIT_TIME, TimeUnit.MILLISECONDS)); @@ -155,7 +152,7 @@ public void mergeSaveForNotExistingItem() throws DataStoreException, Interrupted .build(); ModelMetadata metadata = new ModelMetadata(blogOwner.getModelName() + "|" + blogOwner.getId(), false, 1, - Temporal.Timestamp.now(), blogOwner.getModelName()); + Temporal.Timestamp.now()); // Note that storageAdapter.save(...) is NOT called! // storageAdapter.save(blogOwner, metadata); @@ -187,8 +184,7 @@ public void mergeSaveForExistingItem() throws DataStoreException, InterruptedExc originalModel.getModelName() + "|" + originalModel.getId(), false, 1, - Temporal.Timestamp.now(), - originalModel.getModelName()); + Temporal.Timestamp.now()); storageAdapter.save(originalModel, originalMetadata); // Act: merge a save. @@ -196,8 +192,7 @@ public void mergeSaveForExistingItem() throws DataStoreException, InterruptedExc .name("Jameson The New and Improved") .build(); ModelMetadata updatedMetadata = - new ModelMetadata(originalMetadata.resolveIdentifier(), false, 2, Temporal.Timestamp.now(), - updatedModel.getModelName()); + new ModelMetadata(originalMetadata.resolveIdentifier(), false, 2, Temporal.Timestamp.now()); TestObserver observer = merger.merge(new ModelWithMetadata<>(updatedModel, updatedMetadata)).test(); assertTrue(observer.await(REASONABLE_WAIT_TIME, TimeUnit.MILLISECONDS)); observer.assertComplete().assertNoErrors(); @@ -225,8 +220,7 @@ public void itemIsNotMergedWhenOutboxHasPendingMutation() throws AmplifyExceptio .id(knownId) .build(); ModelMetadata localMetadata = - new ModelMetadata(blogOwner.getId(), false, 1, Temporal.Timestamp.now(), - blogOwner.getModelName()); + new ModelMetadata(blogOwner.getId(), false, 1, Temporal.Timestamp.now()); storageAdapter.save(blogOwner, localMetadata); ModelSchema schema = ModelSchema.fromModelClass(BlogOwner.class); @@ -240,8 +234,7 @@ public void itemIsNotMergedWhenOutboxHasPendingMutation() throws AmplifyExceptio // Act: now, cloud sync happens, and the sync engine tries to apply an update // for the same model ID, into the store. According to the cloud, this same // item should be DELETED. - ModelMetadata cloudMetadata = new ModelMetadata(knownId, true, 2, Temporal.Timestamp.now(), - blogOwner.getModelName()); + ModelMetadata cloudMetadata = new ModelMetadata(knownId, true, 2, Temporal.Timestamp.now()); TestObserver mergeObserver = merger.merge(new ModelWithMetadata<>(blogOwner, cloudMetadata)).test(); mergeObserver.await(REASONABLE_WAIT_TIME, TimeUnit.MILLISECONDS); mergeObserver.assertNoErrors().assertComplete(); @@ -270,8 +263,7 @@ public void itemIsMergedAfterPendingMutationRemovedFromOutbox() throws AmplifyEx .id(knownId) .build(); ModelMetadata localMetadata = - new ModelMetadata(blogOwner.getId(), false, 1, Temporal.Timestamp.now(), - blogOwner.getModelName()); + new ModelMetadata(blogOwner.getId(), false, 1, Temporal.Timestamp.now()); storageAdapter.save(blogOwner, localMetadata); ModelSchema schema = ModelSchema.fromModelClass(BlogOwner.class); @@ -286,8 +278,7 @@ public void itemIsMergedAfterPendingMutationRemovedFromOutbox() throws AmplifyEx // Act: now, cloud sync happens, and the sync engine tries to apply an update // for the same model ID, into the store. According to the cloud, this same // item should be DELETED. - ModelMetadata cloudMetadata = new ModelMetadata(knownId, true, 2, Temporal.Timestamp.now(), - blogOwner.getModelName()); + ModelMetadata cloudMetadata = new ModelMetadata(knownId, true, 2, Temporal.Timestamp.now()); TestObserver observer = mutationOutbox.remove(pendingMutation.getMutationId()) .andThen(merger.merge(new ModelWithMetadata<>(blogOwner, cloudMetadata))) @@ -315,7 +306,7 @@ public void itemWithLowerVersionIsNotMerged() throws AmplifyException, Interrupt .name("Cornelius Daniels") .build(); ModelMetadata existingMetadata = new ModelMetadata(existingModel.getId(), false, 55, - Temporal.Timestamp.now(), existingModel.getModelName()); + Temporal.Timestamp.now()); storageAdapter.save(existingModel, existingMetadata); // Act: try to merge, but specify a LOWER version. @@ -323,8 +314,7 @@ public void itemWithLowerVersionIsNotMerged() throws AmplifyException, Interrupt .name("Cornelius Daniels, but woke af, now.") .build(); ModelMetadata lowerVersionMetadata = - new ModelMetadata(incomingModel.getId(), false, 33, Temporal.Timestamp.now(), - incomingModel.getModelName()); + new ModelMetadata(incomingModel.getId(), false, 33, Temporal.Timestamp.now()); ModelWithMetadata modelWithLowerVersionMetadata = new ModelWithMetadata<>(existingModel, lowerVersionMetadata); TestObserver mergeObserver = merger.merge(modelWithLowerVersionMetadata).test(); @@ -361,8 +351,7 @@ public void itemWithSameVersionIsNotMerged() throws AmplifyException, Interrupte existingModel.getModelName() + "|" + existingModel.getId(), false, 55, - Temporal.Timestamp.now(), - existingModel.getModelName()); + Temporal.Timestamp.now()); storageAdapter.save(existingModel, existingMetadata); // Act: try to merge, but specify a LOWER version. @@ -370,8 +359,7 @@ public void itemWithSameVersionIsNotMerged() throws AmplifyException, Interrupte .name("Cornelius Daniels, but woke af, now.") .build(); ModelMetadata lowerVersionMetadata = - new ModelMetadata(incomingModel.getId(), false, 33, Temporal.Timestamp.now(), - incomingModel.getModelName()); + new ModelMetadata(incomingModel.getId(), false, 33, Temporal.Timestamp.now()); ModelWithMetadata modelWithLowerVersionMetadata = new ModelWithMetadata<>(incomingModel, lowerVersionMetadata); TestObserver mergeObserver = merger.merge(modelWithLowerVersionMetadata).test(); @@ -408,7 +396,7 @@ public void itemWithoutVersionIsNotMerged() throws AmplifyException, Interrupted .name("Cornelius Daniels") .build(); ModelMetadata existingMetadata = new ModelMetadata(existingModel.getId(), false, - 1, Temporal.Timestamp.now(), existingModel.getModelName()); + 1, Temporal.Timestamp.now()); storageAdapter.save(existingModel, existingMetadata); // Act: try to merge, but don't specify a version in the metadata being used to merge. @@ -416,7 +404,7 @@ public void itemWithoutVersionIsNotMerged() throws AmplifyException, Interrupted .name("Cornelius Daniels, but woke af, now.") .build(); ModelMetadata metadataWithoutVersion = new ModelMetadata(incomingModel.getId(), null, null, - null, incomingModel.getModelName()); + null); ModelWithMetadata incomingModelWithMetadata = new ModelWithMetadata<>(existingModel, metadataWithoutVersion); TestObserver mergeObserver = merger.merge(incomingModelWithMetadata).test(); @@ -452,7 +440,7 @@ public void orphanedItemIsNotMerged() throws DataStoreException, InterruptedExce .owner(badOwner) .build(); ModelMetadata metadata = new ModelMetadata(orphanedBlog.getId(), false, 1, - Temporal.Timestamp.now(), orphanedBlog.getModelName()); + Temporal.Timestamp.now()); // Enforce foreign key constraint on in-memory storage adapter doThrow(SQLiteConstraintException.class) diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/MutationProcessorTest.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/MutationProcessorTest.java index 33ab3de830..71bf951403 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/MutationProcessorTest.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/MutationProcessorTest.java @@ -207,7 +207,7 @@ public void conflictHandlerInvokedForUnhandledConflictError() throws AmplifyExce .build(); ModelMetadata metadata = new ModelMetadata(model.getModelName() + "|" + model.getPrimaryKeyString(), false, 1, - Temporal.Timestamp.now(), model.getModelName()); + Temporal.Timestamp.now()); ModelSchema schema = schemaRegistry.getModelSchemaForModelClass(BlogOwner.class); LastSyncMetadata lastSyncMetadata = LastSyncMetadata.baseSyncedAt(schema.getName(), 1_000L); synchronousStorageAdapter.save(model, metadata, lastSyncMetadata); @@ -260,7 +260,7 @@ public void hubEventPublishedForPublicationError() throws DataStoreException { .build(); ModelMetadata metadata = new ModelMetadata(model.getModelName() + "|" + model.getPrimaryKeyString(), false, 1, - Temporal.Timestamp.now(), model.getModelName()); + Temporal.Timestamp.now()); ModelSchema schema = schemaRegistry.getModelSchemaForModelClass(BlogOwner.class); synchronousStorageAdapter.save(model, metadata); @@ -344,7 +344,7 @@ public void retryStrategyAppliedAfterRecoverableError() .build(); ModelMetadata metadata = new ModelMetadata(model.getModelName() + "|" + model.getPrimaryKeyString(), false, 1, - Temporal.Timestamp.now(), model.getModelName()); + Temporal.Timestamp.now()); doAnswer(invocation -> { int indexOfResponseConsumer = 5; Consumer onError = @@ -358,8 +358,7 @@ public void retryStrategyAppliedAfterRecoverableError() Consumer>> onResponse = invocation.getArgument(indexOfResponseConsumer); ModelMetadata modelMetadata = new ModelMetadata(model.getId(), false, 1, - Temporal.Timestamp.now(), - "BlogOwner"); + Temporal.Timestamp.now()); ModelWithMetadata modelWithMetadata = new ModelWithMetadata(model, modelMetadata); retryHandlerInvocationCount.countDown(); diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/OrchestratorTest.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/OrchestratorTest.java index 519d4a1a97..2d49f8b924 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/OrchestratorTest.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/OrchestratorTest.java @@ -89,7 +89,7 @@ public void setup() throws AmplifyException { ModelMetadata metadata = new ModelMetadata(susan.getId(), false, 1, - Temporal.Timestamp.now(), susan.getModelName()); + Temporal.Timestamp.now()); ModelWithMetadata modelWithMetadata = new ModelWithMetadata<>(susan, metadata); // Mock behaviors from for the API category mockApi = mock(GraphQLBehavior.class); diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/SubscriptionProcessorTest.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/SubscriptionProcessorTest.java index c20ddbc928..56feb272d7 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/SubscriptionProcessorTest.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/SubscriptionProcessorTest.java @@ -191,7 +191,7 @@ private boolean isDataMergedWhenBufferDrainedForBlogOwnerNamed(String name) .name(name) .build(); ModelMetadata modelMetadata = new ModelMetadata(model.getPrimaryKeyString(), false, 1, - Temporal.Timestamp.now(), model.getModelName()); + Temporal.Timestamp.now()); ModelWithMetadata modelWithMetadata = new ModelWithMetadata<>(model, modelMetadata); GraphQLResponse> response = new GraphQLResponse<>(modelWithMetadata, null); arrangeDataEmittingSubscription(appSync, diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/SyncProcessorTest.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/SyncProcessorTest.java index e5ee95b437..28e9024f31 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/SyncProcessorTest.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/SyncProcessorTest.java @@ -819,8 +819,7 @@ private static ModelWithMetadata randomBlogOwnerWithMetadata() { .build(); Temporal.Timestamp randomTimestamp = new Temporal.Timestamp(new Random().nextLong(), TimeUnit.SECONDS); return new ModelWithMetadata<>(blogOwner, - new ModelMetadata(blogOwner.getId(), null, new Random().nextInt(), randomTimestamp, - blogOwner.getModelName()) + new ModelMetadata(blogOwner.getId(), null, new Random().nextInt(), randomTimestamp) ); } diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/VersionRepositoryTest.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/VersionRepositoryTest.java index 37bad81185..50073ed1f7 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/VersionRepositoryTest.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/syncengine/VersionRepositoryTest.java @@ -105,7 +105,7 @@ public void emitsErrorWhenMetadataHasNullVersion() throws DataStoreException, In .build(); ModelMetadata metadata = new ModelMetadata(blogOwner.getModelName() + "|" + blogOwner.getId(), null, - null, null, blogOwner.getModelName()); + null, null); storageAdapter.save(blogOwner, metadata); // Act: try to get the version. @@ -141,8 +141,7 @@ public void emitsSuccessWithValueWhenVersionInStore() throws DataStoreException, owner.getModelName() + "|" + owner.getId(), false, expectedVersion, - Temporal.Timestamp.now(), - owner.getModelName())); + Temporal.Timestamp.now())); // Act! Try to obtain it via the Versioning Repository. TestObserver observer = versionRepository.findModelVersion(owner).test(); From eae9cb062645b288a123e9cc3a7a12438bad88e6 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Thu, 17 Nov 2022 15:15:25 -0800 Subject: [PATCH 02/33] Test a potential fix --- .../com/amplifyframework/datastore/appsync/ModelMetadata.java | 2 +- .../datastore/appsync/ModelWithMetadataAdapter.java | 2 +- .../datastore/storage/sqlite/SQLiteCommandFactory.java | 2 ++ .../datastore/ConflictResolverIntegrationTest.java | 4 ++++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java index b74d3e57a5..c520a939f6 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java @@ -99,7 +99,7 @@ public Temporal.Timestamp getLastChangedAt() { * @return modelName */ @Nullable - public String getModelName() { + public String getUnderlyingModelName() { return id.split("\\|")[0]; } diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java index 7823852fad..43920d8f64 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java @@ -124,7 +124,7 @@ public JsonElement serialize( result.add(entry.getKey(), entry.getValue()); } // Additionally serialize the stored model name as the typename, mirroring the deserialization process. - result.addProperty(TYPE_NAME, modelMetadata.getModelName()); + result.addProperty(TYPE_NAME, modelMetadata.getUnderlyingModelName()); JsonObject serializedModel = (JsonObject) context.serialize(src.getModel()); for (Map.Entry entry : serializedModel.entrySet()) { diff --git a/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java b/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java index f9606ae06c..aa3afcf31f 100644 --- a/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java +++ b/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java @@ -484,7 +484,9 @@ private List extractFieldValues(@NonNull Model model) throws DataStoreEx final Map modelFields = schema.getFields(); final List bindings = new ArrayList<>(); Object fieldValue; + System.out.println("Extract field value: model = " + model); for (SQLiteColumn column : table.getSortedColumns()) { + System.out.println("extract field value: column = " + column); if (column.getName().equals(SQLiteTable.PRIMARY_KEY_FIELD_NAME)) { fieldValue = model.getPrimaryKeyString(); } else if (column.isForeignKey()) { diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java index 037aa400e5..4584783565 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java @@ -184,6 +184,7 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { extensions)); onResponse.accept(new GraphQLResponse<>(null, errorList)); // latch makes sure conflict unhandled response is returned. + System.out.println("LATCH 1"); latch.countDown(); return mock(GraphQLOperation.class); }).doAnswer(invocation -> { @@ -198,6 +199,7 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { any(), any()); // latch makes sure success response is returned. + System.out.println("LATCH 2"); latch.countDown(); return mock(GraphQLOperation.class); }).when(mockApiCategory).mutate(any(), any(), any()); @@ -214,6 +216,7 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { PaginatedResult> data = new PaginatedResult<>(Collections.singletonList(modelWithMetadata), null); onResponse.accept(new GraphQLResponse<>(data, Collections.emptyList())); + System.out.println("LATCH 3"); latch.countDown(); return mock(GraphQLOperation.class); @@ -227,6 +230,7 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { PaginatedResult> data = new PaginatedResult<>(Collections.singletonList(modelWithMetadata), null); onResponse.accept(new GraphQLResponse<>(data, Collections.emptyList())); + System.out.println("LATCH 4"); latch.countDown(); return mock(GraphQLOperation.class); }).when(mockApiCategory).query(any(), any(), any()); From 4e80b0c7082d031c772059e778f7d1611f4b069c Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Thu, 17 Nov 2022 15:38:27 -0800 Subject: [PATCH 03/33] Test a potential fix --- .../datastore/appsync/ModelWithMetadataAdapterTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-api-appsync/src/test/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapterTest.java b/aws-api-appsync/src/test/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapterTest.java index f2beccc0be..48eae27558 100644 --- a/aws-api-appsync/src/test/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapterTest.java +++ b/aws-api-appsync/src/test/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapterTest.java @@ -80,7 +80,7 @@ public void adapterCanSerializeMwm() throws JSONException { .put("_lastChangedAt", metadata.getLastChangedAt().getSecondsSinceEpoch()) .put("_deleted", metadata.isDeleted()) .put("_version", metadata.getVersion()) - .put("__typename", mwm.getSyncMetadata().getModelName()) + .put("__typename", mwm.getSyncMetadata().getUnderlyingModelName()) .toString(); String actual = gson.toJson(mwm); JSONAssert.assertEquals(expected, actual, true); From 587826e221e72a88d2c7f21f785da69075faa62b Mon Sep 17 00:00:00 2001 From: Saijad Dhuka <83975678+sdhuka@users.noreply.github.com> Date: Tue, 15 Nov 2022 15:11:17 -0600 Subject: [PATCH 04/33] fix: callbacks not invoked when attached using getTransfer api (#2111) * fix: listener not invoked when attached using getTransfer api * address PR comments * default state to unknown * add comment * add comment * override getRequest in storage operation & make SocketExcpetion retryable * add nullable annotation * address nullable request Co-authored-by: Tyler Roach --- .../storage/s3/AWSS3StorageUploadTest.java | 144 ++++++++++++++++++ .../storage/s3/AWSS3StoragePlugin.java | 111 +++++++------- .../storage/s3/TransferOperations.kt | 5 +- .../AWSS3StorageDownloadFileOperation.kt | 23 +-- .../AWSS3StorageUploadFileOperation.kt | 31 ++-- .../AWSS3StorageUploadInputStreamOperation.kt | 34 +++-- .../AWSS3StorageDownloadFileRequest.java | 3 + .../AWSS3StorageGetPresignedUrlRequest.java | 3 + .../s3/request/AWSS3StorageListRequest.java | 3 + .../s3/request/AWSS3StorageRemoveRequest.java | 3 + .../s3/request/AWSS3StorageUploadRequest.java | 3 + .../storage/s3/transfer/TransferListener.kt | 3 +- .../storage/s3/transfer/TransferObserver.kt | 2 +- .../s3/transfer/TransferStatusUpdater.kt | 10 +- .../s3/transfer/worker/BaseTransferWorker.kt | 7 +- .../storage/s3/StorageComponentTest.java | 6 +- .../core/async/AmplifyOperation.java | 1 + .../StorageDownloadFileOperation.java | 12 ++ .../operation/StorageUploadOperation.java | 12 ++ 19 files changed, 314 insertions(+), 102 deletions(-) diff --git a/aws-storage-s3/src/androidTest/java/com/amplifyframework/storage/s3/AWSS3StorageUploadTest.java b/aws-storage-s3/src/androidTest/java/com/amplifyframework/storage/s3/AWSS3StorageUploadTest.java index 25d418e62d..9fd05dc57e 100644 --- a/aws-storage-s3/src/androidTest/java/com/amplifyframework/storage/s3/AWSS3StorageUploadTest.java +++ b/aws-storage-s3/src/androidTest/java/com/amplifyframework/storage/s3/AWSS3StorageUploadTest.java @@ -30,7 +30,9 @@ import com.amplifyframework.storage.StorageChannelEventName; import com.amplifyframework.storage.TransferState; import com.amplifyframework.storage.operation.StorageUploadFileOperation; +import com.amplifyframework.storage.operation.StorageUploadInputStreamOperation; import com.amplifyframework.storage.options.StorageUploadFileOptions; +import com.amplifyframework.storage.options.StorageUploadInputStreamOptions; import com.amplifyframework.storage.s3.test.R; import com.amplifyframework.storage.s3.util.WorkmanagerTestUtils; import com.amplifyframework.testutils.random.RandomTempFile; @@ -43,6 +45,7 @@ import org.junit.Test; import java.io.File; +import java.io.FileInputStream; import java.util.HashSet; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -122,6 +125,21 @@ public void testUploadSmallFile() throws Exception { synchronousStorage.uploadFile(fileName, uploadFile, options); } + /** + * Tests that small file (single-part) uploads using input stream successfully. + * + * @throws Exception if upload fails + */ + @Test + public void testUploadSmallFileStream() throws Exception { + File uploadFile = new RandomTempFile(4 * 1024 * 1024); + String fileName = uploadFile.getName(); + StorageUploadInputStreamOptions options = StorageUploadInputStreamOptions.builder() + .accessLevel(TESTING_ACCESS_LEVEL) + .build(); + synchronousStorage.uploadInputStream(fileName, new FileInputStream(uploadFile), options); + } + /** * Tests that large file (multi-part) uploads successfully. * @@ -234,4 +252,130 @@ public void testUploadFileIsResumable() throws Exception { assertTrue(completed.await(EXTENDED_TIMEOUT_MS, TimeUnit.MILLISECONDS)); assertNull(errorContainer.get()); } + + /** + * Tests that file upload operation can be paused and resumed + * using getTransfer API. + * + * @throws Exception if upload is not paused, resumed, and + * completed successfully before timeout + */ + @SuppressWarnings("unchecked") + @Test + public void testUploadFileGetTransferOnPause() throws Exception { + final CountDownLatch completed = new CountDownLatch(1); + final CountDownLatch resumed = new CountDownLatch(1); + final AtomicReference transferId = new AtomicReference<>(); + final AtomicReference> opContainer = new AtomicReference<>(); + final AtomicReference errorContainer = new AtomicReference<>(); + + // Create a file large enough that transfer won't finish before being paused + File uploadFile = new RandomTempFile(LARGE_FILE_SIZE); + + // Listen to Hub events to resume when operation has been paused + SubscriptionToken resumeToken = Amplify.Hub.subscribe(HubChannel.STORAGE, hubEvent -> { + if (StorageChannelEventName.UPLOAD_STATE.toString().equals(hubEvent.getName())) { + HubEvent stateEvent = (HubEvent) hubEvent; + TransferState state = TransferState.getState(stateEvent.getData()); + if (TransferState.PAUSED.equals(state)) { + opContainer.get().clearAllListeners(); + storageCategory.getTransfer(transferId.get(), + operation -> { + StorageUploadFileOperation getOp = (StorageUploadFileOperation) operation; + getOp.resume(); + resumed.countDown(); + getOp.setOnSuccess(result -> { + completed.countDown(); + }); + }, errorContainer::set); + } + } + }); + subscriptions.add(resumeToken); + + // Begin uploading a large file + StorageUploadFileOperation op = storageCategory.uploadFile( + uploadFile.getName(), + uploadFile, + options, + progress -> { + if (progress.getCurrentBytes() > 0 && resumed.getCount() > 0) { + opContainer.get().pause(); + } + }, + result -> { }, + errorContainer::set + ); + opContainer.set(op); + transferId.set(op.getTransferId()); + + // Assert that all the required conditions have been met + assertTrue(resumed.await(EXTENDED_TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertTrue(completed.await(EXTENDED_TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertNull(errorContainer.get()); + } + + /** + * Tests that input stream upload operation can be paused and resumed + * using getTransfer API. + * + * @throws Exception if upload is not paused, resumed, and + * completed successfully before timeout + */ + @SuppressWarnings("unchecked") + @Test + public void testUploadInputStreamGetTransferOnPause() throws Exception { + final CountDownLatch completed = new CountDownLatch(1); + final CountDownLatch resumed = new CountDownLatch(1); + final AtomicReference transferId = new AtomicReference<>(); + final AtomicReference> opContainer = new AtomicReference<>(); + final AtomicReference errorContainer = new AtomicReference<>(); + + // Create a file large enough that transfer won't finish before being paused + File uploadFile = new RandomTempFile(LARGE_FILE_SIZE); + + // Listen to Hub events to resume when operation has been paused + SubscriptionToken resumeToken = Amplify.Hub.subscribe(HubChannel.STORAGE, hubEvent -> { + if (StorageChannelEventName.UPLOAD_STATE.toString().equals(hubEvent.getName())) { + HubEvent stateEvent = (HubEvent) hubEvent; + TransferState state = TransferState.getState(stateEvent.getData()); + if (TransferState.PAUSED.equals(state)) { + opContainer.get().clearAllListeners(); + storageCategory.getTransfer(transferId.get(), + operation -> { + StorageUploadFileOperation getOp = (StorageUploadFileOperation) operation; + getOp.resume(); + resumed.countDown(); + getOp.setOnSuccess(result -> { + completed.countDown(); + }); + }, errorContainer::set); + } + } + }); + subscriptions.add(resumeToken); + StorageUploadInputStreamOptions inputStreamOptions = StorageUploadInputStreamOptions.builder() + .accessLevel(TESTING_ACCESS_LEVEL) + .build(); + // Begin uploading a large file + StorageUploadInputStreamOperation op = storageCategory.uploadInputStream( + uploadFile.getName(), + new FileInputStream(uploadFile), + inputStreamOptions, + progress -> { + if (progress.getCurrentBytes() > 0 && resumed.getCount() > 0) { + opContainer.get().pause(); + } + }, + result -> { }, + errorContainer::set + ); + opContainer.set(op); + transferId.set(op.getTransferId()); + + // Assert that all the required conditions have been met + assertTrue(resumed.await(EXTENDED_TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertTrue(completed.await(EXTENDED_TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertNull(errorContainer.get()); + } } diff --git a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/AWSS3StoragePlugin.java b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/AWSS3StoragePlugin.java index e27f4938a1..8c7930172c 100644 --- a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/AWSS3StoragePlugin.java +++ b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/AWSS3StoragePlugin.java @@ -25,6 +25,7 @@ import com.amplifyframework.storage.StorageAccessLevel; import com.amplifyframework.storage.StorageException; import com.amplifyframework.storage.StoragePlugin; +import com.amplifyframework.storage.TransferState; import com.amplifyframework.storage.operation.StorageDownloadFileOperation; import com.amplifyframework.storage.operation.StorageGetUrlOperation; import com.amplifyframework.storage.operation.StorageListOperation; @@ -509,61 +510,67 @@ public void getTransfer( @NonNull Consumer> onReceived, @NonNull Consumer onError) { executorService.submit(() -> { - TransferRecord transferRecord = storageService.getTransfer(transferId); - if (transferRecord != null) { - TransferObserver transferObserver = - new TransferObserver( - transferRecord.getId(), - storageService.getTransferManager().getTransferStatusUpdater(), - transferRecord.getBucketName(), - transferRecord.getKey(), - transferRecord.getFile(), - null, - transferRecord.getState()); - TransferType transferType = transferRecord.getType(); - switch (Objects.requireNonNull(transferType)) { - case UPLOAD: - if (transferRecord.getFile().startsWith(TransferStatusUpdater.TEMP_FILE_PREFIX)) { - AWSS3StorageUploadInputStreamOperation operation = - new AWSS3StorageUploadInputStreamOperation( - transferId, - storageService, - executorService, - authCredentialsProvider, - awsS3StoragePluginConfiguration, - null, - transferObserver); - onReceived.accept(operation); - } else { - AWSS3StorageUploadFileOperation operation = - new AWSS3StorageUploadFileOperation( - transferId, - storageService, - executorService, - authCredentialsProvider, - awsS3StoragePluginConfiguration, - null, - transferObserver); - onReceived.accept(operation); - } - break; - case DOWNLOAD: - AWSS3StorageDownloadFileOperation - downloadFileOperation = new AWSS3StorageDownloadFileOperation( - transferId, - new File(transferRecord.getFile()), - storageService, - executorService, - authCredentialsProvider, - awsS3StoragePluginConfiguration, + try { + TransferRecord transferRecord = storageService.getTransfer(transferId); + if (transferRecord != null) { + TransferObserver transferObserver = + new TransferObserver( + transferRecord.getId(), + storageService.getTransferManager().getTransferStatusUpdater(), + transferRecord.getBucketName(), + transferRecord.getKey(), + transferRecord.getFile(), null, - transferObserver); - onReceived.accept(downloadFileOperation); - break; - default: + transferRecord.getState() != null ? transferRecord.getState() : TransferState.UNKNOWN); + TransferType transferType = transferRecord.getType(); + switch (Objects.requireNonNull(transferType)) { + case UPLOAD: + if (transferRecord.getFile().startsWith(TransferStatusUpdater.TEMP_FILE_PREFIX)) { + AWSS3StorageUploadInputStreamOperation operation = + new AWSS3StorageUploadInputStreamOperation( + transferId, + storageService, + executorService, + authCredentialsProvider, + awsS3StoragePluginConfiguration, + null, + transferObserver); + onReceived.accept(operation); + } else { + AWSS3StorageUploadFileOperation operation = + new AWSS3StorageUploadFileOperation( + transferId, + storageService, + executorService, + authCredentialsProvider, + awsS3StoragePluginConfiguration, + null, + transferObserver); + onReceived.accept(operation); + } + break; + case DOWNLOAD: + AWSS3StorageDownloadFileOperation + downloadFileOperation = new AWSS3StorageDownloadFileOperation( + transferId, + new File(transferRecord.getFile()), + storageService, + executorService, + authCredentialsProvider, + awsS3StoragePluginConfiguration, + null, + transferObserver); + onReceived.accept(downloadFileOperation); + break; + default: + } + } else { + onError.accept(new StorageException("Get transfer failed", + "Please verify that the transfer id is valid and the transfer is not completed")); } - } else { + } catch (Exception exception) { onError.accept(new StorageException("Get transfer failed", + exception, "Please verify that the transfer id is valid and the transfer is not completed")); } }); diff --git a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/TransferOperations.kt b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/TransferOperations.kt index b116d3df2b..6ec103dc00 100644 --- a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/TransferOperations.kt +++ b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/TransferOperations.kt @@ -114,10 +114,13 @@ internal object TransferOperations { workManager: WorkManager ): Boolean { if (!TransferState.isInTerminalState(transferRecord.state)) { - val nextState: TransferState = TransferState.PENDING_CANCEL + var nextState: TransferState = TransferState.PENDING_CANCEL if (TransferState.isPaused(transferRecord.state)) { if (transferRecord.isMultipart == 1) { abortMultipartUploadRequest(transferRecord, pluginKey, workManager) + } else { + // transfer is paused so directly mark it as canceled + nextState = TransferState.CANCELED } } else { workManager.cancelUniqueWork(transferRecord.id.toString()) diff --git a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/operation/AWSS3StorageDownloadFileOperation.kt b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/operation/AWSS3StorageDownloadFileOperation.kt index 5b3aa3db8e..79720f01e5 100644 --- a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/operation/AWSS3StorageDownloadFileOperation.kt +++ b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/operation/AWSS3StorageDownloadFileOperation.kt @@ -14,7 +14,6 @@ */ package com.amplifyframework.storage.s3.operation -import android.util.Log import com.amplifyframework.auth.AuthCredentialsProvider import com.amplifyframework.core.Amplify import com.amplifyframework.core.Consumer @@ -53,14 +52,7 @@ class AWSS3StorageDownloadFileOperation @JvmOverloads internal constructor( ) : StorageDownloadFileOperation(request, transferId, onProgress, onSuccess, onError) { init { - transferObserver?.let { - val listener = DownloadTransferListener() - Log.d( - "AWSS3StorageDownloadFileOperation", - "Setting up new transfer listener ${listener.hashCode()} for operation ${hashCode()}" - ) - it.setTransferListener(listener) - } + transferObserver?.setTransferListener(DownloadTransferListener()) } constructor( @@ -88,18 +80,19 @@ class AWSS3StorageDownloadFileOperation @JvmOverloads internal constructor( override fun start() { // Only start if it hasn't already been started - if (transferObserver != null || request == null) { + if (transferObserver != null) { return } + val downloadRequest = request ?: return executorService.submit( Runnable { awsS3StoragePluginConfiguration.getAWSS3PluginPrefixResolver(authCredentialsProvider).resolvePrefix( - request.accessLevel, - request.targetIdentityId, + downloadRequest.accessLevel, + downloadRequest.targetIdentityId, Consumer { prefix: String -> try { - val serviceKey = prefix + request.key - this.file = request.local + val serviceKey = prefix + downloadRequest.key + this.file = downloadRequest.local transferObserver = storageService.downloadToFile(transferId, serviceKey, file) transferObserver?.setTransferListener(DownloadTransferListener()) } catch (exception: Exception) { @@ -188,7 +181,7 @@ class AWSS3StorageDownloadFileOperation @JvmOverloads internal constructor( } inner class DownloadTransferListener : TransferListener { - override fun onStateChanged(transferId: Int, state: TransferState) { + override fun onStateChanged(transferId: Int, state: TransferState, key: String) { Amplify.Hub.publish( HubChannel.STORAGE, HubEvent.create(StorageChannelEventName.DOWNLOAD_STATE, state.name) diff --git a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/operation/AWSS3StorageUploadFileOperation.kt b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/operation/AWSS3StorageUploadFileOperation.kt index ac714252e7..4d8bf235c7 100644 --- a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/operation/AWSS3StorageUploadFileOperation.kt +++ b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/operation/AWSS3StorageUploadFileOperation.kt @@ -74,28 +74,33 @@ class AWSS3StorageUploadFileOperation @JvmOverloads internal constructor( onError ) + init { + transferObserver?.setTransferListener(UploadTransferListener()) + } + override fun start() { // Only start if it hasn't already been started - if (transferObserver != null || request == null) { + if (transferObserver != null) { return } + val uploadRequest = request ?: return executorService.submit( Runnable { awsS3StoragePluginConfiguration.getAWSS3PluginPrefixResolver(authCredentialsProvider).resolvePrefix( - request.accessLevel, - request.targetIdentityId, + uploadRequest.accessLevel, + uploadRequest.targetIdentityId, Consumer { prefix: String -> try { - val serviceKey = prefix + request.key + val serviceKey = prefix + uploadRequest.key // Grab the file to upload... - val file = request.local + val file = uploadRequest.local // Set up the metadata val objectMetadata = ObjectMetadata() - objectMetadata.userMetadata = request.metadata - objectMetadata.metaData[ObjectMetadata.CONTENT_TYPE] = request.contentType + objectMetadata.userMetadata = uploadRequest.metadata + objectMetadata.metaData[ObjectMetadata.CONTENT_TYPE] = uploadRequest.contentType val storageServerSideEncryption = - request.serverSideEncryption + uploadRequest.serverSideEncryption if (ServerSideEncryption.NONE != storageServerSideEncryption) { objectMetadata.metaData[ObjectMetadata.SERVER_SIDE_ENCRYPTION] = storageServerSideEncryption.getName() @@ -181,20 +186,22 @@ class AWSS3StorageUploadFileOperation @JvmOverloads internal constructor( override fun setOnSuccess(onSuccess: Consumer?) { super.setOnSuccess(onSuccess) - if (transferState == TransferState.COMPLETED) { - onSuccess?.accept(StorageUploadFileResult.fromKey(request.key)) + request?.let { + if (transferState == TransferState.COMPLETED) { + onSuccess?.accept(StorageUploadFileResult.fromKey(it.key)) + } } } private inner class UploadTransferListener : TransferListener { - override fun onStateChanged(transferId: Int, state: TransferState) { + override fun onStateChanged(transferId: Int, state: TransferState, key: String) { Amplify.Hub.publish( HubChannel.STORAGE, HubEvent.create(StorageChannelEventName.UPLOAD_STATE, state.name) ) when (state) { TransferState.COMPLETED -> { - onSuccess?.accept(StorageUploadFileResult.fromKey(request.key)) + onSuccess?.accept(StorageUploadFileResult.fromKey(key)) return } TransferState.FAILED -> {} diff --git a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/operation/AWSS3StorageUploadInputStreamOperation.kt b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/operation/AWSS3StorageUploadInputStreamOperation.kt index db0216c3ca..9289b3398d 100644 --- a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/operation/AWSS3StorageUploadInputStreamOperation.kt +++ b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/operation/AWSS3StorageUploadInputStreamOperation.kt @@ -81,29 +81,33 @@ class AWSS3StorageUploadInputStreamOperation @JvmOverloads internal constructor( onError ) - override fun start() { - // Only start if it hasn't already been started + init { + transferObserver?.setTransferListener(UploadTransferListener()) + } + override fun start() { // Only start if it hasn't already been started - if (transferObserver != null || request == null) { + if (transferObserver != null) { return } + + val uploadRequest = request ?: return executorService.submit( Runnable { awsS3StoragePluginConfiguration.getAWSS3PluginPrefixResolver(authCredentialsProvider).resolvePrefix( - request.accessLevel, - request.targetIdentityId, + uploadRequest.accessLevel, + uploadRequest.targetIdentityId, Consumer { prefix: String -> try { - val serviceKey = prefix + request.key + val serviceKey = prefix + uploadRequest.key // Grab the inputStream to upload... - val inputStream = request.local + val inputStream = uploadRequest.local // Set up the metadata val objectMetadata = ObjectMetadata() - objectMetadata.userMetadata = request.metadata - objectMetadata.metaData[ObjectMetadata.CONTENT_TYPE] = request.contentType + objectMetadata.userMetadata = uploadRequest.metadata + objectMetadata.metaData[ObjectMetadata.CONTENT_TYPE] = uploadRequest.contentType val storageServerSideEncryption = - request.serverSideEncryption + uploadRequest.serverSideEncryption if (ServerSideEncryption.NONE != storageServerSideEncryption) { objectMetadata.metaData[ObjectMetadata.SERVER_SIDE_ENCRYPTION] = storageServerSideEncryption.getName() @@ -194,20 +198,22 @@ class AWSS3StorageUploadInputStreamOperation @JvmOverloads internal constructor( override fun setOnSuccess(onSuccess: Consumer?) { super.setOnSuccess(onSuccess) - if (transferState == TransferState.COMPLETED) { - onSuccess?.accept(StorageUploadInputStreamResult.fromKey(request.key)) + request?.let { + if (transferState == TransferState.COMPLETED) { + onSuccess?.accept(StorageUploadInputStreamResult.fromKey(it.key)) + } } } private inner class UploadTransferListener : TransferListener { - override fun onStateChanged(transferId: Int, state: TransferState) { + override fun onStateChanged(transferId: Int, state: TransferState, key: String) { Amplify.Hub.publish( HubChannel.STORAGE, HubEvent.create(StorageChannelEventName.UPLOAD_STATE, state.name) ) when (state) { TransferState.COMPLETED -> { - onSuccess?.accept(StorageUploadInputStreamResult.fromKey(request.key)) + onSuccess?.accept(StorageUploadInputStreamResult.fromKey(key)) return } TransferState.FAILED -> { diff --git a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageDownloadFileRequest.java b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageDownloadFileRequest.java index e22fcd3480..0e1e89bb32 100644 --- a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageDownloadFileRequest.java +++ b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageDownloadFileRequest.java @@ -34,6 +34,9 @@ public final class AWSS3StorageDownloadFileRequest { /** * Constructs a new AWSS3StorageDownloadFileRequest. + * Although this has public access, it is intended for internal use and should not be used directly by host + * applications. The behavior of this may change without warning. + * * @param key key for item to download * @param local Target file for the downloaded file to be saved to * @param accessLevel Storage access level diff --git a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageGetPresignedUrlRequest.java b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageGetPresignedUrlRequest.java index be54ffa5ab..9c54976eee 100644 --- a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageGetPresignedUrlRequest.java +++ b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageGetPresignedUrlRequest.java @@ -32,6 +32,9 @@ public final class AWSS3StorageGetPresignedUrlRequest { /** * Constructs a new AWSS3StorageGetUrlRequest. + * Although this has public access, it is intended for internal use and should not be used directly by host + * applications. The behavior of this may change without warning. + * * @param key key for item to obtain URL for * @param accessLevel Storage access level * @param targetIdentityId If set, this should override the current user's identity ID. diff --git a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageListRequest.java b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageListRequest.java index c91b1bd04e..238b0636a3 100644 --- a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageListRequest.java +++ b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageListRequest.java @@ -30,6 +30,9 @@ public final class AWSS3StorageListRequest { /** * Constructs a new AWSS3StorageListRequest. + * Although this has public access, it is intended for internal use and should not be used directly by host + * applications. The behavior of this may change without warning. + * * @param path the path in S3 to list items from * @param accessLevel Storage access level * @param targetIdentityId If set, this should override the current user's identity ID. diff --git a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageRemoveRequest.java b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageRemoveRequest.java index f2811c7521..627e440c10 100644 --- a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageRemoveRequest.java +++ b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageRemoveRequest.java @@ -30,6 +30,9 @@ public final class AWSS3StorageRemoveRequest { /** * Constructs a new AWSS3StorageRemoveRequest. + * Although this has public access, it is intended for internal use and should not be used directly by host + * applications. The behavior of this may change without warning. + * * @param key key for item to download * @param accessLevel Storage access level * @param targetIdentityId If set, this should override the current user's identity ID. diff --git a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageUploadRequest.java b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageUploadRequest.java index 9c2c988f44..d5838d741d 100644 --- a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageUploadRequest.java +++ b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/request/AWSS3StorageUploadRequest.java @@ -39,6 +39,9 @@ public final class AWSS3StorageUploadRequest { /** * Constructs a new AWSS3StorageUploadRequest. + * Although this has public access, it is intended for internal use and should not be used directly by host + * applications. The behavior of this may change without warning. + * * @param key key for item to upload * @param local object to upload (e.g. File or InputStream) * @param accessLevel Storage access level diff --git a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/transfer/TransferListener.kt b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/transfer/TransferListener.kt index 466d82a181..2674200417 100644 --- a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/transfer/TransferListener.kt +++ b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/transfer/TransferListener.kt @@ -27,8 +27,9 @@ internal interface TransferListener { * * @param id The id of the transfer record. * @param state The new state of the transfer. + * @param key The key of the transfer */ - fun onStateChanged(id: Int, state: TransferState) + fun onStateChanged(id: Int, state: TransferState, key: String) /** * Called when more bytes are transferred. diff --git a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/transfer/TransferObserver.kt b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/transfer/TransferObserver.kt index eccc837b99..021e06a5b8 100644 --- a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/transfer/TransferObserver.kt +++ b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/transfer/TransferObserver.kt @@ -71,7 +71,7 @@ internal data class TransferObserver @JvmOverloads constructor( totalBytes = bytesTotal } - override fun onStateChanged(id: Int, state: TransferState) { + override fun onStateChanged(id: Int, state: TransferState, key: String) { transferState = state } } diff --git a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/transfer/TransferStatusUpdater.kt b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/transfer/TransferStatusUpdater.kt index ef180800c5..aa696afce2 100644 --- a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/transfer/TransferStatusUpdater.kt +++ b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/transfer/TransferStatusUpdater.kt @@ -110,7 +110,15 @@ internal class TransferStatusUpdater private constructor( } transferStatusListenerMap[transferRecord.id]?.forEach { listener -> - mainHandler.post { listener.onStateChanged(transferRecord.id, newState) } + mainHandler.post { + transferRecord.key?.let { key -> + listener.onStateChanged( + transferRecord.id, + newState, + key + ) + } + } } if (TransferState.isInTerminalState(newState)) { diff --git a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/transfer/worker/BaseTransferWorker.kt b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/transfer/worker/BaseTransferWorker.kt index de399907fa..67fb649352 100644 --- a/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/transfer/worker/BaseTransferWorker.kt +++ b/aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/transfer/worker/BaseTransferWorker.kt @@ -49,6 +49,7 @@ import com.amplifyframework.storage.s3.transfer.TransferRecord import com.amplifyframework.storage.s3.transfer.TransferStatusUpdater import java.io.File import java.lang.Exception +import java.net.SocketException import java.nio.ByteBuffer import kotlinx.coroutines.CancellationException @@ -111,7 +112,7 @@ internal abstract class BaseTransferWorker( } else -> { val ex = result.exceptionOrNull() - logger.error("${this.javaClass.simpleName} failed with exception: $ex, stacktrace: ${ex?.stackTrace}") + logger.error("${this.javaClass.simpleName} failed with exception: $ex") if (isRetryableError(ex)) { Result.retry() } else { @@ -151,7 +152,9 @@ internal abstract class BaseTransferWorker( return isStopped || !isNetworkAvailable(applicationContext) || runAttemptCount < maxRetryCount || - e is CancellationException + e is CancellationException || + // SocketException is thrown when download is terminated due to network disconnection. + e is SocketException } @RequiresApi(Build.VERSION_CODES.O) diff --git a/aws-storage-s3/src/test/java/com/amplifyframework/storage/s3/StorageComponentTest.java b/aws-storage-s3/src/test/java/com/amplifyframework/storage/s3/StorageComponentTest.java index 1486e1f282..91fb399ec8 100644 --- a/aws-storage-s3/src/test/java/com/amplifyframework/storage/s3/StorageComponentTest.java +++ b/aws-storage-s3/src/test/java/com/amplifyframework/storage/s3/StorageComponentTest.java @@ -179,7 +179,7 @@ public void testDownloadToFileGetsFile() throws Exception { // callback, as part of our "happy path" test. doAnswer(invocation -> { TransferListener listener = invocation.getArgument(0); - listener.onStateChanged(0, TransferState.COMPLETED); + listener.onStateChanged(0, TransferState.COMPLETED, fromRemoteKey); return null; }).when(observer) .setTransferListener(any(TransferListener.class)); @@ -256,7 +256,7 @@ public void testUploadFileGetsKey() throws Exception { doAnswer(invocation -> { TransferListener listener = invocation.getArgument(0); - listener.onStateChanged(0, TransferState.COMPLETED); + listener.onStateChanged(0, TransferState.COMPLETED, toRemoteKey); return null; }).when(observer) .setTransferListener(any(com.amplifyframework.storage.s3.transfer.TransferListener.class)); @@ -297,7 +297,7 @@ public void testUploadInputStreamGetsKey() throws Exception { doAnswer(invocation -> { TransferListener listener = invocation.getArgument(0); - listener.onStateChanged(0, TransferState.COMPLETED); + listener.onStateChanged(0, TransferState.COMPLETED, toRemoteKey); return null; }).when(observer) .setTransferListener(any(TransferListener.class)); diff --git a/core/src/main/java/com/amplifyframework/core/async/AmplifyOperation.java b/core/src/main/java/com/amplifyframework/core/async/AmplifyOperation.java index f4ab3c2c42..8f7b3fa207 100644 --- a/core/src/main/java/com/amplifyframework/core/async/AmplifyOperation.java +++ b/core/src/main/java/com/amplifyframework/core/async/AmplifyOperation.java @@ -62,6 +62,7 @@ public final CategoryType getCategoryType() { /** * Gets the request object. + * * @return the request object */ public R getRequest() { diff --git a/core/src/main/java/com/amplifyframework/storage/operation/StorageDownloadFileOperation.java b/core/src/main/java/com/amplifyframework/storage/operation/StorageDownloadFileOperation.java index d2e80f15d5..35330a90b2 100644 --- a/core/src/main/java/com/amplifyframework/storage/operation/StorageDownloadFileOperation.java +++ b/core/src/main/java/com/amplifyframework/storage/operation/StorageDownloadFileOperation.java @@ -67,5 +67,17 @@ protected StorageDownloadFileOperation( public void setOnSuccess(@Nullable Consumer onSuccess) { super.setOnSuccess(onSuccess); } + + /** + * Request will be null if the operation is returned by + * {@link com.amplifyframework.storage.s3.AWSS3StoragePlugin} getTransfer api. + * + * @return the request object + */ + @Override + @Nullable + public R getRequest() { + return super.getRequest(); + } } diff --git a/core/src/main/java/com/amplifyframework/storage/operation/StorageUploadOperation.java b/core/src/main/java/com/amplifyframework/storage/operation/StorageUploadOperation.java index 684ba35308..e31629717d 100644 --- a/core/src/main/java/com/amplifyframework/storage/operation/StorageUploadOperation.java +++ b/core/src/main/java/com/amplifyframework/storage/operation/StorageUploadOperation.java @@ -68,5 +68,17 @@ protected StorageUploadOperation( public void setOnSuccess(@Nullable Consumer onSuccess) { super.setOnSuccess(onSuccess); } + + /** + * Request will be null if the operation is returned by + * {@link com.amplifyframework.storage.s3.AWSS3StoragePlugin} getTransfer api. + * + * @return the request object + */ + @Override + @Nullable + public R getRequest() { + return super.getRequest(); + } } From 4a8b65f344df9b4291d752f089995fc8469099b0 Mon Sep 17 00:00:00 2001 From: Tyler Roach Date: Tue, 15 Nov 2022 17:10:34 -0500 Subject: [PATCH 05/33] Prevent attempting to read backed up EncryptedSharedPreferences that are no longer readable (#2113) --- .../data/EncryptedKeyValueRepository.kt | 69 +++++++++++++++---- .../data/EncryptedKeyValueRepositoryTest.kt | 4 +- 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/aws-auth-cognito/src/main/java/com/amplifyframework/auth/cognito/data/EncryptedKeyValueRepository.kt b/aws-auth-cognito/src/main/java/com/amplifyframework/auth/cognito/data/EncryptedKeyValueRepository.kt index d4be967b38..6993e92063 100644 --- a/aws-auth-cognito/src/main/java/com/amplifyframework/auth/cognito/data/EncryptedKeyValueRepository.kt +++ b/aws-auth-cognito/src/main/java/com/amplifyframework/auth/cognito/data/EncryptedKeyValueRepository.kt @@ -22,36 +22,81 @@ import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV import androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM import androidx.security.crypto.MasterKeys +import java.io.File +import java.util.UUID internal class EncryptedKeyValueRepository( private val context: Context, private val sharedPreferencesName: String, ) : KeyValueRepository { + @VisibleForTesting + internal val sharedPreferences: SharedPreferences by lazy { + EncryptedSharedPreferences.create( + "$sharedPreferencesName.${getInstallationIdentifier(context, sharedPreferencesName)}", + MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC), + context, + AES256_SIV, + AES256_GCM + ) + } + + @VisibleForTesting + internal val editor: SharedPreferences.Editor by lazy { + sharedPreferences.edit() + } + override fun put(dataKey: String, value: String?) { - with(getSharedPreferences().edit()) { + with(editor) { putString(dataKey, value) apply() } } - override fun get(dataKey: String): String? = getSharedPreferences().getString(dataKey, null) + override fun get(dataKey: String): String? = sharedPreferences.getString(dataKey, null) override fun remove(dataKey: String) { - with(getSharedPreferences().edit()) { + with(editor) { remove(dataKey) apply() } } - @VisibleForTesting - fun getSharedPreferences(): SharedPreferences { - return EncryptedSharedPreferences.create( - sharedPreferencesName, - MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC), - context, - AES256_SIV, - AES256_GCM - ) + /** + * EncryptedSharedPreferences may have been backed up by the application, but will be unreadable due to the + * KeyStore record being lost. To prevent an unreadable EncryptedSharedPreferences, we append a suffix to the name + * with a UUID created in the noBackupFilesDir + */ + @Synchronized + private fun getInstallationIdentifier(context: Context, keyValueRepoID: String): String { + val identifierFile = File(context.noBackupFilesDir, "$keyValueRepoID.installationIdentifier") + val previousIdentifier = getExistingInstallationIdentifier(identifierFile) + + return previousIdentifier ?: createInstallationIdentifier(identifierFile) + } + + /** + * Gets the existing installation identifier (if exists) + */ + private fun getExistingInstallationIdentifier(identifierFile: File): String? { + return if (identifierFile.exists()) { + val identifier = identifierFile.readText() + identifier.ifBlank { null } + } else { + null + } + } + + /** + * Creates a new installation identifier for the install + */ + private fun createInstallationIdentifier(identifierFile: File): String { + val newIdentifier = UUID.randomUUID().toString() + try { + identifierFile.writeText(newIdentifier) + } catch (e: Exception) { + // Failed to write identifier to file, session will be forced to be in memory + } + return newIdentifier } } diff --git a/aws-auth-cognito/src/test/java/com/amplifyframework/auth/cognito/data/EncryptedKeyValueRepositoryTest.kt b/aws-auth-cognito/src/test/java/com/amplifyframework/auth/cognito/data/EncryptedKeyValueRepositoryTest.kt index 2cbd1e0ad9..94a4ff4fec 100644 --- a/aws-auth-cognito/src/test/java/com/amplifyframework/auth/cognito/data/EncryptedKeyValueRepositoryTest.kt +++ b/aws-auth-cognito/src/test/java/com/amplifyframework/auth/cognito/data/EncryptedKeyValueRepositoryTest.kt @@ -49,8 +49,8 @@ class EncryptedKeyValueRepositoryTest { @Before fun setup() { - Mockito.`when`(repository.getSharedPreferences()).thenReturn(mockPrefs) - Mockito.`when`(mockPrefs.edit()).thenReturn(mockPrefsEditor) + Mockito.`when`(repository.sharedPreferences).thenReturn(mockPrefs) + Mockito.`when`(repository.editor).thenReturn(mockPrefsEditor) } @Test From f7193387b0456538804061c2928df6367203e574 Mon Sep 17 00:00:00 2001 From: Divyesh Chitroda Date: Tue, 15 Nov 2022 15:04:44 -0800 Subject: [PATCH 06/33] fix(auth): device metadata migration (#2114) --- .../actions/CredentialStoreCognitoActions.kt | 16 ++++++++-------- .../data/AWSCognitoLegacyCredentialStore.kt | 10 +++++++++- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/aws-auth-cognito/src/main/java/com/amplifyframework/auth/cognito/actions/CredentialStoreCognitoActions.kt b/aws-auth-cognito/src/main/java/com/amplifyframework/auth/cognito/actions/CredentialStoreCognitoActions.kt index 407792655c..5a634c4914 100644 --- a/aws-auth-cognito/src/main/java/com/amplifyframework/auth/cognito/actions/CredentialStoreCognitoActions.kt +++ b/aws-auth-cognito/src/main/java/com/amplifyframework/auth/cognito/actions/CredentialStoreCognitoActions.kt @@ -34,15 +34,15 @@ internal object CredentialStoreCognitoActions : CredentialStoreActions { // migrate credentials credentialStore.saveCredential(credentials) legacyCredentialStore.deleteCredential() + } - // migrate device data - if (credentials is AmplifyCredential.UserPoolTypeCredential) { - val username = credentials.signedInData.username - val deviceMetaData = legacyCredentialStore.retrieveDeviceMetadata(username) - if (deviceMetaData != DeviceMetadata.Empty) { - credentialStore.saveDeviceMetadata(username, deviceMetaData) - legacyCredentialStore.deleteDeviceKeyCredential(username) - } + // migrate device data + val lastAuthUserId = legacyCredentialStore.retrieveLastAuthUserId() + lastAuthUserId?.let { + val deviceMetaData = legacyCredentialStore.retrieveDeviceMetadata(lastAuthUserId) + if (deviceMetaData != DeviceMetadata.Empty) { + credentialStore.saveDeviceMetadata(lastAuthUserId, deviceMetaData) + legacyCredentialStore.deleteDeviceKeyCredential(lastAuthUserId) } } diff --git a/aws-auth-cognito/src/main/java/com/amplifyframework/auth/cognito/data/AWSCognitoLegacyCredentialStore.kt b/aws-auth-cognito/src/main/java/com/amplifyframework/auth/cognito/data/AWSCognitoLegacyCredentialStore.kt index 5df12d4cff..90edd67aa9 100644 --- a/aws-auth-cognito/src/main/java/com/amplifyframework/auth/cognito/data/AWSCognitoLegacyCredentialStore.kt +++ b/aws-auth-cognito/src/main/java/com/amplifyframework/auth/cognito/data/AWSCognitoLegacyCredentialStore.kt @@ -91,6 +91,12 @@ internal class AWSCognitoLegacyCredentialStore( override fun saveDeviceMetadata(username: String, deviceMetadata: DeviceMetadata) = Unit override fun saveASFDevice(device: AmplifyCredential.ASFDevice) = Unit + @Synchronized + fun retrieveLastAuthUserId(): String? { + val keys = getTokenKeys() + return keys[APP_LAST_AUTH_USER]?.let { tokensKeyValue.get(it) } + } + @Synchronized override fun retrieveCredential(): AmplifyCredential { val signedInData = retrieveSignedInData() @@ -133,6 +139,9 @@ internal class AWSCognitoLegacyCredentialStore( } override fun deleteDeviceKeyCredential(username: String) { + val keys = getTokenKeys() + keys[APP_LAST_AUTH_USER]?.let { tokensKeyValue.remove(it) } + deviceKeyValue.apply { remove(DEVICE_KEY) remove(DEVICE_GROUP_KEY) @@ -147,7 +156,6 @@ internal class AWSCognitoLegacyCredentialStore( private fun deleteCognitoUserPoolTokens() { val keys = getTokenKeys() - keys[APP_LAST_AUTH_USER]?.let { tokensKeyValue.remove(it) } keys[TOKEN_TYPE_ID]?.let { tokensKeyValue.remove(it) } keys[TOKEN_TYPE_ACCESS]?.let { tokensKeyValue.remove(it) } keys[TOKEN_TYPE_REFRESH]?.let { tokensKeyValue.remove(it) } From ce6609640729da50a732c51e7bbed2e7c8855def Mon Sep 17 00:00:00 2001 From: Thomas Leing Date: Tue, 15 Nov 2022 17:23:20 -0800 Subject: [PATCH 07/33] Number of attributes being too high is not retryable (#2112) * Number of attributes being too high is not retryable * Match iOS at not retrying bad request error -- covering multiple 400 errors * Fix lint * Fix lint * Update Kotlin SDK version Co-authored-by: Thomas Leing --- .../analytics/pinpoint/EventRecorder.kt | 12 +++++++----- build.gradle | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/aws-analytics-pinpoint/src/main/java/com/amplifyframework/analytics/pinpoint/EventRecorder.kt b/aws-analytics-pinpoint/src/main/java/com/amplifyframework/analytics/pinpoint/EventRecorder.kt index 3706734c1f..36e0556cf6 100644 --- a/aws-analytics-pinpoint/src/main/java/com/amplifyframework/analytics/pinpoint/EventRecorder.kt +++ b/aws-analytics-pinpoint/src/main/java/com/amplifyframework/analytics/pinpoint/EventRecorder.kt @@ -57,6 +57,7 @@ internal class EventRecorder( private val defaultMaxSubmissionAllowed = 3 private val defaultMaxSubmissionSize = 1024 * 100 private val serviceDefinedMaxEventsPerBatch: Int = 100 + private val badRequestCode = 400 internal suspend fun recordEvent(pinpointEvent: PinpointEvent): Uri? { return withContext(coroutineDispatcher) { val result = runCatching { @@ -178,7 +179,7 @@ internal class EventRecorder( logger.info("Successfully submitted event with eventId ${pinpointEvent.eventId}") eventIdToDelete.add(pinpointEvent) } else { - if (isRetryableError(message)) { + if (isRetryableError(message, pinpointEventResponse.statusCode)) { logger.error( "Failed to deliver event with ${pinpointEvent.eventId}," + " will be re-delivered later" @@ -194,11 +195,12 @@ internal class EventRecorder( return eventIdToDelete } - private fun isRetryableError(responseCode: String): Boolean { + private fun isRetryableError(message: String, code: Int): Boolean { return !( - responseCode.equals("ValidationException", ignoreCase = true) || - responseCode.equals("SerializationException", ignoreCase = true) || - responseCode.equals("BadRequestException", ignoreCase = true) + message.equals("ValidationException", ignoreCase = true) || + message.equals("SerializationException", ignoreCase = true) || + message.equals("BadRequestException", ignoreCase = true) || + code == badRequestCode ) } diff --git a/build.gradle b/build.gradle index 547a37374c..ab03c555ce 100644 --- a/build.gradle +++ b/build.gradle @@ -69,7 +69,7 @@ ext { compileSdkVersion = 30 minSdkVersion = 24 targetSdkVersion = 30 - awsKotlinSdkVersion = '0.17.10-beta' + awsKotlinSdkVersion = '0.17.12-beta' fragmentVersion = '1.3.1' navigationVersion = '2.3.4' dependency = [ From 51d0492df05a7867ee40150b0b95cd03cd617f8c Mon Sep 17 00:00:00 2001 From: Tyler Roach Date: Wed, 16 Nov 2022 14:05:57 -0500 Subject: [PATCH 08/33] Change errors returned on some apis while federated (#2116) --- .../auth/cognito/RealAWSCognitoAuthPlugin.kt | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/aws-auth-cognito/src/main/java/com/amplifyframework/auth/cognito/RealAWSCognitoAuthPlugin.kt b/aws-auth-cognito/src/main/java/com/amplifyframework/auth/cognito/RealAWSCognitoAuthPlugin.kt index 191dee1dda..1a32d375ee 100644 --- a/aws-auth-cognito/src/main/java/com/amplifyframework/auth/cognito/RealAWSCognitoAuthPlugin.kt +++ b/aws-auth-cognito/src/main/java/com/amplifyframework/auth/cognito/RealAWSCognitoAuthPlugin.kt @@ -971,9 +971,12 @@ internal class RealAWSCognitoAuthPlugin( onError ) } - else -> { + is AuthenticationState.SignedOut -> { onError.accept(SignedOutException()) } + else -> { + onError.accept(InvalidStateException()) + } } } } @@ -1020,9 +1023,12 @@ internal class RealAWSCognitoAuthPlugin( updateDevice(device.id, DeviceRememberedStatusType.NotRemembered, onSuccess, onError) } } - else -> { + is AuthenticationState.SignedOut -> { onError.accept(SignedOutException()) } + else -> { + onError.accept(InvalidStateException()) + } } } } @@ -1036,9 +1042,12 @@ internal class RealAWSCognitoAuthPlugin( is AuthenticationState.SignedIn -> { _fetchDevices(onSuccess, onError) } - else -> { + is AuthenticationState.SignedOut -> { onError.accept(SignedOutException()) } + else -> { + onError.accept(InvalidStateException()) + } } } } @@ -1506,18 +1515,23 @@ internal class RealAWSCognitoAuthPlugin( onError: Consumer ) { authStateMachine.getCurrentState { authState -> - if (authState.authNState !is AuthenticationState.SignedIn) { - onError.accept(SignedOutException()) - return@getCurrentState - } - - GlobalScope.async { - val accessToken = getSession().userPoolTokensResult.value?.accessToken - accessToken?.run { - val userid = SessionHelper.getUserSub(accessToken) ?: "" - val username = SessionHelper.getUsername(accessToken) ?: "" - onSuccess.accept(AuthUser(userid, username)) - } ?: onError.accept(InvalidUserPoolConfigurationException()) + when (authState.authNState) { + is AuthenticationState.SignedIn -> { + GlobalScope.async { + val accessToken = getSession().userPoolTokensResult.value?.accessToken + accessToken?.run { + val userid = SessionHelper.getUserSub(accessToken) ?: "" + val username = SessionHelper.getUsername(accessToken) ?: "" + onSuccess.accept(AuthUser(userid, username)) + } ?: onError.accept(InvalidUserPoolConfigurationException()) + } + } + is AuthenticationState.SignedOut -> { + onError.accept(SignedOutException()) + } + else -> { + onError.accept(InvalidStateException()) + } } } } From 73df9a3183a7105508bc4d027c169e0366cf8681 Mon Sep 17 00:00:00 2001 From: Sunil Timalsina Date: Wed, 16 Nov 2022 12:20:35 -0800 Subject: [PATCH 09/33] release: Amplify Android 2.0.0 (#2115) * release: Amplify Android 2.0.0 * add core-kotlin changelog * add more info in changelog * revert unintended change * remove breaking change for analytics --- CHANGELOG.md | 38 ++++++++++++++++++++++++++++++++++++++ README.md | 12 ++++++------ core-kotlin/CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- rxbindings/README.md | 2 +- 5 files changed, 53 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8cf652f46..d95ab64c52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,41 @@ +## [Release 2.0.0](https://github.com/aws-amplify/amplify-android/releases/tag/release_v2.0.0) + +###Breaking Changes + +#### Android SDK +- Support for **Android SDK API 24: Android 7.0 (Nougat) and higher** + +#### Escape Hatches +- Escape Hatches provide access to AWS SDK for Kotlin instead of `AWSMobileClient` from AWS SDK for Android. + +#### Auth +- `signIn` now returns result with `isSignedIn` instead of `isSignInComplete` +- `confirmResetPassword` API takes additional `username` parameter. +- `signOut` now takes single `onComplete` parameter instead of `onSuccess` and `onError`. +- `fetchAuthSession` now returns `identityIdResult` instead of `identityId`. +- `getCurrentUser` API is now asynchronous and requires `onSuccess` and `onError` parameters. `AuthUser` is returned in `onSuccess` +- The escape hatch now provides access to the underlying `CognitoIdentityProviderClient` and `CognitoIdentityClient` instance. +- Parameters `signInQueryParameters`, `signOutQueryParameters`, and `tokenQueryParameters` are dropped from `AuthWebUISignInOptions`. +- `federationProviderName` has been dropped from `AWSCognitoAuthWebUISignInOptions`. +- `signIn` will now return an error if you attempt to call sign in, while already signed in. + +### Features +Replace underlying AWS SDK with AWS SDK for Kotlin. + +#### Auth +- Federate to Identity Pool +- Custom auth flow now supports without SRP flow +- Supports user migration flow +- Force refresh token. + +#### Storage +- Add support to query local enqueued transfers. + +### Miscellaneous +- All the categories use the same version number + +[See all changes between 2.0.0 and 1.37.6](https://github.com/aws-amplify/amplify-android/compare/release_v1.37.6...release_v2.0.0) + ## [Release 1.37.6](https://github.com/aws-amplify/amplify-android/releases/tag/release_v1.37.6) ### Miscellaneous diff --git a/README.md b/README.md index 07b1513519..f33c7255ca 100644 --- a/README.md +++ b/README.md @@ -69,12 +69,12 @@ dependencies section: ```groovy dependencies { // Only specify modules that provide functionality your app will use - implementation 'com.amplifyframework:aws-analytics-pinpoint:1.37.0-dev-preview.0' - implementation 'com.amplifyframework:aws-api:1.37.0-dev-preview.0' - implementation 'com.amplifyframework:aws-auth-cognito:1.37.0-dev-preview.0' - implementation 'com.amplifyframework:aws-datastore:1.37.0-dev-preview.0' - implementation 'com.amplifyframework:aws-predictions:1.37.0-dev-preview.0' - implementation 'com.amplifyframework:aws-storage-s3:1.37.0-dev-preview.0' + implementation 'com.amplifyframework:aws-analytics-pinpoint:2.0.0' + implementation 'com.amplifyframework:aws-api:2.0.0' + implementation 'com.amplifyframework:aws-auth-cognito:2.0.0' + implementation 'com.amplifyframework:aws-datastore:2.0.0' + implementation 'com.amplifyframework:aws-predictions:2.0.0' + implementation 'com.amplifyframework:aws-storage-s3:2.0.0' } ``` diff --git a/core-kotlin/CHANGELOG.md b/core-kotlin/CHANGELOG.md index dcd39b0929..ba1c18187d 100644 --- a/core-kotlin/CHANGELOG.md +++ b/core-kotlin/CHANGELOG.md @@ -1,3 +1,10 @@ +## [Release 2.0.0](https://github.com/aws-amplify/amplify-android/releases/tag/release_v2.0.0) + +### Miscellaneous +- core-kotlin module release is combined with Amplify release. + +[See all changes between 0.21.6 and 2.0.0](https://github.com/aws-amplify/amplify-android/compare/release-kotlin_v0.21.5...release_v2.0.0) + ## [Release 0.21.6](https://github.com/aws-amplify/amplify-android/releases/tag/release-kotlin_v0.21.6) ### Miscellaneous diff --git a/gradle.properties b/gradle.properties index 5c2b46ff2c..f8678fd73d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,7 @@ org.gradle.jvmargs=-Xmx4g # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects org.gradle.parallel=true -VERSION_NAME=2.0.0-SNAPSHOT +VERSION_NAME=2.0.0 POM_GROUP=com.amplifyframework POM_URL=https://github.com/aws-amplify/amplify-android diff --git a/rxbindings/README.md b/rxbindings/README.md index 521684a1bc..f4142ee48e 100644 --- a/rxbindings/README.md +++ b/rxbindings/README.md @@ -24,7 +24,7 @@ library. In your module's `build.gradle`: ```gradle dependencies { // Add this line. - implementation 'com.amplifyframework:rxbindings:1.37.0-dev-preview.0' + implementation 'com.amplifyframework:rxbindings:2.0.0' } ``` From 009d069ae05ff40cd6bc52223065d69ce8b3196e Mon Sep 17 00:00:00 2001 From: Michael Law <1365977+lawmicha@users.noreply.github.com> Date: Wed, 2 Nov 2022 19:38:30 -0400 Subject: [PATCH 10/33] fix(datastore): remove typename from ModelMetadata --- .../datastore/appsync/ModelWithMetadataAdapter.java | 1 - .../src/androidTest/res/raw/amplifyconfigurationupdated.json | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) create mode 120000 aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java index 43920d8f64..297c09f9b8 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java @@ -29,7 +29,6 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; -import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; diff --git a/aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json b/aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json new file mode 120000 index 0000000000..8eebf9cad1 --- /dev/null +++ b/aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json @@ -0,0 +1 @@ +/Users/mikschn/.aws-amplify/amplify-android/aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json \ No newline at end of file From 2d6bf4968467bf79fdadb036dba507a6cb7fe883 Mon Sep 17 00:00:00 2001 From: Michael Law <1365977+lawmicha@users.noreply.github.com> Date: Wed, 2 Nov 2022 19:38:30 -0400 Subject: [PATCH 11/33] fix(datastore): remove typename from ModelMetadata --- .../datastore/appsync/ModelWithMetadataAdapter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java index 43920d8f64..297c09f9b8 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java @@ -29,7 +29,6 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; -import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; From dd791333f7bc466745a7ace847b7d64839e425da Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Fri, 18 Nov 2022 15:27:54 -0800 Subject: [PATCH 12/33] remove unneeded file --- .../src/androidTest/res/raw/amplifyconfigurationupdated.json | 1 - 1 file changed, 1 deletion(-) delete mode 120000 aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json diff --git a/aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json b/aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json deleted file mode 120000 index 8eebf9cad1..0000000000 --- a/aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json +++ /dev/null @@ -1 +0,0 @@ -/Users/mikschn/.aws-amplify/amplify-android/aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json \ No newline at end of file From b56bf684cfabde16fc507bd4a0ddd53f3da03a87 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Fri, 18 Nov 2022 17:00:13 -0800 Subject: [PATCH 13/33] small cleanup --- .../com/amplifyframework/datastore/appsync/ModelMetadata.java | 2 +- .../datastore/storage/sqlite/SQLiteCommandFactory.java | 2 -- .../datastore/ConflictResolverIntegrationTest.java | 4 ---- .../datastore/appsync/AppSyncRequestFactoryTest.java | 2 -- 4 files changed, 1 insertion(+), 9 deletions(-) diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java index c520a939f6..e96c5a6ad1 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java @@ -95,7 +95,7 @@ public Temporal.Timestamp getLastChangedAt() { } /** - * Gets the model name. + * Gets the name of the model this meta-model represents. * @return modelName */ @Nullable diff --git a/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java b/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java index aa3afcf31f..f9606ae06c 100644 --- a/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java +++ b/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java @@ -484,9 +484,7 @@ private List extractFieldValues(@NonNull Model model) throws DataStoreEx final Map modelFields = schema.getFields(); final List bindings = new ArrayList<>(); Object fieldValue; - System.out.println("Extract field value: model = " + model); for (SQLiteColumn column : table.getSortedColumns()) { - System.out.println("extract field value: column = " + column); if (column.getName().equals(SQLiteTable.PRIMARY_KEY_FIELD_NAME)) { fieldValue = model.getPrimaryKeyString(); } else if (column.isForeignKey()) { diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java index 4584783565..037aa400e5 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java @@ -184,7 +184,6 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { extensions)); onResponse.accept(new GraphQLResponse<>(null, errorList)); // latch makes sure conflict unhandled response is returned. - System.out.println("LATCH 1"); latch.countDown(); return mock(GraphQLOperation.class); }).doAnswer(invocation -> { @@ -199,7 +198,6 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { any(), any()); // latch makes sure success response is returned. - System.out.println("LATCH 2"); latch.countDown(); return mock(GraphQLOperation.class); }).when(mockApiCategory).mutate(any(), any(), any()); @@ -216,7 +214,6 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { PaginatedResult> data = new PaginatedResult<>(Collections.singletonList(modelWithMetadata), null); onResponse.accept(new GraphQLResponse<>(data, Collections.emptyList())); - System.out.println("LATCH 3"); latch.countDown(); return mock(GraphQLOperation.class); @@ -230,7 +227,6 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { PaginatedResult> data = new PaginatedResult<>(Collections.singletonList(modelWithMetadata), null); onResponse.accept(new GraphQLResponse<>(data, Collections.emptyList())); - System.out.println("LATCH 4"); latch.countDown(); return mock(GraphQLOperation.class); }).when(mockApiCategory).query(any(), any(), any()); diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncRequestFactoryTest.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncRequestFactoryTest.java index 2aa9721f1f..17fab052c4 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncRequestFactoryTest.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/appsync/AppSyncRequestFactoryTest.java @@ -422,8 +422,6 @@ public void validateMutationGenerationOnCreateItemWithCustomForeignKeyAndSortKey ModelSchema schema = ModelSchema.fromModelClass(OtherBlog.class); String expected = Resources.readAsString("create-other-blog.txt"); String actual = AppSyncRequestFactory.buildCreationRequest(schema, blog, DEFAULT_STRATEGY).getContent(); - System.out.println(" Actual: " + actual); - System.out.println("Expected: " + expected); JSONAssert.assertEquals( expected, actual, From 1b51d27b61dd4787f57c3f49785482ca5ba4aa4f Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Fri, 18 Nov 2022 17:19:39 -0800 Subject: [PATCH 14/33] remove print statements --- .../datastore/appsync/ModelMetadata.java | 16 ++++++++-------- .../appsync/ModelWithMetadataAdapter.java | 2 +- .../appsync/ModelWithMetadataAdapterTest.java | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java index e96c5a6ad1..6f3b290c6c 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java @@ -86,21 +86,21 @@ public Integer getVersion() { } /** - * Gets last changed at time. - * @return last changed at time + * Gets the name of the model this meta-model represents. + * @return modelName */ @Nullable - public Temporal.Timestamp getLastChangedAt() { - return _lastChangedAt; + public String getTypename() { + return id.split("\\|")[0]; } /** - * Gets the name of the model this meta-model represents. - * @return modelName + * Gets last changed at time. + * @return last changed at time */ @Nullable - public String getUnderlyingModelName() { - return id.split("\\|")[0]; + public Temporal.Timestamp getLastChangedAt() { + return _lastChangedAt; } @Override diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java index 297c09f9b8..1f63354941 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java @@ -123,7 +123,7 @@ public JsonElement serialize( result.add(entry.getKey(), entry.getValue()); } // Additionally serialize the stored model name as the typename, mirroring the deserialization process. - result.addProperty(TYPE_NAME, modelMetadata.getUnderlyingModelName()); + result.addProperty(TYPE_NAME, modelMetadata.getTypename()); JsonObject serializedModel = (JsonObject) context.serialize(src.getModel()); for (Map.Entry entry : serializedModel.entrySet()) { diff --git a/aws-api-appsync/src/test/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapterTest.java b/aws-api-appsync/src/test/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapterTest.java index 48eae27558..de7683b098 100644 --- a/aws-api-appsync/src/test/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapterTest.java +++ b/aws-api-appsync/src/test/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapterTest.java @@ -80,7 +80,7 @@ public void adapterCanSerializeMwm() throws JSONException { .put("_lastChangedAt", metadata.getLastChangedAt().getSecondsSinceEpoch()) .put("_deleted", metadata.isDeleted()) .put("_version", metadata.getVersion()) - .put("__typename", mwm.getSyncMetadata().getUnderlyingModelName()) + .put("__typename", mwm.getSyncMetadata().getTypename()) .toString(); String actual = gson.toJson(mwm); JSONAssert.assertEquals(expected, actual, true); From 82c9c65479d69e84b686f8f46771ec315281c2af Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Mon, 21 Nov 2022 09:26:47 -0800 Subject: [PATCH 15/33] fix comment --- .../com/amplifyframework/datastore/appsync/ModelMetadata.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java index 6f3b290c6c..8988915242 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java @@ -86,8 +86,8 @@ public Integer getVersion() { } /** - * Gets the name of the model this meta-model represents. - * @return modelName + * Gets the type of the model. + * @return Type of the Model. */ @Nullable public String getTypename() { From 43bcfcd4e70740a662e812c398c699aa9f2353c1 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Mon, 21 Nov 2022 14:15:36 -0800 Subject: [PATCH 16/33] force tests From 312e5082d9ffe6331cfc5bafe99d41b89fd8b5e4 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Mon, 21 Nov 2022 16:47:55 -0800 Subject: [PATCH 17/33] force tests From e3bf22d5a0647b55cc6b69abb7ef07978cac1d64 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Tue, 22 Nov 2022 12:59:19 -0800 Subject: [PATCH 18/33] force tests From 070ce3a1198e129d7688478ac14b085327bad1f3 Mon Sep 17 00:00:00 2001 From: Michael Law <1365977+lawmicha@users.noreply.github.com> Date: Wed, 2 Nov 2022 19:38:30 -0400 Subject: [PATCH 19/33] fix(datastore): remove typename from ModelMetadata --- .../src/androidTest/res/raw/amplifyconfigurationupdated.json | 1 + 1 file changed, 1 insertion(+) create mode 120000 aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json diff --git a/aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json b/aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json new file mode 120000 index 0000000000..8eebf9cad1 --- /dev/null +++ b/aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json @@ -0,0 +1 @@ +/Users/mikschn/.aws-amplify/amplify-android/aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json \ No newline at end of file From 3f0098c7ccaa9acb757bb5554083f64fc5e6d53c Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Mon, 21 Nov 2022 14:15:36 -0800 Subject: [PATCH 20/33] force tests From 6ce1c488da21ee46ac702a5f308569fa705b13be Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Mon, 21 Nov 2022 16:47:55 -0800 Subject: [PATCH 21/33] force tests From c52f450a5c957253e07565cc2c517cac719db639 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Tue, 22 Nov 2022 12:59:19 -0800 Subject: [PATCH 22/33] force tests From e581ebf189755ba10f055dba08aa0e4f529dcfd7 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Wed, 23 Nov 2022 08:36:43 -0800 Subject: [PATCH 23/33] fix merge errors --- .../datastore/appsync/ModelMetadata.java | 16 ++++++++-------- .../appsync/ModelWithMetadataAdapter.java | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java index 8988915242..e92fc46712 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java @@ -86,21 +86,21 @@ public Integer getVersion() { } /** - * Gets the type of the model. - * @return Type of the Model. + * Gets last changed at time. + * @return last changed at time */ @Nullable - public String getTypename() { - return id.split("\\|")[0]; + public Temporal.Timestamp getLastChangedAt() { + return _lastChangedAt; } /** - * Gets last changed at time. - * @return last changed at time + * Gets the model name. + * @return modelName */ @Nullable - public Temporal.Timestamp getLastChangedAt() { - return _lastChangedAt; + public String getTypename() { + return id.split("\\|")[0]; } @Override diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java index 1f63354941..10e5aedc43 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java @@ -29,6 +29,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; From 05a394231d9249dae517efd08d54776d1d2900f6 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Thu, 17 Nov 2022 15:15:25 -0800 Subject: [PATCH 24/33] Merge fix --- .../com/amplifyframework/datastore/appsync/ModelMetadata.java | 4 ++++ .../datastore/appsync/ModelWithMetadataAdapter.java | 4 ++++ .../datastore/storage/sqlite/SQLiteCommandFactory.java | 2 ++ .../datastore/ConflictResolverIntegrationTest.java | 4 ++++ 4 files changed, 14 insertions(+) diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java index e92fc46712..d5a51339a5 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java @@ -99,7 +99,11 @@ public Temporal.Timestamp getLastChangedAt() { * @return modelName */ @Nullable +<<<<<<< HEAD public String getTypename() { +======= + public String getUnderlyingModelName() { +>>>>>>> eae9cb06 (Test a potential fix) return id.split("\\|")[0]; } diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java index 10e5aedc43..d727d0b32a 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java @@ -124,7 +124,11 @@ public JsonElement serialize( result.add(entry.getKey(), entry.getValue()); } // Additionally serialize the stored model name as the typename, mirroring the deserialization process. +<<<<<<< HEAD result.addProperty(TYPE_NAME, modelMetadata.getTypename()); +======= + result.addProperty(TYPE_NAME, modelMetadata.getUnderlyingModelName()); +>>>>>>> eae9cb06 (Test a potential fix) JsonObject serializedModel = (JsonObject) context.serialize(src.getModel()); for (Map.Entry entry : serializedModel.entrySet()) { diff --git a/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java b/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java index f9606ae06c..aa3afcf31f 100644 --- a/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java +++ b/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java @@ -484,7 +484,9 @@ private List extractFieldValues(@NonNull Model model) throws DataStoreEx final Map modelFields = schema.getFields(); final List bindings = new ArrayList<>(); Object fieldValue; + System.out.println("Extract field value: model = " + model); for (SQLiteColumn column : table.getSortedColumns()) { + System.out.println("extract field value: column = " + column); if (column.getName().equals(SQLiteTable.PRIMARY_KEY_FIELD_NAME)) { fieldValue = model.getPrimaryKeyString(); } else if (column.isForeignKey()) { diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java index 037aa400e5..4584783565 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java @@ -184,6 +184,7 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { extensions)); onResponse.accept(new GraphQLResponse<>(null, errorList)); // latch makes sure conflict unhandled response is returned. + System.out.println("LATCH 1"); latch.countDown(); return mock(GraphQLOperation.class); }).doAnswer(invocation -> { @@ -198,6 +199,7 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { any(), any()); // latch makes sure success response is returned. + System.out.println("LATCH 2"); latch.countDown(); return mock(GraphQLOperation.class); }).when(mockApiCategory).mutate(any(), any(), any()); @@ -214,6 +216,7 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { PaginatedResult> data = new PaginatedResult<>(Collections.singletonList(modelWithMetadata), null); onResponse.accept(new GraphQLResponse<>(data, Collections.emptyList())); + System.out.println("LATCH 3"); latch.countDown(); return mock(GraphQLOperation.class); @@ -227,6 +230,7 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { PaginatedResult> data = new PaginatedResult<>(Collections.singletonList(modelWithMetadata), null); onResponse.accept(new GraphQLResponse<>(data, Collections.emptyList())); + System.out.println("LATCH 4"); latch.countDown(); return mock(GraphQLOperation.class); }).when(mockApiCategory).query(any(), any(), any()); From bb262f58937612512028ac325c6c89ed7871f239 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Fri, 18 Nov 2022 17:00:13 -0800 Subject: [PATCH 25/33] small cleanup --- .../com/amplifyframework/datastore/appsync/ModelMetadata.java | 2 +- .../datastore/storage/sqlite/SQLiteCommandFactory.java | 2 -- .../datastore/ConflictResolverIntegrationTest.java | 4 ---- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java index d5a51339a5..c9eb088e34 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java @@ -95,7 +95,7 @@ public Temporal.Timestamp getLastChangedAt() { } /** - * Gets the model name. + * Gets the name of the model this meta-model represents. * @return modelName */ @Nullable diff --git a/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java b/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java index aa3afcf31f..f9606ae06c 100644 --- a/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java +++ b/aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/SQLiteCommandFactory.java @@ -484,9 +484,7 @@ private List extractFieldValues(@NonNull Model model) throws DataStoreEx final Map modelFields = schema.getFields(); final List bindings = new ArrayList<>(); Object fieldValue; - System.out.println("Extract field value: model = " + model); for (SQLiteColumn column : table.getSortedColumns()) { - System.out.println("extract field value: column = " + column); if (column.getName().equals(SQLiteTable.PRIMARY_KEY_FIELD_NAME)) { fieldValue = model.getPrimaryKeyString(); } else if (column.isForeignKey()) { diff --git a/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java b/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java index 4584783565..037aa400e5 100644 --- a/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java +++ b/aws-datastore/src/test/java/com/amplifyframework/datastore/ConflictResolverIntegrationTest.java @@ -184,7 +184,6 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { extensions)); onResponse.accept(new GraphQLResponse<>(null, errorList)); // latch makes sure conflict unhandled response is returned. - System.out.println("LATCH 1"); latch.countDown(); return mock(GraphQLOperation.class); }).doAnswer(invocation -> { @@ -199,7 +198,6 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { any(), any()); // latch makes sure success response is returned. - System.out.println("LATCH 2"); latch.countDown(); return mock(GraphQLOperation.class); }).when(mockApiCategory).mutate(any(), any(), any()); @@ -216,7 +214,6 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { PaginatedResult> data = new PaginatedResult<>(Collections.singletonList(modelWithMetadata), null); onResponse.accept(new GraphQLResponse<>(data, Collections.emptyList())); - System.out.println("LATCH 3"); latch.countDown(); return mock(GraphQLOperation.class); @@ -230,7 +227,6 @@ private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) { PaginatedResult> data = new PaginatedResult<>(Collections.singletonList(modelWithMetadata), null); onResponse.accept(new GraphQLResponse<>(data, Collections.emptyList())); - System.out.println("LATCH 4"); latch.countDown(); return mock(GraphQLOperation.class); }).when(mockApiCategory).query(any(), any(), any()); From 937ae6264e1384e106241f0145cb2bd8128249a9 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Fri, 18 Nov 2022 17:19:39 -0800 Subject: [PATCH 26/33] remove print statements --- .../datastore/appsync/ModelMetadata.java | 22 ++++++++----------- .../appsync/ModelWithMetadataAdapter.java | 4 ---- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java index c9eb088e34..87a561eb10 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java @@ -86,28 +86,24 @@ public Integer getVersion() { } /** - * Gets last changed at time. - * @return last changed at time + * Gets the name of the model this meta-model represents. + * @return modelName */ @Nullable - public Temporal.Timestamp getLastChangedAt() { - return _lastChangedAt; + public String getTypename() { + return id.split("\\|")[0]; } /** - * Gets the name of the model this meta-model represents. - * @return modelName + * Gets last changed at time. + * @return last changed at time */ @Nullable -<<<<<<< HEAD - public String getTypename() { -======= - public String getUnderlyingModelName() { ->>>>>>> eae9cb06 (Test a potential fix) - return id.split("\\|")[0]; + public Temporal.Timestamp getLastChangedAt() { + return _lastChangedAt; } - @Override + @Override public boolean equals(Object thatObject) { if (this == thatObject) { return true; diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java index d727d0b32a..10e5aedc43 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java @@ -124,11 +124,7 @@ public JsonElement serialize( result.add(entry.getKey(), entry.getValue()); } // Additionally serialize the stored model name as the typename, mirroring the deserialization process. -<<<<<<< HEAD result.addProperty(TYPE_NAME, modelMetadata.getTypename()); -======= - result.addProperty(TYPE_NAME, modelMetadata.getUnderlyingModelName()); ->>>>>>> eae9cb06 (Test a potential fix) JsonObject serializedModel = (JsonObject) context.serialize(src.getModel()); for (Map.Entry entry : serializedModel.entrySet()) { From 8e16f773fe4a516fb61a0dae8796b065a20e60ac Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Mon, 21 Nov 2022 14:15:36 -0800 Subject: [PATCH 27/33] force tests From 04853c4a6b06022f7fb87a51a99323ff289e7bec Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Mon, 21 Nov 2022 16:47:55 -0800 Subject: [PATCH 28/33] force tests From 8fa701907285e4ab6e6fe53a308d391831235391 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Tue, 22 Nov 2022 12:59:19 -0800 Subject: [PATCH 29/33] force tests From fed2a36168e03d801f0a83d00abe3df1de2c646b Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Wed, 23 Nov 2022 09:03:03 -0800 Subject: [PATCH 30/33] remove file which should not be checked in --- .gitignore | 2 +- .../com/amplifyframework/datastore/appsync/ModelMetadata.java | 4 ++-- .../src/androidTest/res/raw/amplifyconfigurationupdated.json | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) delete mode 120000 aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json diff --git a/.gitignore b/.gitignore index 7e51e899f3..a56d67e74d 100644 --- a/.gitignore +++ b/.gitignore @@ -111,4 +111,4 @@ build-artifacts.tar.gz # sample app resources sample* -.graphqlconfig.yml \ No newline at end of file +.graphqlconfig.ymlaws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java index 87a561eb10..f69c8198c7 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java @@ -86,8 +86,8 @@ public Integer getVersion() { } /** - * Gets the name of the model this meta-model represents. - * @return modelName + * Gets the type of the model. + * @return Type of the Model. */ @Nullable public String getTypename() { diff --git a/aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json b/aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json deleted file mode 120000 index 8eebf9cad1..0000000000 --- a/aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json +++ /dev/null @@ -1 +0,0 @@ -/Users/mikschn/.aws-amplify/amplify-android/aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json \ No newline at end of file From 13088cdbfea03aa64ec48b5a422082fab15a2275 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Wed, 23 Nov 2022 09:49:09 -0800 Subject: [PATCH 31/33] force tests From 7f256a973219c9c0e7fac4a4c7a776cf2189b1e3 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Wed, 23 Nov 2022 10:00:41 -0800 Subject: [PATCH 32/33] fix typo --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a56d67e74d..dd2f83eff2 100644 --- a/.gitignore +++ b/.gitignore @@ -111,4 +111,5 @@ build-artifacts.tar.gz # sample app resources sample* -.graphqlconfig.ymlaws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json +.graphqlconfig.yml +aws-datastore/src/androidTest/res/raw/amplifyconfigurationupdated.json From 2bc6be392059714a2d5cf2d1cb6fa72f68434aa1 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Wed, 23 Nov 2022 10:55:16 -0800 Subject: [PATCH 33/33] fix typos --- .../com/amplifyframework/datastore/appsync/ModelMetadata.java | 2 +- .../datastore/appsync/ModelWithMetadataAdapter.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java index f69c8198c7..8988915242 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelMetadata.java @@ -103,7 +103,7 @@ public Temporal.Timestamp getLastChangedAt() { return _lastChangedAt; } - @Override + @Override public boolean equals(Object thatObject) { if (this == thatObject) { return true; diff --git a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java index 10e5aedc43..1f63354941 100644 --- a/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java +++ b/aws-api-appsync/src/main/java/com/amplifyframework/datastore/appsync/ModelWithMetadataAdapter.java @@ -29,7 +29,6 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; -import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer;