Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added endpoint to get list of Zoom Phone users #344

Merged
merged 4 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions Source/ZoomNet.UnitTests/Models/PhoneUserTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System.Text.Json;
using Shouldly;
using Xunit;
using ZoomNet.Json;
using ZoomNet.Models;

namespace ZoomNet.UnitTests.Models
{
public class PhoneUserTests
{
#region constants

internal const string PHONE_USER = @"{
""id"": ""NL3cEpSdRc-c2t8aLoZqiw"",
""phone_user_id"": ""u7pnC468TaS46OuNoEw6GA"",
""email"": ""test_phone_user@testapi.com"",
""name"": ""test phone user"",
""extension_id"": ""CcrEGgmeQem1uyJsuIRKwA"",
""extension_number"": 123,
""status"": ""activate"",
""calling_plans"": [
{
""type"": 600,
""name"": ""Delhi billing"",
""billing_account_id"": ""3WWAEiEjTj2IQuyDiKMd_A"",
""billing_account_name"": ""Delhi billing""
}
],
""phone_numbers"": [
{
""id"": ""---M1padRvSUtw7YihN7sA"",
""number"": ""14232058798""
}
],
""site"": {
""id"": ""8f71O6rWT8KFUGQmJIFAdQ"",
""name"": ""Test Site""
},
""department"": ""Test"",
""cost_center"": ""Cost Test Center""
}";

#endregion

#region tests

[Fact]
public void Parse_Json_PhoneUserTests()
{
// Arrange

// Act
var result = JsonSerializer.Deserialize<PhoneUser>(
PHONE_USER, JsonFormatter.SerializerOptions);

// Assert
result.Id.ShouldBe("NL3cEpSdRc-c2t8aLoZqiw");
result.PhoneUserId.ShouldBe("u7pnC468TaS46OuNoEw6GA");
result.Email.ShouldBe("test_phone_user@testapi.com");
result.Name.ShouldBe("test phone user");
result.ExtensionId.ShouldBe("CcrEGgmeQem1uyJsuIRKwA");
result.ExtensionNumber.ShouldBe(123);
result.Status.ShouldBe(PhoneCallUserStatus.Active);
result.CallingPlans.ShouldNotBeNull();
result.CallingPlans.Length.ShouldBe(1);
result.PhoneNumbers.ShouldNotBeNull();
result.PhoneNumbers.Length.ShouldBe(1);
result.Department.ShouldBe("Test");
result.CostCenter.ShouldBe("Cost Test Center");
result.Site.Id.ShouldBe("8f71O6rWT8KFUGQmJIFAdQ");
result.Site.Name.ShouldBe("Test Site");

var callingPlan = result.CallingPlans[0];
callingPlan.BillingAccountId.ShouldBe("3WWAEiEjTj2IQuyDiKMd_A");
callingPlan.BillingAccountName.ShouldBe("Delhi billing");

var phoneNumber = result.PhoneNumbers[0];
phoneNumber.PhoneNumberId.ShouldBe("---M1padRvSUtw7YihN7sA");
phoneNumber.PhoneNumber.ShouldBe("14232058798");
}

#endregion
}
}
114 changes: 114 additions & 0 deletions Source/ZoomNet.UnitTests/Resources/PhoneUserTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using RichardSzalay.MockHttp;
using Shouldly;
using Xunit;
using ZoomNet.Resources;

