Skip to content

Commit 3fc9bf8

Browse files
committed
Merge pull request #117 from ParsePlatform/grantland.authentication_async
Convert `restoreAuthentication` to asynchronous
2 parents 29a3795 + 7284301 commit 3fc9bf8

7 files changed

+145
-95
lines changed

Parse/src/main/java/com/parse/AnonymousAuthenticationProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ public Task<Void> deauthenticateAsync() {
3838
}
3939

4040
@Override
41-
public boolean restoreAuthentication(Map<String, String> authData) {
42-
return true;
41+
public Task<Boolean> restoreAuthenticationAsync(Map<String, String> authData) {
42+
return Task.forResult(true);
4343
}
4444

4545
@Override

Parse/src/main/java/com/parse/CachedCurrentUserController.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,24 @@ public Task<Void> then(Task<Void> task) throws Exception {
5555
if (oldCurrentUser != null && oldCurrentUser != user) {
5656
// We don't need to revoke the token since we're not explicitly calling logOut
5757
// We don't need to remove persisted files since we're overwriting them
58-
return oldCurrentUser.logOutAsync(false);
58+
return oldCurrentUser.logOutAsync(false).continueWith(new Continuation<Void, Void>() {
59+
@Override
60+
public Void then(Task<Void> task) throws Exception {
61+
return null; // ignore errors
62+
}
63+
});
5964
}
6065
return task;
6166
}
62-
}).continueWithTask(new Continuation<Void, Task<Void>>() {
67+
}).onSuccessTask(new Continuation<Void, Task<Void>>() {
68+
@Override
69+
public Task<Void> then(Task<Void> task) throws Exception {
70+
user.setIsCurrentUser(true);
71+
return user.synchronizeAllAuthDataAsync();
72+
}
73+
}).onSuccessTask(new Continuation<Void, Task<Void>>() {
6374
@Override
6475
public Task<Void> then(Task<Void> task) throws Exception {
65-
synchronized (user.mutex) {
66-
user.setIsCurrentUser(true);
67-
user.synchronizeAllAuthData();
68-
}
69-
7076
return store.setAsync(user).continueWith(new Continuation<Void, Void>() {
7177
@Override
7278
public Void then(Task<Void> task) throws Exception {

Parse/src/main/java/com/parse/ParseAuthenticationManager.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import bolts.Continuation;
1515
import bolts.Task;
1616

17-
/*** package */ class ParseAuthenticationManager {
17+
/** package */ class ParseAuthenticationManager {
1818

1919
private final Object lock = new Object();
2020
private final Map<String, ParseAuthenticationProvider> authenticationProviders = new HashMap<>();
@@ -44,24 +44,27 @@ public void register(ParseAuthenticationProvider provider) {
4444
}
4545

4646
// Synchronize the current user with the auth provider.
47-
controller.getAsync(false).onSuccess(new Continuation<ParseUser, Void>() {
47+
controller.getAsync(false).onSuccessTask(new Continuation<ParseUser, Task<Void>>() {
4848
@Override
49-
public Void then(Task<ParseUser> task) throws Exception {
49+
public Task<Void> then(Task<ParseUser> task) throws Exception {
5050
ParseUser user = task.getResult();
5151
if (user != null) {
52-
user.synchronizeAuthData(authType);
52+
return user.synchronizeAuthDataAsync(authType);
5353
}
5454
return null;
5555
}
5656
});
5757
}
5858

59-
public boolean restoreAuthentication(String authType, Map<String, String> authData) {
59+
public Task<Boolean> restoreAuthenticationAsync(String authType, Map<String, String> authData) {
6060
ParseAuthenticationProvider provider;
6161
synchronized (lock) {
6262
provider = authenticationProviders.get(authType);
6363
}
64-
return provider == null || provider.restoreAuthentication(authData);
64+
if (provider == null) {
65+
return Task.forResult(true);
66+
}
67+
return provider.restoreAuthenticationAsync(authData);
6568
}
6669

6770
public Task<Void> deauthenticateAsync(String authType) {

Parse/src/main/java/com/parse/ParseAuthenticationProvider.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,16 @@
2424
String getAuthType();
2525

2626
/**
27-
* Begins the authentication process and invokes onSuccess() or onError() on
28-
* the callback upon completion. This call should not block.
27+
* Authenticates with the service.
2928
*
30-
* @return A task that will be resolved upon the completion of authentication.
29+
* @return A {@code Task} that will be resolved when authentication is complete.
3130
*/
3231
Task<Map<String, String>> authenticateAsync();
3332

3433
/**
35-
* Deauthenticates (logs out) the user associated with this provider. This
36-
* call may block.
34+
* Deauthenticates (logs out) the user associated with this provider. This call may block.
35+
*
36+
* @return A {@link Task} that resolves when deauthentication is complete.
3737
*/
3838
Task<Void> deauthenticateAsync();
3939

@@ -44,9 +44,10 @@
4444
* @param authData
4545
* the auth data for the provider. This value may be null when
4646
* unlinking an account.
47-
* @return true iff the authData was successfully synchronized. A false return
48-
* value indicates that the user should no longer be associated
49-
* because of bad auth data.
47+
*
48+
* @return A {@link Task} that resolves to {@code true} iff the {@code authData} was successfully
49+
* synchronized or {@code false} if user should no longer be associated because of bad
50+
* {@code authData}.
5051
*/
51-
boolean restoreAuthentication(Map<String, String> authData);
52+
Task<Boolean> restoreAuthenticationAsync(Map<String, String> authData);
5253
}

Parse/src/main/java/com/parse/ParseUser.java

Lines changed: 92 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -247,28 +247,32 @@ public void remove(String key) {
247247
return super.toRest(state, cleanOperationSetQueue, objectEncoder);
248248
}
249249

250-
/* package for tests */ void cleanUpAuthData() {
250+
/* package for tests */ Task<Void> cleanUpAuthDataAsync() {
251251
ParseAuthenticationManager controller = getAuthenticationManager();
252+
Map<String, Map<String, String>> authData;
252253
synchronized (mutex) {
253-
Map<String, Map<String, String>> authData = getState().authData();
254+
authData = getState().authData();
254255
if (authData.size() == 0) {
255-
return; // Nothing to see or do here...
256+
return Task.forResult(null); // Nothing to see or do here...
256257
}
258+
}
257259

258-
Iterator<Map.Entry<String, Map<String, String>>> i = authData.entrySet().iterator();
259-
while (i.hasNext()) {
260-
Map.Entry<String, Map<String, String>> entry = i.next();
261-
if (entry.getValue() == null) {
262-
i.remove();
263-
controller.restoreAuthentication(entry.getKey(), null);
264-
}
260+
List<Task<Void>> tasks = new ArrayList<>();
261+
Iterator<Map.Entry<String, Map<String, String>>> i = authData.entrySet().iterator();
262+
while (i.hasNext()) {
263+
Map.Entry<String, Map<String, String>> entry = i.next();
264+
if (entry.getValue() == null) {
265+
i.remove();
266+
tasks.add(controller.restoreAuthenticationAsync(entry.getKey(), null).makeVoid());
265267
}
266-
267-
State newState = getState().newBuilder()
268-
.authData(authData)
269-
.build();
270-
setState(newState);
271268
}
269+
270+
State newState = getState().newBuilder()
271+
.authData(authData)
272+
.build();
273+
setState(newState);
274+
275+
return Task.whenAll(tasks);
272276
}
273277

274278
@Override
@@ -488,17 +492,22 @@ private void restoreAnonymity(Map<String, String> anonymousData) {
488492
task = super.saveAsync(sessionToken, toAwait);
489493
}
490494

491-
return task.onSuccessTask(new Continuation<Void, Task<Void>>() {
492-
@Override
493-
public Task<Void> then(Task<Void> task) throws Exception {
494-
// If the user is the currently logged in user, we persist all data to disk
495-
if (isCurrentUser()) {
496-
cleanUpAuthData();
495+
if (isCurrentUser()) {
496+
// If the user is the currently logged in user, we persist all data to disk
497+
return task.onSuccessTask(new Continuation<Void, Task<Void>>() {
498+
@Override
499+
public Task<Void> then(Task<Void> task) throws Exception {
500+
return cleanUpAuthDataAsync();
501+
}
502+
}).onSuccessTask(new Continuation<Void, Task<Void>>() {
503+
@Override
504+
public Task<Void> then(Task<Void> task) throws Exception {
497505
return saveCurrentUserAsync(ParseUser.this);
498506
}
499-
return Task.forResult(null);
500-
}
501-
});
507+
});
508+
}
509+
510+
return task;
502511
}
503512

504513
@Override
@@ -531,29 +540,34 @@ public ParseUser fetch() throws ParseException {
531540
@Override
532541
/* package */ <T extends ParseObject> Task<T> fetchAsync(
533542
String sessionToken, Task<Void> toAwait) {
534-
synchronized (mutex) {
535-
//TODO (grantland): It doesn't seem like we should do this.. Why don't we error like we do
536-
// when fetching an unsaved ParseObject?
537-
if (isLazy()) {
538-
return Task.forResult((T) this);
539-
}
543+
//TODO (grantland): It doesn't seem like we should do this.. Why don't we error like we do
544+
// when fetching an unsaved ParseObject?
545+
if (isLazy()) {
546+
return Task.forResult((T) this);
547+
}
540548

541-
return super.<T> fetchAsync(sessionToken, toAwait).onSuccessTask(new Continuation<T, Task<T>>() {
549+
Task<T> task = super.fetchAsync(sessionToken, toAwait);
550+
551+
if (isCurrentUser()) {
552+
return task.onSuccessTask(new Continuation<T, Task<Void>>() {
542553
@Override
543-
public Task<T> then(final Task<T> fetchAsyncTask) throws Exception {
544-
if (isCurrentUser()) {
545-
cleanUpAuthData();
546-
return saveCurrentUserAsync(ParseUser.this).continueWithTask(new Continuation<Void, Task<T>>() {
547-
@Override
548-
public Task<T> then(Task<Void> task) throws Exception {
549-
return fetchAsyncTask;
550-
}
551-
});
552-
}
553-
return fetchAsyncTask;
554+
public Task<Void> then(final Task<T> fetchAsyncTask) throws Exception {
555+
return cleanUpAuthDataAsync();
556+
}
557+
}).onSuccessTask(new Continuation<Void, Task<Void>>() {
558+
@Override
559+
public Task<Void> then(Task<Void> task) throws Exception {
560+
return saveCurrentUserAsync(ParseUser.this);
561+
}
562+
}).onSuccess(new Continuation<Void, T>() {
563+
@Override
564+
public T then(Task<Void> task) throws Exception {
565+
return (T) ParseUser.this;
554566
}
555567
});
556568
}
569+
570+
return task;
557571
}
558572

559573
/**
@@ -676,7 +690,8 @@ public Task<Void> then(Task<Void> task) throws Exception {
676690
@Override
677691
public Task<Void> then(final Task<ParseUser.State> signUpTask) throws Exception {
678692
ParseUser.State result = signUpTask.getResult();
679-
return handleSaveResultAsync(result, operations).continueWithTask(new Continuation<Void, Task<Void>>() {
693+
return handleSaveResultAsync(result,
694+
operations).continueWithTask(new Continuation<Void, Task<Void>>() {
680695
@Override
681696
public Task<Void> then(Task<Void> task) throws Exception {
682697
if (!signUpTask.isCancelled() && !signUpTask.isFaulted()) {
@@ -1068,29 +1083,48 @@ public ParseUser fetchIfNeeded() throws ParseException {
10681083
return authData.containsKey(authType) && authData.get(authType) != null;
10691084
}
10701085

1071-
/* package */ void synchronizeAuthData(String authType) {
1086+
/**
1087+
* Ensures that all auth providers have auth data (e.g. access tokens, etc.) that matches this
1088+
* user.
1089+
*/
1090+
/* package */ Task<Void> synchronizeAllAuthDataAsync() {
1091+
Map<String, Map<String, String>> authData;
10721092
synchronized (mutex) {
10731093
if (!isCurrentUser()) {
1074-
return;
1075-
}
1076-
boolean success = getAuthenticationManager()
1077-
.restoreAuthentication(authType, getAuthData(authType));
1078-
if (!success) {
1079-
unlinkFromAsync(authType);
1094+
return Task.forResult(null);
10801095
}
1096+
authData = getAuthData();
10811097
}
1098+
List<Task<Void>> tasks = new ArrayList<>(authData.size());
1099+
for (String authType : authData.keySet()) {
1100+
tasks.add(synchronizeAuthDataAsync(authType));
1101+
}
1102+
return Task.whenAll(tasks);
10821103
}
10831104

1084-
/**
1085-
* Ensures that all auth providers have auth data (e.g. access tokens, etc.) that matches this
1086-
* user.
1087-
*/
1088-
/* package */ void synchronizeAllAuthData() {
1105+
/* package */ Task<Void> synchronizeAuthDataAsync(String authType) {
1106+
Map<String, String> authData;
10891107
synchronized (mutex) {
1090-
for (Map.Entry<String, Map<String, String>> entry : getAuthData().entrySet()) {
1091-
synchronizeAuthData(entry.getKey());
1108+
if (!isCurrentUser()) {
1109+
return Task.forResult(null);
10921110
}
1111+
authData = getAuthData(authType);
10931112
}
1113+
return synchronizeAuthDataAsync(getAuthenticationManager(), authType, authData);
1114+
}
1115+
1116+
private Task<Void> synchronizeAuthDataAsync(
1117+
ParseAuthenticationManager manager, final String authType, Map<String, String> authData) {
1118+
return manager.restoreAuthenticationAsync(authType, authData).onSuccessTask(new Continuation<Boolean, Task<Void>>() {
1119+
@Override
1120+
public Task<Void> then(Task<Boolean> task) throws Exception {
1121+
boolean success = task.getResult();
1122+
if (!success) {
1123+
return unlinkFromAsync(authType);
1124+
}
1125+
return task.makeVoid();
1126+
}
1127+
});
10941128
}
10951129

10961130
/* package */ Task<Void> unlinkFromAsync(final String authType) {
@@ -1236,8 +1270,7 @@ public Task<Void> then(Task<Void> task) throws Exception {
12361270
restoreAnonymity(oldAnonymousData);
12371271
return task;
12381272
}
1239-
synchronizeAuthData(authType);
1240-
return task;
1273+
return synchronizeAuthDataAsync(authType);
12411274
}
12421275
}
12431276
});

Parse/src/test/java/com/parse/ParseAuthenticationManagerTest.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
import org.junit.Rule;
1313
import org.junit.Test;
1414
import org.junit.rules.ExpectedException;
15+
import org.mockito.Matchers;
1516

1617
import java.util.HashMap;
1718
import java.util.Map;
1819

1920
import bolts.Task;
2021

22+
import static org.junit.Assert.assertTrue;
2123
import static org.mockito.Mockito.mock;
2224
import static org.mockito.Mockito.verify;
2325
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -70,20 +72,22 @@ public void testRegister() {
7072

7173
manager.register(provider);
7274
verify(controller).getAsync(false);
73-
verify(user).synchronizeAuthData("test_provider");
75+
verify(user).synchronizeAuthDataAsync("test_provider");
7476
}
7577

7678
//endregion
7779

7880
@Test
79-
public void testRestoreAuthentication() {
81+
public void testRestoreAuthentication() throws ParseException {
8082
when(controller.getAsync(false)).thenReturn(Task.<ParseUser>forResult(null));
83+
when(provider.restoreAuthenticationAsync(Matchers.<Map<String, String>>any()))
84+
.thenReturn(Task.forResult(true));
8185
manager.register(provider);
8286

8387
Map<String, String> authData = new HashMap<>();
84-
manager.restoreAuthentication("test_provider", authData);
88+
assertTrue(ParseTaskUtils.wait(manager.restoreAuthenticationAsync("test_provider", authData)));
8589

86-
verify(provider).restoreAuthentication(authData);
90+
verify(provider).restoreAuthenticationAsync(authData);
8791
}
8892

8993
@Test

0 commit comments

Comments
 (0)