use of com.amplifyframework.datastore.appsync.ModelWithMetadata in project amplify-android by aws-amplify.
the class SubscriptionProcessorTest method isDataMergedWhenBufferDrainedForBlogOwnerNamed.
/**
* Return whether a response with a BlogOwner with the given name gets merged with the merger.
* @param name name of the BlogOwner returned in the subscription
* @return whether the data was merged
* @throws DataStoreException On failure to arrange mocking
* @throws InterruptedException On failure to await latch
*/
private boolean isDataMergedWhenBufferDrainedForBlogOwnerNamed(String name) throws DataStoreException, InterruptedException {
// By default, start the subscriptions up.
arrangeStartedSubscriptions(appSync, modelSchemas, SubscriptionType.values());
// Arrange some subscription data
BlogOwner model = BlogOwner.builder().name(name).build();
ModelMetadata modelMetadata = new ModelMetadata(model.getId(), false, 1, Temporal.Timestamp.now());
ModelWithMetadata<BlogOwner> modelWithMetadata = new ModelWithMetadata<>(model, modelMetadata);
GraphQLResponse<ModelWithMetadata<BlogOwner>> response = new GraphQLResponse<>(modelWithMetadata, null);
arrangeDataEmittingSubscription(appSync, schemaRegistry.getModelSchemaForModelInstance(model), SubscriptionType.ON_CREATE, response);
// Merge will be invoked for the subcription data, when we start draining...
CountDownLatch latch = new CountDownLatch(1);
doAnswer(invocation -> {
latch.countDown();
return Completable.complete();
}).when(merger).merge(eq(response.getData()));
// Start draining....
subscriptionProcessor.startSubscriptions();
subscriptionProcessor.startDrainingMutationBuffer();
// Was the data merged?
return latch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
}
use of com.amplifyframework.datastore.appsync.ModelWithMetadata in project amplify-android by aws-amplify.
the class SyncProcessorTest method dataStoreHubEventsTriggered.
/**
* During a base sync, there are a series of events that should be emitted.
* This test verifies that these events are published via Amplify Hub depending
* on actions takes for each available model.
* @throws DataStoreException Not expected.
* @throws InterruptedException Not expected.
*/
@Test
public void dataStoreHubEventsTriggered() throws DataStoreException, InterruptedException {
// Arrange - BEGIN
int expectedModelCount = Arrays.asList(Post.class, BlogOwner.class).size();
// Collects one syncQueriesStarted event.
HubAccumulator syncStartAccumulator = createAccumulator(syncQueryStartedForModels(modelCount), 1);
// Collects one syncQueriesReady event.
HubAccumulator syncQueryReadyAccumulator = createAccumulator(forEvent(DataStoreChannelEventName.SYNC_QUERIES_READY), 1);
// Collects one modelSynced event for each model.
HubAccumulator modelSyncedAccumulator = createAccumulator(forEvent(DataStoreChannelEventName.MODEL_SYNCED), expectedModelCount);
// Add a couple of seed records so they can be deleted/updated.
storageAdapter.save(DRUM_POST.getModel());
storageAdapter.save(BLOGGER_ISLA.getModel());
// Mock sync query results for a couple of models.
AppSyncMocking.sync(appSync).mockSuccessResponse(Post.class, DELETED_DRUM_POST).mockSuccessResponse(BlogOwner.class, BLOGGER_ISLA, BLOGGER_JAMESON);
// Start the accumulators.
syncQueryReadyAccumulator.start();
syncStartAccumulator.start();
modelSyncedAccumulator.start();
TestObserver<ModelWithMetadata<? extends Model>> hydrationObserver = TestObserver.create();
// Arrange - END
// Act: kickoff sync.
syncProcessor.hydrate().subscribe(hydrationObserver);
// Check - BEGIN
// Verify that sync completes.
assertTrue(hydrationObserver.await(OP_TIMEOUT_MS, TimeUnit.MILLISECONDS));
hydrationObserver.assertNoErrors();
hydrationObserver.assertComplete();
// Verify that syncQueriesStarted was emitted once.
assertEquals(1, syncStartAccumulator.await((int) OP_TIMEOUT_MS, TimeUnit.MILLISECONDS).size());
// Verify that syncQueriesReady was emitted once.
assertEquals(1, syncQueryReadyAccumulator.await((int) OP_TIMEOUT_MS, TimeUnit.MILLISECONDS).size());
// Get the list of modelSynced events captured.
List<HubEvent<?>> hubEvents = modelSyncedAccumulator.await((int) OP_TIMEOUT_MS, TimeUnit.MILLISECONDS);
// Verify that [number of events] = [number of models]
assertEquals(expectedModelCount, hubEvents.size());
ModelSyncedEvent expectedBlogOwnerCounts = new ModelSyncedEvent("BlogOwner", true, 1, 1, 0);
ModelSyncedEvent expectedPostCounts = new ModelSyncedEvent("Post", true, 0, 0, 1);
// For each event (excluding system models), verify the desired count.
for (HubEvent<?> event : hubEvents) {
ModelSyncedEvent eventData = (ModelSyncedEvent) event.getData();
assertTrue(eventData.isFullSync());
assertFalse(eventData.isDeltaSync());
String eventModel = eventData.getModel();
switch(eventModel) {
case "BlogOwner":
// One BlogOwner added and one updated.
assertEquals(expectedBlogOwnerCounts, eventData);
break;
case "Post":
// One post deleted.
assertEquals(expectedPostCounts, eventData);
break;
default:
// Exclude system models
if (!SYSTEM_MODEL_NAMES.contains(eventModel)) {
ModelSyncedEvent otherCounts = new ModelSyncedEvent(eventModel, true, 0, 0, 0);
assertEquals(otherCounts, eventData);
}
}
}
// Check - END
}
use of com.amplifyframework.datastore.appsync.ModelWithMetadata in project amplify-android by aws-amplify.
the class SyncProcessorTest method randomBlogOwnerWithMetadata.
private static ModelWithMetadata<BlogOwner> randomBlogOwnerWithMetadata() {
BlogOwner blogOwner = BlogOwner.builder().name(RandomString.string()).id(RandomString.string()).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));
}
use of com.amplifyframework.datastore.appsync.ModelWithMetadata in project amplify-android by aws-amplify.
the class ConflictResolverTest method conflictIsResolvedByApplyingRemoteData.
/**
* When the user elects to apply the remote data, the following is expected:
* 1. No additional calls are made to app sync.
* @throws DataStoreException On failure to obtain configuration from provider,
* or on failure to arrange metadata into storage
*/
@Test
public void conflictIsResolvedByApplyingRemoteData() throws DataStoreException {
// The user provides a conflict handler which will always apply the remote
// copy of the data.
when(configurationProvider.getConfiguration()).thenReturn(DataStoreConfiguration.builder().conflictHandler(DataStoreConflictHandler.alwaysApplyRemote()).build());
// Arrange some local pending mutation
BlogOwner localSusan = BlogOwner.builder().name("Local Susan").build();
PendingMutation<BlogOwner> mutation = PendingMutation.update(localSusan, schema);
// Arrange some server data, that is in conflict
BlogOwner serverSusan = localSusan.copyOfBuilder().name("Remote Susan").build();
Temporal.Timestamp now = Temporal.Timestamp.now();
ModelMetadata modelMetadata = new ModelMetadata(serverSusan.getId(), false, 2, now);
ModelWithMetadata<BlogOwner> serverData = new ModelWithMetadata<>(serverSusan, modelMetadata);
// Arrange a conflict error that we could hypothetically get from AppSync
AppSyncConflictUnhandledError<BlogOwner> unhandledConflictError = AppSyncConflictUnhandledErrorFactory.createUnhandledConflictError(serverData);
// When we try to resolve the conflict, the final resolved conflict
// is identically the server's version.
resolver.resolve(mutation, unhandledConflictError).test().awaitDone(TIMEOUT_SECONDS, TimeUnit.SECONDS).assertValue(serverData);
// AppSync wasn't called, since the server was already "correct."
verifyNoInteractions(appSync);
}
use of com.amplifyframework.datastore.appsync.ModelWithMetadata in project amplify-android by aws-amplify.
the class ConflictResolverTest method conflictIsResolvedByRetryingLocalDataWithFlutterSerializedModel.
/**
* When the user elects to retry the mutation using the local copy of the data,
* the following is expected:
* 1. The AppSync API is invoked, with the local mutation data
* 2. We assume that the AppSync API will respond differently
* upon retry.
* @throws AmplifyException On failure to arrange metadata into storage
*/
@Test
public void conflictIsResolvedByRetryingLocalDataWithFlutterSerializedModel() throws AmplifyException {
// Arrange for the user-provided conflict handler to always request local retry.
when(configurationProvider.getConfiguration()).thenReturn(DataStoreConfiguration.builder().conflictHandler(DataStoreConflictHandler.alwaysRetryLocal()).build());
// Arrange a pending mutation that includes the local data
Map<String, Object> blogOwnerData = new HashMap<>();
blogOwnerData.put("name", "A seasoned writer");
blogOwnerData.put("id", "e50ffa8f-783b-4780-89b4-27043ffc35be");
SerializedModel serializedOwner = SerializedModel.builder().serializedData(blogOwnerData).modelSchema(schemaFrom()).build();
PendingMutation<SerializedModel> mutation = PendingMutation.update(serializedOwner, schema);
// Arrange server state for the model, in conflict to local data
Map<String, Object> serverBlogOwnerData = new HashMap<>();
serverBlogOwnerData.put("name", "A seasoned writer");
serverBlogOwnerData.put("id", "e50ffa8f-783b-4780-89b4-27043ffc35be");
SerializedModel serverModel = SerializedModel.builder().serializedData(serverBlogOwnerData).modelSchema(null).build();
Temporal.Timestamp now = Temporal.Timestamp.now();
ModelMetadata metadata = new ModelMetadata(serverModel.getId(), false, 4, now);
ModelWithMetadata<SerializedModel> serverData = new ModelWithMetadata<>(serializedOwner, metadata);
// Arrange a hypothetical conflict error from AppSync
AppSyncConflictUnhandledError<SerializedModel> unhandledConflictError = AppSyncConflictUnhandledErrorFactory.createUnhandledConflictError(serverData);
// Assume that the AppSync call succeeds this time.
ModelWithMetadata<SerializedModel> versionFromAppSyncResponse = new ModelWithMetadata<>(serializedOwner, metadata);
AppSyncMocking.update(appSync).mockSuccessResponse(serializedOwner, metadata.getVersion(), versionFromAppSyncResponse);
// Act: when the resolver is invoked, we expect the resolved version
// to include the server's metadata, but with the local data.
resolver.resolve(mutation, unhandledConflictError).test();
// The handler should have called up to AppSync to update the model
verify(appSync).update(eq(serializedOwner), any(), eq(metadata.getVersion()), any(), any());
}
Aggregations