Skip to content

Commit

Permalink
Resolves uncaught "call is canceled" subscription exception issue #202 (
Browse files Browse the repository at this point in the history
  • Loading branch information
TrekSoft authored and Karthikeyan committed Oct 15, 2019
1 parent b93e27f commit 0b0e5d1
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 17 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
### Misc. Updates
* `AWSAppSync` now depends on `AWSCore` version `2.16.1` instead of `2.13.5`.

### Bug Fixes
* If an App Sync subscription attempts to be executed while in a non-Idle state, it will now pass the error back through the callback vs. crashing the app with a runtime exception. See [issue#202](https://github.com/awslabs/aws-mobile-appsync-sdk-android/issues/202)

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

## Enhancements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public RealAppSyncSubscriptionCall(
}

@Override
public void execute(@Nonnull final Callback<T> callback) {
public synchronized void execute(@Nonnull final Callback<T> callback) {
if ( callback == null ) {
logger.w("Subscription Infrastructure: Callback passed into subscription [" + subscription +"] was null. Will not subscribe.");
return;
Expand All @@ -68,23 +68,22 @@ public void run() {
userCallback = callback;
subscriptionManager.addListener(subscription, callback);

//Ensure that the call is only made once.
synchronized (this) {
switch (state.get()) {
case IDLE: {
state.set(ACTIVE);
break;
}
switch (state.get()) {
case IDLE: {
state.set(ACTIVE);
break;
}

case CANCELED:
throw new RuntimeException("Cancelled", new ApolloCanceledException("Call is cancelled."));
case CANCELED:
callback.onFailure(new ApolloCanceledException("Call is cancelled."));
break;

case ACTIVE:
throw new IllegalStateException("Already Executed");
case ACTIVE:
callback.onFailure(new ApolloException("Already Executed"));
break;

default:
throw new IllegalStateException("Unknown state");
}
default:
callback.onFailure(new ApolloException("Unknown state"));
}

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import android.content.Context;
import android.content.res.Resources;
import android.util.Log;

import com.amazonaws.auth.CognitoCredentialsProvider;
import com.amazonaws.mobile.config.AWSConfiguration;
Expand All @@ -11,7 +10,15 @@
import com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider;
import com.amazonaws.mobileconnectors.appsync.sigv4.OidcAuthProvider;
import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserPool;
import com.google.android.apps.common.testing.accessibility.framework.proto.FrameworkProtos;
import com.apollographql.apollo.ApolloClient;
import com.apollographql.apollo.api.Response;
import com.apollographql.apollo.api.Subscription;
import com.apollographql.apollo.exception.ApolloCanceledException;
import com.apollographql.apollo.exception.ApolloException;
import com.apollographql.apollo.internal.ApolloLogger;
import com.apollographql.apollo.internal.RealAppSyncCall;
import com.apollographql.apollo.internal.RealAppSyncSubscriptionCall;
import com.apollographql.apollo.internal.subscription.SubscriptionManager;

import org.junit.Before;
import org.junit.Test;
Expand All @@ -22,9 +29,15 @@
import org.robolectric.shadows.ShadowApplication;

import java.io.ByteArrayInputStream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import javax.annotation.Nonnull;

import static junit.framework.TestCase.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

/**
* Example local unit test, which will execute on the development machine (host).
Expand All @@ -38,6 +51,11 @@ public class AppSyncClientUnitTest {
Resources res;
Context mockContext;
Context shadowContext;
ApolloLogger mockLogger;
Subscription<?, ?, ?> mockSubscription;
SubscriptionManager mockSubscriptionManager;
ApolloClient mockApolloClient;
RealAppSyncCall<?> mockSubscriptionMetadataRequest;

private static final String TAG = AppSyncClientUnitTest.class.getSimpleName();

Expand All @@ -48,6 +66,11 @@ public class AppSyncClientUnitTest {
public void setup() {
shadowContext = ShadowApplication.getInstance().getApplicationContext();
mockContext = Mockito.mock(Context.class);
mockLogger = Mockito.mock(ApolloLogger.class);
mockSubscription = Mockito.mock(Subscription.class);
mockSubscriptionManager = Mockito.mock(SubscriptionManager.class);
mockApolloClient = Mockito.mock(ApolloClient.class);
mockSubscriptionMetadataRequest = Mockito.mock(RealAppSyncCall.class);
res = Mockito.mock(Resources.class);
Mockito.when(mockContext.getResources()).thenReturn(res);
jsonConfig = "{\n" +
Expand Down Expand Up @@ -163,6 +186,34 @@ public String getLatestAuthToken() {
assertNotNull(awsAppSyncClient);
}

@Test
public void testRealAppSyncSubscriptionCallErrorHandling() throws InterruptedException {
RealAppSyncSubscriptionCall<Object> call = new RealAppSyncSubscriptionCall(mockSubscription, mockSubscriptionManager, mockApolloClient, mockLogger, mockSubscriptionMetadataRequest);
call.cancel();
Mockito.timeout(500);
final CountDownLatch waitForCall = new CountDownLatch(1);
call.execute(new AppSyncSubscriptionCall.Callback<Object>() {

@Override
public void onResponse(@Nonnull Response<Object> response) {
fail("Execute should not succeed in canceled state");
}

@Override
public void onFailure(@Nonnull ApolloException e) {
assertEquals(ApolloCanceledException.class, e.getClass());
waitForCall.countDown();
}

@Override
public void onCompleted() {
fail("Execute should not complete in canceled state");
}
});

assertTrue(waitForCall.await(100, TimeUnit.MILLISECONDS));
}

@Test(expected = RuntimeException.class)
public void testConfigMismatch_ApiKey() {
awsConfiguration.setConfiguration("AwsIam");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mock-maker-inline

0 comments on commit 0b0e5d1

Please sign in to comment.