use of com.amplifyframework.datastore.storage.StorageItemChange in project amplify-android by aws-amplify.
the class SQLiteStorageAdapterSaveTest method patchItemOnlyHasAllFields.
/**
* Verify that saving an item that already exists emits a StorageItemChange event with a patchItem that only
* contains the fields that are different.
*
* @throws AmplifyException On failure to obtain ModelSchema from model class.
* @throws InterruptedException If interrupted while awaiting terminal result in test observer
*/
@Test
public void patchItemOnlyHasAllFields() throws AmplifyException, InterruptedException {
// Create a BlogOwner.
final BlogOwner johnSmith = BlogOwner.builder().name("John Smith").wea("ther").build();
adapter.save(johnSmith);
// Start observing for changes
TestObserver<StorageItemChange<? extends Model>> observer = adapter.observe().test();
// Update one field on the BlogOwner.
BlogOwner johnAdams = johnSmith.copyOfBuilder().name("John Adams").build();
adapter.save(johnAdams);
// Observe that the StorageItemChange contains an item with only the fields that changed (`id`, and `name`, but
// not `wea`)
Map<String, Object> serializedData = new HashMap<>();
serializedData.put("id", johnAdams.getId());
serializedData.put("name", "John Adams");
serializedData.put("wea", johnAdams.getWea());
serializedData.put("createdAt", johnAdams.getCreatedAt());
serializedData.put("updatedAt", johnAdams.getUpdatedAt());
SerializedModel expectedItem = SerializedModel.builder().serializedData(serializedData).modelSchema(ModelSchema.fromModelClass(BlogOwner.class)).build();
observer.await(1, TimeUnit.SECONDS);
observer.assertValueCount(1);
observer.assertValueAt(0, storageItemChange -> storageItemChange.patchItem().equals(expectedItem));
}
use of com.amplifyframework.datastore.storage.StorageItemChange in project amplify-android by aws-amplify.
the class MutationPersistenceInstrumentationTest method deletionIsObservedForPersistentRecord.
/**
* When an {@link PendingMutation.PersistentRecord} is deleted from the DataStore, we will expect
* to see an event on item consumer of {@link LocalStorageAdapter#observe(Consumer, Consumer, Action)}.
* @throws DataStoreException from storage item change, or on failure to manipulate I/O to DataStore
*/
@Test
public void deletionIsObservedForPersistentRecord() throws DataStoreException {
// We are observing a stream of changes to models.
// In this test, the <? extends Model> type happens to be PersistentRecord (itself, implementing Model.)
TestObserver<StorageItemChange<? extends Model>> storageObserver = storage.observe().test();
BlogOwner beatrice = BlogOwner.builder().name("Beatrice Stone").build();
ModelSchema schema = schemaRegistry.getModelSchemaForModelClass(BlogOwner.class);
PendingMutation<BlogOwner> createBeatrice = PendingMutation.creation(beatrice, schema);
PendingMutation.PersistentRecord createBeatriceRecord = converter.toRecord(createBeatrice);
storage.save(createBeatriceRecord);
// Assert that we do observe the PersistentRecord being saved ...
assertEquals(createBeatriceRecord, storageObserver.awaitCount(1).values().get(0).item());
storageObserver.dispose();
TestObserver<StorageItemChange<? extends Model>> deletionObserver = storage.observe().test();
// Try to delete Beatrice's record.
storage.delete(createBeatriceRecord);
// Should receive a notification of the deletion on the observer.
// The notification refers to the deleted item, in its contents.
assertEquals(createBeatriceRecord, deletionObserver.awaitCount(1).values().get(0).item());
deletionObserver.dispose();
}
use of com.amplifyframework.datastore.storage.StorageItemChange in project amplify-android by aws-amplify.
the class MutationPersistenceInstrumentationTest method updatesAreObservedForPersistentRecords.
/**
* When {@link LocalStorageAdapter#save(Model, StorageItemChange.Initiator, QueryPredicate, Consumer, Consumer)}
* is called to save a {@link PendingMutation.PersistentRecord}, we should expect to observe a change event
* /containing/ that record within it. It will be received by the value consumer of
* {@link LocalStorageAdapter#observe(Consumer, Consumer, Action)}.
*
* Similarly, when we update the {@link PendingMutation.PersistentRecord} that we had just saved,
* we should see an update notification on the subscription consumer. The type will be
* StorageItemChange, and inside of it ill be a reference to the {@link PendingMutation.PersistentRecord}.
*
* @throws DataStoreException from storage item change, or on failure to manipulate I/O to DataStore
*/
@Test
public void updatesAreObservedForPersistentRecords() throws DataStoreException {
// Establish a subscription to listen for storage change records
TestObserver<StorageItemChange<? extends Model>> storageObserver = storage.observe().test();
// Create a record for Joe, and a change to save him into storage
BlogOwner joeLastNameMisspelled = BlogOwner.builder().name("Joe Sweeneyy").build();
ModelSchema schema = schemaRegistry.getModelSchemaForModelClass(BlogOwner.class);
PendingMutation<BlogOwner> createJoeWrongLastName = PendingMutation.creation(joeLastNameMisspelled, schema);
// Save our saveJoeWrongLastName change item, as a PersistentRecord.
PendingMutation.PersistentRecord createJoeWrongLastNameAsRecord = converter.toRecord(createJoeWrongLastName);
storage.save(createJoeWrongLastNameAsRecord);
// Now, suppose we have to update that pending mutation. Maybe it contained a bad item payload.
BlogOwner joeWithLastNameFix = BlogOwner.builder().name("Joe Sweeney").id(joeLastNameMisspelled.getId()).build();
PendingMutation<BlogOwner> createJoeCorrectLastName = PendingMutation.creation(joeWithLastNameFix, schema);
// Save an update (same model type, same unique ID) to the mutation we saved previously.
PendingMutation.PersistentRecord createJoeCorrectLastNameAsRecord = converter.toRecord(createJoeCorrectLastName);
storage.save(createJoeCorrectLastNameAsRecord);
// Our observer got the records to save Joe with wrong age, and also to save joe with right age
assertEquals(Arrays.asList(createJoeWrongLastNameAsRecord, createJoeCorrectLastNameAsRecord), Observable.fromIterable(storageObserver.awaitCount(2).values()).map(StorageItemChange::item).toList().blockingGet());
storageObserver.dispose();
}
use of com.amplifyframework.datastore.storage.StorageItemChange in project amplify-android by aws-amplify.
the class SyncProcessorTest method syncAndExpect.
private void syncAndExpect(int numPages, int maxSyncRecords) throws AmplifyException, InterruptedException {
initSyncProcessor(maxSyncRecords);
// Arrange a subscription to the storage adapter. We're going to watch for changes.
// We expect to see content here as a result of the SyncProcessor applying updates.
final TestObserver<StorageItemChange<? extends Model>> adapterObserver = storageAdapter.observe().test();
// Arrange: return some responses for the sync() call on the RemoteModelState
AppSyncMocking.SyncConfigurator configurator = AppSyncMocking.sync(appSync);
List<ModelWithMetadata<BlogOwner>> expectedResponseItems = new ArrayList<>();
String token = null;
for (int pageIndex = 0; pageIndex < numPages; pageIndex++) {
String nextToken = pageIndex < numPages - 1 ? RandomString.string() : null;
ModelWithMetadata<BlogOwner> randomBlogOwner = randomBlogOwnerWithMetadata();
configurator.mockSuccessResponse(BlogOwner.class, token, nextToken, randomBlogOwner);
if (expectedResponseItems.size() < maxSyncRecords) {
expectedResponseItems.add(randomBlogOwner);
}
token = nextToken;
}
// Act: Call hydrate, and await its completion - assert it completed without error
TestObserver<ModelWithMetadata<? extends Model>> hydrationObserver = TestObserver.create();
syncProcessor.hydrate().subscribe(hydrationObserver);
// Wait 2 seconds, or 1 second per 100 pages, whichever is greater
long timeoutMs = Math.max(OP_TIMEOUT_MS, TimeUnit.SECONDS.toMillis(numPages / 100));
assertTrue(hydrationObserver.await(timeoutMs, TimeUnit.MILLISECONDS));
hydrationObserver.assertNoErrors();
hydrationObserver.assertComplete();
// Since hydrate() completed, the storage adapter observer should see some values.
// There should be a total of four changes on storage adapter
// A model and a metadata save for each of the two BlogOwner-type items
// Additionally, there should be 4 last sync time records, one for each of the
// models managed by the system.
adapterObserver.awaitCount(expectedResponseItems.size() * 2 + 4);
// Validate the changes emitted from the storage adapter's observe().
assertEquals(// Expect items as described above.
Observable.fromIterable(expectedResponseItems).flatMap(modelWithMutation -> Observable.fromArray(modelWithMutation.getModel(), modelWithMutation.getSyncMetadata())).toSortedList(SortByModelId::compare).blockingGet(), // Actually...
Observable.fromIterable(adapterObserver.values()).map(StorageItemChange::item).filter(item -> !LastSyncMetadata.class.isAssignableFrom(item.getClass())).toSortedList(SortByModelId::compare).blockingGet());
// Lastly: validate the current contents of the storage adapter.
// There should be 2 BlogOwners, and 2 MetaData records.
List<? extends Model> itemsInStorage = storageAdapter.query(modelProvider);
assertEquals(itemsInStorage.toString(), expectedResponseItems.size() * 2 + modelProvider.models().size(), itemsInStorage.size());
assertEquals(// Expect the 4 items for the bloggers (2 models and their metadata)
Observable.fromIterable(expectedResponseItems).flatMap(blogger -> Observable.fromArray(blogger.getModel(), blogger.getSyncMetadata())).toList().map(HashSet::new).blockingGet(), Observable.fromIterable(storageAdapter.query(modelProvider)).filter(item -> !LastSyncMetadata.class.isAssignableFrom(item.getClass())).toList().map(HashSet::new).blockingGet());
adapterObserver.dispose();
hydrationObserver.dispose();
}
use of com.amplifyframework.datastore.storage.StorageItemChange in project amplify-android by aws-amplify.
the class SQLiteStorageAdapterDeleteTest method deleteModelTypeWithPredicateCascades.
/**
* Assert that delete model type with predicate deletes items in
* the SQLite database without violating foreign key constraints.
* @throws DataStoreException On unexpected failure manipulating items in/out of DataStore
*/
@Test
public void deleteModelTypeWithPredicateCascades() throws DataStoreException {
// Create 1 blog owner, which has 3 blogs each, which has 3 posts each.
// Insert 1 blog owner, 3 blogs, 9 posts
Set<String> expected = new HashSet<>();
BlogOwner ownerModel = BlogOwner.builder().name("Blog Owner 1").build();
adapter.save(ownerModel);
for (int blog = 1; blog <= 3; blog++) {
Blog blogModel = Blog.builder().name("Blog " + blog).owner(ownerModel).build();
adapter.save(blogModel);
expected.add(blogModel.getId());
for (int post = 1; post <= 3; post++) {
Post postModel = Post.builder().title("Post " + blog + "-" + post).status(PostStatus.INACTIVE).rating(5).blog(blogModel).build();
adapter.save(postModel);
expected.add(postModel.getId());
}
}
// Observe deletions
TestObserver<String> deleteObserver = adapter.observe().filter(change -> StorageItemChange.Type.DELETE.equals(change.type())).map(StorageItemChange::item).map(Model::getId).test();
// Triggers a delete of all blogs.
// All posts will be deleted by cascade.
adapter.delete(Blog.class, QueryPredicates.all());
// Assert 3 blogs and 9 posts are deleted.
deleteObserver.assertValueCount(12);
assertEquals(expected, new HashSet<>(deleteObserver.values()));
// Get the BlogOwner from the database. Should not have been deleted.
final List<BlogOwner> blogOwners = adapter.query(BlogOwner.class);
assertEquals(Collections.singletonList(ownerModel), blogOwners);
// Get the Blogs and Posts from the database. Should be deleted.
assertTrue(adapter.query(Blog.class).isEmpty());
assertTrue(adapter.query(Post.class).isEmpty());
}
Aggregations