Search in sources :

Example 21 with ModelMetadata

use of com.amplifyframework.datastore.appsync.ModelMetadata in project amplify-android by aws-amplify.

the class MergerTest method itemIsNotMergedWhenOutboxHasPendingMutation.

/**
 * When an item comes into the merger to be merged,
 * if there is a pending mutation in the outbox, for a model of the same ID,
 * then that item shall NOT be merged.
 * @throws DataStoreException On failure to arrange data into store
 * @throws InterruptedException If interrupted while awaiting terminal result in test observer
 * @throws AmplifyException On failure to arrange model schema
 */
@Test
public void itemIsNotMergedWhenOutboxHasPendingMutation() throws AmplifyException, InterruptedException {
    // Arrange: some model with a well known ID exists on the system.
    // We pretend that the user has recently updated it via the DataStore update() API.
    String knownId = RandomString.string();
    BlogOwner blogOwner = BlogOwner.builder().name("Jameson").id(knownId).build();
    ModelMetadata localMetadata = new ModelMetadata(blogOwner.getId(), false, 1, Temporal.Timestamp.now());
    storageAdapter.save(blogOwner, localMetadata);
    ModelSchema schema = ModelSchema.fromModelClass(BlogOwner.class);
    PendingMutation<BlogOwner> pendingMutation = PendingMutation.instance(blogOwner, schema, PendingMutation.Type.CREATE, QueryPredicates.all());
    TestObserver<Void> enqueueObserver = mutationOutbox.enqueue(pendingMutation).test();
    enqueueObserver.await(REASONABLE_WAIT_TIME, TimeUnit.MILLISECONDS);
    enqueueObserver.assertNoErrors().assertComplete();
    // 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());
    TestObserver<Void> mergeObserver = merger.merge(new ModelWithMetadata<>(blogOwner, cloudMetadata)).test();
    mergeObserver.await(REASONABLE_WAIT_TIME, TimeUnit.MILLISECONDS);
    mergeObserver.assertNoErrors().assertComplete();
    // Assert: the item is NOT deleted from the local store.
    // The original is still there.
    // Or in other words, the cloud data was NOT merged.
    final List<BlogOwner> blogOwnersInStorage = storageAdapter.query(BlogOwner.class);
    assertEquals(1, blogOwnersInStorage.size());
    assertEquals(blogOwner, blogOwnersInStorage.get(0));
}
Also used : ModelSchema(com.amplifyframework.core.model.ModelSchema) ModelWithMetadata(com.amplifyframework.datastore.appsync.ModelWithMetadata) BlogOwner(com.amplifyframework.testmodels.commentsblog.BlogOwner) RandomString(com.amplifyframework.testutils.random.RandomString) ModelMetadata(com.amplifyframework.datastore.appsync.ModelMetadata) Test(org.junit.Test)

Example 22 with ModelMetadata

use of com.amplifyframework.datastore.appsync.ModelMetadata in project amplify-android by aws-amplify.

the class MergerTest method orphanedItemIsNotMerged.

/**
 * Assume item A is dependent on item B, but the remote store has an
 * orphaned item A without item B. Then, we try to merge a save for a
 * item A. This should gracefully fail, with A not being in the local
 * store, at the end.
 * @throws DataStoreException On failure to query results for assertions
 * @throws InterruptedException If interrupted while awaiting terminal result in test observer
 */
@Test
public void orphanedItemIsNotMerged() throws DataStoreException, InterruptedException {
    // Arrange: an item and its parent are not in the local store
    BlogOwner badOwner = BlogOwner.builder().name("Raphael").build();
    Blog orphanedBlog = Blog.builder().name("How Not To Save Blogs").owner(badOwner).build();
    ModelMetadata metadata = new ModelMetadata(orphanedBlog.getId(), false, 1, Temporal.Timestamp.now());
    // Enforce foreign key constraint on in-memory storage adapter
    doThrow(SQLiteConstraintException.class).when(inMemoryStorageAdapter).save(eq(orphanedBlog), any(), any(), any(), any());
    // Act: merge a creation for an item
    TestObserver<Void> observer = merger.merge(new ModelWithMetadata<>(orphanedBlog, metadata)).test();
    assertTrue(observer.await(REASONABLE_WAIT_TIME, TimeUnit.MILLISECONDS));
    observer.assertNoErrors().assertComplete();
    // Assert: orphaned model was not merged locally
    final List<Blog> blogsInStorage = storageAdapter.query(Blog.class);
    assertTrue(blogsInStorage.isEmpty());
}
Also used : ModelWithMetadata(com.amplifyframework.datastore.appsync.ModelWithMetadata) BlogOwner(com.amplifyframework.testmodels.commentsblog.BlogOwner) Blog(com.amplifyframework.testmodels.commentsblog.Blog) ModelMetadata(com.amplifyframework.datastore.appsync.ModelMetadata) Test(org.junit.Test)

