Skip to content

Commit 48c8ac7

Browse files
committed
Bulk get users
1 parent c9648cf commit 48c8ac7

File tree

12 files changed

+763
-4
lines changed

12 files changed

+763
-4
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2020 Google Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.auth;
18+
19+
import com.google.firebase.internal.NonNull;
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
import java.util.Map;
23+
24+
/**
25+
* Used for looking up an account by email.
26+
*
27+
* @see {FirebaseAuth#getUsers}
28+
*/
29+
public class EmailIdentifier extends UserIdentifier {
30+
private static final String EMAIL_KEY = "email";
31+
private final String email;
32+
33+
public EmailIdentifier(@NonNull String email) {
34+
UserRecord.checkEmail(email);
35+
this.email = email;
36+
}
37+
38+
@Override
39+
public String toString() {
40+
return "EmailIdentifier(" + email + ")";
41+
}
42+
43+
@Override
44+
void populate(@NonNull Map<String, Object> payload) {
45+
if (!payload.containsKey(EMAIL_KEY)) {
46+
payload.put(EMAIL_KEY, new ArrayList<String>());
47+
}
48+
Object emailPayload = payload.get(EMAIL_KEY);
49+
50+
@SuppressWarnings("unchecked")
51+
List<String> emailList = (List<String>)emailPayload;
52+
53+
emailList.add(email);
54+
}
55+
56+
@Override
57+
boolean matches(@NonNull UserRecord userRecord) {
58+
return email.equals(userRecord.getEmail());
59+
}
60+
}

src/main/java/com/google/firebase/auth/FirebaseAuth.java

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,11 @@
4242
import com.google.firebase.internal.Nullable;
4343

4444
import java.io.IOException;
45+
import java.util.Collection;
46+
import java.util.HashSet;
4547
import java.util.List;
4648
import java.util.Map;
49+
import java.util.Set;
4750
import java.util.concurrent.atomic.AtomicBoolean;
4851

