Search in sources :

Example 11 with ModelMetadata

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

the class MutationProcessorTest method hubEventPublishedForPublicationError.

/**
 * If the AppSync response to the mutation contains not-empty GraphQLResponse error
 * list without any ConflictUnhandled error, then
 * {@link DataStoreChannelEventName#OUTBOX_MUTATION_FAILED} event is published via Hub.
 * @throws DataStoreException On failure to save model and metadata
 */
@Test
public void hubEventPublishedForPublicationError() throws DataStoreException {
    // Save a model, its metadata, and its last sync data.
    BlogOwner model = BlogOwner.builder().name("Average Joe").build();
    ModelMetadata metadata = new ModelMetadata(model.getModelName() + "|" + model.getId(), false, 1, Temporal.Timestamp.now());
    ModelSchema schema = schemaRegistry.getModelSchemaForModelClass(BlogOwner.class);
    synchronousStorageAdapter.save(model, metadata);
    // Enqueue an update in the mutation outbox
    assertTrue(mutationOutbox.enqueue(PendingMutation.update(model, schema)).blockingAwait(TIMEOUT_SECONDS, TimeUnit.SECONDS));
    // When AppSync receives that update, have it respond with an error.
    AppSyncMocking.update(appSync).mockErrorResponse(model, 1);
    // Start listening for publication events.
    HubAccumulator errorAccumulator = HubAccumulator.create(HubChannel.DATASTORE, DataStoreChannelEventName.OUTBOX_MUTATION_FAILED, 1).start();
    // Start the mutation processor and wait for hub event.
    mutationProcessor.startDrainingMutationOutbox();
    errorAccumulator.await();
}
Also used : ModelSchema(com.amplifyframework.core.model.ModelSchema) BlogOwner(com.amplifyframework.testmodels.commentsblog.BlogOwner) HubAccumulator(com.amplifyframework.testutils.HubAccumulator) ModelMetadata(com.amplifyframework.datastore.appsync.ModelMetadata) Test(org.junit.Test)

Example 12 with ModelMetadata

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

the class VersionRepositoryTest method emitsSuccessWithValueWhenVersionInStore.

/**
 * When there is metadata for a model in the store, and that metadata includes a version -
 * for heaven's sake, man - do please emit the dang thing.
 * @throws DataStoreException On failure to arrange data into store
 * @throws InterruptedException If interrupted while awaiting terminal result in test observer
 */
@Test
public void emitsSuccessWithValueWhenVersionInStore() throws DataStoreException, InterruptedException {
    // Arrange versioning info into the store.
    BlogOwner owner = BlogOwner.builder().name("Jameson").build();
    final int maxRandomVersion = 1_000;
    int expectedVersion = new Random().nextInt(maxRandomVersion);
    storageAdapter.save(new ModelMetadata(owner.getModelName() + "|" + owner.getId(), false, expectedVersion, Temporal.Timestamp.now()));
    // Act! Try to obtain it via the Versioning Repository.
    TestObserver<Integer> observer = versionRepository.findModelVersion(owner).test();
    assertTrue(observer.await(REASONABLE_WAIT_TIME, TimeUnit.MILLISECONDS));
    // Assert: we got a version.
    observer.assertNoErrors().assertComplete().assertValue(expectedVersion);
}
Also used : Random(java.util.Random) BlogOwner(com.amplifyframework.testmodels.commentsblog.BlogOwner) ModelMetadata(com.amplifyframework.datastore.appsync.ModelMetadata) Test(org.junit.Test)

Example 13 with ModelMetadata

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

the class ConflictResolverIntegrationTest method setupApiMock.

