Skip to content

Commit

Permalink
Merge branch 'release/0.79.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Jericho committed Jul 23, 2024
2 parents bbf26d3 + 74d9b57 commit a1a8a67
Show file tree
Hide file tree
Showing 12 changed files with 124 additions and 38 deletions.
2 changes: 2 additions & 0 deletions Source/ZoomNet.UnitTests/Json/ParticipantDeviceConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ public void Write_multiple()
[InlineData("H.323/SIP", ParticipantDevice.Sip)]
[InlineData("Windows", ParticipantDevice.Windows)]
[InlineData("WIN", ParticipantDevice.Windows)]
[InlineData("win 11", ParticipantDevice.Windows)]
[InlineData("Zoom Rooms", ParticipantDevice.ZoomRoom)]
[InlineData("win 10+ 17763", ParticipantDevice.Windows)]
public void Read_single(string value, ParticipantDevice expectedValue)
{
// Arrange
Expand Down
37 changes: 37 additions & 0 deletions Source/ZoomNet.UnitTests/Resources/CloudRecordingsTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using RichardSzalay.MockHttp;
using Shouldly;
using System;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading;
Expand Down Expand Up @@ -531,5 +532,41 @@ public async Task GetRecordingsForUserAsync()
result.Records.ShouldNotBeNull();
result.Records.Length.ShouldBe(10);
}