namespace ZoomNet.UnitTests.Resources
{
public class PhoneUserTests
{
#region constants

internal const string PHONE_USERS_PAGINATED_OBJECT = @"{
""next_page_token"": ""F2qwertyg5eIqRRgC2YMauur8ZHUaJqtS3i"",
""page_size"": 1,
""total_records"": 10,
""users"": [
{
""id"": ""NL3cEpSdRc-c2t8aLoZqiw"",
""phone_user_id"": ""u7pnC468TaS46OuNoEw6GA"",
""email"": ""test_phone_user@testapi.com"",
""name"": ""test phone user"",
""extension_id"": ""CcrEGgmeQem1uyJsuIRKwA"",
""extension_number"": 123,
""status"": ""activate"",
""calling_plans"": [
{
""type"": 600,
""name"": ""Delhi billing"",
""billing_account_id"": ""3WWAEiEjTj2IQuyDiKMd_A"",
""billing_account_name"": ""Delhi billing""
}
],
""phone_numbers"": [
{
""id"": ""---M1padRvSUtw7YihN7sA"",
""number"": ""14232058798""
}
],
""site"": {
""id"": ""8f71O6rWT8KFUGQmJIFAdQ"",
""name"": ""Test Site""
},
""department"": ""Test"",
""cost_center"": ""Cost Test Center""
}
]
}";

#endregion

#region tests

[Fact]
public async Task GetPhoneUsersPaginatedResponseTestsAsync()
{
// Arrange
var pageSize = 1;

var mockHttp = new MockHttpMessageHandler();
mockHttp
.Expect(
HttpMethod.Get,
Utils.GetZoomApiUri("phone/users"))
.Respond(
"application/json",
PHONE_USERS_PAGINATED_OBJECT);

var client = Utils.GetFluentClient(mockHttp);
var phone = new Phone(client);

// Act
var result = await phone
.ListPhoneUsersAsync(pageSize: pageSize)
.ConfigureAwait(true);

// Assert
mockHttp.VerifyNoOutstandingExpectation();
mockHttp.VerifyNoOutstandingRequest();
result.NextPageToken.ShouldNotBeNullOrEmpty();
result.PageSize.ShouldBe(1);
result.TotalRecords.ShouldBe(10);
result.Records.ShouldNotBeNull();
result.Records.Length.ShouldBe(1);
result.Records[0].Id.ShouldBe("NL3cEpSdRc-c2t8aLoZqiw");
result.Records[0].PhoneNumbers[0].PhoneNumberId.ShouldBe("---M1padRvSUtw7YihN7sA");
result.Records[0].PhoneNumbers[0].PhoneNumber.ShouldBe("14232058798");
}

[Theory]
[InlineData(0)]
[InlineData(101)]
public void InvalidPageSize_GetPhoneUsersPaginatedResponseTests(int pageSize)
{
// Arrange
var mockHttp = new MockHttpMessageHandler();

var client = Utils.GetFluentClient(mockHttp);
var phone = new Phone(client);

// Act and Assert
var exception = Assert.Throws<ArgumentOutOfRangeException>(() => phone
.ListPhoneUsersAsync(pageSize: pageSize)
.ConfigureAwait(true));

exception.ParamName.ShouldBe(nameof(pageSize));
exception.Message.ShouldBe($"Records per page must be between 1 and 100 (Parameter '{nameof(pageSize)}')");
}

#endregion
}
}
4 changes: 4 additions & 0 deletions Source/ZoomNet/Json/ZoomNetJsonSerializerContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ namespace ZoomNet.Json
[JsonSerializable(typeof(ZoomNet.Models.PhoneCallUserStatus))]
[JsonSerializable(typeof(ZoomNet.Models.PhoneNumber))]
[JsonSerializable(typeof(ZoomNet.Models.PhoneType))]
[JsonSerializable(typeof(ZoomNet.Models.PhoneUser))]
[JsonSerializable(typeof(ZoomNet.Models.PmiMeetingPasswordRequirementType))]
[JsonSerializable(typeof(ZoomNet.Models.Poll))]
[JsonSerializable(typeof(ZoomNet.Models.PollAnswer))]
Expand Down Expand Up @@ -208,6 +209,7 @@ namespace ZoomNet.Json
[JsonSerializable(typeof(ZoomNet.Models.ScreenshareDetails))]
[JsonSerializable(typeof(ZoomNet.Models.SecuritySettings))]
[JsonSerializable(typeof(ZoomNet.Models.SharingAndRecordingDetail))]
[JsonSerializable(typeof(ZoomNet.Models.Site))]
[JsonSerializable(typeof(ZoomNet.Models.SmsAttachment))]
[JsonSerializable(typeof(ZoomNet.Models.SmsAttachmentType))]
[JsonSerializable(typeof(ZoomNet.Models.SmsDirection))]
Expand Down Expand Up @@ -448,6 +450,7 @@ namespace ZoomNet.Json
[JsonSerializable(typeof(ZoomNet.Models.PhoneCallUserStatus[]))]
[JsonSerializable(typeof(ZoomNet.Models.PhoneNumber[]))]
[JsonSerializable(typeof(ZoomNet.Models.PhoneType[]))]
[JsonSerializable(typeof(ZoomNet.Models.PhoneUser[]))]
[JsonSerializable(typeof(ZoomNet.Models.PmiMeetingPasswordRequirementType[]))]
[JsonSerializable(typeof(ZoomNet.Models.Poll[]))]
[JsonSerializable(typeof(ZoomNet.Models.PollAnswer[]))]
Expand Down Expand Up @@ -504,6 +507,7 @@ namespace ZoomNet.Json
[JsonSerializable(typeof(ZoomNet.Models.ScreenshareDetails[]))]
[JsonSerializable(typeof(ZoomNet.Models.SecuritySettings[]))]
[JsonSerializable(typeof(ZoomNet.Models.SharingAndRecordingDetail[]))]
[JsonSerializable(typeof(ZoomNet.Models.Site[]))]
[JsonSerializable(typeof(ZoomNet.Models.SmsAttachment[]))]
[JsonSerializable(typeof(ZoomNet.Models.SmsAttachmentType[]))]
[JsonSerializable(typeof(ZoomNet.Models.SmsDirection[]))]
Expand Down
2 changes: 1 addition & 1 deletion Source/ZoomNet/Models/PhoneCallUserProfile.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Text.Json.Serialization;
using System.Text.Json.Serialization;