Example 23 with ModelMetadata

use of com.amplifyframework.datastore.appsync.ModelMetadata in project amplify-android by aws-amplify.

the class MergerTest method itemIsMergedAfterPendingMutationRemovedFromOutbox.

/**
 * When processing a mutation response, the pending mutation should be removed from the outbox, and the mutation
 * should be merged to local storage.
 * @throws InterruptedException If interrupted while awaiting terminal result in test observer
 * @throws AmplifyException On failure to arrange model schema
 */
@Test
public void itemIsMergedAfterPendingMutationRemovedFromOutbox() throws AmplifyException, InterruptedException {
    // Arrange: some model with a well known ID exists on the system.
    // We pretend that the user has recently updated it via the DataStore update() API.
    String knownId = RandomString.string();
    BlogOwner blogOwner = BlogOwner.builder().name("Jameson").id(knownId).build();
    ModelMetadata localMetadata = new ModelMetadata(blogOwner.getId(), false, 1, Temporal.Timestamp.now());
    storageAdapter.save(blogOwner, localMetadata);
    ModelSchema schema = ModelSchema.fromModelClass(BlogOwner.class);
    PendingMutation<BlogOwner> pendingMutation = PendingMutation.instance(blogOwner, schema, PendingMutation.Type.DELETE, QueryPredicates.all());
    TestObserver<Void> enqueueObserver = mutationOutbox.enqueue(pendingMutation).test();
    enqueueObserver.await(REASONABLE_WAIT_TIME, TimeUnit.MILLISECONDS);
    enqueueObserver.assertNoErrors().assertComplete();
    // 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());
    TestObserver<Void> observer = mutationOutbox.remove(pendingMutation.getMutationId()).andThen(merger.merge(new ModelWithMetadata<>(blogOwner, cloudMetadata))).test();
    observer.await(REASONABLE_WAIT_TIME, TimeUnit.MILLISECONDS);
    observer.assertNoErrors().assertComplete();
    // Assert: the item IS deleted from the local store.
    // Or in other words, the cloud data WAS merged.
    final List<BlogOwner> blogOwnersInStorage = storageAdapter.query(BlogOwner.class);
    assertEquals(0, blogOwnersInStorage.size());
}
Also used : ModelSchema(com.amplifyframework.core.model.ModelSchema) BlogOwner(com.amplifyframework.testmodels.commentsblog.BlogOwner) RandomString(com.amplifyframework.testutils.random.RandomString) ModelMetadata(com.amplifyframework.datastore.appsync.ModelMetadata) Test(org.junit.Test)

Example 24 with ModelMetadata

use of com.amplifyframework.datastore.appsync.ModelMetadata in project amplify-android by aws-amplify.

the class OrchestratorTest method setup.

/**
 * Setup mocks and other common elements.
 * @throws AmplifyException Not expected.
 */