@SuppressWarnings("unchecked")
private Person setupApiMock(CountDownLatch latch, ApiCategory mockApiCategory) {
    Person person1 = createPerson("Test", "Dummy I");
    // Mock success on subscription.
    doAnswer(invocation -> {
        int indexOfStartConsumer = 1;
        Consumer<String> onStart = invocation.getArgument(indexOfStartConsumer);
        GraphQLOperation<?> mockOperation = mock(GraphQLOperation.class);
        doAnswer(opAnswer -> {
            return null;
        }).when(mockOperation).cancel();
        // Trigger the subscription start event.
        onStart.accept(RandomString.string());
        return mockOperation;
    }).when(mockApiCategory).subscribe(any(GraphQLRequest.class), any(Consumer.class), any(Consumer.class), any(Consumer.class), any(Action.class));
    // When mutate is called on the appsync for the first time unhandled conflict error is returned.
    doAnswer(invocation -> {
        int indexOfResponseConsumer = 1;
        Consumer<GraphQLResponse<ModelWithMetadata<Person>>> onResponse = invocation.getArgument(indexOfResponseConsumer);
        List<GraphQLLocation> locations = new ArrayList<>();
        locations.add(new GraphQLLocation(2, 3));
        List<GraphQLPathSegment> path = new ArrayList<>();
        path.add(new GraphQLPathSegment("updatePost"));
        Map<String, Object> serverModelData = new HashMap<>();
        serverModelData.put("id", "5c895eae-88ef-4ce8-9d58-e27d0c7cbe99");
        serverModelData.put("createdAt", "2022-02-04T19:41:05.973Z");
        serverModelData.put("first_name", "test");
        serverModelData.put("last_name", "server last");
        serverModelData.put("_version", 92);
        serverModelData.put("_deleted", false);
        serverModelData.put("_lastChangedAt", 1_000);
        Map<String, Object> extensions = new HashMap<>();
        extensions.put("errorInfo", null);
        extensions.put("data", serverModelData);
        extensions.put("errorType", "ConflictUnhandled");
        ArrayList<GraphQLResponse.Error> errorList = new ArrayList<>();
        errorList.add(new GraphQLResponse.Error("Conflict resolver rejects mutation.", locations, path, extensions));
        onResponse.accept(new GraphQLResponse<>(null, errorList));
        // latch makes sure conflict unhandled response is returned.
        latch.countDown();
        return mock(GraphQLOperation.class);
    }).doAnswer(invocation -> {
        // When mutate is called on the appsync for the second time success response is returned
        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()));
        verify(mockApiCategory, atLeast(2)).mutate(argThat(getMatcherFor(person1)), any(), any());
        // latch makes sure success response is returned.
        latch.countDown();
        return mock(GraphQLOperation.class);
    }).when(mockApiCategory).mutate(any(), any(), any());
    // Setup to mimic successful sync
    doAnswer(invocation -> {
        int indexOfResponseConsumer = 1;
        ModelMetadata modelMetadata = new ModelMetadata(person1.getId(), false, 1, Temporal.Timestamp.now());
        ModelWithMetadata<Person> modelWithMetadata = new ModelWithMetadata<>(person1, modelMetadata);
        // Mock the API emitting an ApiEndpointStatusChangeEvent event.
        Consumer<GraphQLResponse<PaginatedResult<ModelWithMetadata<Person>>>> onResponse = invocation.getArgument(indexOfResponseConsumer);
        PaginatedResult<ModelWithMetadata<Person>> data = new PaginatedResult<>(Collections.singletonList(modelWithMetadata), null);
        onResponse.accept(new GraphQLResponse<>(data, Collections.emptyList()));
        latch.countDown();
        return mock(GraphQLOperation.class);
    }).doAnswer(invocation -> {
        int indexOfResponseConsumer = 1;
        Car car = Car.builder().build();
        ModelMetadata modelMetadata = new ModelMetadata(car.getId(), false, 1, Temporal.Timestamp.now());
        ModelWithMetadata<Car> modelWithMetadata = new ModelWithMetadata<>(car, modelMetadata);
        Consumer<GraphQLResponse<PaginatedResult<ModelWithMetadata<Car>>>> onResponse = invocation.getArgument(indexOfResponseConsumer);
        PaginatedResult<ModelWithMetadata<Car>> data = new PaginatedResult<>(Collections.singletonList(modelWithMetadata), null);
        onResponse.accept(new GraphQLResponse<>(data, Collections.emptyList()));
        latch.countDown();
        return mock(GraphQLOperation.class);
    }).when(mockApiCategory).query(any(), any(), any());
    return person1;
}
Also used : AmplifyException(com.amplifyframework.AmplifyException) ApplicationProvider.getApplicationContext(androidx.test.core.app.ApplicationProvider.getApplicationContext) ArgumentMatchers.argThat(org.mockito.ArgumentMatchers.argThat) ModelWithMetadata(com.amplifyframework.datastore.appsync.ModelWithMetadata) ModelProvider(com.amplifyframework.core.model.ModelProvider) GraphQLPathSegment(com.amplifyframework.api.graphql.GraphQLPathSegment) ApiChannelEventName(com.amplifyframework.api.events.ApiChannelEventName) ArgumentMatcher(org.mockito.ArgumentMatcher) JSONException(org.json.JSONException) JSONObject(org.json.JSONObject) GraphQLLocation(com.amplifyframework.api.graphql.GraphQLLocation) Map(java.util.Map) Mockito.doAnswer(org.mockito.Mockito.doAnswer) Mockito.atLeast(org.mockito.Mockito.atLeast) GraphQLResponse(com.amplifyframework.api.graphql.GraphQLResponse) Assert.fail(org.junit.Assert.fail) HubEvent(com.amplifyframework.hub.HubEvent) HubChannel(com.amplifyframework.hub.HubChannel) ApiCategory(com.amplifyframework.api.ApiCategory) InitializationStatus(com.amplifyframework.core.InitializationStatus) RobolectricTestRunner(org.robolectric.RobolectricTestRunner) Person(com.amplifyframework.testmodels.personcar.Person) CountDownLatch(java.util.concurrent.CountDownLatch) List(java.util.List) RandomString(com.amplifyframework.testutils.random.RandomString) Mockito.any(org.mockito.Mockito.any) Mockito.mock(org.mockito.Mockito.mock) Context(android.content.Context) GraphQLRequest(com.amplifyframework.api.graphql.GraphQLRequest) RunWith(org.junit.runner.RunWith) HashMap(java.util.HashMap) Car(com.amplifyframework.testmodels.personcar.Car) Mockito.spy(org.mockito.Mockito.spy) ArrayList(java.util.ArrayList) Consumer(com.amplifyframework.core.Consumer) ApiEndpointStatusChangeEvent(com.amplifyframework.api.events.ApiEndpointStatusChangeEvent) ModelMetadata(com.amplifyframework.datastore.appsync.ModelMetadata) PaginatedResult(com.amplifyframework.api.graphql.PaginatedResult) Before(org.junit.Before) Amplify(com.amplifyframework.core.Amplify) SynchronousDataStore(com.amplifyframework.testutils.sync.SynchronousDataStore) ApiPlugin(com.amplifyframework.api.ApiPlugin) CategoryType(com.amplifyframework.core.category.CategoryType) Assert.assertTrue(org.junit.Assert.assertTrue) Test(org.junit.Test) Completable(io.reactivex.rxjava3.core.Completable) Action(com.amplifyframework.core.Action) Mockito.when(org.mockito.Mockito.when) AmplifyCliGeneratedModelProvider(com.amplifyframework.testmodels.personcar.AmplifyCliGeneratedModelProvider) Mockito.verify(org.mockito.Mockito.verify) TimeUnit(java.util.concurrent.TimeUnit) ApiCategoryConfiguration(com.amplifyframework.api.ApiCategoryConfiguration) Temporal(com.amplifyframework.core.model.temporal.Temporal) GraphQLOperation(com.amplifyframework.api.graphql.GraphQLOperation) Collections(java.util.Collections) Assert.assertEquals(org.junit.Assert.assertEquals) Action(com.amplifyframework.core.Action) GraphQLLocation(com.amplifyframework.api.graphql.GraphQLLocation) ArrayList(java.util.ArrayList) GraphQLPathSegment(com.amplifyframework.api.graphql.GraphQLPathSegment) RandomString(com.amplifyframework.testutils.random.RandomString) GraphQLRequest(com.amplifyframework.api.graphql.GraphQLRequest) Consumer(com.amplifyframework.core.Consumer) List(java.util.List) ArrayList(java.util.ArrayList) ModelWithMetadata(com.amplifyframework.datastore.appsync.ModelWithMetadata) PaginatedResult(com.amplifyframework.api.graphql.PaginatedResult) GraphQLResponse(com.amplifyframework.api.graphql.GraphQLResponse) Car(com.amplifyframework.testmodels.personcar.Car) GraphQLOperation(com.amplifyframework.api.graphql.GraphQLOperation) Person(com.amplifyframework.testmodels.personcar.Person) Map(java.util.Map) HashMap(java.util.HashMap) ModelMetadata(com.amplifyframework.datastore.appsync.ModelMetadata)

