use of com.amplifyframework.util.Time in project amplify-android by aws-amplify.
the class SyncProcessor method syncModel.
/**
* Sync models for a given model class.
* This involves three steps:
* 1. Lookup the last time the model class was synced;
* 2. Make a request to the AppSync endpoint. If the last sync time is within a recent window
* of time, then request a *delta* sync. If the last sync time is outside a recent window of time,
* perform a *base* sync. A base sync is preformed by passing null.
* 3. Continue fetching paged results until !hasNextResult() or we have synced the max records.
*
* @param schema The schema of the model to sync
* @param syncTime The time of a last successful sync.
* @param <T> The type of model to sync.
* @return a stream of all ModelWithMetadata<T> objects from all pages for the provided model.
* @throws DataStoreException if dataStoreConfigurationProvider.getConfiguration() fails
*/
private <T extends Model> Flowable<List<ModelWithMetadata<T>>> syncModel(ModelSchema schema, SyncTime syncTime) throws DataStoreException {
final Long lastSyncTimeAsLong = syncTime.exists() ? syncTime.toLong() : null;
final Integer syncPageSize = dataStoreConfigurationProvider.getConfiguration().getSyncPageSize();
final Integer syncMaxRecords = dataStoreConfigurationProvider.getConfiguration().getSyncMaxRecords();
AtomicReference<Integer> recordsFetched = new AtomicReference<>(0);
QueryPredicate predicate = queryPredicateProvider.getPredicate(schema.getName());
// Create a BehaviorProcessor, and set the default value to a GraphQLRequest that fetches the first page.
BehaviorProcessor<GraphQLRequest<PaginatedResult<ModelWithMetadata<T>>>> processor = BehaviorProcessor.createDefault(appSync.buildSyncRequest(schema, lastSyncTimeAsLong, syncPageSize, predicate));
return processor.concatMap(request -> {
if (isSyncRetryEnabled) {
return syncPageWithRetry(request).toFlowable();
} else {
return syncPage(request).toFlowable();
}
}).doOnNext(paginatedResult -> {
if (paginatedResult.hasNextResult()) {
processor.onNext(paginatedResult.getRequestForNextResult());
} else {
processor.onComplete();
}
}).map(paginatedResult -> Flowable.fromIterable(paginatedResult).map(modelWithMetadata -> hydrateSchemaIfNeeded(modelWithMetadata, schema)).toList().blockingGet()).takeUntil(items -> recordsFetched.accumulateAndGet(items.size(), Integer::sum) >= syncMaxRecords);
}
use of com.amplifyframework.util.Time 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();
}
Aggregations