@SuppressWarnings("unchecked")
@Before
public void setup() throws AmplifyException {
    ShadowLog.stream = System.out;
    // Arrange: create a BlogOwner
    susan = BlogOwner.builder().name("Susan Quimby").build();
    // SYNC_QUERIES_READY indicates that the sync queries have completed.
    orchestratorInitObserver = HubAccumulator.create(HubChannel.DATASTORE, DataStoreChannelEventName.SYNC_QUERIES_READY, 1).start();
    ModelMetadata metadata = new ModelMetadata(susan.getId(), false, 1, Temporal.Timestamp.now());
    ModelWithMetadata<BlogOwner> modelWithMetadata = new ModelWithMetadata<>(susan, metadata);
    // Mock behaviors from for the API category
    mockApi = mock(GraphQLBehavior.class);
    ApiMocking.mockSubscriptionStart(mockApi);
    ApiMocking.mockSuccessfulMutation(mockApi, susan.getId(), modelWithMetadata);
    ApiMocking.mockSuccessfulQuery(mockApi, modelWithMetadata);
    AppSyncClient appSync = AppSyncClient.via(mockApi);
    localStorageAdapter = InMemoryStorageAdapter.create();
    ModelProvider modelProvider = SimpleModelProvider.withRandomVersion(BlogOwner.class);
    SchemaRegistry schemaRegistry = SchemaRegistry.instance();
    schemaRegistry.clear();
    schemaRegistry.register(modelProvider.models());
    orchestrator = new Orchestrator(modelProvider, schemaRegistry, localStorageAdapter, appSync, DataStoreConfiguration::defaults, () -> Orchestrator.State.SYNC_VIA_API, true);
}
Also used : GraphQLBehavior(com.amplifyframework.api.graphql.GraphQLBehavior) ModelWithMetadata(com.amplifyframework.datastore.appsync.ModelWithMetadata) AppSyncClient(com.amplifyframework.datastore.appsync.AppSyncClient) ModelProvider(com.amplifyframework.core.model.ModelProvider) SimpleModelProvider(com.amplifyframework.datastore.model.SimpleModelProvider) BlogOwner(com.amplifyframework.testmodels.commentsblog.BlogOwner) ModelMetadata(com.amplifyframework.datastore.appsync.ModelMetadata) SchemaRegistry(com.amplifyframework.core.model.SchemaRegistry) Before(org.junit.Before)

Example 25 with ModelMetadata

use of com.amplifyframework.datastore.appsync.ModelMetadata in project amplify-android by aws-amplify.

the class AWSDataStorePluginTest method clearStopsSyncAndDeletesDatabase.

/**
 * Verify that when the clear method is called, the following happens
 * - All remote synchronization processes are stopped
 * - The database is deleted.
 * - On the next interaction with the DataStore, the synchronization processes are restarted.
 * @throws JSONException on failure to arrange plugin config
 * @throws AmplifyException on failure to arrange API plugin via Amplify facade
 */
