Skip to content

Commit a1e8234

Browse files
authored
feat(auth): Added ListTenantsAsync() and DeleteTenantAsync() APIs (#225)
* feat(auth): Added ListTenantsAsync() and DeleteTenantAsync() APIs * fix: Added newline at eof
1 parent 38545b9 commit a1e8234

File tree

6 files changed

+685
-1
lines changed

6 files changed

+685
-1
lines changed

FirebaseAdmin/FirebaseAdmin.Tests/Auth/Multitenancy/TenantManagerTest.cs

Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
// limitations under the License.
1414

1515
using System;
16+
using System.Collections;
1617
using System.Collections.Generic;
18+
using System.Linq;
1719
using System.Net;
1820
using System.Net.Http;
1921
using System.Threading.Tasks;
@@ -59,6 +61,24 @@ public class TenantManagerTest
5961
private static readonly GoogleCredential MockCredential =
6062
GoogleCredential.FromAccessToken("test-token");
6163

64+
private static readonly IList<string> ListTenantsResponses = new List<string>()
65+
{
66+
$@"{{
67+
""nextPageToken"": ""token"",
68+
""tenants"": [
69+
{TenantResponse},
70+
{TenantResponse},
71+
{TenantResponse}
72+
]
73+
}}",
74+
$@"{{
75+
""tenants"": [
76+
{TenantResponse},
77+
{TenantResponse}
78+
]
79+
}}",
80+
};
81+
6282
[Fact]
6383
public async Task GetTenant()
6484
{
@@ -318,6 +338,271 @@ public async Task UpdateTenantError()
318338
Assert.Null(exception.InnerException);
319339
}
320340