Example 14 with ModelMetadata

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

the class OutboxMutationEvent method create.

/**
 * Constructs an outbox mutation event containing both the model and its
 * sync metadata.
 * This format will be used for representing a pending mutation that has
 * successfully undergone cloud publication.
 * @param modelName Name of the model that has been processed (e.g., "Blog".)
 * @param modelWithMetadata Processed model with its sync metadata.
 * @param <M> Class type of the model.
 * @return Outbox mutation event with sync metadata.
 */
@NonNull
public static <M extends Model> OutboxMutationEvent<M> create(@NonNull String modelName, @NonNull ModelWithMetadata<M> modelWithMetadata) {
    Objects.requireNonNull(modelName);
    Objects.requireNonNull(modelWithMetadata);
    M model = modelWithMetadata.getModel();
    ModelMetadata metadata = modelWithMetadata.getSyncMetadata();
    Integer version = metadata.getVersion();
    Long lastChangedAt = metadata.getLastChangedAt() != null ? metadata.getLastChangedAt().getSecondsSinceEpoch() : null;
    Boolean deleted = metadata.isDeleted();
    OutboxMutationEventElement<M> element = new OutboxMutationEventElement<>(model, version, lastChangedAt, deleted);
    return new OutboxMutationEvent<>(modelName, element);
}
Also used : ModelMetadata(com.amplifyframework.datastore.appsync.ModelMetadata) NonNull(androidx.annotation.NonNull)