/// <summary>
/// This unit test simulates a scenario where we attempt to download a file but our oAuth token has expired.
/// In this situation, we expect the token to be refreshed and the download request to be reissued.
/// See <a href="https://github.com/Jericho/ZoomNet/issues/348">Issue 348</a> for more details.
/// </summary>
[Fact]
public async Task DownloadFileAsync_with_expired_token()
{
// Arrange
var downloadUrl = "http://dummywebsite.com/dummyfile.txt";

var mockTokenHttp = new MockHttpMessageHandler();
mockTokenHttp // Issue a new token
.When(HttpMethod.Post, "https://api.zoom.us/oauth/token")
.Respond(HttpStatusCode.OK, "application/json", "{\"refresh_token\":\"new refresh token\",\"access_token\":\"new access token\"}");

var mockHttp = new MockHttpMessageHandler();
mockHttp // The first time the file is requested, we return "401 Unauthorized" to simulate an expired token.
.Expect(HttpMethod.Get, downloadUrl)
.Respond(HttpStatusCode.Unauthorized, new StringContent("{\"message\":\"access token is expired\"}"));
mockHttp // The second time the file is requested, we return "200 OK" with the file content.
.Expect(HttpMethod.Get, downloadUrl)
.Respond(HttpStatusCode.OK, new StringContent("This is the content of the file"));

var client = Utils.GetFluentClient(mockHttp, mockTokenHttp);
var recordings = new CloudRecordings(client);

// Act
var result = await recordings.DownloadFileAsync(downloadUrl, CancellationToken.None).ConfigureAwait(true);

// Assert
mockHttp.VerifyNoOutstandingExpectation();
mockHttp.VerifyNoOutstandingRequest();
result.ShouldNotBeNull();
}
}
}
38 changes: 38 additions & 0 deletions Source/ZoomNet/Extensions/Public.cs
Original file line number Diff line number Diff line change
Expand Up @@ -312,5 +312,43 @@ public static Task InviteParticipantAsync(this IMeetings meetingsResource, long
{
return meetingsResource.InviteParticipantsAsync(meetingId, new[] { emailAddress }, cancellationToken);
}

/// <summary>
/// Retrieve the details of a meeting.
/// </summary>
/// <param name="meetingResource">The meeting resource.</param>
/// <param name="meetingId">The meeting ID.</param>
/// <param name="occurrenceId">The meeting occurrence id.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// The <see cref="Meeting" />.
/// </returns>
/// <remarks>
/// Please note that when retrieving a recurring meeting, this method will omit previous occurrences.
/// Use <see cref="IMeetings.GetAsync(long, string, bool, CancellationToken)"/> if you want past occurrences to be included.
/// </remarks>
public static Task<Meeting> GetAsync(this IMeetings meetingResource, long meetingId, string occurrenceId = null, CancellationToken cancellationToken = default)
{
return meetingResource.GetAsync(meetingId, occurrenceId, false, cancellationToken);
}

/// <summary>
/// Retrieve the details of a webinar.
/// </summary>
/// <param name="webinarResource">The webinar resource.</param>
/// <param name="webinarId">The webinar ID.</param>
/// <param name="occurrenceId">The webinar occurrence id.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// The <see cref="Webinar" />.
/// </returns>
/// <remarks>
/// Please note that when retrieving a recurring meeting, this method will omit previous occurrences.
/// Use <see cref="IWebinars.GetAsync(long, string, bool, CancellationToken)"/> if you want past occurrences to be included.
/// </remarks>
public static Task<Webinar> GetAsync(this IWebinars webinarResource, long webinarId, string occurrenceId = null, CancellationToken cancellationToken = default)
{
return webinarResource.GetAsync(webinarId, occurrenceId, false, cancellationToken);
}
}
}
1 change: 1 addition & 0 deletions Source/ZoomNet/Json/ParticipantDeviceConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public override ParticipantDevice[] Read(ref Utf8JsonReader reader, Type typeToC
var stringValue = reader.GetString();
var items = stringValue
.Split(new[] { '+' })
.Where(item => !long.TryParse(item, out _)) // Filter out values like "17763" which is a Windows build number. See https://github.com/Jericho/ZoomNet/issues/354 for details
.Select(item => Convert(item))
.ToArray();

Expand Down
12 changes: 12 additions & 0 deletions Source/ZoomNet/Models/DashboardParticipant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ public class DashboardParticipant
[JsonPropertyName("device")]
public ParticipantDevice[] Devices { get; set; }

/// <summary>
/// Gets or sets the device operation system.
/// </summary>
[JsonPropertyName("os")]
public string OperatingSystem { get; set; }

/// <summary>
/// Gets or sets the device operation system version.
/// </summary>
[JsonPropertyName("os_version")]
public string OperatingSystemVersion { get; set; }

/// <summary>
/// Gets or sets the participant's IP address.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion Source/ZoomNet/Models/ParticipantDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public enum ParticipantDevice
/// <summary>
/// The participant joined via VoIP using a Windows device.
/// </summary>
[MultipleValuesEnumMember(DefaultValue = "Windows", OtherValues = new[] { "WIN" })]
[MultipleValuesEnumMember(DefaultValue = "Windows", OtherValues = new[] { "WIN", "win 10", "win 11" })]
Windows,

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions Source/ZoomNet/Resources/CloudRecordings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public Task<PaginatedResponseWithTokenAndDateRange<Recording>> GetRecordingsForU

return _client
.GetAsync($"users/{userId}/recordings")
.WithArgument("trash", queryTrash.ToString().ToLower())
.WithArgument("trash", queryTrash.ToString().ToLowerInvariant())
.WithArgument("from", from?.ToZoomFormat(dateOnly: true))
.WithArgument("to", to?.ToZoomFormat(dateOnly: true))
.WithArgument("page_size", recordsPerPage)
Expand Down Expand Up @@ -86,7 +86,7 @@ public Task<PaginatedResponseWithTokenAndDateRange<Recording>> GetRecordingsForU

return _client
.GetAsync($"users/{userId}/recordings")
.WithArgument("trash", queryTrash.ToString().ToLower())
.WithArgument("trash", queryTrash.ToString().ToLowerInvariant())
.WithArgument("from", from?.ToZoomFormat(dateOnly: true))
.WithArgument("to", to?.ToZoomFormat(dateOnly: true))
.WithArgument("page_size", recordsPerPage)
Expand Down
11 changes: 8 additions & 3 deletions Source/ZoomNet/Resources/IMeetings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public interface IMeetings
/// An array of <see cref="MeetingSummary">meeting summaries</see>.
/// </returns>
/// <remarks>
/// To obtain the full details about a given meeting you must invoke <see cref="Meetings.GetAsync(long, string, CancellationToken)"/>.
/// To obtain the full details about a given meeting you must invoke <see cref="Meetings.GetAsync(long, string, bool, CancellationToken)"/>.
/// </remarks>
[Obsolete("Zoom is in the process of deprecating the \"page number\" and \"page count\" fields.")]
Task<PaginatedResponse<MeetingSummary>> GetAllAsync(string userId, MeetingListType type = MeetingListType.Scheduled, int recordsPerPage = 30, int page = 1, CancellationToken cancellationToken = default);
Expand All @@ -43,7 +43,7 @@ public interface IMeetings
/// An array of <see cref="MeetingSummary">meeting summaries</see>.
/// </returns>
/// <remarks>
/// To obtain the full details about a given meeting you must invoke <see cref="Meetings.GetAsync(long, string, CancellationToken)"/>.
/// To obtain the full details about a given meeting you must invoke <see cref="Meetings.GetAsync(long, string, bool, CancellationToken)"/>.
/// </remarks>
Task<PaginatedResponseWithToken<MeetingSummary>> GetAllAsync(string userId, MeetingListType type = MeetingListType.Scheduled, int recordsPerPage = 30, string pagingToken = null, CancellationToken cancellationToken = default);

Expand Down Expand Up @@ -165,11 +165,16 @@ public interface IMeetings
/// </summary>
/// <param name="meetingId">The meeting ID.</param>
/// <param name="occurrenceId">The meeting occurrence id.</param>
/// <param name="includePreviousOccurrences">Set this parameter to true to view meeting details of all previous occurrences of a recurring meeting.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// The <see cref="Meeting" />.
/// </returns>
Task<Meeting> GetAsync(long meetingId, string occurrenceId = null, CancellationToken cancellationToken = default);
/// <remarks>
/// Please note that 'includePreviousOccurences' is applicable only when fetching a recurring meeting.
/// It will be ignored if you are fetching any other type of meeting such as scheduled, personal or instant for example.
/// </remarks>
Task<Meeting> GetAsync(long meetingId, string occurrenceId = null, bool includePreviousOccurrences = false, CancellationToken cancellationToken = default);

/// <summary>
/// Delete a meeting.
Expand Down
11 changes: 8 additions & 3 deletions Source/ZoomNet/Resources/IWebinars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public interface IWebinars
/// An array of <see cref="WebinarSummary">webinar summaries</see>.
/// </returns>
/// <remarks>
/// To obtain the full details about a given webinar you must invoke <see cref="Webinars.GetAsync(long, string, CancellationToken)"/>.
/// To obtain the full details about a given webinar you must invoke <see cref="Webinars.GetAsync(long, string, bool, CancellationToken)"/>.
/// </remarks>
[Obsolete("Zoom is in the process of deprecating the \"page number\" and \"page count\" fields.")]
Task<PaginatedResponse<WebinarSummary>> GetAllAsync(string userId, int recordsPerPage = 30, int page = 1, CancellationToken cancellationToken = default);
Expand All @@ -41,7 +41,7 @@ public interface IWebinars
/// An array of <see cref="WebinarSummary">webinar summaries</see>.
/// </returns>
/// <remarks>
/// To obtain the full details about a given webinar you must invoke <see cref="Webinars.GetAsync(long, string, CancellationToken)"/>.
/// To obtain the full details about a given webinar you must invoke <see cref="Webinars.GetAsync(long, string, bool, CancellationToken)"/>.
/// </remarks>
Task<PaginatedResponseWithToken<WebinarSummary>> GetAllAsync(string userId, int recordsPerPage = 30, string pagingToken = null, CancellationToken cancellationToken = default);

Expand Down Expand Up @@ -144,11 +144,16 @@ public interface IWebinars
/// </summary>
/// <param name="webinarId">The webinar ID.</param>
/// <param name="occurrenceId">The webinar occurrence id.</param>
/// <param name="includePreviousOccurrences">Set this parameter to true to view meeting details of all previous occurrences of a recurring meeting.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// The <see cref="Webinar" />.
/// </returns>
Task<Webinar> GetAsync(long webinarId, string occurrenceId = null, CancellationToken cancellationToken = default);
/// <remarks>
/// Please note that 'includePreviousOccurences' is applicable only when fetching a recurring webinar.
/// It will be ignored if you are fetching any other type of meeting such as scheduled, personal or instant for example.
/// </remarks>
Task<Webinar> GetAsync(long webinarId, string occurrenceId = null, bool includePreviousOccurrences = false, CancellationToken cancellationToken = default);

/// <summary>
/// Delete a webinar.
Expand Down
17 changes: 5 additions & 12 deletions Source/ZoomNet/Resources/Meetings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,20 +214,13 @@ public Task<RecurringMeeting> CreateRecurringMeetingAsync(
.AsObject<RecurringMeeting>();
}

/// <summary>
/// Retrieve the details of a meeting.
/// </summary>
/// <param name="meetingId">The meeting ID.</param>
/// <param name="occurrenceId">The meeting occurrence id.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// The <see cref="Meeting" />.
/// </returns>
public Task<Meeting> GetAsync(long meetingId, string occurrenceId = null, CancellationToken cancellationToken = default)
/// <inheritdoc/>
public Task<Meeting> GetAsync(long meetingId, string occurrenceId = null, bool includePreviousOccurrences = false, CancellationToken cancellationToken = default)
{
return _client
.GetAsync($"meetings/{meetingId}")
.WithArgument("occurrence_id", occurrenceId)
.WithArgument("show_previous_occurrences", includePreviousOccurrences.ToString().ToLowerInvariant())
.WithCancellationToken(cancellationToken)
.AsObject<Meeting>();
}
Expand Down Expand Up @@ -361,8 +354,8 @@ public Task DeleteAsync(long meetingId, string occurrenceId = null, bool notifyH
return _client
.DeleteAsync($"meetings/{meetingId}")
.WithArgument("occurrence_id", occurrenceId)
.WithArgument("schedule_for_reminder", notifyHost.ToString().ToLower())
.WithArgument("cancel_meeting_reminder", notifyRegistrants.ToString().ToLower())
.WithArgument("schedule_for_reminder", notifyHost.ToString().ToLowerInvariant())
.WithArgument("cancel_meeting_reminder", notifyRegistrants.ToString().ToLowerInvariant())
.WithCancellationToken(cancellationToken)
.AsMessage();
}
Expand Down
12 changes: 6 additions & 6 deletions Source/ZoomNet/Resources/Users.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,9 @@ public Task DeleteAsync(string userId, string transferEmail, bool transferMeetin
.DeleteAsync($"users/{userId}")
.WithArgument("action", "delete")
.WithArgument("transfer_email", transferEmail)
.WithArgument("transfer_meetings", transferMeetings.ToString().ToLower())
.WithArgument("transfer_webinars", transferWebinars.ToString().ToLower())
.WithArgument("transfer_recordings", transferRecordings.ToString().ToLower())
.WithArgument("transfer_meetings", transferMeetings.ToString().ToLowerInvariant())
.WithArgument("transfer_webinars", transferWebinars.ToString().ToLowerInvariant())
.WithArgument("transfer_recordings", transferRecordings.ToString().ToLowerInvariant())
.WithCancellationToken(cancellationToken)
.AsMessage();
}
Expand All @@ -217,9 +217,9 @@ public Task DisassociateAsync(string userId, string transferEmail, bool transfer
.DeleteAsync($"users/{userId}")
.WithArgument("action", "disassociate")
.WithArgument("transfer_email", transferEmail)
.WithArgument("transfer_meetings", transferMeetings.ToString().ToLower())
.WithArgument("transfer_webinars", transferWebinars.ToString().ToLower())
.WithArgument("transfer_recordings", transferRecordings.ToString().ToLower())
.WithArgument("transfer_meetings", transferMeetings.ToString().ToLowerInvariant())
.WithArgument("transfer_webinars", transferWebinars.ToString().ToLowerInvariant())
.WithArgument("transfer_recordings", transferRecordings.ToString().ToLowerInvariant())
.WithCancellationToken(cancellationToken)
.AsMessage();
}
Expand Down
15 changes: 4 additions & 11 deletions Source/ZoomNet/Resources/Webinars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,20 +257,13 @@ public Task UpdateRecurringWebinarAsync(long webinarId, string topic = null, str
.AsMessage();
}

/// <summary>
/// Retrieve the details of a webinar.
/// </summary>
/// <param name="webinarId">The webinar ID.</param>
/// <param name="occurrenceId">The webinar occurrence id.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// The <see cref="Webinar" />.
/// </returns>
public Task<Webinar> GetAsync(long webinarId, string occurrenceId = null, CancellationToken cancellationToken = default)
/// <inheritdoc/>
public Task<Webinar> GetAsync(long webinarId, string occurrenceId = null, bool includePreviousOccurrences = false, CancellationToken cancellationToken = default)
{
return _client
.GetAsync($"webinars/{webinarId}")
.WithArgument("occurrence_id", occurrenceId)
.WithArgument("show_previous_occurrences", includePreviousOccurrences.ToString().ToLowerInvariant())
.WithCancellationToken(cancellationToken)
.AsObject<Webinar>();
}
Expand All @@ -290,7 +283,7 @@ public Task DeleteAsync(long webinarId, string occurrenceId = null, bool sendNot
return _client
.DeleteAsync($"webinars/{webinarId}")
.WithArgument("occurrence_id", occurrenceId)
.WithArgument("cancel_webinar_reminder", sendNotification.ToString().ToLower())
.WithArgument("cancel_webinar_reminder", sendNotification.ToString().ToLowerInvariant())
.WithCancellationToken(cancellationToken)
.AsMessage();
}
Expand Down

0 comments on commit a1a8a67

Please sign in to comment.