use of com.amplifyframework.core.model.ModelSchema 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();
}
use of com.amplifyframework.core.model.ModelSchema in project amplify-android by aws-amplify.
the class PendingMutationTest method pendingMutationsAreComparable.
/**
* It is possible to sort pending mutations according to their TimeBasedUUID field.
* @throws AmplifyException On failure to arrange model schema
*/
@Test
public void pendingMutationsAreComparable() throws AmplifyException {
// First, create a bunch of pending mutations.
final List<PendingMutation<? extends Model>> expectedOrder = new ArrayList<>();
final int desiredQuantity = 10;
for (int index = 0; index < desiredQuantity; index++) {
// to make sure the comparator can work across model types
if (new SecureRandom().nextBoolean()) {
BlogOwner blogger = BlogOwner.builder().name(String.format(Locale.US, "Blogger #%d", index)).build();
ModelSchema schema = ModelSchema.fromModelClass(BlogOwner.class);
expectedOrder.add(PendingMutation.creation(blogger, schema));
} else {
Post post = Post.builder().title(String.format(Locale.US, "Title #%d", index)).status(PostStatus.ACTIVE).rating(5).build();
ModelSchema schema = ModelSchema.fromModelClass(Post.class);
expectedOrder.add(PendingMutation.creation(post, schema));
}
}
// Okay! Now, shuffle them.
List<PendingMutation<? extends Model>> shuffled = new ArrayList<>(expectedOrder);
Collections.shuffle(shuffled, new SecureRandom());
// Now sort them according to the item comparator, {@link PendingMutation#compareTo(Object)}.
List<PendingMutation<? extends Model>> actualOrder = new ArrayList<>(shuffled);
Collections.sort(actualOrder);
assertEquals(expectedOrder, actualOrder);
}
use of com.amplifyframework.core.model.ModelSchema in project amplify-android by aws-amplify.
the class InMemoryStorageAdapter method delete.
// item.getClass() -> Class<?>, but type is T. So cast as Class<T> is OK.
@SuppressWarnings("unchecked")
@Override
public <T extends Model> void delete(@NonNull final T item, @NonNull final StorageItemChange.Initiator initiator, @NonNull final QueryPredicate predicate, @NonNull final Consumer<StorageItemChange<T>> onSuccess, @NonNull final Consumer<DataStoreException> onError) {
final int index = indexOf(item);
if (index < 0) {
onError.accept(new DataStoreException("This item was not found in the datastore: " + item.toString(), "Use save() function to create models to store."));
return;
}
Model savedItem = items.remove(index);
final ModelSchema schema;
final SerializedModel patchItem;
try {
schema = ModelSchema.fromModelClass(item.getClass());
patchItem = SerializedModel.create(savedItem, schema);
} catch (AmplifyException schemaBuildFailure) {
onError.accept(new DataStoreException("Failed to build model schema.", schemaBuildFailure, "Verify your model."));
return;
}
if (!predicate.evaluate(savedItem)) {
onError.accept(new DataStoreException("Conditional check failed.", "Verify that there is a saved model that matches the provided predicate."));
return;
}
StorageItemChange<T> deletion = StorageItemChange.<T>builder().item((T) savedItem).patchItem(patchItem).modelSchema(schema).type(StorageItemChange.Type.DELETE).predicate(predicate).initiator(initiator).build();
itemChangeStream.onNext(deletion);
onSuccess.accept(deletion);
}
use of com.amplifyframework.core.model.ModelSchema in project amplify-android by aws-amplify.
the class InMemoryStorageAdapter method save.
@Override
public <T extends Model> void save(@NonNull final T item, @NonNull final StorageItemChange.Initiator initiator, @NonNull final QueryPredicate predicate, @NonNull final Consumer<StorageItemChange<T>> onSuccess, @NonNull final Consumer<DataStoreException> onError) {
StorageItemChange.Type type = StorageItemChange.Type.CREATE;
final int index = indexOf(item);
Model savedItem = null;
if (index > -1) {
// There is an existing record with that ID; this is an update.
type = StorageItemChange.Type.UPDATE;
savedItem = items.get(index);
if (!predicate.evaluate(savedItem)) {
onError.accept(new DataStoreException("Conditional check failed.", "Verify that there is a saved model that matches the provided predicate."));
return;
} else {
items.remove(index);
}
}
final ModelSchema schema;
final SerializedModel patchItem;
try {
schema = ModelSchema.fromModelClass(item.getClass());
patchItem = SerializedModel.difference(item, savedItem, schema);
} catch (AmplifyException schemaBuildFailure) {
onError.accept(new DataStoreException("Failed to build model schema.", schemaBuildFailure, "Verify your model."));
return;
}
items.add(item);
StorageItemChange<T> change = StorageItemChange.<T>builder().item(item).patchItem(patchItem).modelSchema(schema).type(type).predicate(predicate).initiator(initiator).build();
itemChangeStream.onNext(change);
onSuccess.accept(change);
}
use of com.amplifyframework.core.model.ModelSchema in project amplify-android by aws-amplify.
the class ObserveQueryExecutorTest method observeQueryReturnsRecordsBasedOnMaxTime.
/**
* observe Query Returns batched Records Based On MaxTime.
* @throws InterruptedException InterruptedException
* @throws DataStoreException DataStoreException
*/
@Test
public void observeQueryReturnsRecordsBasedOnMaxTime() throws InterruptedException, DataStoreException {
CountDownLatch latch = new CountDownLatch(1);
CountDownLatch changeLatch = new CountDownLatch(1);
AtomicInteger count = new AtomicInteger();
BlogOwner blogOwner = BlogOwner.builder().name("Alan Turing").build();
List<BlogOwner> datastoreResultList = new ArrayList<>();
int maxRecords = 50;
datastoreResultList.add(blogOwner);
Consumer<Cancelable> observationStarted = NoOpConsumer.create();
SyncStatus mockSyncStatus = mock(SyncStatus.class);
when(mockSyncStatus.get(any(), any())).thenReturn(false);
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(6, value.getItems().size());
changeLatch.countDown();
}
count.getAndIncrement();
};
Consumer<DataStoreException> onObservationError = NoOpConsumer.create();
Action onObservationComplete = () -> {
};
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, 1);
observeQueryExecutor.observeQuery(BlogOwner.class, new ObserveQueryOptions(), 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();
try {
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());
} catch (AmplifyException exception) {
exception.printStackTrace();
}
}
Assert.assertTrue(changeLatch.await(5, TimeUnit.SECONDS));
}
Aggregations