Example 15 with ModelMetadata

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

the class BasicCloudSyncInstrumentationTest method syncDownFromCloudIsWorking.

/**
 * The sync engine should receive mutations for its managed models, through its
 * subscriptions. When we create a model remotely, the sync engine should respond
 * by processing the subscription event and saving the model locally.
 * @throws DataStoreException On failure to query the local data store for
 *                            local presence of arranged data (second step)
 * @throws AmplifyException On failure to arrange a {@link DataStoreCategory} via the
 *                          {@link DataStoreCategoryConfigurator}
 */
@Test
public void syncDownFromCloudIsWorking() throws AmplifyException {
    // This model will get saved to the cloud.
    BlogOwner jameson = BlogOwner.builder().name("Jameson Williams").build();
    // Start watching locally, to see if it shows up on the client.
    HubAccumulator receiptAccumulator = HubAccumulator.create(HubChannel.DATASTORE, receiptOf(jameson.getId()), 1).start();
    // Act: create the model in the cloud
    ModelSchema schema = ModelSchema.fromModelClass(BlogOwner.class);
    GraphQLResponse<ModelWithMetadata<BlogOwner>> createResponse = appSync.create(jameson, schema);
    ModelMetadata metadata = createResponse.getData().getSyncMetadata();
    assertEquals(Integer.valueOf(1), metadata.getVersion());
    // Wait for the events to show up on Hub.
    receiptAccumulator.awaitFirst(TIMEOUT_SECONDS, TimeUnit.SECONDS);
    // Jameson should be in the local DataStore.
    BlogOwner owner = dataStore.get(BlogOwner.class, jameson.getId());
    assertEquals("Jameson Williams", owner.getName());
    assertEquals(jameson.getId(), owner.getId());
}
Also used : ModelSchema(com.amplifyframework.core.model.ModelSchema) ModelWithMetadata(com.amplifyframework.datastore.appsync.ModelWithMetadata) BlogOwner(com.amplifyframework.testmodels.commentsblog.BlogOwner) HubAccumulator(com.amplifyframework.testutils.HubAccumulator) 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