Skip to content

Commit 52d6cb5

Browse files
authored
Add createTenant and updateTenant operations. (#377)
Added createTenant and updateTenant to the TenantManager class. Also added the relevant unit tests to FirebaseUserManagerTest. This is part of the initiative to adding multi-tenancy support (see issue #332).
1 parent c3ef972 commit 52d6cb5

File tree

6 files changed

+315
-132
lines changed

6 files changed

+315
-132
lines changed

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

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@
3636
import com.google.common.base.Strings;
3737
import com.google.common.collect.ImmutableList;
3838
import com.google.common.collect.ImmutableMap;
39+
import com.google.common.collect.ImmutableSortedSet;
3940
import com.google.firebase.FirebaseApp;
4041
import com.google.firebase.ImplFirebaseTrampolines;
41-
import com.google.firebase.auth.UserRecord.CreateRequest;
42-
import com.google.firebase.auth.UserRecord.UpdateRequest;
42+
import com.google.firebase.auth.UserRecord;
4343
import com.google.firebase.auth.internal.DownloadAccountResponse;
4444
import com.google.firebase.auth.internal.GetAccountInfoResponse;
4545
import com.google.firebase.auth.internal.HttpErrorResponse;
@@ -171,7 +171,7 @@ UserRecord getUserByPhoneNumber(String phoneNumber) throws FirebaseAuthException
171171
return new UserRecord(response.getUsers().get(0), jsonFactory);
172172
}
173173

174-
String createUser(CreateRequest request) throws FirebaseAuthException {
174+
String createUser(UserRecord.CreateRequest request) throws FirebaseAuthException {
175175
GenericJson response = post(
176176
"/accounts", request.getProperties(), GenericJson.class);
177177
if (response != null) {
@@ -183,7 +183,8 @@ String createUser(CreateRequest request) throws FirebaseAuthException {
183183
throw new FirebaseAuthException(INTERNAL_ERROR, "Failed to create new user");
184184
}
185185

186-
void updateUser(UpdateRequest request, JsonFactory jsonFactory) throws FirebaseAuthException {
186+
void updateUser(UserRecord.UpdateRequest request, JsonFactory jsonFactory)
187+
throws FirebaseAuthException {
187188
GenericJson response = post(
188189
"/accounts:update", request.getProperties(jsonFactory), GenericJson.class);
189190
if (response == null || !request.getUid().equals(response.get("localId"))) {
@@ -230,20 +231,33 @@ UserImportResult importUsers(UserImportRequest request) throws FirebaseAuthExcep
230231

231232
Tenant getTenant(String tenantId) throws FirebaseAuthException {
232233
GenericUrl url = new GenericUrl(tenantMgtBaseUrl + "/tenants/" + tenantId);
233-
Tenant response = sendRequest("GET", url, null, Tenant.class);
234-
if (Strings.isNullOrEmpty(response.getTenantId())) {
235-
throw new FirebaseAuthException(TENANT_NOT_FOUND_ERROR, "Failed to get tenant.");
236-
}
237-
return response;
234+
return sendRequest("GET", url, null, Tenant.class);
235+
}
236+
237+
Tenant createTenant(Tenant.CreateRequest request) throws FirebaseAuthException {
238+
GenericUrl url = new GenericUrl(tenantMgtBaseUrl + "/tenants");
239+
return sendRequest("POST", url, request.getProperties(), Tenant.class);
240+
}
241+
242+
Tenant updateTenant(Tenant.UpdateRequest request) throws FirebaseAuthException {
243+
Map<String, Object> properties = request.getProperties();
244+
checkArgument(!properties.isEmpty(), "tenant update must have at least one property set");
245+
GenericUrl url = new GenericUrl(tenantMgtBaseUrl + "/tenants/" + request.getTenantId());
246+
url.put("updateMask", generateMask(properties));
247+
return sendRequest("PATCH", url, properties, Tenant.class);
248+
}
249+
250+
private static String generateMask(Map<String, Object> properties) {
251+
// This implementation does not currently handle the case of nested properties. This is fine
252+
// since we do not currently generate masks for any properties with nested values. When it
253+
// comes time to implement this, we can check if a property has nested properties by checking
254+
// if it is an instance of the Map class.
255+
return String.join(",", ImmutableSortedSet.copyOf(properties.keySet()));
238256
}
239257

240258
void deleteTenant(String tenantId) throws FirebaseAuthException {
241259
GenericUrl url = new GenericUrl(tenantMgtBaseUrl + "/tenants/" + tenantId);
242-
GenericJson response = sendRequest("DELETE", url, null, GenericJson.class);
243-
if (response == null) {
244-
throw new FirebaseAuthException(TENANT_NOT_FOUND_ERROR,
245-
"Failed to delete tenant: " + tenantId);
246-
}
260+
sendRequest("DELETE", url, null, GenericJson.class);
247261
}
248262

249263
ListTenantsResponse listTenants(int maxResults, String pageToken)

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ Map<String, Object> getProperties() {
134134
*/
135135
public static final class UpdateRequest {
136136

137+
private final String tenantId;
137138
private final Map<String,Object> properties = new HashMap<>();
138139

139140
/**
@@ -148,11 +149,11 @@ public static final class UpdateRequest {
148149
*/
149150
public UpdateRequest(String tenantId) {
150151
checkArgument(!Strings.isNullOrEmpty(tenantId), "tenant ID must not be null or empty");
151-
properties.put("name", tenantId);
152+
this.tenantId = tenantId;
152153
}
153154

154155
String getTenantId() {
155-
return (String) properties.get("name");
156+
return tenantId;
156157
}
157158

158159
/**

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

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@
3636
* This class can be used to perform a variety of tenant-related operations, including creating,
3737
* updating, and listing tenants.
3838
*
39-
* <p>TODO(micahstairs): Implement the following methods: getAuthForTenant(), createTenant(), and
40-
* updateTenant().
39+
* <p>TODO(micahstairs): Implement getAuthForTenant().
4140
*/
4241
public final class TenantManager {
4342

@@ -154,6 +153,80 @@ protected ListTenantsPage execute() throws FirebaseAuthException {
154153
};
155154
}
156155

156+
/**
157+
* Creates a new tenant with the attributes contained in the specified {@link CreateRequest}.
158+
*
159+
* @param request A non-null {@link CreateRequest} instance.
160+
* @return A {@link Tenant} instance corresponding to the newly created tenant.
161+
* @throws NullPointerException if the provided request is null.
162+
* @throws FirebaseAuthException if an error occurs while creating the tenant.
163+
*/
164+
public Tenant createTenant(@NonNull CreateRequest request) throws FirebaseAuthException {
165+
return createTenantOp(request).call();
166+
}
167+
168+
/**
169+
* Similar to {@link #createTenant(CreateRequest)} but performs the operation asynchronously.
170+
*
171+
* @param request A non-null {@link CreateRequest} instance.
172+
* @return An {@code ApiFuture} which will complete successfully with a {@link Tenant}
173+
* instance corresponding to the newly created tenant. If an error occurs while creating the
174+
* tenant, the future throws a {@link FirebaseAuthException}.
175+
* @throws NullPointerException if the provided request is null.
176+
*/
177+
public ApiFuture<Tenant> createTenantAsync(@NonNull CreateRequest request) {
178+
return createTenantOp(request).callAsync(firebaseApp);
179+
}
180+
181+
/**
182+
* Updates an existing user account with the attributes contained in the specified {@link
183+
* UpdateRequest}.
184+
*
185+
* @param request A non-null {@link UpdateRequest} instance.
186+
* @return A {@link Tenant} instance corresponding to the updated user account.
187+
* @throws NullPointerException if the provided update request is null.
188+
* @throws FirebaseAuthException if an error occurs while updating the user account.
189+
*/
190+
public Tenant updateTenant(@NonNull UpdateRequest request) throws FirebaseAuthException {
191+
return updateTenantOp(request).call();
192+
}
193+
194+
/**
195+
* Similar to {@link #updateTenant(UpdateRequest)} but performs the operation asynchronously.
196+
*
197+
* @param request A non-null {@link UpdateRequest} instance.
198+
* @return An {@code ApiFuture} which will complete successfully with a {@link Tenant}
199+
* instance corresponding to the updated user account. If an error occurs while updating the
200+
* user account, the future throws a {@link FirebaseAuthException}.
201+
*/
202+
public ApiFuture<Tenant> updateTenantAsync(@NonNull UpdateRequest request) {
203+
return updateTenantOp(request).callAsync(firebaseApp);
204+
}
205+
206+
private CallableOperation<Tenant, FirebaseAuthException> updateTenantOp(
207+
final UpdateRequest request) {
208+
// TODO(micahstairs): Add a check to make sure the app has not been destroyed yet.
209+
checkNotNull(request, "update request must not be null");
210+
return new CallableOperation<Tenant, FirebaseAuthException>() {
211+
@Override
212+
protected Tenant execute() throws FirebaseAuthException {
213+
return userManager.updateTenant(request);
214+
}
215+
};
216+
}
217+
218+
private CallableOperation<Tenant, FirebaseAuthException> createTenantOp(
219+
final CreateRequest request) {
220+
// TODO(micahstairs): Add a check to make sure the app has not been destroyed yet.
221+
checkNotNull(request, "create request must not be null");
222+
return new CallableOperation<Tenant, FirebaseAuthException>() {
223+
@Override
224+
protected Tenant execute() throws FirebaseAuthException {
225+
return userManager.createTenant(request);
226+
}
227+
};
228+
}
229+
157230
/**
158231
* Deletes the tenant identified by the specified tenant ID.
159232
*

0 commit comments

Comments
 (0)