From 3c2b39555521dde780a2a1dca46a5094786852c3 Mon Sep 17 00:00:00 2001 From: Joe Hansche Date: Fri, 6 Jan 2017 12:50:58 -0800 Subject: [PATCH 1/5] Add support for `sessionToken` in "connect" and "subscribe" operations --- .../src/main/java/com/parse/ParseLiveQueryClientImpl.java | 6 ++++-- .../src/main/java/com/parse/SubscribeClientOperation.java | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClientImpl.java b/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClientImpl.java index b06c0e8..aef4ae5 100644 --- a/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClientImpl.java +++ b/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClientImpl.java @@ -224,7 +224,8 @@ private Subscription subscriptionForRequestId(int requestId) { } private void sendSubscription(Subscription subscription) { - sendOperationAsync(new SubscribeClientOperation<>(subscription.getRequestId(), subscription.getQueryState())); + String sessionToken = ParseUser.getCurrentSessionToken(); + sendOperationAsync(new SubscribeClientOperation<>(subscription.getRequestId(), subscription.getQueryState(), sessionToken)); } private void sendUnsubscription(Subscription subscription) { @@ -236,7 +237,8 @@ private WebSocketClient.WebSocketClientCallback getWebSocketClientCallback() { @Override public void onOpen() { Log.v(LOG_TAG, "Socket opened"); - sendOperationAsync(new ConnectClientOperation(applicationId, "")).continueWith(new Continuation() { + String sessionToken = ParseUser.getCurrentSessionToken(); + sendOperationAsync(new ConnectClientOperation(applicationId, sessionToken)).continueWith(new Continuation() { public Void then(Task task) { Exception error = task.getError(); if (error != null) { diff --git a/ParseLiveQuery/src/main/java/com/parse/SubscribeClientOperation.java b/ParseLiveQuery/src/main/java/com/parse/SubscribeClientOperation.java index 7a31ee2..8c87d9b 100644 --- a/ParseLiveQuery/src/main/java/com/parse/SubscribeClientOperation.java +++ b/ParseLiveQuery/src/main/java/com/parse/SubscribeClientOperation.java @@ -7,10 +7,12 @@ private final int requestId; private final ParseQuery.State state; + private final String sessionToken; - /* package */ SubscribeClientOperation(int requestId, final ParseQuery.State state) { + /* package */ SubscribeClientOperation(int requestId, ParseQuery.State state, String sessionToken) { this.requestId = requestId; this.state = state; + this.sessionToken = sessionToken; } @Override @@ -18,6 +20,7 @@ JSONObject jsonObject = new JSONObject(); jsonObject.put("op", "subscribe"); jsonObject.put("requestId", requestId); + if (sessionToken != null) jsonObject.put("sessionToken", sessionToken); JSONObject queryJsonObject = state.toJSON(NoObjectsEncoder.get()); From 55fb3e5e11adbce3f5a080ac66627f469f583d84 Mon Sep 17 00:00:00 2001 From: Joe Hansche Date: Fri, 3 Feb 2017 11:38:35 -0800 Subject: [PATCH 2/5] Adding unit tests for sessionToken support --- .../com/parse/TestParseLiveQueryClient.java | 99 ++++++++++++++++--- 1 file changed, 84 insertions(+), 15 deletions(-) diff --git a/ParseLiveQuery/src/test/java/com/parse/TestParseLiveQueryClient.java b/ParseLiveQuery/src/test/java/com/parse/TestParseLiveQueryClient.java index 5d2b205..e4e9e90 100644 --- a/ParseLiveQuery/src/test/java/com/parse/TestParseLiveQueryClient.java +++ b/ParseLiveQuery/src/test/java/com/parse/TestParseLiveQueryClient.java @@ -7,19 +7,28 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.annotation.Config; import java.net.URI; import java.util.concurrent.Executor; +import bolts.Task; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; +import static org.mockito.AdditionalMatchers.and; +import static org.mockito.AdditionalMatchers.not; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.contains; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @RunWith(RobolectricGradleTestRunner.class) @Config(constants = BuildConfig.class, sdk = 21) @@ -28,10 +37,23 @@ public class TestParseLiveQueryClient { private WebSocketClient webSocketClient; private WebSocketClient.WebSocketClientCallback webSocketClientCallback; private ParseLiveQueryClient parseLiveQueryClient; + private ParseUser mockUser; @Before public void setUp() throws Exception { ParsePlugins.initialize("1234", "1234"); + + // Register a mock currentUserController to make getCurrentUser work + mockUser = mock(ParseUser.class); + ParseCurrentUserController currentUserController = mock(ParseCurrentUserController.class); + when(currentUserController.getAsync(anyBoolean())).thenAnswer(new Answer>() { + @Override + public Task answer(InvocationOnMock invocation) throws Throwable { + return Task.forResult(mockUser); + } + }); + ParseCorePlugins.getInstance().registerCurrentUserController(currentUserController); + parseLiveQueryClient = ParseLiveQueryClient.Factory.getClient(new URI(""), new WebSocketClientFactory() { @Override public WebSocketClient createInstance(WebSocketClient.WebSocketClientCallback webSocketClientCallback, URI hostUrl) { @@ -67,12 +89,14 @@ public void testSubscribeWhenSubscribedToCallback() throws Exception { @Test public void testUnsubscribeWhenSubscribedToCallback() throws Exception { ParseQuery parseQuery = new ParseQuery<>("test"); - SubscriptionHandling subscriptionHandling = createSubscription(parseQuery, mock(SubscriptionHandling.HandleSubscribeCallback.class)); + SubscriptionHandling subscriptionHandling = createSubscription(parseQuery, + mock(SubscriptionHandling.HandleSubscribeCallback.class)); parseLiveQueryClient.unsubscribe(parseQuery); verify(webSocketClient, times(1)).send(any(String.class)); - SubscriptionHandling.HandleUnsubscribeCallback unsubscribeMockCallback = mock(SubscriptionHandling.HandleUnsubscribeCallback.class); + SubscriptionHandling.HandleUnsubscribeCallback unsubscribeMockCallback = mock( + SubscriptionHandling.HandleUnsubscribeCallback.class); subscriptionHandling.handleUnsubscribe(unsubscribeMockCallback); webSocketClientCallback.onMessage(createUnsubscribedMessage(subscriptionHandling.getRequestId()).toString()); @@ -82,7 +106,8 @@ public void testUnsubscribeWhenSubscribedToCallback() throws Exception { @Test public void testErrorWhenSubscribedToCallback() throws Exception { ParseQuery parseQuery = new ParseQuery<>("test"); - SubscriptionHandling subscriptionHandling = createSubscription(parseQuery, mock(SubscriptionHandling.HandleSubscribeCallback.class)); + SubscriptionHandling subscriptionHandling = createSubscription(parseQuery, + mock(SubscriptionHandling.HandleSubscribeCallback.class)); SubscriptionHandling.HandleErrorCallback errorMockCallback = mock(SubscriptionHandling.HandleErrorCallback.class); subscriptionHandling.handleError(errorMockCallback); @@ -103,7 +128,8 @@ public void testErrorWhenSubscribedToCallback() throws Exception { @Test public void testCreateEventWhenSubscribedToCallback() throws Exception { ParseQuery parseQuery = new ParseQuery<>("test"); - SubscriptionHandling subscriptionHandling = createSubscription(parseQuery, mock(SubscriptionHandling.HandleSubscribeCallback.class)); + SubscriptionHandling subscriptionHandling = createSubscription(parseQuery, + mock(SubscriptionHandling.HandleSubscribeCallback.class)); SubscriptionHandling.HandleEventCallback eventMockCallback = mock(SubscriptionHandling.HandleEventCallback.class); subscriptionHandling.handleEvent(SubscriptionHandling.Event.CREATE, eventMockCallback); @@ -119,7 +145,8 @@ public void testCreateEventWhenSubscribedToCallback() throws Exception { @Test public void testEnterEventWhenSubscribedToCallback() throws Exception { ParseQuery parseQuery = new ParseQuery<>("test"); - SubscriptionHandling subscriptionHandling = createSubscription(parseQuery, mock(SubscriptionHandling.HandleSubscribeCallback.class)); + SubscriptionHandling subscriptionHandling = createSubscription(parseQuery, + mock(SubscriptionHandling.HandleSubscribeCallback.class)); SubscriptionHandling.HandleEventCallback eventMockCallback = mock(SubscriptionHandling.HandleEventCallback.class); subscriptionHandling.handleEvent(SubscriptionHandling.Event.ENTER, eventMockCallback); @@ -135,7 +162,8 @@ public void testEnterEventWhenSubscribedToCallback() throws Exception { @Test public void testUpdateEventWhenSubscribedToCallback() throws Exception { ParseQuery parseQuery = new ParseQuery<>("test"); - SubscriptionHandling subscriptionHandling = createSubscription(parseQuery, mock(SubscriptionHandling.HandleSubscribeCallback.class)); + SubscriptionHandling subscriptionHandling = createSubscription(parseQuery, + mock(SubscriptionHandling.HandleSubscribeCallback.class)); SubscriptionHandling.HandleEventCallback eventMockCallback = mock(SubscriptionHandling.HandleEventCallback.class); subscriptionHandling.handleEvent(SubscriptionHandling.Event.UPDATE, eventMockCallback); @@ -151,7 +179,8 @@ public void testUpdateEventWhenSubscribedToCallback() throws Exception { @Test public void testLeaveEventWhenSubscribedToCallback() throws Exception { ParseQuery parseQuery = new ParseQuery<>("test"); - SubscriptionHandling subscriptionHandling = createSubscription(parseQuery, mock(SubscriptionHandling.HandleSubscribeCallback.class)); + SubscriptionHandling subscriptionHandling = createSubscription(parseQuery, + mock(SubscriptionHandling.HandleSubscribeCallback.class)); SubscriptionHandling.HandleEventCallback eventMockCallback = mock(SubscriptionHandling.HandleEventCallback.class); subscriptionHandling.handleEvent(SubscriptionHandling.Event.LEAVE, eventMockCallback); @@ -164,11 +193,11 @@ public void testLeaveEventWhenSubscribedToCallback() throws Exception { validateSameObject(eventMockCallback, parseQuery, parseObject); } - @Test public void testDeleteEventWhenSubscribedToCallback() throws Exception { ParseQuery parseQuery = new ParseQuery<>("test"); - SubscriptionHandling subscriptionHandling = createSubscription(parseQuery, mock(SubscriptionHandling.HandleSubscribeCallback.class)); + SubscriptionHandling subscriptionHandling = createSubscription(parseQuery, + mock(SubscriptionHandling.HandleSubscribeCallback.class)); SubscriptionHandling.HandleEventCallback eventMockCallback = mock(SubscriptionHandling.HandleEventCallback.class); subscriptionHandling.handleEvent(SubscriptionHandling.Event.DELETE, eventMockCallback); @@ -184,7 +213,8 @@ public void testDeleteEventWhenSubscribedToCallback() throws Exception { @Test public void testCreateEventWhenSubscribedToAnyCallback() throws Exception { ParseQuery parseQuery = new ParseQuery<>("test"); - SubscriptionHandling subscriptionHandling = createSubscription(parseQuery, mock(SubscriptionHandling.HandleSubscribeCallback.class)); + SubscriptionHandling subscriptionHandling = createSubscription(parseQuery, + mock(SubscriptionHandling.HandleSubscribeCallback.class)); SubscriptionHandling.HandleEventsCallback eventsMockCallback = mock(SubscriptionHandling.HandleEventsCallback.class); subscriptionHandling.handleEvents(eventsMockCallback); @@ -205,12 +235,14 @@ public void testCreateEventWhenSubscribedToAnyCallback() throws Exception { @Test public void testSubscriptionStoppedAfterUnsubscribe() throws Exception { ParseQuery parseQuery = new ParseQuery<>("test"); - SubscriptionHandling subscriptionHandling = createSubscription(parseQuery, mock(SubscriptionHandling.HandleSubscribeCallback.class)); + SubscriptionHandling subscriptionHandling = createSubscription(parseQuery, + mock(SubscriptionHandling.HandleSubscribeCallback.class)); SubscriptionHandling.HandleEventCallback eventMockCallback = mock(SubscriptionHandling.HandleEventCallback.class); subscriptionHandling.handleEvent(SubscriptionHandling.Event.CREATE, eventMockCallback); - SubscriptionHandling.HandleUnsubscribeCallback unsubscribeMockCallback = mock(SubscriptionHandling.HandleUnsubscribeCallback.class); + SubscriptionHandling.HandleUnsubscribeCallback unsubscribeMockCallback = mock( + SubscriptionHandling.HandleUnsubscribeCallback.class); subscriptionHandling.handleUnsubscribe(unsubscribeMockCallback); parseLiveQueryClient.unsubscribe(parseQuery); @@ -239,14 +271,52 @@ public void testSubscriptionReplayedAfterReconnect() throws Exception { verify(webSocketClient, times(2)).send(any(String.class)); } + @Test + public void testSessionTokenSentOnConnect() { + when(mockUser.getSessionToken()).thenReturn("the token"); + parseLiveQueryClient.reconnect(); + webSocketClientCallback.onOpen(); + verify(webSocketClient, times(1)).send(contains("\"sessionToken\":\"the token\"")); + } - private SubscriptionHandling createSubscription(ParseQuery parseQuery, SubscriptionHandling.HandleSubscribeCallback subscribeMockCallback) throws Exception { + @Test + public void testEmptySessionTokenOnConnect() { + parseLiveQueryClient.reconnect(); + webSocketClientCallback.onOpen(); + verify(webSocketClient, times(1)).send(not(contains("\"sessionToken\":"))); + } + + @Test + public void testSessionTokenSentOnSubscribe() { + when(mockUser.getSessionToken()).thenReturn("the token"); + when(webSocketClient.getState()).thenReturn(WebSocketClient.State.CONNECTED); + parseLiveQueryClient.subscribe(ParseQuery.getQuery("Test")); + verify(webSocketClient, times(1)).send(and( + contains("\"op\":\"subscribe\""), + contains("\"sessionToken\":\"the token\""))); + } + + @Test + public void testEmptySessionTokenOnSubscribe() { + when(mockUser.getSessionToken()).thenReturn("the token"); + when(webSocketClient.getState()).thenReturn(WebSocketClient.State.CONNECTED); + parseLiveQueryClient.subscribe(ParseQuery.getQuery("Test")); + verify(webSocketClient, times(1)).send(contains("\"op\":\"connect\"")); + verify(webSocketClient, times(1)).send(and( + contains("\"op\":\"subscribe\""), + contains("\"sessionToken\":\"the token\""))); + } + + private SubscriptionHandling createSubscription(ParseQuery parseQuery, + SubscriptionHandling.HandleSubscribeCallback subscribeMockCallback) throws Exception { SubscriptionHandling subscriptionHandling = parseLiveQueryClient.subscribe(parseQuery).handleSubscribe(subscribeMockCallback); webSocketClientCallback.onMessage(createSubscribedMessage(subscriptionHandling.getRequestId()).toString()); return subscriptionHandling; } - private void validateSameObject(SubscriptionHandling.HandleEventCallback eventMockCallback, ParseQuery parseQuery, ParseObject originalParseObject) { + private void validateSameObject(SubscriptionHandling.HandleEventCallback eventMockCallback, + ParseQuery parseQuery, + ParseObject originalParseObject) { ArgumentCaptor objectCaptor = ArgumentCaptor.forClass(ParseObject.class); verify(eventMockCallback, times(1)).onEvent(eq(parseQuery), objectCaptor.capture()); @@ -335,5 +405,4 @@ private static JSONObject createObjectDeleteMessage(int requestId, ParseObject p jsonObject.put("object", PointerEncoder.get().encodeRelatedObject(parseObject)); return jsonObject; } - } From 6f95882477a3ba9f7ba48e7ad6db9107a07fb9f0 Mon Sep 17 00:00:00 2001 From: Joe Hansche Date: Fri, 3 Feb 2017 13:03:50 -0800 Subject: [PATCH 3/5] Change to getCurrentSessionTokenAsync() This allows the results to be chained together via tasks, and also allows the CurrentUserController to be overridden to provide additional functionality for retrieving the session token. This matches how the ParseCloud and ParseObject retrieve the session token. --- .../java/com/parse/ParseLiveQueryClientImpl.java | 12 +++++++----- .../java/com/parse/TestParseLiveQueryClient.java | 7 +++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClientImpl.java b/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClientImpl.java index aef4ae5..a7aea14 100644 --- a/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClientImpl.java +++ b/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClientImpl.java @@ -1,6 +1,5 @@ package com.parse; - import android.util.Log; import android.util.SparseArray; @@ -179,7 +178,6 @@ private void parseMessage(String message) throws LiveQueryException { } catch (JSONException e) { throw new LiveQueryException.InvalidResponseException(message); } - } private void handleSubscribedEvent(JSONObject jsonObject) throws JSONException { @@ -237,8 +235,13 @@ private WebSocketClient.WebSocketClientCallback getWebSocketClientCallback() { @Override public void onOpen() { Log.v(LOG_TAG, "Socket opened"); - String sessionToken = ParseUser.getCurrentSessionToken(); - sendOperationAsync(new ConnectClientOperation(applicationId, sessionToken)).continueWith(new Continuation() { + ParseUser.getCurrentSessionTokenAsync().onSuccessTask(new Continuation>() { + @Override + public Task then(Task task) throws Exception { + String sessionToken = task.getResult(); + return sendOperationAsync(new ConnectClientOperation(applicationId, sessionToken)); + } + }).continueWith(new Continuation() { public Void then(Task task) { Exception error = task.getError(); if (error != null) { @@ -279,5 +282,4 @@ public void stateChanged() { } }; } - } diff --git a/ParseLiveQuery/src/test/java/com/parse/TestParseLiveQueryClient.java b/ParseLiveQuery/src/test/java/com/parse/TestParseLiveQueryClient.java index e4e9e90..482cda7 100644 --- a/ParseLiveQuery/src/test/java/com/parse/TestParseLiveQueryClient.java +++ b/ParseLiveQuery/src/test/java/com/parse/TestParseLiveQueryClient.java @@ -37,6 +37,7 @@ public class TestParseLiveQueryClient { private WebSocketClient webSocketClient; private WebSocketClient.WebSocketClientCallback webSocketClientCallback; private ParseLiveQueryClient parseLiveQueryClient; + private ParseUser mockUser; @Before @@ -52,6 +53,12 @@ public Task answer(InvocationOnMock invocation) throws Throwable { return Task.forResult(mockUser); } }); + when(currentUserController.getCurrentSessionTokenAsync()).thenAnswer(new Answer>() { + @Override + public Task answer(InvocationOnMock invocation) throws Throwable { + return Task.forResult(mockUser.getSessionToken()); + } + }); ParseCorePlugins.getInstance().registerCurrentUserController(currentUserController); parseLiveQueryClient = ParseLiveQueryClient.Factory.getClient(new URI(""), new WebSocketClientFactory() { From 5c4dc50439aa95cb9799305087b4e3de23837b81 Mon Sep 17 00:00:00 2001 From: Joe Hansche Date: Fri, 3 Feb 2017 13:19:45 -0800 Subject: [PATCH 4/5] Switch subscribe() to use getCurrentSessionTokenAsync() --- .../main/java/com/parse/ParseLiveQueryClientImpl.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClientImpl.java b/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClientImpl.java index a7aea14..40bb1d3 100644 --- a/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClientImpl.java +++ b/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClientImpl.java @@ -221,9 +221,14 @@ private Subscription subscriptionForRequestId(int requestId) { return subscriptions.get(requestId); } - private void sendSubscription(Subscription subscription) { - String sessionToken = ParseUser.getCurrentSessionToken(); - sendOperationAsync(new SubscribeClientOperation<>(subscription.getRequestId(), subscription.getQueryState(), sessionToken)); + private void sendSubscription(final Subscription subscription) { + ParseUser.getCurrentSessionTokenAsync().onSuccessTask(new Continuation>() { + @Override + public Task then(Task task) throws Exception { + String sessionToken = task.getResult(); + return sendOperationAsync(new SubscribeClientOperation<>(subscription.getRequestId(), subscription.getQueryState(), sessionToken)); + } + }); } private void sendUnsubscription(Subscription subscription) { From 773054c316f29142598fbd6345766c55a2894f0e Mon Sep 17 00:00:00 2001 From: Joe Hansche Date: Thu, 16 Feb 2017 11:34:51 -0500 Subject: [PATCH 5/5] Remove unnecessary null check --- .../src/main/java/com/parse/SubscribeClientOperation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ParseLiveQuery/src/main/java/com/parse/SubscribeClientOperation.java b/ParseLiveQuery/src/main/java/com/parse/SubscribeClientOperation.java index 8c87d9b..4cc9c38 100644 --- a/ParseLiveQuery/src/main/java/com/parse/SubscribeClientOperation.java +++ b/ParseLiveQuery/src/main/java/com/parse/SubscribeClientOperation.java @@ -20,7 +20,7 @@ JSONObject jsonObject = new JSONObject(); jsonObject.put("op", "subscribe"); jsonObject.put("requestId", requestId); - if (sessionToken != null) jsonObject.put("sessionToken", sessionToken); + jsonObject.put("sessionToken", sessionToken); JSONObject queryJsonObject = state.toJSON(NoObjectsEncoder.get());