341+
[Fact]
342+
public async Task DeleteTenant()
343+
{
344+
var handler = new MockMessageHandler()
345+
{
346+
Response = TenantResponse,
347+
};
348+
var auth = CreateFirebaseAuth(handler);
349+
350+
await auth.TenantManager.DeleteTenantAsync("tenant1");
351+
352+
Assert.Equal(1, handler.Requests.Count);
353+
var request = handler.Requests[0];
354+
Assert.Equal(HttpMethod.Delete, request.Method);
355+
Assert.Equal("/v2/projects/project1/tenants/tenant1", request.Url.PathAndQuery);
356+
AssertClientVersionHeader(request);
357+
}
358+
359+
[Theory]
360+
[MemberData(nameof(InvalidStrings))]
361+
public async Task DeleteTenantNoId(string tenantId)
362+
{
363+
var auth = CreateFirebaseAuth();
364+
365+
var exception = await Assert.ThrowsAsync<ArgumentException>(
366+
() => auth.TenantManager.DeleteTenantAsync(tenantId));
367+
Assert.Equal("Tenant ID cannot be null or empty.", exception.Message);
368+
}
369+
370+
[Fact]
371+
public async Task DeleteTenantNotFoundError()
372+
{
373+
var handler = new MockMessageHandler()
374+
{
375+
StatusCode = HttpStatusCode.NotFound,
376+
Response = TenantNotFoundResponse,
377+
};
378+
var auth = CreateFirebaseAuth(handler);
379+
380+
var exception = await Assert.ThrowsAsync<FirebaseAuthException>(
381+
() => auth.TenantManager.DeleteTenantAsync("tenant1"));
382+
Assert.Equal(ErrorCode.NotFound, exception.ErrorCode);
383+
Assert.Equal(AuthErrorCode.TenantNotFound, exception.AuthErrorCode);
384+
Assert.Equal(
385+
"No tenant found for the given identifier (TENANT_NOT_FOUND).",
386+
exception.Message);
387+
Assert.NotNull(exception.HttpResponse);
388+
Assert.Null(exception.InnerException);
389+
}
390+
391+
[Fact]
392+
public async Task ListTenants()
393+
{
394+
var handler = new MockMessageHandler()
395+
{
396+
Response = ListTenantsResponses,
397+
};
398+
var auth = CreateFirebaseAuth(handler);
399+
var tenants = new List<Tenant>();
400+
401+
var pagedEnumerable = auth.TenantManager.ListTenantsAsync(null);
402+
var enumerator = pagedEnumerable.GetEnumerator();
403+
while (await enumerator.MoveNext())
404+
{
405+
tenants.Add(enumerator.Current);
406+
}
407+
408+
Assert.Equal(5, tenants.Count);
409+
Assert.All(tenants, AssertTenant);
410+
411+
Assert.Equal(2, handler.Requests.Count);
412+
var query = ExtractQueryParams(handler.Requests[0]);
413+
Assert.Single(query);
414+
Assert.Equal("100", query["pageSize"]);
415+
416+
query = ExtractQueryParams(handler.Requests[1]);
417+
Assert.Equal(2, query.Count);
418+
Assert.Equal("100", query["pageSize"]);
419+
Assert.Equal("token", query["pageToken"]);
420+
421+
Assert.All(handler.Requests, AssertClientVersionHeader);
422+
}
423+
424+
[Fact]
425+
public void ListTenantsForEach()
426+
{
427+
var handler = new MockMessageHandler()
428+
{
429+
Response = ListTenantsResponses,
430+
};
431+
var auth = CreateFirebaseAuth(handler);
432+
var tenants = new List<Tenant>();
433+
434+
var pagedEnumerable = auth.TenantManager.ListTenantsAsync(null);
435+
foreach (var tenant in pagedEnumerable.ToEnumerable())
436+
{
437+
tenants.Add(tenant);
438+
}
439+
440+
Assert.Equal(5, tenants.Count);
441+
Assert.All(tenants, AssertTenant);
442+
443+
Assert.Equal(2, handler.Requests.Count);
444+
var query = ExtractQueryParams(handler.Requests[0]);
445+
Assert.Single(query);
446+
Assert.Equal("100", query["pageSize"]);
447+
448+
query = ExtractQueryParams(handler.Requests[1]);
449+
Assert.Equal(2, query.Count);
450+
Assert.Equal("100", query["pageSize"]);
451+
Assert.Equal("token", query["pageToken"]);
452+
453+
Assert.All(handler.Requests, AssertClientVersionHeader);
454+
}
455+
456+
[Fact]
457+
public async Task ListTenantsByPages()
458+
{
459+
var handler = new MockMessageHandler()
460+
{
461+
Response = ListTenantsResponses,
462+
};
463+
var auth = CreateFirebaseAuth(handler);
464+
var tenants = new List<Tenant>();
465+
466+
// Read page 1.
467+
var pagedEnumerable = auth.TenantManager.ListTenantsAsync(null);
468+
var tenantsPage = await pagedEnumerable.ReadPageAsync(3);
469+
470+
Assert.Equal(3, tenantsPage.Count());
471+
Assert.Equal("token", tenantsPage.NextPageToken);
472+
473+
Assert.Single(handler.Requests);
474+
var query = ExtractQueryParams(handler.Requests[0]);
475+
Assert.Single(query);
476+
Assert.Equal("3", query["pageSize"]);
477+
tenants.AddRange(tenantsPage);
478+
479+
// Read page 2.
480+
pagedEnumerable = auth.TenantManager.ListTenantsAsync(new ListTenantsOptions()
481+
{
482+
PageToken = tenantsPage.NextPageToken,
483+
});
484+
tenantsPage = await pagedEnumerable.ReadPageAsync(3);
485+
486+
Assert.Equal(2, tenantsPage.Count());
487+
Assert.Null(tenantsPage.NextPageToken);
488+
489+
Assert.Equal(2, handler.Requests.Count);
490+
query = ExtractQueryParams(handler.Requests[1]);
491+
Assert.Equal(2, query.Count);
492+
Assert.Equal("3", query["pageSize"]);
493+
Assert.Equal("token", query["pageToken"]);
494+
tenants.AddRange(tenantsPage);
495+
496+
Assert.Equal(5, tenants.Count);
497+
Assert.All(tenants, AssertTenant);
498+
}
499+
500+
[Fact]
501+
public async Task ListTenantsAsRawResponses()
502+
{
503+
var handler = new MockMessageHandler()
504+
{
505+
Response = ListTenantsResponses,
506+
};
507+
var auth = CreateFirebaseAuth(handler);
508+
var tenants = new List<Tenant>();
509+
var tokens = new List<string>();
510+
511+
var pagedEnumerable = auth.TenantManager.ListTenantsAsync(null);
512+
var responses = pagedEnumerable.AsRawResponses().GetEnumerator();
513+
while (await responses.MoveNext())
514+
{
515+
tenants.AddRange(responses.Current.Tenants);
516+
tokens.Add(responses.Current.NextPageToken);
517+
Assert.Equal(tokens.Count, handler.Requests.Count);
518+
}
519+
520+
Assert.Equal(new List<string>() { "token", null }, tokens);
521+
Assert.Equal(5, tenants.Count);
522+
Assert.All(tenants, AssertTenant);
523+
524+
Assert.Equal(2, handler.Requests.Count);
525+
var query = ExtractQueryParams(handler.Requests[0]);
526+
Assert.Single(query);
527+
Assert.Equal("100", query["pageSize"]);
528+
529+
query = ExtractQueryParams(handler.Requests[1]);
530+
Assert.Equal(2, query.Count);
531+
Assert.Equal("100", query["pageSize"]);
532+
Assert.Equal("token", query["pageToken"]);
533+
}
534+
535+
[Fact]
536+
public void ListTenantsOptions()
537+
{
538+
var handler = new MockMessageHandler()
539+
{
540+
Response = ListTenantsResponses,
541+
};
542+
var auth = CreateFirebaseAuth(handler);
543+
var tenants = new List<Tenant>();
544+
var customOptions = new ListTenantsOptions()
545+
{
546+
PageSize = 3,
547+
PageToken = "custom-token",
548+
};
549+
550+
var pagedEnumerable = auth.TenantManager.ListTenantsAsync(customOptions);
551+
foreach (var tenant in pagedEnumerable.ToEnumerable())
552+
{
553+
tenants.Add(tenant);
554+
}
555+
556+
Assert.Equal(5, tenants.Count);
557+
Assert.All(tenants, AssertTenant);
558+
559+
Assert.Equal(2, handler.Requests.Count);
560+
var query = ExtractQueryParams(handler.Requests[0]);
561+
Assert.Equal(2, query.Count);
562+
Assert.Equal("3", query["pageSize"]);
563+
Assert.Equal("custom-token", query["pageToken"]);
564+
565+
query = ExtractQueryParams(handler.Requests[1]);
566+
Assert.Equal(2, query.Count);
567+
Assert.Equal("3", query["pageSize"]);
568+
Assert.Equal("token", query["pageToken"]);
569+
570+
Assert.All(handler.Requests, AssertClientVersionHeader);
571+
}
572+
573+
[Theory]
574+
[ClassData(typeof(TenantManagerTest.InvalidListOptions))]
575+
public void ListInvalidOptions(ListTenantsOptions options, string expected)
576+
{
577+
var handler = new MockMessageHandler()
578+
{
579+
Response = ListTenantsResponses,
580+
};
581+
var auth = CreateFirebaseAuth(handler);
582+
583+
var exception = Assert.Throws<ArgumentException>(
584+
() => auth.TenantManager.ListTenantsAsync(options));
585+
586+
Assert.Equal(expected, exception.Message);
587+
Assert.Empty(handler.Requests);
588+
}
589+
590+
[Fact]
591+
public async Task ListReadPageSizeTooLarge()
592+
{
593+
var handler = new MockMessageHandler()
594+
{
595+
Response = ListTenantsResponses,
596+
};
597+
var auth = CreateFirebaseAuth(handler);
598+
var pagedEnumerable = auth.TenantManager.ListTenantsAsync(null);
599+
600+
await Assert.ThrowsAsync<ArgumentException>(
601+
async () => await pagedEnumerable.ReadPageAsync(101));
602+
603+
Assert.Empty(handler.Requests);
604+
}
605+
321606
private static FirebaseAuth CreateFirebaseAuth(HttpMessageHandler handler = null)
322607
{
323608
var tenantManager = new TenantManager(new TenantManager.Args
@@ -344,5 +629,57 @@ private static void AssertClientVersionHeader(MockMessageHandler.IncomingRequest
344629
{
345630
Assert.Contains(ClientVersion, request.Headers.GetValues("X-Client-Version"));
346631
}
632+
633+
private static IDictionary<string, string> ExtractQueryParams(
634+
MockMessageHandler.IncomingRequest req)
635+
{
636+
return req.Url.Query.Substring(1).Split('&').ToDictionary(
637+
entry => entry.Split('=')[0], entry => entry.Split('=')[1]);
638+
}
639+
640+
public class InvalidListOptions : IEnumerable<object[]>
641+
{
642+
public IEnumerator<object[]> GetEnumerator()
643+
{
644+
// {
645+
// 1st element: InvalidInput,
646+
// 2nd element: ExpectedError,
647+
// }
648+
yield return new object[]
649+
{
650+
new ListTenantsOptions()
651+
{
652+
PageSize = 101,
653+
},
654+
"Page size must not exceed 100.",
655+
};
656+
yield return new object[]
657+
{
658+
new ListTenantsOptions()
659+
{
660+
PageSize = 0,
661+
},
662+
"Page size must be positive.",
663+
};
664+
yield return new object[]
665+
{
666+
new ListTenantsOptions()
667+
{
668+
PageSize = -1,
669+
},
670+
"Page size must be positive.",
671+
};
672+
yield return new object[]
673+
{
674+
new ListTenantsOptions()
675+
{
676+
PageToken = string.Empty,
677+
},
678+
"Page token must not be empty.",
679+
};
680+
}
681+
682+
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
683+
}
347684
}
348685
}

FirebaseAdmin/FirebaseAdmin/Auth/ListUsersRequest.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
using System.Threading.Tasks;
2222
using FirebaseAdmin.Util;
2323
using Google.Apis.Discovery;
24-
using Google.Apis.Http;
2524
using Google.Apis.Requests;
2625
using Google.Apis.Services;
2726

0 commit comments

Comments
 (0)