namespace ZoomNet.Models;

Expand Down
81 changes: 81 additions & 0 deletions Source/ZoomNet/Models/PhoneUser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System.Text.Json.Serialization;

namespace ZoomNet.Models;

/// <summary>
/// Zoom Phone user information.
/// </summary>
public class PhoneUser
{
/// <summary>
/// Gets or sets the calling plans.
/// </summary>
[JsonPropertyName("calling_plans")]
public CallingPlan[] CallingPlans { get; set; }

/// <summary>
/// Gets or sets the cost center.
/// </summary>
[JsonPropertyName("cost_center")]
public string CostCenter { get; set; }

/// <summary>
/// Gets or sets the department of the object.
/// </summary>
[JsonPropertyName("department")]
public string Department { get; set; }

/// <summary>
/// Gets or sets the email address.
/// </summary>
[JsonPropertyName("email")]
public string Email { get; set; }

/// <summary>
/// Gets or sets the extension ID.
/// </summary>
[JsonPropertyName("extension_id")]
public string ExtensionId { get; set; }

/// <summary>
/// Gets or sets the extension number.
/// </summary>
[JsonPropertyName("extension_number")]
public int ExtensionNumber { get; set; }

/// <summary>
/// Gets or sets the Zoom user ID.
/// </summary>
[JsonPropertyName("id")]
public string Id { get; set; }

/// <summary>
/// Gets or sets the Zoom user name.
/// </summary>
[JsonPropertyName("name")]
public string Name { get; set; }

/// <summary>
/// Gets or sets the phone numbers.
/// </summary>
[JsonPropertyName("phone_numbers")]
public PhoneCallPhoneNumber[] PhoneNumbers { get; set; }

/// <summary>
/// Gets or sets the Zoom phone user id.
/// </summary>
[JsonPropertyName("phone_user_id")]
public string PhoneUserId { get; set; }

/// <summary>
/// Gets or sets a site.
/// </summary>
[JsonPropertyName("site")]
public Site Site { get; set; }

/// <summary>
/// Gets or sets the status of the user.
/// </summary>
[JsonPropertyName("status")]
public PhoneCallUserStatus Status { get; set; }
}
21 changes: 21 additions & 0 deletions Source/ZoomNet/Models/Site.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Text.Json.Serialization;

namespace ZoomNet.Models;

/// <summary>
/// Site information to which a user belongs to.
/// </summary>
public class Site
{
/// <summary>
/// Gets or sets the Id.
/// </summary>
[JsonPropertyName("id")]
public string Id { get; set; }

/// <summary>
/// Gets or sets the name.
/// </summary>
[JsonPropertyName("name")]
public string Name { get; set; }
}
30 changes: 30 additions & 0 deletions Source/ZoomNet/Resources/IPhone.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,36 @@ Task<PhoneCallRecordingTranscript> GetRecordingTranscriptAsync(
Task<PhoneCallUserProfile> GetPhoneCallUserProfileAsync(
string userId, CancellationToken cancellationToken = default);

/// <summary>
/// Retrieves a list of all of an account's users who are assigned a Zoom Phone license.
/// </summary>
/// <param name="pageSize">The number of records returned from a single API call. Default is 30.</param>
/// <param name="nextPageToken">
/// The next page token paginates through a large set of results.
/// A next page token is returned whenever the set of available results exceeds the current page size.
/// The expiration period for this token is 15 minutes.
/// </param>
/// <param name="siteId">The unique identifier of the site.</param>
/// <param name="callingType">The type of calling plan.</param>
/// <param name="status">The status of the Zoom Phone user.</param>
/// <param name="department">The department where the user belongs.</param>
/// <param name="costCenter">The cost center where the user belongs.</param>
/// <param name="keyword">The partial string of user's name, extension number, or phone number.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
/// <returns>
/// A task representing the asynchronous operation. The task result contains an array of Zoom Phone users in type of <see cref="PhoneUser"/>.
/// </returns>
Task<PaginatedResponseWithToken<PhoneUser>> ListPhoneUsersAsync(
int pageSize = 30,
string nextPageToken = null,
string siteId = null,
int? callingType = null,
PhoneCallUserStatus? status = null,
string department = null,
string costCenter = null,
string keyword = null,
CancellationToken cancellationToken = default);

#endregion
}
}
Loading