diff --git a/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClientImpl.java b/ParseLiveQuery/src/main/java/com/parse/ParseLiveQueryClientImpl.java index b06c0e8..40bb1d3 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 { @@ -223,8 +221,14 @@ private Subscription subscriptionForRequestId(int requestId) { return subscriptions.get(requestId); } - private void sendSubscription(Subscription subscription) { - sendOperationAsync(new SubscribeClientOperation<>(subscription.getRequestId(), subscription.getQueryState())); + 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) { @@ -236,7 +240,13 @@ private WebSocketClient.WebSocketClientCallback getWebSocketClientCallback() { @Override public void onOpen() { Log.v(LOG_TAG, "Socket opened"); - sendOperationAsync(new ConnectClientOperation(applicationId, "")).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) { @@ -277,5 +287,4 @@ public void stateChanged() { } }; } - } diff --git a/ParseLiveQuery/src/main/java/com/parse/SubscribeClientOperation.java b/ParseLiveQuery/src/main/java/com/parse/SubscribeClientOperation.java index 7a31ee2..4cc9c38 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); + jsonObject.put("sessionToken", sessionToken); JSONObject queryJsonObject = state.toJSON(NoObjectsEncoder.get()); diff --git a/ParseLiveQuery/src/test/java/com/parse/TestParseLiveQueryClient.java b/ParseLiveQuery/src/test/java/com/parse/TestParseLiveQueryClient.java index 5d2b205..482cda7 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) @@ -29,9 +38,29 @@ public class TestParseLiveQueryClient { 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); + } + }); + 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() { @Override public WebSocketClient createInstance(WebSocketClient.WebSocketClientCallback webSocketClientCallback, URI hostUrl) { @@ -67,12 +96,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 +113,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 +135,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 +152,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 +169,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 +186,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 +200,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 +220,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 +242,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 +278,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 +412,4 @@ private static JSONObject createObjectDeleteMessage(int requestId, ParseObject p jsonObject.put("object", PointerEncoder.get().encodeRelatedObject(parseObject)); return jsonObject; } - }