Skip to content

Commit

Permalink
Merge branch 'release/0.29.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Jericho committed May 12, 2021
2 parents a61bf19 + bcb7159 commit 6f02dca
Show file tree
Hide file tree
Showing 69 changed files with 2,257 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<ItemGroup>
<PackageReference Include="Logzio.DotNet.NLog" Version="1.0.10" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
<PackageReference Include="NLog.Extensions.Logging" Version="1.7.1" />
<PackageReference Include="NLog.Extensions.Logging" Version="1.7.2" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 0 additions & 2 deletions Source/ZoomNet.UnitTests/Resources/CloudRecordingsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ public class CloudRecordingsTests
{
#region FIELDS

private const string ENDPOINT = "api_keys";

private const string SINGLE_CLOUD_RECORDING_JSON = @"{
'uuid': 'ODfDKShNRqKkXbGD09Sk4A==',
'id': 94488262913,
Expand Down
243 changes: 243 additions & 0 deletions Source/ZoomNet.UnitTests/WebhookParserTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
using Shouldly;
using System;
using Xunit;
using ZoomNet.Models;
using ZoomNet.Models.Webhooks;

namespace ZoomNet.UnitTests
{
public class WebhookParserTests
{
#region FIELDS

private const string MEETING_CREATED_WEBHOOK = @"
{
'event': 'meeting.created',
'payload': {
'account_id': 'VjZoEArIT5y-HlWxkV-tVA',
'operator': '[email protected]',
'operator_id': '8lzIwvZTSOqjndWPbPqzuA',
'object': {
'uuid': '5yDZGNlQSV6qOjg4NxajHQ==',
'id': 98884753832,
'host_id': '8lzIwvZTSOqjndWPbPqzuA',
'topic': 'ZoomNet Unit Testing: instant meeting',
'type': 1,
'duration': 60,
'timezone': 'America/New_York',
'join_url': 'https://zoom.us/j/98884753832?pwd=c21EQzg0SXY2dlNTOFF2TENpSm1aQT09',
'password': 'PaSsWoRd',
'settings': {
'use_pmi': false,
'alternative_hosts': ''
}
}
},
'event_ts': 1617628462392
}";

private const string MEETING_DELETED_WEBHOOK = @"
{
'event': 'meeting.deleted',
'payload': {
'account_id': 'VjZoEArIT5y-HlWxkV-tVA',
'operator': '[email protected]',
'operator_id': '8lzIwvZTSOqjndWPbPqzuA',
'object': {
'uuid': '5yDZGNlQSV6qOjg4NxajHQ==',
'id': 98884753832,
'host_id': '8lzIwvZTSOqjndWPbPqzuA',
'topic': 'ZoomNet Unit Testing: instant meeting',
'type': 1,
'duration': 60,
'timezone': 'America/New_York'
}
},
'event_ts': 1617628462764
}";

private const string MEETING_UPDATED_WEBHOOK = @"
{
'event': 'meeting.updated',
'payload': {
'account_id': 'VjZoEArIT5y-HlWxkV-tVA',
'operator': '[email protected]',
'operator_id': '8lzIwvZTSOqjndWPbPqzuA',
'object': {
'id': 94890226305,
'topic': 'ZoomNet Unit Testing: UPDATED scheduled meeting',
'settings': { 'audio': 'voip' }
},
'old_object': {
'id': 94890226305,
'topic': 'ZoomNet Unit Testing: scheduled meeting',
'settings': { 'audio': 'telephony' }
},
'time_stamp': 1617628464664
},
'event_ts': 1617628464664
}";

private const string MEETING_STARTED_WEBHOOK = @"
{
'event':'meeting.started',
'payload': {
'account_id':'VjZoEArIT5y-HlWxkV-tVA',
'object': {
'duration':0,
'start_time':'2021-04-21T14:49:04Z',
'timezone':'America/New_York',
'topic':'My Personal Meeting Room',
'id':'3479130610',
'type':4,
'uuid':'mOG8pEaFQqeDm6Vd/3xopA==',
'host_id':'8lzIwvZTSOqjndWPbPqzuA'
}
},
'event_ts':1619016544371
}";

private const string MEETING_SHARING_STARTED_WEBHOOK = @"
{
'event': 'meeting.sharing_started',
'event_ts': 1234566789900,
'payload': {
'object': {
'duration': 60,
'start_time': '2019-07-16T17:14:39Z',
'timezone': 'America/Los_Angeles',
'topic': 'My Meeting',
'id': '6962400003',
'type': 2,
'uuid': '4118UHIiRCAAAtBlDkcVyw==',
'host_id': 'z8yCxTTTTSiw02QgCAp8uQ',
'participant': {
'id': 's0AAAASoSE1V8KIFOCYw',
'user_id': '16778000',
'user_name': 'Arya Arya',
'sharing_details': {
'link_source': 'in_meeting',
'file_link': '',
'source': 'dropbox',
'date_time': '2019-07-16T17:19:11Z',
'content': 'application'
}
}
},
'account_id': 'EPeQtiABC000VYxHMA'
}
}";

#endregion

[Fact]
public void MeetingCreated()
{
var parsedEvent = (MeetingCreatedEvent)new WebhookParser().ParseEventWebhook(MEETING_CREATED_WEBHOOK);

parsedEvent.EventType.ShouldBe(EventType.MeetingCreated);
parsedEvent.Operator.ShouldBe("[email protected]");
parsedEvent.OperatorId.ShouldBe("8lzIwvZTSOqjndWPbPqzuA");
parsedEvent.Meeting.ShouldNotBeNull();
parsedEvent.Meeting.Uuid.ShouldBe("5yDZGNlQSV6qOjg4NxajHQ==");
parsedEvent.Meeting.Id.ShouldBe(98884753832);
parsedEvent.Meeting.HostId.ShouldBe("8lzIwvZTSOqjndWPbPqzuA");
parsedEvent.Meeting.Topic.ShouldBe("ZoomNet Unit Testing: instant meeting");
parsedEvent.Meeting.Type.ShouldBe(Models.MeetingType.Instant);
parsedEvent.Meeting.JoinUrl.ShouldBe("https://zoom.us/j/98884753832?pwd=c21EQzg0SXY2dlNTOFF2TENpSm1aQT09");
parsedEvent.Meeting.Password.ShouldBe("PaSsWoRd");
parsedEvent.Meeting.Settings.ShouldNotBeNull();
parsedEvent.Meeting.Settings.UsePmi.HasValue.ShouldBeTrue();
parsedEvent.Meeting.Settings.UsePmi.Value.ShouldBeFalse();
parsedEvent.Meeting.Settings.AlternativeHosts.ShouldBeEmpty();
}

[Fact]
public void MeetingDeleted()
{
var parsedEvent = (MeetingDeletedEvent)new WebhookParser().ParseEventWebhook(MEETING_DELETED_WEBHOOK);

parsedEvent.EventType.ShouldBe(EventType.MeetingDeleted);
parsedEvent.Operator.ShouldBe("[email protected]");
parsedEvent.OperatorId.ShouldBe("8lzIwvZTSOqjndWPbPqzuA");
parsedEvent.Meeting.ShouldNotBeNull();
parsedEvent.Meeting.Uuid.ShouldBe("5yDZGNlQSV6qOjg4NxajHQ==");
parsedEvent.Meeting.Id.ShouldBe(98884753832);
parsedEvent.Meeting.HostId.ShouldBe("8lzIwvZTSOqjndWPbPqzuA");
parsedEvent.Meeting.Topic.ShouldBe("ZoomNet Unit Testing: instant meeting");
parsedEvent.Meeting.Type.ShouldBe(Models.MeetingType.Instant);
parsedEvent.Meeting.JoinUrl.ShouldBeNull();
parsedEvent.Meeting.Password.ShouldBeNull();
parsedEvent.Meeting.Settings.ShouldBeNull();
}

[Fact]
public void MeetingUpdated()
{
var parsedEvent = (MeetingUpdatedEvent)new WebhookParser().ParseEventWebhook(MEETING_UPDATED_WEBHOOK);

parsedEvent.EventType.ShouldBe(EventType.MeetingUpdated);
parsedEvent.Operator.ShouldBe("[email protected]");
parsedEvent.OperatorId.ShouldBe("8lzIwvZTSOqjndWPbPqzuA");
parsedEvent.ModifiedFields.ShouldNotBeNull();
parsedEvent.ModifiedFields.Length.ShouldBe(3);
parsedEvent.ModifiedFields[0].FieldName.ShouldBe("id");
parsedEvent.ModifiedFields[0].OldValue.ShouldBe(94890226305);
parsedEvent.ModifiedFields[0].NewValue.ShouldBe(94890226305);
parsedEvent.ModifiedFields[1].FieldName.ShouldBe("topic");
parsedEvent.ModifiedFields[1].OldValue.ShouldBe("ZoomNet Unit Testing: scheduled meeting");
parsedEvent.ModifiedFields[1].NewValue.ShouldBe("ZoomNet Unit Testing: UPDATED scheduled meeting");
parsedEvent.ModifiedFields[2].FieldName.ShouldBe("settings");
}

[Fact]
public void MeetingStarted()
{
var parsedEvent = (MeetingStartedEvent)new WebhookParser().ParseEventWebhook(MEETING_STARTED_WEBHOOK);

parsedEvent.EventType.ShouldBe(EventType.MeetingStarted);
parsedEvent.AccountId.ShouldBe("VjZoEArIT5y-HlWxkV-tVA");
parsedEvent.Timestamp.ShouldBe(new DateTime(2021, 4, 21, 14, 49, 4, 371, DateTimeKind.Utc));

parsedEvent.Meeting.GetType().ShouldBe(typeof(InstantMeeting));
var parsedMeeting = (InstantMeeting)parsedEvent.Meeting;
parsedMeeting.Id.ShouldBe(3479130610);
parsedMeeting.Topic.ShouldBe("My Personal Meeting Room");
parsedMeeting.Uuid.ShouldBe("mOG8pEaFQqeDm6Vd/3xopA==");
parsedMeeting.HostId.ShouldBe("8lzIwvZTSOqjndWPbPqzuA");
}

[Fact]
public void MeetingSharingStarted()
{
var parsedEvent = (MeetingSharingStartedEvent)new WebhookParser().ParseEventWebhook(MEETING_SHARING_STARTED_WEBHOOK);

parsedEvent.EventType.ShouldBe(EventType.MeetingSharingStarted);
parsedEvent.AccountId.ShouldBe("EPeQtiABC000VYxHMA");
parsedEvent.Timestamp.ShouldBe(new DateTime(2009, 2, 13, 23, 13, 9, 900, DateTimeKind.Utc));

parsedEvent.Participant.ShouldNotBeNull();
parsedEvent.Participant.DisplayName.ShouldBe("Arya Arya");
parsedEvent.Participant.Email.ShouldBeNullOrEmpty();
parsedEvent.Participant.ParticipantId.ShouldBe("16778000");
parsedEvent.Participant.UserId.ShouldBe("s0AAAASoSE1V8KIFOCYw");

parsedEvent.ScreenshareDetails.ShouldNotBeNull();
parsedEvent.ScreenshareDetails.ContentType.ShouldBe(ScreenshareContentType.Application);
parsedEvent.ScreenshareDetails.Date.ShouldBe(new DateTime(2019, 7, 16, 17, 19, 11, 0, DateTimeKind.Utc));
parsedEvent.ScreenshareDetails.SharingMethod.ShouldBe("in_meeting");
parsedEvent.ScreenshareDetails.Source.ShouldBe("dropbox");
parsedEvent.ScreenshareDetails.Link.ShouldBe("");

parsedEvent.Meeting.GetType().ShouldBe(typeof(ScheduledMeeting));
var parsedMeeting = (ScheduledMeeting)parsedEvent.Meeting;
parsedMeeting.Id.ShouldBe(6962400003);
parsedMeeting.Topic.ShouldBe("My Meeting");
parsedMeeting.Uuid.ShouldBe("4118UHIiRCAAAtBlDkcVyw==");
parsedMeeting.HostId.ShouldBe("z8yCxTTTTSiw02QgCAp8uQ");
parsedMeeting.StartTime.ShouldBe(new DateTime(2019, 7, 16, 17, 14, 39, 0, DateTimeKind.Utc));
parsedMeeting.Duration.ShouldBe(60);
parsedMeeting.Timezone.ShouldBe("America/Los_Angeles");
}
}
}
2 changes: 1 addition & 1 deletion Source/ZoomNet.UnitTests/ZoomNet.UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="RichardSzalay.MockHttp" Version="6.0.0" />
<PackageReference Include="Shouldly" Version="4.0.3" />
Expand Down
55 changes: 43 additions & 12 deletions Source/ZoomNet/Extensions/Internal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,45 @@ namespace ZoomNet
/// </summary>
internal static class Internal
{
internal enum UnixTimePrecision
{
Seconds = 0,
Milliseconds = 1
}

private static readonly DateTime EPOCH = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

/// <summary>
/// Converts a 'unix time' (which is expressed as the number of seconds since midnight on
/// January 1st 1970) to a .Net <see cref="DateTime" />.
/// Converts a 'unix time' (which is expressed as the number of seconds/milliseconds since
/// midnight on January 1st 1970) to a .Net <see cref="DateTime" />.
/// </summary>
/// <param name="unixTime">The unix time.</param>
/// <param name="precision">The desired precision.</param>
/// <returns>
/// The <see cref="DateTime" />.
/// </returns>
internal static DateTime FromUnixTime(this long unixTime)
internal static DateTime FromUnixTime(this long unixTime, UnixTimePrecision precision = UnixTimePrecision.Seconds)
{
return EPOCH.AddSeconds(unixTime);
if (precision == UnixTimePrecision.Seconds) return EPOCH.AddSeconds(unixTime);
if (precision == UnixTimePrecision.Milliseconds) return EPOCH.AddMilliseconds(unixTime);
throw new Exception($"Unknown precision: {precision}");
}

/// <summary>
/// Converts a .Net <see cref="DateTime" /> into a 'Unix time' (which is expressed as the number
/// of seconds since midnight on January 1st 1970).
/// of seconds/milliseconds since midnight on January 1st 1970).
/// </summary>
/// <param name="date">The date.</param>
/// <param name="precision">The desired precision.</param>
/// <returns>
/// The numer of seconds since midnight on January 1st 1970.
/// The numer of seconds/milliseconds since midnight on January 1st 1970.
/// </returns>
internal static long ToUnixTime(this DateTime date)
internal static long ToUnixTime(this DateTime date, UnixTimePrecision precision = UnixTimePrecision.Seconds)
{
return Convert.ToInt64((date.ToUniversalTime() - EPOCH).TotalSeconds);
var diff = date.ToUniversalTime() - EPOCH;
if (precision == UnixTimePrecision.Seconds) return Convert.ToInt64(diff.TotalSeconds);
if (precision == UnixTimePrecision.Milliseconds) return Convert.ToInt64(diff.TotalMilliseconds);
throw new Exception($"Unknown precision: {precision}");
}

/// <summary>
Expand Down Expand Up @@ -472,16 +485,34 @@ internal static void AddDeepProperty(this JObject jsonObject, string propertyNam
}
}

internal static JToken GetProperty(this JToken item, string name, bool throwIfMissing = true)
{
var parts = name.Split('/');
var property = item[parts[0]];
if (property == null && throwIfMissing) throw new ArgumentException($"Unable to find '{name}'", nameof(name));

foreach (var part in parts.Skip(1))
{
property = property[part];
if (property == null && throwIfMissing) throw new ArgumentException($"Unable to find '{part}'", nameof(name));
}

return property;
}

internal static T GetPropertyValue<T>(this JToken item, string name, T defaultValue = default)
{
if (item[name] == null) return defaultValue;
return item[name].Value<T>();
var property = item.GetProperty(name);
if (property == null) return defaultValue;
return property.Value<T>();
}

internal static T GetPropertyValue<T>(this JToken item, string name, bool throwIfMissing = true)
{
if (item[name] == null && throwIfMissing) throw new ArgumentException($"The response does not contain a field called '{name}'", nameof(name));
return item.GetPropertyValue(name, default(T));
var property = item.GetProperty(name);
if (property == null && throwIfMissing) throw new ArgumentException($"Unable to find '{name}'", nameof(name));
if (property == null) return default;
return property.Value<T>();
}

internal static async Task<TResult[]> ForEachAsync<T, TResult>(this IEnumerable<T> items, Func<T, Task<TResult>> action, int maxDegreeOfParalellism)
Expand Down
19 changes: 19 additions & 0 deletions Source/ZoomNet/Extensions/Public.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using ZoomNet.Models;
using ZoomNet.Models.Webhooks;
using ZoomNet.Resources;

namespace ZoomNet
Expand Down Expand Up @@ -195,5 +197,22 @@ public static Task DeleteMessageToChannelAsync(this IChat chatResource, string m
{
return chatResource.DeleteMessageToChannelAsync(messageId, "me", channelId, cancellationToken);
}

/// <summary>
/// Parses the event webhook asynchronously.
/// </summary>
/// <param name="parser">The webhook parser.</param>
/// <param name="stream">The stream.</param>
/// <returns>An <see cref="Event" />.</returns>
public static async Task<Event> ParseEventWebhookAsync(IWebhookParser parser, Stream stream)
{
string requestBody;
using (var streamReader = new StreamReader(stream))
{
requestBody = await streamReader.ReadToEndAsync().ConfigureAwait(false);
}

return parser.ParseEventWebhook(requestBody);
}
}
}
Loading

0 comments on commit 6f02dca

Please sign in to comment.