Repository: firebase/firebase-admin-java
Pull request #371 (2020-02-20 19:47:26)

Add getTenant operation to TenantManager.
This pull request add getTenant to the TenantManager class. I've added the relevant unit tests to FirebaseUserManagerTest (based off of the getUser unit tests). This is part of the initiative to adding multi-tenancy support (see issue #332).

REST API: https://identitytoolkit.googleapis.com/$discovery/rest?version=v2

micahstairs: I think I'm also going to assert that the URL of the outgoing request is correct, like you suggested on my other PR.
micahstairs: > I think I'm also going to assert that the URL of the outgoing request is correct, like you suggested on my other PR.

Done.
micahstairs: I had to rebase since the other PR was submitted first. Hopefully I did that right.
micahstairs: It took a lot of work to get this rebased properly, but I learned a lot about git in the process :)

The individual commits are bit messed up (the code wasn't always in a state where it could be compiled after each commit), however, it's all about to get squashed anyway.

Patch:
diff --git a/src/main/java/com/google/firebase/auth/FirebaseUserManager.java b/src/main/java/com/google/firebase/auth/FirebaseUserManager.java
index 4bab926..1d86fac 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseUserManager.java
+++ b/src/main/java/com/google/firebase/auth/FirebaseUserManager.java
@@ -83,8 +83,8 @@ class FirebaseUserManager {
       .put("INVALID_PAGE_SELECTION", "invalid-page-token")
       .put("INVALID_PHONE_NUMBER", "invalid-phone-number")
       .put("PHONE_NUMBER_EXISTS", "phone-number-already-exists")
-      .put("TENANT_NOT_FOUND", TENANT_NOT_FOUND_ERROR)
       .put("PROJECT_NOT_FOUND", "project-not-found")
+      .put("TENANT_NOT_FOUND", TENANT_NOT_FOUND_ERROR)
       .put("USER_NOT_FOUND", USER_NOT_FOUND_ERROR)
       .put("WEAK_PASSWORD", "invalid-password")
       .put("UNAUTHORIZED_DOMAIN", "unauthorized-continue-uri")
@@ -228,6 +228,15 @@ class FirebaseUserManager {
     return new UserImportResult(request.getUsersCount(), response);
   }
 
+  Tenant getTenant(String tenantId) throws FirebaseAuthException {
+    GenericUrl url = new GenericUrl(tenantMgtBaseUrl + "/tenants/" + tenantId);
+    Tenant response = sendRequest("GET", url, null, Tenant.class);
+    if (Strings.isNullOrEmpty(response.getTenantId())) {
+      throw new FirebaseAuthException(TENANT_NOT_FOUND_ERROR, "Failed to get tenant.");
+    }
+    return response;
+  }
+
   void deleteTenant(String tenantId) throws FirebaseAuthException {
     GenericUrl url = new GenericUrl(tenantMgtBaseUrl + "/tenants/" + tenantId);
     GenericJson response = sendRequest("DELETE", url, null, GenericJson.class);
diff --git a/src/main/java/com/google/firebase/auth/TenantManager.java b/src/main/java/com/google/firebase/auth/TenantManager.java
index 1d1948c..9d8f638 100644
--- a/src/main/java/com/google/firebase/auth/TenantManager.java
+++ b/src/main/java/com/google/firebase/auth/TenantManager.java
@@ -36,8 +36,8 @@ import com.google.firebase.internal.Nullable;
  * This class can be used to perform a variety of tenant-related operations, including creating,
  * updating, and listing tenants.
  *
- * <p>TODO(micahstairs): Implement the following methods: getAuthForTenant(), getTenant(),
- * deleteTenant(), createTenant(), and updateTenant().
+ * <p>TODO(micahstairs): Implement the following methods: getAuthForTenant(), createTenant(), and
+ * updateTenant().
  */
 public final class TenantManager {
 
@@ -49,6 +49,42 @@ public final class TenantManager {
     this.userManager = userManager;
   }
 
+  /**
+   * Gets the tenant corresponding to the specified tenant ID.
+   *
+   * @param tenantId A tenant ID string.
+   * @return A {@link Tenant} instance.
+   * @throws IllegalArgumentException If the tenant ID string is null or empty.
+   * @throws FirebaseAuthException If an error occurs while retrieving user data.
+   */
+  public Tenant getTenant(@NonNull String tenantId) throws FirebaseAuthException {
+    return getTenantOp(tenantId).call();
+  }
+
+  /**
+   * Similar to {@link #getTenant(String)} but performs the operation asynchronously.
+   *
+   * @param tenantId A tenantId string.
+   * @return An {@code ApiFuture} which will complete successfully with a {@link Tenant} instance
+   *     If an error occurs while retrieving tenant data or if the specified tenant ID does not
+   *     exist, the future throws a {@link FirebaseAuthException}.
+   * @throws IllegalArgumentException If the tenant ID string is null or empty.
+   */
+  public ApiFuture<Tenant> getTenantAsync(@NonNull String tenantId) {
+    return getTenantOp(tenantId).callAsync(firebaseApp);
+  }
+
+  private CallableOperation<Tenant, FirebaseAuthException> getTenantOp(final String tenantId) {
+    // TODO(micahstairs): Add a check to make sure the app has not been destroyed yet.
+    checkArgument(!Strings.isNullOrEmpty(tenantId), "tenantId must not be null or empty");
+    return new CallableOperation<Tenant, FirebaseAuthException>() {
+      @Override
+      protected Tenant execute() throws FirebaseAuthException {
+        return userManager.getTenant(tenantId);
+      }
+    };
+  }
+
   /**
    * Gets a page of tenants starting from the specified {@code pageToken}. Page size will be limited
    * to 1000 tenants.


Test patch:
diff --git a/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java b/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java
index c1ef26c..efc59df 100644
--- a/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java
+++ b/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java
@@ -434,6 +434,32 @@ public class FirebaseUserManagerTest {
     }
   }
 
+  @Test
+  public void testGetTenant() throws Exception {
+    TestResponseInterceptor interceptor = initializeAppForUserManagement(
+        TestUtils.loadResource("getTenant.json"));
+    Tenant tenant = FirebaseAuth.getInstance().getTenantManager().getTenantAsync("TENANT_1").get();
+    checkTenant(tenant, "TENANT_1");
+    checkRequestHeaders(interceptor);
+    checkUrl(interceptor, "GET", TENANTS_BASE_URL + "/TENANT_1");
+  }
+
+  @Test
+  public void testGetTenantWithNotFoundError() throws Exception {
+    TestResponseInterceptor interceptor =
+        initializeAppForUserManagementWithStatusCode(404,
+            "{\"error\": {\"message\": \"TENANT_NOT_FOUND\"}}");
+    try {
+      FirebaseAuth.getInstance().getTenantManager().getTenantAsync("UNKNOWN").get();
+      fail("No error thrown for invalid response");
+    } catch (ExecutionException e) {
+      assertThat(e.getCause(), instanceOf(FirebaseAuthException.class));
+      FirebaseAuthException authException = (FirebaseAuthException) e.getCause();
+      assertEquals(FirebaseUserManager.TENANT_NOT_FOUND_ERROR, authException.getErrorCode());
+    }
+    checkUrl(interceptor, "GET", TENANTS_BASE_URL + "/UNKNOWN");
+  }
+
   @Test
   public void testListTenants() throws Exception {
     final TestResponseInterceptor interceptor = initializeAppForUserManagement(
diff --git a/src/test/resources/getTenant.json b/src/test/resources/getTenant.json
new file mode 100644
index 0000000..cc8565f
--- /dev/null
+++ b/src/test/resources/getTenant.json
@@ -0,0 +1,8 @@
+{
+  "name" : "TENANT_1",
+  "displayName" : "DISPLAY_NAME",
+  "allowPasswordSignup" : true,
+  "enableEmailLinkSignin" : false,
+  "disableAuth" : true,
+  "enableAnonymousUser" : false
+}