Skip to content

Commit

Permalink
Support for Mutation Queue handling and bug fixes. (#106)
Browse files Browse the repository at this point in the history
* Bug fix to handle scenario where mutation is canceled in its own callback.

* Fixed bug in setting mutation timeout and added support for checking if mutation queue is empty and for clearing it.

* Updated Core SDK dependency, Changelog and made version bump changes

* Changed method name to "isMutationQueueEmpty"
  • Loading branch information
scb01 authored Jan 24, 2019
1 parent 757d8e0 commit 1dd1fd9
Show file tree
Hide file tree
Showing 8 changed files with 265 additions and 12 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Change Log - AWS AppSync SDK for Android

## [Release 2.7.6](https://github.com/awslabs/aws-mobile-appsync-sdk-android/releases/tag/release_v2.7.6)

### Misc. Updates
* `AWSAppSync` now depends on `AWSCore` version `2.11.0` instead of `2.10.1`.
* Added support to check if mutation queue is empty and to clear mutation queue. See [issue #96](https://github.com/awslabs/aws-mobile-appsync-sdk-android/issues/96), and [issue #101](https://github.com/awslabs/aws-mobile-appsync-sdk-android/issues/101)

### Bug Fixes
* Fixed bug in mutationQueue Execution set method. [issue #105](https://github.com/awslabs/aws-mobile-appsync-sdk-android/issues/105)
* Fixed bug in mutation processing logic to handle case where cancel is called in the mutation callback. See [issue #102](https://github.com/awslabs/aws-mobile-appsync-sdk-android/issues/102)

## [Release 2.7.5](https://github.com/awslabs/aws-mobile-appsync-sdk-android/releases/tag/release_v2.7.5)

### Misc. Updates
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// Generated file. Do not edit!
package com.apollographql.android
val VERSION = "2.7.5"
val VERSION = "2.7.6"
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,204 @@ public void testUpdateWithInvalidID() {

}

@Test
public void testCancelMutationWithinCallback() {

AWSAppSyncClient awsAppSyncClient = AppSyncTestSetupHelper.createAppSyncClientWithIAM();
assertNotNull(awsAppSyncClient);
final CountDownLatch add2CountDownlatch = new CountDownLatch(1);

AddPostMutation.Data expected = new AddPostMutation.Data(new AddPostMutation.CreatePost(
"Post",
"",
"",
"",
"",
"",
null,
null,
0
));

CreatePostInput createPostInput1 = CreatePostInput.builder()
.title("L.A. Woman")
.author("Doors" + System.currentTimeMillis())
.url("Doors.com")
.content("City at night")
.ups(new Integer(1))
.downs(new Integer(0))
.build();

AddPostMutation addPostMutation1 = AddPostMutation.builder().input(createPostInput1).build();
final AppSyncMutationCall call1 = awsAppSyncClient.mutate(addPostMutation1, expected);

CreatePostInput createPostInput2 = CreatePostInput.builder()
.title("Break On Through")
.author("Doors" + System.currentTimeMillis())
.url("Doors.com")
.content("To the other side")
.ups(new Integer(1))
.downs(new Integer(0))
.build();
AddPostMutation addPostMutation2 = AddPostMutation.builder().input(createPostInput2).build();
final AppSyncMutationCall call2 = awsAppSyncClient.mutate(addPostMutation2, expected);


call1.enqueue(new GraphQLCall.Callback<AddPostMutation.Data>() {
@Override
public void onResponse(@Nonnull final Response<AddPostMutation.Data> response) {
call1.cancel();
}

@Override
public void onFailure(@Nonnull final ApolloException e) {
e.printStackTrace();
assertTrue("OnError received for first mutation. Unexpected", false);
}
});

call2.enqueue(new GraphQLCall.Callback<AddPostMutation.Data>() {
@Override
public void onResponse(@Nonnull final Response<AddPostMutation.Data> response) {
call2.cancel();
add2CountDownlatch.countDown();
}

@Override
public void onFailure(@Nonnull final ApolloException e) {
e.printStackTrace();
assertTrue("OnError received for Second mutation. Unexpected", false);
add2CountDownlatch.countDown();
}
});

try {
add2CountDownlatch.await(60, TimeUnit.SECONDS);
} catch (InterruptedException iex) {
iex.printStackTrace();
}

}

@Test
public void testMutationQueueEmpty() {
AWSAppSyncClient awsAppSyncClient = AppSyncTestSetupHelper.createAppSyncClientWithIAM(true,2*1000);
assertNotNull(awsAppSyncClient);
assertTrue(awsAppSyncClient.isMutationQueueEmpty());

final CountDownLatch addCountdownlatch = new CountDownLatch(1);

AddPostMutation.Data expected = new AddPostMutation.Data(new AddPostMutation.CreatePost(
"Post",
"",
"",
"",
"",
"",
null,
null,
0
));

CreatePostInput createPostInput = CreatePostInput.builder()
.title("Lonely Day")
.author("SOAD" + System.currentTimeMillis())
.url("SOAD.com")
.content("Such a lonely day")
.ups(new Integer(1))
.downs(new Integer(0))
.build();

AddPostMutation addPostMutation = AddPostMutation.builder().input(createPostInput).build();
final AppSyncMutationCall call = awsAppSyncClient.mutate(addPostMutation, expected);

call.enqueue(new GraphQLCall.Callback<AddPostMutation.Data>() {
@Override
public void onResponse(@Nonnull final Response<AddPostMutation.Data> response) {
call.cancel();
addCountdownlatch.countDown();
}

@Override
public void onFailure(@Nonnull final ApolloException e) {
e.printStackTrace();
assertTrue("OnError received for Second mutation. Unexpected", false);
addCountdownlatch.countDown();
}
});
assertFalse(awsAppSyncClient.isMutationQueueEmpty());
try {
addCountdownlatch.await(60, TimeUnit.SECONDS);
} catch (InterruptedException iex) {
iex.printStackTrace();
}
assertTrue(awsAppSyncClient.isMutationQueueEmpty());

}

@Test
public void testMutationQueueClear() {
AWSAppSyncClient awsAppSyncClient = AppSyncTestSetupHelper.createAppSyncClientWithIAM(true,2*1000);
assertNotNull(awsAppSyncClient);
assertTrue(awsAppSyncClient.isMutationQueueEmpty());

final int lastMutation = 10;
final CountDownLatch addCountdownlatch = new CountDownLatch(1);

for (int i = 0; i < lastMutation; i++ ) {
final int currentIteration = i;
AddPostMutation.Data expected = new AddPostMutation.Data(new AddPostMutation.CreatePost(
"Post",
"",
"",
"",
"",
"",
null,
null,
0
));

CreatePostInput createPostInput = CreatePostInput.builder()
.title("Lonely Day")
.author("SOAD" + System.currentTimeMillis())
.url("SOAD.com")
.content("Such a lonely day")
.ups(new Integer(1))
.downs(new Integer(0))
.build();

AddPostMutation addPostMutation = AddPostMutation.builder().input(createPostInput).build();
AppSyncMutationCall call = awsAppSyncClient.mutate(addPostMutation, expected);

call.enqueue(new GraphQLCall.Callback<AddPostMutation.Data>() {
@Override
public void onResponse(@Nonnull final Response<AddPostMutation.Data> response) {
if (currentIteration == lastMutation) {
addCountdownlatch.countDown();
}
}

@Override
public void onFailure(@Nonnull final ApolloException e) {
if (currentIteration == lastMutation) {
addCountdownlatch.countDown();
}
}
});
}
assertFalse(awsAppSyncClient.isMutationQueueEmpty());
awsAppSyncClient.clearMutationQueue();
assertTrue(awsAppSyncClient.isMutationQueueEmpty());

try {
assertFalse(addCountdownlatch.await(60, TimeUnit.SECONDS));
} catch (InterruptedException iex) {
iex.printStackTrace();
}

}


private void queryPosts(AWSAppSyncClient awsAppSyncClient, final ResponseFetcher responseFetcher) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public class AWSAppSyncClient {

//Map that houses retried mutations.
private Map<Mutation, MutationInformation> mutationsToRetryAfterConflictResolution;
private AppSyncOfflineMutationManager mAppSyncOfflineMutationManager = null;

private enum AuthMode {
API_KEY("API_KEY"),
Expand Down Expand Up @@ -172,16 +173,18 @@ private AWSAppSyncClient(AWSAppSyncClient.Builder builder) {
builder.mPersistentMutationsCallback,
builder.mS3ObjectManager);

//Create the Apollo Client and setup the interceptor chain.
mAppSyncOfflineMutationManager = new AppSyncOfflineMutationManager(builder.mContext,
builder.customTypeAdapters,
sqlCacheOperations,
networkInvoker);

//Create the Apollo Client and setup the interceptor chain.
ApolloClient.Builder clientBuilder = ApolloClient.builder()
.serverUrl(builder.mServerUrl)
.normalizedCache(builder.mNormalizedCacheFactory, builder.mResolver)
.addApplicationInterceptor(optimisticUpdateInterceptor)
.addApplicationInterceptor(new AppSyncOfflineMutationInterceptor(
new AppSyncOfflineMutationManager(builder.mContext,
builder.customTypeAdapters,
sqlCacheOperations,
networkInvoker),
mAppSyncOfflineMutationManager,
false,
builder.mContext,
mutationsToRetryAfterConflictResolution,
Expand Down Expand Up @@ -427,7 +430,7 @@ public Builder subscriptionsAutoReconnect( boolean subscriptionsAutoReconnect) {
* @return
*/
public Builder mutationQueueExecutionTimeout(long mutationQueueExecutionTimeout) {
mutationQueueExecutionTimeout = mutationQueueExecutionTimeout;
mMutationQueueExecutionTimeout = mutationQueueExecutionTimeout;
return this;
}

Expand Down Expand Up @@ -698,4 +701,22 @@ public <D extends Query.Data, T, V extends Query.Variables> Cancelable sync(
return this.sync(baseQuery, baseQueryCallback, subscription, subscriptionCallback, null, null, 0 );
}

/**
* Used to check if the mutation queue is empty.
* @return true if queue is empty, false otherwise.
*/
public boolean isMutationQueueEmpty() {
if (mAppSyncOfflineMutationManager != null ) {
return mAppSyncOfflineMutationManager.mutationQueueEmpty();
}
return true;
}

/**
* Clear the mutation queue. A Mutation that is currently in progress will continue to execute until finished.
*
*/
public void clearMutationQueue() {
mAppSyncOfflineMutationManager.clearMutationQueue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ public void processNextInQueueMutation() {

void setInProgressMutationAsCompleted(String recordIdentifier) {
persistentOfflineMutationManager.removePersistentMutationObject(recordIdentifier);
inMemoryOfflineMutationManager.removeFirstInQueue();
inMemoryOfflineMutationManager.removeFromQueue(recordIdentifier);
queueHandler.setMutationInProgressStatusToFalse();
queueHandler.clearInMemoryOfflineMutationObjectBeingExecuted();
queueHandler.clearPersistentOfflineMutationObjectBeingExecuted();
Expand Down Expand Up @@ -362,5 +362,14 @@ String getClientStateFromMutation(Mutation mutation) {
}
return clientState;
}

boolean mutationQueueEmpty() {
return ( persistentOfflineMutationManager.isQueueEmpty() && inMemoryOfflineMutationManager.isQueueEmpty());
}

void clearMutationQueue(){
inMemoryOfflineMutationManager.clearMutationQueue();
persistentOfflineMutationManager.clearMutationQueue();
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,13 @@ public void addMutationObjectInQueue(InMemoryOfflineMutationObject object) {
}
}

public InMemoryOfflineMutationObject removeFirstInQueue() {
public InMemoryOfflineMutationObject removeFromQueue(String recordIdentifer) {
synchronized ( lock ) {
if (!inMemoryOfflineMutationObjects.isEmpty() ) {
return inMemoryOfflineMutationObjects.remove(0);
InMemoryOfflineMutationObject mutationObject = inMemoryOfflineMutationObjects.get(0);
if (mutationObject != null && recordIdentifer.equals(mutationObject.recordIdentifier)) {
return inMemoryOfflineMutationObjects.remove(0);
}
}
}
return null;
Expand Down Expand Up @@ -107,4 +110,11 @@ InMemoryOfflineMutationObject getMutationObject(Mutation m) {
}
return null;
}

void clearMutationQueue() {
synchronized (lock) {
inMemoryOfflineMutationObjects.clear();
cancelledMutations.clear();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,9 @@ synchronized void removeTimedoutMutation(PersistentOfflineMutationObject p ) {
synchronized Set<PersistentOfflineMutationObject> getTimedoutMutations() {
return timedOutMutations;
}

synchronized void clearMutationQueue() {
mutationSqlCacheOperations.clearCurrentCache();
persistentOfflineMutationObjectList.clear();
}
}
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ org.gradle.jvmargs=-Xmx1536m
org.gradle.parallel=true

GROUP=com.amazonaws
VERSION_NAME=2.7.5
AWS_CORE_SDK_VERSION=2.10.1
VERSION_NAME=2.7.6
AWS_CORE_SDK_VERSION=2.11.0

POM_URL=https://github.com/awslabs/aws-mobile-appsync-sdk-android
POM_SCM_URL=https://github.com/awslabs/aws-mobile-appsync-sdk-android
Expand Down

0 comments on commit 1dd1fd9

Please sign in to comment.