4952
/**
@@ -600,6 +603,81 @@ protected UserRecord execute() throws FirebaseAuthException {
600603
};
601604
}
602605

606+
/**
607+
* Gets the user data corresponding to the specified identifiers.
608+
*
609+
* <p>There are no ordering guarantees; in particular, the nth entry in the users result list is
610+
* not guaranteed to correspond to the nth entry in the input parameters list.
611+
*
612+
* <p>Only a maximum of 100 identifiers may be supplied. If more than 100 identifiers are
613+
* supplied, this method will immediately throw an IllegalArgumentException.
614+
*
615+
* @param identifiers The identifiers used to indicate which user records should be returned. Must
616+
* have 100 or fewer entries.
617+
* @return The corresponding user records.
618+
* @throws IllegalArgumentException If any of the identifiers are invalid or if more than 100
619+
* identifiers are specified.
620+
* @throws NullPointerException If the identifiers parameter is null.
621+
* @throws FirebaseAuthException If an error occurs while retrieving user data.
622+
*/
623+
public GetUsersResult getUsers(@NonNull Collection<UserIdentifier> identifiers)
624+
throws FirebaseAuthException {
625+
return getUsersOp(identifiers).call();
626+
}
627+
628+
/**
629+
* Gets the user data corresponding to the specified identifiers.
630+
*
631+
* <p>There are no ordering guarantees; in particular, the nth entry in the users result list is
632+
* not guaranteed to correspond to the nth entry in the input parameters list.
633+
*
634+
* <p>Only a maximum of 100 identifiers may be supplied. If more than 100 identifiers are
635+
* supplied, this method will immediately throw an IllegalArgumentException.
636+
*
637+
* @param identifiers The identifiers used to indicate which user records should be returned.
638+
* Must have 100 or fewer entries.
639+
* @return An {@code ApiFuture} that resolves to the corresponding user records.
640+
* @throws IllegalArgumentException If any of the identifiers are invalid or if more than 100
641+
* identifiers are specified.
642+
* @throws NullPointerException If the identifiers parameter is null.
643+
*/
644+
public ApiFuture<GetUsersResult> getUsersAsync(@NonNull Collection<UserIdentifier> identifiers) {
645+
return getUsersOp(identifiers).callAsync(firebaseApp);
646+
}
647+
648+
private CallableOperation<GetUsersResult, FirebaseAuthException> getUsersOp(
649+
@NonNull final Collection<UserIdentifier> identifiers) {
650+
checkNotDestroyed();
651+
checkNotNull(identifiers, "identifiers must not be null");
652+
checkArgument(identifiers.size() <= FirebaseUserManager.MAX_GET_ACCOUNTS_BATCH_SIZE,
653+
"identifiers parameter must have <= " + FirebaseUserManager.MAX_GET_ACCOUNTS_BATCH_SIZE
654+
+ " entries.");
655+
656+
final FirebaseUserManager userManager = getUserManager();
657+
return new CallableOperation<GetUsersResult, FirebaseAuthException>() {
658+
@Override
659+
protected GetUsersResult execute() throws FirebaseAuthException {
660+
Set<UserRecord> users = userManager.getAccountInfo(identifiers);
661+
Set<UserIdentifier> notFound = new HashSet<>();
662+
for (UserIdentifier id : identifiers) {
663+
if (!isUserFound(id, users)) {
664+
notFound.add(id);
665+
}
666+
}
667+
return new GetUsersResult(users, notFound);
668+
}
669+
};
670+
}
671+
672+
private boolean isUserFound(UserIdentifier id, Collection<UserRecord> userRecords) {
673+
for (UserRecord userRecord : userRecords) {
674+
if (id.matches(userRecord)) {
675+
return true;
676+
}
677+
}
678+
return false;
679+
}
680+
603681
/**
604682
* Gets a page of users starting from the specified {@code pageToken}. Page size will be
605683
* limited to 1000 users.

src/main/java/com/google/firebase/auth/FirebaseUserManager.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,12 @@
5050
import com.google.firebase.internal.SdkUtils;
5151

5252
import java.io.IOException;
53+
import java.util.Collection;
54+
import java.util.HashMap;
55+
import java.util.HashSet;
5356
import java.util.List;
5457
import java.util.Map;
58+
import java.util.Set;
5559

5660
/**
5761
* FirebaseUserManager provides methods for interacting with the Google Identity Toolkit via its
@@ -86,6 +90,7 @@ class FirebaseUserManager {
8690
.put("INVALID_DYNAMIC_LINK_DOMAIN", "invalid-dynamic-link-domain")
8791
.build();
8892

93+
static final int MAX_GET_ACCOUNTS_BATCH_SIZE = 100;
8994
static final int MAX_LIST_USERS_RESULTS = 1000;
9095
static final int MAX_IMPORT_USERS = 1000;
9196

@@ -171,6 +176,37 @@ UserRecord getUserByPhoneNumber(String phoneNumber) throws FirebaseAuthException
171176
return new UserRecord(response.getUsers().get(0), jsonFactory);
172177
}
173178

179+
/**
180+
* @pre identifiers != null
181+
* @pre identifiers.size() <= MAX_GET_ACCOUNTS_BATCH_SIZE
182+
*/
183+
Set<UserRecord> getAccountInfo(@NonNull Collection<UserIdentifier> identifiers)
184+
throws FirebaseAuthException {
185+
if (identifiers.isEmpty()) {
186+
return new HashSet<UserRecord>();
187+
}
188+
189+
Map<String, Object> payload = new HashMap<>();
190+
for (UserIdentifier id : identifiers) {
191+
id.populate(payload);
192+
}
193+
194+
GetAccountInfoResponse response = post(
195+
"/accounts:lookup", payload, GetAccountInfoResponse.class);
196+
197+
if (response == null) {
198+
throw new FirebaseAuthException(INTERNAL_ERROR, "Failed to parse server response");
199+
}
200+
201+
Set<UserRecord> results = new HashSet<>();
202+
if (response.getUsers() != null) {
203+
for (GetAccountInfoResponse.User user : response.getUsers()) {
204+
results.add(new UserRecord(user, jsonFactory));
205+
}
206+
}
207+
return results;
208+
}
209+
174210
String createUser(CreateRequest request) throws FirebaseAuthException {
175211
GenericJson response = post(
176212
"/accounts", request.getProperties(), GenericJson.class);
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2020 Google Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.auth;
18+
19+
import static com.google.common.base.Preconditions.checkNotNull;
20+
21+
import com.google.firebase.internal.NonNull;
22+
import java.util.Set;
23+
24+
/**
25+
* Represents the result of the {@link FirebaseAuth#getUsersAsync(Collection)} API.
26+
*/
27+
public final class GetUsersResult {
28+
private Set<UserRecord> users;
29+
private Set<UserIdentifier> notFound;
30+
31+
GetUsersResult(@NonNull Set<UserRecord> users, @NonNull Set<UserIdentifier> notFound) {
32+
this.users = checkNotNull(users);
33+
this.notFound = checkNotNull(notFound);
34+
}
35+
36+
/**
37+
* Set of user records, corresponding to the set of users that were requested. Only users
38+
* that were found are listed here. The result set is unordered.
39+
*/
40+
@NonNull
41+
public Set<UserRecord> getUsers() {
42+
return this.users;
43+
}
44+
45+
/**
46+
* Set of identifiers that were requested, but not found.
47+
*/
48+
@NonNull
49+
Set<UserIdentifier> getNotFound() {
50+
return this.notFound;
51+
}
52+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2020 Google Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.auth;
18+
19+
import com.google.firebase.internal.NonNull;
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
import java.util.Map;
23+
24+
/**
25+
* Used for looking up an account by phone number.
26+
*
27+
* @see {FirebaseAuth#getUsers}
28+
*/
29+
public class PhoneIdentifier extends UserIdentifier {
30+
private static final String PHONE_NUMBER_KEY = "phoneNumber";
31+
private final String phoneNumber;
32+
33+
public PhoneIdentifier(@NonNull String phoneNumber) {
34+
UserRecord.checkPhoneNumber(phoneNumber);
35+
this.phoneNumber = phoneNumber;
36+
}
37+
38+
@Override
39+
public String toString() {
40+
return "PhoneIdentifier(" + phoneNumber + ")";
41+
}
42+
43+
@Override
44+
void populate(@NonNull Map<String, Object> payload) {
45+
if (!payload.containsKey(PHONE_NUMBER_KEY)) {
46+
payload.put(PHONE_NUMBER_KEY, new ArrayList<String>());
47+
}
48+
Object phoneNumberPayload = payload.get(PHONE_NUMBER_KEY);
49+
50+
@SuppressWarnings("unchecked")
51+
List<String> phoneNumberList = (List<String>)phoneNumberPayload;
52+
53+
phoneNumberList.add(phoneNumber);
54+
}
55+
56+
@Override
57+
boolean matches(@NonNull UserRecord userRecord) {
58+
return phoneNumber.equals(userRecord.getPhoneNumber());
59+
}
60+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2020 Google Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.auth;
18+
19+
import com.google.firebase.internal.NonNull;
20+
import java.util.HashMap;
21+
import java.util.Map;
22+
23+
/**
24+
* Used for looking up an account by phone number.
25+
*
26+
* @see {FirebaseAuth#getUsers}
27+
*/
28+
public class ProviderIdentifier extends UserIdentifier {
29+
private static final String FEDERATED_USER_ID_KEY = "federatedUserId";
30+
private final String providerId;
31+
private final String providerUid;
32+
33+
public ProviderIdentifier(@NonNull String providerId, @NonNull String providerUid) {
34+
UserRecord.checkProvider(providerId, providerUid);
35+
this.providerId = providerId;
36+
this.providerUid = providerUid;
37+
}
38+
39+
@Override
40+
public String toString() {
41+
return "ProviderIdentifier(" + providerId + ", " + providerUid + ")";
42+
}
43+
44+
@Override
45+
void populate(@NonNull Map<String, Object> payload) {
46+
if (!payload.containsKey(FEDERATED_USER_ID_KEY)) {
47+
payload.put(FEDERATED_USER_ID_KEY, new HashMap<String, Object>());
48+
}
49+
Object federatedUserIdPayload = payload.get(FEDERATED_USER_ID_KEY);
50+
51+
@SuppressWarnings("unchecked")
52+
Map<String, Object> federatedUserIdMap = (Map<String, Object>)federatedUserIdPayload;
53+
54+
federatedUserIdMap.put("providerId", providerId);
55+
federatedUserIdMap.put("rawId", providerUid);
56+
}
57+
58+
@Override
59+
boolean matches(@NonNull UserRecord userRecord) {
60+
for (UserInfo userInfo : userRecord.getProviderData()) {
61+
if (providerId.equals(userInfo.getProviderId()) && providerUid.equals(userInfo.getUid())) {
62+
return true;
63+
}
64+
}
65+
return false;
66+
}
67+
}

0 commit comments

Comments
 (0)