@Test
public void clearStopsSyncAndDeletesDatabase() throws AmplifyException, JSONException {
    ApiCategory mockApiCategory = mockApiCategoryWithGraphQlApi();
    ApiPlugin<?> mockApiPlugin = mockApiCategory.getPlugin(MOCK_API_PLUGIN_NAME);
    JSONObject dataStorePluginJson = new JSONObject().put("syncIntervalInMinutes", 60);
    AWSDataStorePlugin awsDataStorePlugin = AWSDataStorePlugin.builder().modelProvider(modelProvider).apiCategory(mockApiCategory).build();
    SynchronousDataStore synchronousDataStore = SynchronousDataStore.delegatingTo(awsDataStorePlugin);
    awsDataStorePlugin.configure(dataStorePluginJson, context);
    awsDataStorePlugin.initialize(context);
    // Trick the DataStore since it's not getting initialized as part of the Amplify.initialize call chain
    Amplify.Hub.publish(HubChannel.DATASTORE, HubEvent.create(InitializationStatus.SUCCEEDED));
    // Setup objects
    Person person1 = createPerson("Test", "Dummy I");
    Person person2 = createPerson("Test", "Dummy II");
    // Mock responses for person 1
    doAnswer(invocation -> {
        int indexOfResponseConsumer = 1;
        Consumer<GraphQLResponse<ModelWithMetadata<Person>>> onResponse = invocation.getArgument(indexOfResponseConsumer);
        ModelMetadata modelMetadata = new ModelMetadata(person1.getId(), false, 1, Temporal.Timestamp.now());
        ModelWithMetadata<Person> modelWithMetadata = new ModelWithMetadata<>(person1, modelMetadata);
        onResponse.accept(new GraphQLResponse<>(modelWithMetadata, Collections.emptyList()));
        return mock(GraphQLOperation.class);
    }).when(mockApiPlugin).mutate(any(), any(), any());
    HubAccumulator apiInteractionObserver = HubAccumulator.create(HubChannel.DATASTORE, DataStoreChannelEventName.OUTBOX_MUTATION_PROCESSED, 1).start();
    // Save person 1
    synchronousDataStore.save(person1);
    Person result1 = synchronousDataStore.get(Person.class, person1.getId());
    assertEquals(person1, result1);
    apiInteractionObserver.await();
    verify(mockApiCategory).mutate(argThat(getMatcherFor(person1)), any(), any());
    // Mock responses for person 2
    doAnswer(invocation -> {
        int indexOfResponseConsumer = 1;
        Consumer<GraphQLResponse<ModelWithMetadata<Person>>> onResponse = invocation.getArgument(indexOfResponseConsumer);
        ModelMetadata modelMetadata = new ModelMetadata(person2.getId(), false, 1, Temporal.Timestamp.now());
        ModelWithMetadata<Person> modelWithMetadata = new ModelWithMetadata<>(person2, modelMetadata);
        onResponse.accept(new GraphQLResponse<>(modelWithMetadata, Collections.emptyList()));
        return mock(GraphQLOperation.class);
    }).when(mockApiPlugin).mutate(any(), any(), any());
    // Do the thing!
    synchronousDataStore.clear();
    assertRemoteSubscriptionsCancelled();
    apiInteractionObserver = HubAccumulator.create(HubChannel.DATASTORE, DataStoreChannelEventName.OUTBOX_MUTATION_PROCESSED, 1).start();
    HubAccumulator orchestratorInitObserver = HubAccumulator.create(HubChannel.DATASTORE, DataStoreChannelEventName.READY, 1).start();
    // Interact with the DataStore after the clear
    synchronousDataStore.save(person2);
    // Verify person 2 was published to the cloud
    apiInteractionObserver.await();
    // Verify the orchestrator started back up and subscriptions are active.
    orchestratorInitObserver.await();
    assertRemoteSubscriptionsStarted();
    Person result2 = synchronousDataStore.get(Person.class, person2.getId());
    assertEquals(person2, result2);
    verify(mockApiCategory, atLeastOnce()).mutate(argThat(getMatcherFor(person2)), any(), any());
}
Also used : ModelWithMetadata(com.amplifyframework.datastore.appsync.ModelWithMetadata) GraphQLResponse(com.amplifyframework.api.graphql.GraphQLResponse) JSONObject(org.json.JSONObject) SynchronousDataStore(com.amplifyframework.testutils.sync.SynchronousDataStore) ApiCategory(com.amplifyframework.api.ApiCategory) HubAccumulator(com.amplifyframework.testutils.HubAccumulator) Person(com.amplifyframework.testmodels.personcar.Person) ModelMetadata(com.amplifyframework.datastore.appsync.ModelMetadata) Test(org.junit.Test)

Aggregations

ModelMetadata (com.amplifyframework.datastore.appsync.ModelMetadata)29 ModelWithMetadata (com.amplifyframework.datastore.appsync.ModelWithMetadata)23 Test (org.junit.Test)23 BlogOwner (com.amplifyframework.testmodels.commentsblog.BlogOwner)22 Temporal (com.amplifyframework.core.model.temporal.Temporal)7 GraphQLResponse (com.amplifyframework.api.graphql.GraphQLResponse)6 ModelSchema (com.amplifyframework.core.model.ModelSchema)6 HubAccumulator (com.amplifyframework.testutils.HubAccumulator)4 NonNull (androidx.annotation.NonNull)3 ApiCategory (com.amplifyframework.api.ApiCategory)3 SerializedModel (com.amplifyframework.core.model.SerializedModel)3 Person (com.amplifyframework.testmodels.personcar.Person)3 RandomString (com.amplifyframework.testutils.random.RandomString)3 SynchronousDataStore (com.amplifyframework.testutils.sync.SynchronousDataStore)3 HashMap (java.util.HashMap)3 CountDownLatch (java.util.concurrent.CountDownLatch)3 GraphQLLocation (com.amplifyframework.api.graphql.GraphQLLocation)2 GraphQLPathSegment (com.amplifyframework.api.graphql.GraphQLPathSegment)2 Amplify (com.amplifyframework.core.Amplify)2 Consumer (com.amplifyframework.core.Consumer)2