use of com.amplifyframework.core.model.Model in project amplify-android by aws-amplify.
the class ObserveQueryExecutorTest method observeQueryReturnsOfflineData.
/**
* Tests for ObserveQueryExecutor.
* @throws InterruptedException InterruptedException
* @throws DataStoreException DataStoreException
*/
@Test
public void observeQueryReturnsOfflineData() throws InterruptedException, DataStoreException {
CountDownLatch latch = new CountDownLatch(1);
final BlogOwner blogOwner = BlogOwner.builder().name("Alan Turing").build();
List<BlogOwner> resultList = new ArrayList<>();
resultList.add(blogOwner);
Consumer<Cancelable> observationStarted = NoOpConsumer.create();
Consumer<DataStoreQuerySnapshot<BlogOwner>> onQuerySnapshot = value -> {
Assert.assertTrue(value.getItems().contains(blogOwner));
latch.countDown();
};
Consumer<DataStoreException> onObservationError = NoOpConsumer.create();
Action onObservationComplete = NoOpAction.create();
SqlQueryProcessor mockSqlQueryProcessor = mock(SqlQueryProcessor.class);
when(mockSqlQueryProcessor.queryOfflineData(eq(BlogOwner.class), any(), any())).thenReturn(resultList);
Subject<StorageItemChange<? extends Model>> subject = PublishSubject.<StorageItemChange<? extends Model>>create().toSerialized();
ExecutorService threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 5);
ObserveQueryExecutor<BlogOwner> observeQueryExecutor = new ObserveQueryExecutor<>(subject, mockSqlQueryProcessor, threadPool, mock(SyncStatus.class), new ModelSorter<>(), DataStoreConfiguration.defaults());
observeQueryExecutor.observeQuery(BlogOwner.class, new ObserveQueryOptions(null, null), observationStarted, onQuerySnapshot, onObservationError, onObservationComplete);
Assert.assertTrue(latch.await(1, TimeUnit.SECONDS));
}
use of com.amplifyframework.core.model.Model in project amplify-android by aws-amplify.
the class ObserveQueryExecutorTest method observeQueryCancelsTheOperationOnQueryError.
/**
* testing cancel on observe query.
* @throws DataStoreException DataStoreException
*/
@Test
public void observeQueryCancelsTheOperationOnQueryError() throws DataStoreException {
Consumer<DataStoreQuerySnapshot<BlogOwner>> onQuerySnapshot = NoOpConsumer.create();
Consumer<DataStoreException> onObservationError = NoOpConsumer.create();
Action onObservationComplete = NoOpAction.create();
SQLCommandProcessor sqlCommandProcessor = mock(SQLCommandProcessor.class);
when(sqlCommandProcessor.rawQuery(any())).thenThrow(new DataStoreException("test", "test"));
SqlQueryProcessor sqlQueryProcessor = new SqlQueryProcessor(sqlCommandProcessor, mock(SQLiteCommandFactory.class), mock(SchemaRegistry.class));
Subject<StorageItemChange<? extends Model>> subject = PublishSubject.<StorageItemChange<? extends Model>>create().toSerialized();
ExecutorService threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 5);
ObserveQueryExecutor<BlogOwner> observeQueryExecutor = new ObserveQueryExecutor<>(subject, sqlQueryProcessor, threadPool, mock(SyncStatus.class), new ModelSorter<>(), DataStoreConfiguration.defaults());
Consumer<Cancelable> observationStarted = value -> {
value.cancel();
Assert.assertTrue(observeQueryExecutor.getIsCancelled());
assertEquals(0, observeQueryExecutor.getCompleteMap().size());
assertEquals(0, observeQueryExecutor.getChangeList().size());
subject.test().assertNoErrors().isDisposed();
};
observeQueryExecutor.observeQuery(BlogOwner.class, new ObserveQueryOptions(null, null), observationStarted, onQuerySnapshot, onObservationError, onObservationComplete);
}
use of com.amplifyframework.core.model.Model in project amplify-android by aws-amplify.
the class ObserveQueryExecutorTest method observeQueryReturnsRecordsBasedOnMaxRecords.
/**
* observe Query Returns Records Based On Max Records.
* @throws InterruptedException InterruptedException
* @throws DataStoreException DataStoreException
* @throws AmplifyException AmplifyException
*/
@Test
public void observeQueryReturnsRecordsBasedOnMaxRecords() throws InterruptedException, AmplifyException {
CountDownLatch latch = new CountDownLatch(1);
CountDownLatch changeLatch = new CountDownLatch(3);
AtomicInteger count = new AtomicInteger();
BlogOwner blogOwner = BlogOwner.builder().name("Alan Turing").build();
List<BlogOwner> datastoreResultList = new ArrayList<>();
int maxRecords = 2;
datastoreResultList.add(blogOwner);
Consumer<Cancelable> observationStarted = NoOpConsumer.create();
SyncStatus mockSyncStatus = mock(SyncStatus.class);
when(mockSyncStatus.get(any(), any())).thenReturn(false).thenReturn(true).thenReturn(true);
Subject<StorageItemChange<? extends Model>> subject = PublishSubject.<StorageItemChange<? extends Model>>create().toSerialized();
Consumer<DataStoreQuerySnapshot<BlogOwner>> onQuerySnapshot = value -> {
if (count.get() == 0) {
Assert.assertTrue(value.getItems().contains(blogOwner));
latch.countDown();
} else if (count.get() == 1) {
Assert.assertEquals(3, value.getItems().size());
Assert.assertTrue(value.getIsSynced());
changeLatch.countDown();
} else if (count.get() == 2) {
Assert.assertEquals(4, value.getItems().size());
changeLatch.countDown();
} else {
Assert.assertEquals(5, value.getItems().size());
changeLatch.countDown();
}
count.getAndIncrement();
};
Consumer<DataStoreException> onObservationError = NoOpConsumer.create();
Action onObservationComplete = NoOpAction.create();
SqlQueryProcessor mockSqlQueryProcessor = mock(SqlQueryProcessor.class);
when(mockSqlQueryProcessor.queryOfflineData(eq(BlogOwner.class), any(), any())).thenReturn(datastoreResultList);
when(mockSqlQueryProcessor.modelExists(any(), any())).thenReturn(true);
ExecutorService threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 5);
ObserveQueryExecutor<BlogOwner> observeQueryExecutor = new ObserveQueryExecutor<>(subject, mockSqlQueryProcessor, threadPool, mockSyncStatus, new ModelSorter<>(), maxRecords, maxRecords);
observeQueryExecutor.observeQuery(BlogOwner.class, new ObserveQueryOptions(null, null), observationStarted, onQuerySnapshot, onObservationError, onObservationComplete);
Assert.assertTrue(latch.await(1, TimeUnit.SECONDS));
for (int i = 0; i < 5; i++) {
BlogOwner itemChange = BlogOwner.builder().name("Alan Turing" + i).build();
subject.onNext(StorageItemChange.<BlogOwner>builder().changeId(UUID.randomUUID().toString()).initiator(StorageItemChange.Initiator.SYNC_ENGINE).item(itemChange).patchItem(SerializedModel.create(itemChange, ModelSchema.fromModelClass(BlogOwner.class))).modelSchema(ModelSchema.fromModelClass(BlogOwner.class)).predicate(QueryPredicates.all()).type(StorageItemChange.Type.UPDATE).build());
}
Assert.assertTrue(changeLatch.await(7, TimeUnit.SECONDS));
}
use of com.amplifyframework.core.model.Model in project amplify-android by aws-amplify.
the class MutationPersistenceInstrumentationTest method saveIsObservedForPersistentRecord.
/**
* When {@link LocalStorageAdapter#save(Model, StorageItemChange.Initiator, QueryPredicate, Consumer, Consumer)}
* is called to save a {@link PendingMutation.PersistentRecord}, we should see an event emitted on the
* {@link LocalStorageAdapter#observe(Consumer, Consumer, Action)}'s item consumer.
* @throws DataStoreException On failure to convert received value out of record format, or
* on failure to manipulate data in/out of DataStore
*/
@Test
public void saveIsObservedForPersistentRecord() throws DataStoreException {
// Start watching observe().
// The storage adapter emits StorageItemChange.
TestObserver<StorageItemChange<? extends Model>> saveObserver = storage.observe().test();
// Save something ..
BlogOwner juan = BlogOwner.builder().name("Juan Gonzales").build();
ModelSchema schema = schemaRegistry.getModelSchemaForModelClass(BlogOwner.class);
PendingMutation<BlogOwner> change = PendingMutation.creation(juan, schema);
PendingMutation.PersistentRecord thingWeSaved = converter.toRecord(change);
// Wait for it to save...
storage.save(thingWeSaved);
// Assert that our observer got the item;
// The observed change makes reference to an item. That referred item is our
// PendingMutation.PersistentRecord. It should be identical to the item that we saved.
assertEquals(thingWeSaved, saveObserver.awaitCount(1).values().get(0).item());
saveObserver.dispose();
}
use of com.amplifyframework.core.model.Model in project amplify-android by aws-amplify.
the class SubscriptionProcessor method subscriptionObservable.
private <T extends Model> Observable<SubscriptionEvent<? extends Model>> subscriptionObservable(AppSync appSync, SubscriptionType subscriptionType, AbortableCountDownLatch<DataStoreException> latch, ModelSchema modelSchema) {
return Observable.<GraphQLResponse<ModelWithMetadata<T>>>create(emitter -> {
SubscriptionMethod method = subscriptionMethodFor(appSync, subscriptionType);
AtomicReference<String> subscriptionId = new AtomicReference<>();
Cancelable cancelable = method.subscribe(modelSchema, token -> {
LOG.debug("Subscription started for " + subscriptionType.name() + " " + modelSchema.getName() + " subscriptionId: " + token);
subscriptionId.set(token);
latch.countDown();
}, emitter::onNext, dataStoreException -> {
if (isExceptionType(dataStoreException, AppSyncErrorType.UNAUTHORIZED)) {
// Ignore Unauthorized errors, so that DataStore can still be used even if the user is only
// authorized to read a subset of the models.
latch.countDown();
LOG.warn("Unauthorized failure:" + subscriptionType.name() + " " + modelSchema.getName());
} else if (isExceptionType(dataStoreException, AppSyncErrorType.OPERATION_DISABLED)) {
// Ignore OperationDisabled errors, so that DataStore can be used even without subscriptions.
// This logic is only in place to address a specific use case, and should not be used without
// unless you have consulted with AWS. It is subject to be deprecated/removed in the future.
latch.countDown();
LOG.warn("Operation disabled:" + subscriptionType.name() + " " + modelSchema.getName());
} else {
if (latch.getCount() > 0) {
// An error occurred during startup. Abort and notify the Orchestrator by throwing the
// exception from startSubscriptions.
latch.abort(dataStoreException);
} else {
// An error occurred after startup. Notify the Orchestrator via the onFailure action.
onFailure.accept(dataStoreException);
}
}
}, () -> {
LOG.debug("Subscription completed:" + subscriptionId.get());
emitter.onComplete();
});
// When the observable is disposed, we need to call cancel() on the subscription
// so it can properly dispose of resources if necessary. For the AWS API plugin,
// this means closing the underlying network connection.
emitter.setDisposable(AmplifyDisposables.fromCancelable(cancelable));
}).doOnError(subscriptionError -> LOG.warn("An error occurred on the remote " + subscriptionType.name() + " subscription for model " + modelSchema.getName(), subscriptionError)).subscribeOn(Schedulers.io()).observeOn(Schedulers.io()).map(SubscriptionProcessor::unwrapResponse).filter(modelWithMetadata -> {
QueryPredicate predicate = queryPredicateProvider.getPredicate(modelSchema.getName());
return predicate.evaluate(modelWithMetadata.getModel());
}).map(modelWithMetadata -> SubscriptionEvent.<T>builder().type(fromSubscriptionType(subscriptionType)).modelWithMetadata(modelWithMetadata).modelSchema(modelSchema).build());
}
Aggregations