From e9003f938a25e65cf4d850b44e4c8f06c0f4383f Mon Sep 17 00:00:00 2001 From: Jeremie Desautels Date: Sat, 30 Mar 2019 10:07:32 -0400 Subject: [PATCH 01/17] Webinars resource Regarding #6 --- Source/ZoomNet/IZoomClient.cs | 8 +++ Source/ZoomNet/Models/Webinar.cs | 85 +++++++++++++++++++++++++++ Source/ZoomNet/Models/WebinarType.cs | 23 ++++++++ Source/ZoomNet/Resources/IWebinars.cs | 27 +++++++++ Source/ZoomNet/Resources/Webinars.cs | 55 +++++++++++++++++ Source/ZoomNet/ZoomClient.cs | 9 +++ 6 files changed, 207 insertions(+) create mode 100644 Source/ZoomNet/Models/Webinar.cs create mode 100644 Source/ZoomNet/Models/WebinarType.cs create mode 100644 Source/ZoomNet/Resources/IWebinars.cs create mode 100644 Source/ZoomNet/Resources/Webinars.cs diff --git a/Source/ZoomNet/IZoomClient.cs b/Source/ZoomNet/IZoomClient.cs index 37ee5d80..172da59a 100644 --- a/Source/ZoomNet/IZoomClient.cs +++ b/Source/ZoomNet/IZoomClient.cs @@ -22,5 +22,13 @@ public interface IZoomClient /// The past meetings resource. /// IPastMeetings PastMeetings { get; } + + /// + /// Gets the resource which allows you to manage webinars. + /// + /// + /// The webinars resource. + /// + IWebinars Webinars { get; } } } diff --git a/Source/ZoomNet/Models/Webinar.cs b/Source/ZoomNet/Models/Webinar.cs new file mode 100644 index 00000000..45bb4ed8 --- /dev/null +++ b/Source/ZoomNet/Models/Webinar.cs @@ -0,0 +1,85 @@ +using Newtonsoft.Json; +using StrongGrid.Models; +using System; + +namespace ZoomNet.Models +{ + /// + /// A webinar. + /// + public abstract class Webinar + { + /// + /// Gets or sets the unique id. + /// + /// + /// The unique id. + /// + [JsonProperty("uuid", NullValueHandling = NullValueHandling.Ignore)] + public string Uuid { get; set; } + + /// + /// Gets or sets the webinar id, also known as the webinar number. + /// + /// + /// The id. + /// + [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)] + public long Id { get; set; } + + /// + /// Gets or sets the ID of the user who is set as the host of the webinar. + /// + /// + /// The user id. + /// + [JsonProperty("host_id", NullValueHandling = NullValueHandling.Ignore)] + public string HostId { get; set; } + + /// + /// Gets or sets the topic of the meeting. + /// + /// + /// The topic. + /// + [JsonProperty("topic", NullValueHandling = NullValueHandling.Ignore)] + public string Topic { get; set; } + + /// + /// Gets or sets the webinar type. + /// + /// The webinar type. + [JsonProperty(PropertyName = "type", NullValueHandling = NullValueHandling.Ignore)] + public WebinarType Type { get; set; } + + /// + /// Gets or sets the duration in minutes. + /// + /// The duration in minutes. + [JsonProperty(PropertyName = "duration", NullValueHandling = NullValueHandling.Ignore)] + public int Duration { get; set; } + + /// + /// Gets or sets the timezone. + /// For example, "America/Los_Angeles". + /// Please reference our timezone list for supported timezones and their formats. + /// + /// The webinar timezone. For example, "America/Los_Angeles". Please reference our timezone list for supported timezones and their formats. + [JsonProperty(PropertyName = "timezone", NullValueHandling = NullValueHandling.Ignore)] + public string Timezone { get; set; } + + /// + /// Gets or sets the date and time when the meeting was created. + /// + /// The meeting created time. + [JsonProperty(PropertyName = "created_at", NullValueHandling = NullValueHandling.Ignore)] + public DateTime CreatedOn { get; set; } + + /// + /// Gets or sets the URL to join the webinar. + /// + /// The join URL. + [JsonProperty(PropertyName = "join_url", NullValueHandling = NullValueHandling.Ignore)] + public string JoinUrl { get; set; } + } +} diff --git a/Source/ZoomNet/Models/WebinarType.cs b/Source/ZoomNet/Models/WebinarType.cs new file mode 100644 index 00000000..779513cc --- /dev/null +++ b/Source/ZoomNet/Models/WebinarType.cs @@ -0,0 +1,23 @@ +namespace StrongGrid.Models +{ + /// + /// Enumeration to indicate the type of webinar. + /// + public enum WebinarType + { + /// + /// Five. + /// + Five = 5, + + /// + /// Six. + /// + Six = 6, + + /// + /// Nine + /// + Nine = 9 + } +} diff --git a/Source/ZoomNet/Resources/IWebinars.cs b/Source/ZoomNet/Resources/IWebinars.cs new file mode 100644 index 00000000..a7304cd7 --- /dev/null +++ b/Source/ZoomNet/Resources/IWebinars.cs @@ -0,0 +1,27 @@ +using System.Threading; +using System.Threading.Tasks; +using ZoomNet.Models; + +namespace ZoomNet.Resources +{ + /// + /// Allows you to manage webinars. + /// + /// + /// See Zoom documentation for more information. + /// + public interface IWebinars + { + /// + /// Retrieve all webinars for a user. + /// + /// The user Id or email address. + /// The number of records returned within a single API call. + /// The current page number of returned records. + /// The cancellation token. + /// + /// An array of webinars. + /// + Task> GetAllAsync(string userId, int recordsPerPage = 30, int page = 1, CancellationToken cancellationToken = default(CancellationToken)); + } +} diff --git a/Source/ZoomNet/Resources/Webinars.cs b/Source/ZoomNet/Resources/Webinars.cs new file mode 100644 index 00000000..5e907734 --- /dev/null +++ b/Source/ZoomNet/Resources/Webinars.cs @@ -0,0 +1,55 @@ +using Pathoschild.Http.Client; +using System; +using System.Threading; +using System.Threading.Tasks; +using ZoomNet.Models; +using ZoomNet.Utilities; + +namespace ZoomNet.Resources +{ + /// + /// Allows you to manage webinars. + /// + /// + /// + /// See Zoom documentation for more information. + /// + public class Webinars : IWebinars + { + private readonly Pathoschild.Http.Client.IClient _client; + + /// + /// Initializes a new instance of the class. + /// + /// The HTTP client. + internal Webinars(Pathoschild.Http.Client.IClient client) + { + _client = client; + } + + /// + /// Retrieve all webinars for a user. + /// + /// The user Id or email address. + /// The number of records returned within a single API call. + /// The current page number of returned records. + /// The cancellation token. + /// + /// An array of . + /// + public Task> GetAllAsync(string userId, int recordsPerPage = 30, int page = 1, CancellationToken cancellationToken = default(CancellationToken)) + { + if (recordsPerPage < 1 || recordsPerPage > 300) + { + throw new ArgumentOutOfRangeException(nameof(recordsPerPage), "Records per page must be between 1 and 300"); + } + + return _client + .GetAsync($"users/{userId}/webinars") + .WithArgument("page_size", recordsPerPage) + .WithArgument("page", page) + .WithCancellationToken(cancellationToken) + .AsPaginatedResponse("webinars"); + } + } +} diff --git a/Source/ZoomNet/ZoomClient.cs b/Source/ZoomNet/ZoomClient.cs index 9b0cd96f..2152da41 100644 --- a/Source/ZoomNet/ZoomClient.cs +++ b/Source/ZoomNet/ZoomClient.cs @@ -71,6 +71,14 @@ public static string Version /// public IPastMeetings PastMeetings { get; private set; } + /// + /// Gets the resource which allows you to manage webinars. + /// + /// + /// The webinars resource. + /// + public IWebinars Webinars { get; private set; } + #endregion #region CTOR @@ -157,6 +165,7 @@ private ZoomClient(IConnectionInfo connectionInfo, HttpClient httpClient, bool d Meetings = new Meetings(_fluentClient); PastMeetings = new PastMeetings(_fluentClient); + Webinars = new Webinars(_fluentClient); } /// From 612ebcc0f2cd2cf8f465bc458ac66088c9105780 Mon Sep 17 00:00:00 2001 From: Jeremie Desautels Date: Sat, 30 Mar 2019 10:31:54 -0400 Subject: [PATCH 02/17] Define the types of webinars Regarding #6 --- Source/ZoomNet/Models/WebinarType.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/ZoomNet/Models/WebinarType.cs b/Source/ZoomNet/Models/WebinarType.cs index 779513cc..6dd6dd77 100644 --- a/Source/ZoomNet/Models/WebinarType.cs +++ b/Source/ZoomNet/Models/WebinarType.cs @@ -6,18 +6,18 @@ public enum WebinarType { /// - /// Five. + /// Regular webinar. /// - Five = 5, + Regular = 5, /// - /// Six. + /// Recurring webinar with no fixed time. /// - Six = 6, + RecurringNoFixedTime = 6, /// - /// Nine + /// Recurring webinar with fixed time. /// - Nine = 9 + RecurringFixedTime = 9 } } From e63da11c766ad2d3a9d4ac2a2a091b3f8c9a692a Mon Sep 17 00:00:00 2001 From: Jeremie Desautels Date: Wed, 5 Jun 2019 14:27:41 -0400 Subject: [PATCH 03/17] Fix "'default' expression can be simplified" --- Source/ZoomNet/Resources/IWebinars.cs | 4 ++-- Source/ZoomNet/Resources/Webinars.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/ZoomNet/Resources/IWebinars.cs b/Source/ZoomNet/Resources/IWebinars.cs index a7304cd7..1dfd884a 100644 --- a/Source/ZoomNet/Resources/IWebinars.cs +++ b/Source/ZoomNet/Resources/IWebinars.cs @@ -1,4 +1,4 @@ -using System.Threading; +using System.Threading; using System.Threading.Tasks; using ZoomNet.Models; @@ -22,6 +22,6 @@ public interface IWebinars /// /// An array of webinars. /// - Task> GetAllAsync(string userId, int recordsPerPage = 30, int page = 1, CancellationToken cancellationToken = default(CancellationToken)); + Task> GetAllAsync(string userId, int recordsPerPage = 30, int page = 1, CancellationToken cancellationToken = default); } } diff --git a/Source/ZoomNet/Resources/Webinars.cs b/Source/ZoomNet/Resources/Webinars.cs index 5e907734..93c2b0c6 100644 --- a/Source/ZoomNet/Resources/Webinars.cs +++ b/Source/ZoomNet/Resources/Webinars.cs @@ -1,4 +1,4 @@ -using Pathoschild.Http.Client; +using Pathoschild.Http.Client; using System; using System.Threading; using System.Threading.Tasks; @@ -37,7 +37,7 @@ internal Webinars(Pathoschild.Http.Client.IClient client) /// /// An array of . /// - public Task> GetAllAsync(string userId, int recordsPerPage = 30, int page = 1, CancellationToken cancellationToken = default(CancellationToken)) + public Task> GetAllAsync(string userId, int recordsPerPage = 30, int page = 1, CancellationToken cancellationToken = default) { if (recordsPerPage < 1 || recordsPerPage > 300) { From f718c1585f768bbbfdd33ab020c936212dbfbbf0 Mon Sep 17 00:00:00 2001 From: Jericho Date: Fri, 15 May 2020 16:21:18 -0400 Subject: [PATCH 04/17] Create webinar --- .../{MeetingAudioType.cs => AudioType.cs} | 4 +- Source/ZoomNet/Models/MeetingSettings.cs | 11 +- Source/ZoomNet/Models/RecordingType.cs | 31 ++++ Source/ZoomNet/Models/RecurringWebinar.cs | 23 +++ Source/ZoomNet/Models/WebinarSettings.cs | 151 ++++++++++++++++++ Source/ZoomNet/Resources/IWebinars.cs | 39 +++++ Source/ZoomNet/Resources/Webinars.cs | 83 ++++++++++ 7 files changed, 334 insertions(+), 8 deletions(-) rename Source/ZoomNet/Models/{MeetingAudioType.cs => AudioType.cs} (90%) create mode 100644 Source/ZoomNet/Models/RecordingType.cs create mode 100644 Source/ZoomNet/Models/RecurringWebinar.cs create mode 100644 Source/ZoomNet/Models/WebinarSettings.cs diff --git a/Source/ZoomNet/Models/MeetingAudioType.cs b/Source/ZoomNet/Models/AudioType.cs similarity index 90% rename from Source/ZoomNet/Models/MeetingAudioType.cs rename to Source/ZoomNet/Models/AudioType.cs index 13ba10ea..2de3ae83 100644 --- a/Source/ZoomNet/Models/MeetingAudioType.cs +++ b/Source/ZoomNet/Models/AudioType.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using Newtonsoft.Json; using Newtonsoft.Json.Converters; using System.Runtime.Serialization; @@ -8,7 +8,7 @@ namespace ZoomNet.Models /// Enumeration to indicate the type of audio available to attendees. /// [JsonConverter(typeof(StringEnumConverter))] - public enum MeetingAudioType + public enum AudioType { /// /// VOIP. diff --git a/Source/ZoomNet/Models/MeetingSettings.cs b/Source/ZoomNet/Models/MeetingSettings.cs index e48bef2e..e10e3d7e 100644 --- a/Source/ZoomNet/Models/MeetingSettings.cs +++ b/Source/ZoomNet/Models/MeetingSettings.cs @@ -1,5 +1,4 @@ -using Newtonsoft.Json; -using ZoomNet.Models; +using Newtonsoft.Json; namespace ZoomNet.Models { @@ -72,13 +71,13 @@ public class MeetingSettings /// Gets or sets the value indicating how participants can join the audio portion of the meeting. /// [JsonProperty(PropertyName = "audio")] - public MeetingAudioType? Audio { get; set; } + public AudioType? Audio { get; set; } /// - /// Gets or sets AutoRecording. + /// Gets or sets the value indicating if audio is recorded and if so, when the audio is saved. /// [JsonProperty(PropertyName = "auto_recording")] - public string AutoRecording { get; set; } + public RecordingType AutoRecording { get; set; } /// /// Gets or sets the value indicating that only signed-in users can join this meeting. @@ -129,7 +128,7 @@ public class MeetingSettings public string ContactName { get; set; } /// - /// Gets or sets the contat email for registration. + /// Gets or sets the contact email for registration. /// [JsonProperty(PropertyName = "contact_email")] public string ContactEmail { get; set; } diff --git a/Source/ZoomNet/Models/RecordingType.cs b/Source/ZoomNet/Models/RecordingType.cs new file mode 100644 index 00000000..7e55c767 --- /dev/null +++ b/Source/ZoomNet/Models/RecordingType.cs @@ -0,0 +1,31 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System.Runtime.Serialization; + +namespace ZoomNet.Models +{ + /// + /// Enumeration to indicate where the audio recording is saved. + /// + [JsonConverter(typeof(StringEnumConverter))] + public enum RecordingType + { + /// + /// Record on local. + /// + [EnumMember(Value = "local")] + OnLocal, + + /// + /// Record on cloud. + /// + [EnumMember(Value = "cloud")] + OnCloud, + + /// + /// Do not record. + /// + [EnumMember(Value = "none")] + Disabled + } +} diff --git a/Source/ZoomNet/Models/RecurringWebinar.cs b/Source/ZoomNet/Models/RecurringWebinar.cs new file mode 100644 index 00000000..7ea34a1a --- /dev/null +++ b/Source/ZoomNet/Models/RecurringWebinar.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; + +namespace ZoomNet.Models +{ + /// + /// A meeting. + /// + /// + public class RecurringWebinar : Webinar + { + /// + /// Gets or sets the occurrences. + /// + [JsonProperty(PropertyName = "occurrences", NullValueHandling = NullValueHandling.Ignore)] + public MeetingOccurrence[] Occurrences { get; set; } + + /// + /// Gets or sets the recurrence info. + /// + [JsonProperty(PropertyName = "recurrence", NullValueHandling = NullValueHandling.Ignore)] + public RecurrenceInfo RecurrenceInfo { get; set; } + } +} diff --git a/Source/ZoomNet/Models/WebinarSettings.cs b/Source/ZoomNet/Models/WebinarSettings.cs new file mode 100644 index 00000000..c1f2a2e7 --- /dev/null +++ b/Source/ZoomNet/Models/WebinarSettings.cs @@ -0,0 +1,151 @@ +using Newtonsoft.Json; + +namespace ZoomNet.Models +{ + /// + /// Webinar Settings. + /// + public class WebinarSettings + { + /// + /// Gets or sets the value indicating whether to start video when host joins the webinar. + /// + [JsonProperty(PropertyName = "host_video")] + public bool? StartVideoWhenHostJoins { get; set; } + + /// + /// Gets or sets the value indicating whether to start video when panelists join the webinar. + /// + [JsonProperty(PropertyName = "panelists_video")] + public bool? StartVideoWhenPanelistsJoin { get; set; } + + /// + /// Gets or sets the value indicating whether to enable practice session. + /// + [JsonProperty(PropertyName = "practice_session")] + public bool? EnablePracticeSession { get; set; } + + /// + /// Gets or sets the value indicating whether to enable HD video. + /// + [JsonProperty(PropertyName = "hd_video")] + public bool? EnableHighDefinitionVideo { get; set; } + + /// + /// Gets or sets the approval type. + /// + [JsonProperty(PropertyName = "approval_type")] + public MeetingApprovalType? ApprovalType { get; set; } + + /// + /// Gets or sets the registration type. Used for recurring meeting with fixed time only. + /// + [JsonProperty(PropertyName = "registration_type")] + public MeetingRegistrationType? RegistrationType { get; set; } + + /// + /// Gets or sets the value indicating how participants can join the audio portion of the meeting. + /// + [JsonProperty(PropertyName = "audio")] + public AudioType? Audio { get; set; } + + /// + /// Gets or sets the value indicating if audio is recorded and if so, when the audio is saved. + /// + [JsonProperty(PropertyName = "auto_recording")] + public RecordingType AutoRecording { get; set; } + + /// + /// Gets or sets the value indicating that only signed-in users can join this meeting. + /// + [JsonProperty(PropertyName = "enforce_login")] + public bool? EnforceLogin { get; set; } + + /// + /// Gets or sets the value indicating only signed-in users with specified domains can join this meeting. + /// + [JsonProperty(PropertyName = "enforce_login_domains")] + public string EnforceLoginDomains { get; set; } + + /// + /// Gets or sets the value indicating alternative hosts emails or IDs. Multiple value separated by comma. + /// + [JsonProperty(PropertyName = "alternative_hosts")] + public string AlternativeHosts { get; set; } + + /// + /// Gets or sets the value indicating whether registration is closed after event date. + /// + [JsonProperty(PropertyName = "close_registration")] + public bool? CloseRegistration { get; set; } + + /// + /// Gets or sets the value indicating whether to show the social share buttons on the registration page. + /// + [JsonProperty(PropertyName = "show_share_button")] + public bool? ShowSocialShareButtons { get; set; } + + /// + /// Gets or sets the value indicating whether to allow attendees to join from multiple devices. + /// + [JsonProperty(PropertyName = "allow_multiple_devices")] + public bool? AllowMultipleDevices { get; set; } + + /// + /// Gets or sets the value indicating whether to make the webinar on-demand. + /// + [JsonProperty(PropertyName = "on_demand")] + public bool? OnDemand { get; set; } + + /// + /// Gets or sets the list of global dial-in countries. + /// + [JsonProperty(PropertyName = "global_dial_in_countries")] + public string[] GlobalDialInCountries { get; set; } + + /// + /// Gets or sets the contact name for registration. + /// + [JsonProperty(PropertyName = "contact_name")] + public string ContactName { get; set; } + + /// + /// Gets or sets the contact email for registration. + /// + [JsonProperty(PropertyName = "contact_email")] + public string ContactEmail { get; set; } + + /// + /// Gets or sets the maximum number of registrants. + /// + /// + /// Omitting this value, setting it to a negative value or setting it to zero indicates that the number of registrants will not be restricted. + /// + [JsonProperty(PropertyName = "registrants_restrict_number")] + public int? MaximumNumberOfRegistrants { get; set; } + + /// + /// Gets or sets the URL of the survey displayed in attendees' browsers after leaving the webinar. + /// + [JsonProperty(PropertyName = "survey_url")] + public string SurveyUrl { get; set; } + + /// + /// Gets or sets the value indicating whether email notifications are sent about approval, cancellation, denial of registration. + /// + [JsonProperty(PropertyName = "registrants_email_notification")] + public bool? SendRegistrationConfirmationEmail { get; set; } + + /// + /// Gets or sets the value indicating that only authenticated users can join the webinar. + /// + [JsonProperty(PropertyName = "meeting_authentication")] + public bool? AuthenticatedUsersOnly { get; set; } + + /// + /// Gets or sets the authentication type for users to join a webinar when is set to true. + /// + [JsonProperty(PropertyName = "authentication_option")] + public bool? AuthenticationType { get; set; } + } +} diff --git a/Source/ZoomNet/Resources/IWebinars.cs b/Source/ZoomNet/Resources/IWebinars.cs index 1dfd884a..8e1356af 100644 --- a/Source/ZoomNet/Resources/IWebinars.cs +++ b/Source/ZoomNet/Resources/IWebinars.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using ZoomNet.Models; @@ -23,5 +25,42 @@ public interface IWebinars /// An array of webinars. /// Task> GetAllAsync(string userId, int recordsPerPage = 30, int page = 1, CancellationToken cancellationToken = default); + + /// + /// Creates a scheduled webinar for a user. + /// + /// The user Id or email address. + /// Webinar topic. + /// Webinar description. + /// Webinar start time. + /// Webinar duration (minutes). + /// Password to join the webinar. Password may only contain the following characters: [a-z A-Z 0-9 @ - _ *]. Max of 10 characters. + /// Webinar settings. + /// Tracking fields. + /// The cancellation token. + /// + /// The new webinar. + /// + /// Thrown when an exception occured while creating the webinar. + Task CreateScheduledWebinarAsync(string userId, string topic, string agenda, DateTime start, int duration, string password = null, WebinarSettings settings = null, IDictionary trackingFields = null, CancellationToken cancellationToken = default); + + /// + /// Creates a recurring webinar for a user. + /// + /// The user Id or email address. + /// Webinar topic. + /// Webinar description. + /// Webinar start time. + /// Webinar duration (minutes). + /// Recurrence information. + /// Password to join the webinar. Password may only contain the following characters: [a-z A-Z 0-9 @ - _ *]. Max of 10 characters. + /// Webinar settings. + /// Tracking fields. + /// The cancellation token. + /// + /// The new webinar. + /// + /// Thrown when an exception occured while creating the webinar. + Task CreateRecurringWebinarAsync(string userId, string topic, string agenda, DateTime? start, int duration, RecurrenceInfo recurrence, string password = null, WebinarSettings settings = null, IDictionary trackingFields = null, CancellationToken cancellationToken = default); } } diff --git a/Source/ZoomNet/Resources/Webinars.cs b/Source/ZoomNet/Resources/Webinars.cs index 93c2b0c6..2181fb4c 100644 --- a/Source/ZoomNet/Resources/Webinars.cs +++ b/Source/ZoomNet/Resources/Webinars.cs @@ -1,5 +1,8 @@ +using Newtonsoft.Json.Linq; using Pathoschild.Http.Client; using System; +using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using ZoomNet.Models; @@ -51,5 +54,85 @@ public Task> GetAllAsync(string userId, int recordsPe .WithCancellationToken(cancellationToken) .AsPaginatedResponse("webinars"); } + + /// + /// Creates a scheduled webinar for a user. + /// + /// The user Id or email address. + /// Webinar topic. + /// Webinar description. + /// Webinar start time. + /// Webinar duration (minutes). + /// Password to join the webinar. Password may only contain the following characters: [a-z A-Z 0-9 @ - _ *]. Max of 10 characters. + /// Webinar settings. + /// Tracking fields. + /// The cancellation token. + /// + /// The new webinar. + /// + /// Thrown when an exception occured while creating the webinar. + public Task CreateScheduledWebinarAsync(string userId, string topic, string agenda, DateTime start, int duration, string password = null, WebinarSettings settings = null, IDictionary trackingFields = null, CancellationToken cancellationToken = default) + { + var data = new JObject() + { + { "type", 5 } + }; + data.AddPropertyIfValue("topic", topic); + data.AddPropertyIfValue("agenda", agenda); + data.AddPropertyIfValue("password", password); + data.AddPropertyIfValue("start_time", start.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss'Z'")); + data.AddPropertyIfValue("duration", duration); + data.AddPropertyIfValue("timezone", "UTC"); + data.AddPropertyIfValue("settings", settings); + data.AddPropertyIfValue("tracking_fields", trackingFields?.Select(tf => new JObject() { { "field", tf.Key }, { "value", tf.Value } })); + + return _client + .PostAsync($"users/{userId}/webinars") + .WithJsonBody(data) + .WithCancellationToken(cancellationToken) + .AsObject(); + } + + /// + /// Creates a recurring webinar for a user. + /// + /// The user Id or email address. + /// Webinar topic. + /// Webinar description. + /// Webinar start time. + /// Webinar duration (minutes). + /// Recurrence information. + /// Password to join the webinar. Password may only contain the following characters: [a-z A-Z 0-9 @ - _ *]. Max of 10 characters. + /// Webinar settings. + /// Tracking fields. + /// The cancellation token. + /// + /// The new webinar. + /// + /// Thrown when an exception occured while creating the webinar. + public Task CreateRecurringWebinarAsync(string userId, string topic, string agenda, DateTime? start, int duration, RecurrenceInfo recurrence, string password = null, WebinarSettings settings = null, IDictionary trackingFields = null, CancellationToken cancellationToken = default) + { + var data = new JObject() + { + // 6 = Recurring with no fixed time + // 9 = Recurring with fixed time + { "type", start.HasValue ? 9 : 6 } + }; + data.AddPropertyIfValue("topic", topic); + data.AddPropertyIfValue("agenda", agenda); + data.AddPropertyIfValue("password", password); + data.AddPropertyIfValue("start_time", start?.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss'Z'")); + data.AddPropertyIfValue("duration", duration); + data.AddPropertyIfValue("recurrence", recurrence); + data.AddPropertyIfValue("timezone", "UTC"); + data.AddPropertyIfValue("settings", settings); + data.AddPropertyIfValue("tracking_fields", trackingFields?.Select(tf => new JObject() { { "field", tf.Key }, { "value", tf.Value } })); + + return _client + .PostAsync($"users/{userId}/webinars") + .WithJsonBody(data) + .WithCancellationToken(cancellationToken) + .AsObject(); + } } } From 271e88c70e4839c67893db0503b0cad7e41241b1 Mon Sep 17 00:00:00 2001 From: Jericho Date: Sat, 16 May 2020 12:33:41 -0400 Subject: [PATCH 05/17] Get webinar --- Source/ZoomNet/Models/ScheduledWebinar.cs | 28 +++++ Source/ZoomNet/Models/Webinar.cs | 11 +- Source/ZoomNet/Resources/IWebinars.cs | 12 ++ Source/ZoomNet/Resources/Webinars.cs | 19 ++++ Source/ZoomNet/Utilities/WebinarConverter.cs | 111 +++++++++++++++++++ 5 files changed, 171 insertions(+), 10 deletions(-) create mode 100644 Source/ZoomNet/Models/ScheduledWebinar.cs create mode 100644 Source/ZoomNet/Utilities/WebinarConverter.cs diff --git a/Source/ZoomNet/Models/ScheduledWebinar.cs b/Source/ZoomNet/Models/ScheduledWebinar.cs new file mode 100644 index 00000000..c2518ace --- /dev/null +++ b/Source/ZoomNet/Models/ScheduledWebinar.cs @@ -0,0 +1,28 @@ +using Newtonsoft.Json; +using System; + +namespace ZoomNet.Models +{ + /// + /// A scheduled webinar. + /// + /// + public class ScheduledWebinar : Webinar + { + /// + /// Gets or sets the webinar start time. + /// + /// The webinar start time. + [JsonProperty(PropertyName = "start_time", NullValueHandling = NullValueHandling.Ignore)] + public DateTime StartTime { get; set; } + + /// + /// Gets or sets the timezone. + /// For example, "America/Los_Angeles". + /// Please reference our timezone list for supported timezones and their formats. + /// + /// The webinar timezone. For example, "America/Los_Angeles". Please reference our timezone list for supported timezones and their formats. + [JsonProperty(PropertyName = "timezone", NullValueHandling = NullValueHandling.Ignore)] + public string Timezone { get; set; } + } +} diff --git a/Source/ZoomNet/Models/Webinar.cs b/Source/ZoomNet/Models/Webinar.cs index 45bb4ed8..e446b2da 100644 --- a/Source/ZoomNet/Models/Webinar.cs +++ b/Source/ZoomNet/Models/Webinar.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using Newtonsoft.Json; using StrongGrid.Models; using System; @@ -59,15 +59,6 @@ public abstract class Webinar [JsonProperty(PropertyName = "duration", NullValueHandling = NullValueHandling.Ignore)] public int Duration { get; set; } - /// - /// Gets or sets the timezone. - /// For example, "America/Los_Angeles". - /// Please reference our timezone list for supported timezones and their formats. - /// - /// The webinar timezone. For example, "America/Los_Angeles". Please reference our timezone list for supported timezones and their formats. - [JsonProperty(PropertyName = "timezone", NullValueHandling = NullValueHandling.Ignore)] - public string Timezone { get; set; } - /// /// Gets or sets the date and time when the meeting was created. /// diff --git a/Source/ZoomNet/Resources/IWebinars.cs b/Source/ZoomNet/Resources/IWebinars.cs index 8e1356af..498164b6 100644 --- a/Source/ZoomNet/Resources/IWebinars.cs +++ b/Source/ZoomNet/Resources/IWebinars.cs @@ -62,5 +62,17 @@ public interface IWebinars /// /// Thrown when an exception occured while creating the webinar. Task CreateRecurringWebinarAsync(string userId, string topic, string agenda, DateTime? start, int duration, RecurrenceInfo recurrence, string password = null, WebinarSettings settings = null, IDictionary trackingFields = null, CancellationToken cancellationToken = default); + + /// + /// Retrieve the details of a webinar. + /// + /// The user Id or email address. + /// The webinar ID. + /// The meeting occurrence id. + /// The cancellation token. + /// + /// The . + /// + Task GetAsync(string userId, long webinarId, string occurrenceId = null, CancellationToken cancellationToken = default); } } diff --git a/Source/ZoomNet/Resources/Webinars.cs b/Source/ZoomNet/Resources/Webinars.cs index 2181fb4c..1c5381bb 100644 --- a/Source/ZoomNet/Resources/Webinars.cs +++ b/Source/ZoomNet/Resources/Webinars.cs @@ -134,5 +134,24 @@ public Task CreateRecurringWebinarAsync(string userId, string .WithCancellationToken(cancellationToken) .AsObject(); } + + /// + /// Retrieve the details of a webinar. + /// + /// The user Id or email address. + /// The webinar ID. + /// The meeting occurrence id. + /// The cancellation token. + /// + /// The . + /// + public Task GetAsync(string userId, long webinarId, string occurrenceId = null, CancellationToken cancellationToken = default) + { + return _client + .GetAsync($"webinars/{webinarId}") + .WithArgument("occurrence_id", occurrenceId) + .WithCancellationToken(cancellationToken) + .AsObject(null, new WebinarConverter()); + } } } diff --git a/Source/ZoomNet/Utilities/WebinarConverter.cs b/Source/ZoomNet/Utilities/WebinarConverter.cs new file mode 100644 index 00000000..e555ab40 --- /dev/null +++ b/Source/ZoomNet/Utilities/WebinarConverter.cs @@ -0,0 +1,111 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using StrongGrid.Models; +using System; +using System.Linq; +using ZoomNet.Models; + +namespace ZoomNet.Utilities +{ + /// + /// Converts a JSON string into and array of webinars. + /// + /// + internal class WebinarConverter : JsonConverter + { + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + return objectType == typeof(Webinar); + } + + /// + /// Gets a value indicating whether this can read JSON. + /// + /// + /// true if this can read JSON; otherwise, false. + /// + public override bool CanRead + { + get { return true; } + } + + /// + /// Gets a value indicating whether this can write JSON. + /// + /// + /// true if this can write JSON; otherwise, false. + /// + public override bool CanWrite + { + get { return false; } + } + + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// + /// The object value. + /// + /// Unable to determine the field type. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.StartArray) + { + var jArray = JArray.Load(reader); + var items = jArray + .OfType() + .Select(item => Convert(item, serializer)) + .Where(item => item != null) + .ToArray(); + + return items; + } + else if (reader.TokenType == JsonToken.StartObject) + { + var jObject = JObject.Load(reader); + return Convert(jObject, serializer); + } + + throw new Exception("Unable to convert to Webinar"); + } + + private Webinar Convert(JObject jsonObject, JsonSerializer serializer) + { + jsonObject.TryGetValue("type", StringComparison.OrdinalIgnoreCase, out JToken webinarTypeJsonProperty); + var webinarType = (WebinarType)webinarTypeJsonProperty.ToObject(typeof(WebinarType)); + + switch (webinarType) + { + case WebinarType.Regular: + return jsonObject.ToObject(serializer); + case WebinarType.RecurringFixedTime: + case WebinarType.RecurringNoFixedTime: + return jsonObject.ToObject(serializer); + default: + throw new Exception($"{webinarTypeJsonProperty} is an unknown webinar type"); + } + } + } +} From 21688d954c3c0322726589689db232fcf97e336c Mon Sep 17 00:00:00 2001 From: Jericho Date: Sat, 16 May 2020 13:17:01 -0400 Subject: [PATCH 06/17] Use the proper converter when retrieving webinars --- Source/ZoomNet/Resources/Webinars.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/ZoomNet/Resources/Webinars.cs b/Source/ZoomNet/Resources/Webinars.cs index 1c5381bb..342ad6c9 100644 --- a/Source/ZoomNet/Resources/Webinars.cs +++ b/Source/ZoomNet/Resources/Webinars.cs @@ -52,7 +52,7 @@ public Task> GetAllAsync(string userId, int recordsPe .WithArgument("page_size", recordsPerPage) .WithArgument("page", page) .WithCancellationToken(cancellationToken) - .AsPaginatedResponse("webinars"); + .AsPaginatedResponse("webinars", new WebinarConverter()); } /// From fc62fc184c47d2f84d2c3597374c4f4ad51077a3 Mon Sep 17 00:00:00 2001 From: Jericho Date: Sat, 16 May 2020 13:23:16 -0400 Subject: [PATCH 07/17] Delete and end a webinar --- Source/ZoomNet/Resources/IWebinars.cs | 24 +++++++++++++-- Source/ZoomNet/Resources/Webinars.cs | 43 +++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/Source/ZoomNet/Resources/IWebinars.cs b/Source/ZoomNet/Resources/IWebinars.cs index 498164b6..fd192727 100644 --- a/Source/ZoomNet/Resources/IWebinars.cs +++ b/Source/ZoomNet/Resources/IWebinars.cs @@ -66,13 +66,33 @@ public interface IWebinars /// /// Retrieve the details of a webinar. /// - /// The user Id or email address. /// The webinar ID. /// The meeting occurrence id. /// The cancellation token. /// /// The . /// - Task GetAsync(string userId, long webinarId, string occurrenceId = null, CancellationToken cancellationToken = default); + Task GetAsync(long webinarId, string occurrenceId = null, CancellationToken cancellationToken = default); + + /// + /// Delete a webinar. + /// + /// The webinar ID. + /// The webinar occurrence id. + /// The cancellation token. + /// + /// The async task. + /// + Task DeleteAsync(long webinarId, string occurrenceId = null, CancellationToken cancellationToken = default); + + /// + /// End a webinar. + /// + /// The webinar ID. + /// The cancellation token. + /// + /// The async task. + /// + Task EndAsync(long webinarId, CancellationToken cancellationToken = default); } } diff --git a/Source/ZoomNet/Resources/Webinars.cs b/Source/ZoomNet/Resources/Webinars.cs index 342ad6c9..d5598c8c 100644 --- a/Source/ZoomNet/Resources/Webinars.cs +++ b/Source/ZoomNet/Resources/Webinars.cs @@ -138,14 +138,13 @@ public Task CreateRecurringWebinarAsync(string userId, string /// /// Retrieve the details of a webinar. /// - /// The user Id or email address. /// The webinar ID. /// The meeting occurrence id. /// The cancellation token. /// /// The . /// - public Task GetAsync(string userId, long webinarId, string occurrenceId = null, CancellationToken cancellationToken = default) + public Task GetAsync(long webinarId, string occurrenceId = null, CancellationToken cancellationToken = default) { return _client .GetAsync($"webinars/{webinarId}") @@ -153,5 +152,45 @@ public Task GetAsync(string userId, long webinarId, string occurrenceId .WithCancellationToken(cancellationToken) .AsObject(null, new WebinarConverter()); } + + /// + /// Delete a webinar. + /// + /// The webinar ID. + /// The webinar occurrence id. + /// The cancellation token. + /// + /// The async task. + /// + public Task DeleteAsync(long webinarId, string occurrenceId = null, CancellationToken cancellationToken = default) + { + return _client + .DeleteAsync($"webinars/{webinarId}") + .WithArgument("occurrence_id", occurrenceId) + .WithCancellationToken(cancellationToken) + .AsMessage(); + } + + /// + /// End a webinar. + /// + /// The webinar ID. + /// The cancellation token. + /// + /// The async task. + /// + public Task EndAsync(long webinarId, CancellationToken cancellationToken = default) + { + var data = new JObject() + { + { "action", "end" } + }; + + return _client + .PutAsync($"webinars/{webinarId}/status") + .WithJsonBody(data) + .WithCancellationToken(cancellationToken) + .AsMessage(); + } } } From 0b6d87e825dace84d6a5aa694278063f73b01ab4 Mon Sep 17 00:00:00 2001 From: Jericho Date: Sat, 16 May 2020 13:23:35 -0400 Subject: [PATCH 08/17] Simplify string interpolation --- Source/ZoomNet/Utilities/MeetingConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/ZoomNet/Utilities/MeetingConverter.cs b/Source/ZoomNet/Utilities/MeetingConverter.cs index 68b87cf4..78ae00d5 100644 --- a/Source/ZoomNet/Utilities/MeetingConverter.cs +++ b/Source/ZoomNet/Utilities/MeetingConverter.cs @@ -105,7 +105,7 @@ private Meeting Convert(JObject jsonObject, JsonSerializer serializer) case MeetingType.RecurringNoFixedTime: return jsonObject.ToObject(serializer); default: - throw new Exception($"{meetingTypeJsonProperty.ToString()} is an unknown meeting type"); + throw new Exception($"{meetingTypeJsonProperty} is an unknown meeting type"); } } } From 437ede7c7e47826c0f3ec4e966cf19dc7ea69a48 Mon Sep 17 00:00:00 2001 From: Jericho Date: Sat, 16 May 2020 14:06:52 -0400 Subject: [PATCH 09/17] Methods to manage panelists --- Source/ZoomNet/Models/Panelist.cs | 37 ++++++++++++ Source/ZoomNet/Resources/IWebinars.cs | 58 +++++++++++++++++- Source/ZoomNet/Resources/Webinars.cs | 85 +++++++++++++++++++++++++++ 3 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 Source/ZoomNet/Models/Panelist.cs diff --git a/Source/ZoomNet/Models/Panelist.cs b/Source/ZoomNet/Models/Panelist.cs new file mode 100644 index 00000000..6d431de4 --- /dev/null +++ b/Source/ZoomNet/Models/Panelist.cs @@ -0,0 +1,37 @@ +using Newtonsoft.Json; + +namespace ZoomNet.Models +{ + /// + /// Panelist. + /// + public class Panelist + { + /// + /// Gets or sets the panelist id. + /// + /// + /// The id. + /// + [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)] + public string Id { get; set; } + + /// + /// Gets or sets the panelist's email address. + /// + [JsonProperty(PropertyName = "email")] + public string Email { get; set; } + + /// + /// Gets or sets the panelist's full name. + /// + [JsonProperty(PropertyName = "name")] + public string FullName { get; set; } + + /// + /// Gets or sets the panelist's join URL. + /// + [JsonProperty(PropertyName = "join_url")] + public string JoinUrl { get; set; } + } +} diff --git a/Source/ZoomNet/Resources/IWebinars.cs b/Source/ZoomNet/Resources/IWebinars.cs index fd192727..34bcc58c 100644 --- a/Source/ZoomNet/Resources/IWebinars.cs +++ b/Source/ZoomNet/Resources/IWebinars.cs @@ -67,10 +67,10 @@ public interface IWebinars /// Retrieve the details of a webinar. /// /// The webinar ID. - /// The meeting occurrence id. + /// The webinar occurrence id. /// The cancellation token. /// - /// The . + /// The . /// Task GetAsync(long webinarId, string occurrenceId = null, CancellationToken cancellationToken = default); @@ -94,5 +94,59 @@ public interface IWebinars /// The async task. /// Task EndAsync(long webinarId, CancellationToken cancellationToken = default); + + /// + /// List panelists of a webinar. + /// + /// The webinar ID. + /// The cancellation token. + /// + /// An array of . + /// + Task GetPanelistsAsync(long webinarId, CancellationToken cancellationToken = default); + + /// + /// Add a single panelist to a webinar. + /// + /// The webinar ID. + /// Panelist's email address. + /// Panelist's full name. + /// The cancellation token. + /// + /// A . + /// + Task AddPanelistAsync(long webinarId, string email, string fullName, CancellationToken cancellationToken = default); + + /// + /// Add multiple panelists to a webinar. + /// + /// The webinar ID. + /// The panelists to add to the webinar. + /// The cancellation token. + /// + /// The async task. + /// + Task AddPanelistsAsync(long webinarId, IEnumerable<(string Email, string FullName)> panelists, CancellationToken cancellationToken = default); + + /// + /// Remove a single panelist from a webinar. + /// + /// The webinar ID. + /// Panelist's email address. + /// The cancellation token. + /// + /// The async task. + /// + Task RemovePanelistAsync(long webinarId, string panelistId, CancellationToken cancellationToken = default); + + /// + /// Remove all panelists from a webinar. + /// + /// The webinar ID. + /// The cancellation token. + /// + /// The async task. + /// + Task RemoveAllPanelistsAsync(long webinarId, CancellationToken cancellationToken = default); } } diff --git a/Source/ZoomNet/Resources/Webinars.cs b/Source/ZoomNet/Resources/Webinars.cs index d5598c8c..8df77cf7 100644 --- a/Source/ZoomNet/Resources/Webinars.cs +++ b/Source/ZoomNet/Resources/Webinars.cs @@ -192,5 +192,90 @@ public Task EndAsync(long webinarId, CancellationToken cancellationToken = defau .WithCancellationToken(cancellationToken) .AsMessage(); } + + /// + /// List panelists of a webinar. + /// + /// The webinar ID. + /// The cancellation token. + /// + /// An array of . + /// + public Task GetPanelistsAsync(long webinarId, CancellationToken cancellationToken = default) + { + return _client + .GetAsync($"webinars/{webinarId}/panelists") + .WithCancellationToken(cancellationToken) + .AsObject("panelists"); + } + + /// + /// Add a single panelist to a webinar. + /// + /// The webinar ID. + /// Panelist's email address. + /// Panelist's full name. + /// The cancellation token. + /// + /// The unique identifier of the new panelist. + /// + public Task AddPanelistAsync(long webinarId, string email, string fullName, CancellationToken cancellationToken = default) + { + return AddPanelistsAsync(webinarId, new (string Email, string FullName)[] { (email, fullName) }, cancellationToken); + } + + /// + /// Add multiple panelists to a webinar. + /// + /// The webinar ID. + /// The panelists to add to the webinar. + /// The cancellation token. + /// + /// The async task. + /// + public Task AddPanelistsAsync(long webinarId, IEnumerable<(string Email, string FullName)> panelists, CancellationToken cancellationToken = default) + { + var data = new JObject(); + data.AddPropertyIfValue("panelists", panelists.Select(p => new { email = p.Email, name = p.FullName }).ToArray()); + + return _client + .PostAsync($"webinars/{webinarId}/panelists") + .WithJsonBody(data) + .WithCancellationToken(cancellationToken) + .AsMessage(); + } + + /// + /// Remove a single panelist from a webinar. + /// + /// The webinar ID. + /// Panelist's email address. + /// The cancellation token. + /// + /// The async task. + /// + public Task RemovePanelistAsync(long webinarId, string panelistId, CancellationToken cancellationToken = default) + { + return _client + .DeleteAsync($"webinars/{webinarId}/panelists/{panelistId}") + .WithCancellationToken(cancellationToken) + .AsMessage(); + } + + /// + /// Remove all panelists from a webinar. + /// + /// The webinar ID. + /// The cancellation token. + /// + /// The async task. + /// + public Task RemoveAllPanelistsAsync(long webinarId, CancellationToken cancellationToken = default) + { + return _client + .DeleteAsync($"webinars/{webinarId}/panelists") + .WithCancellationToken(cancellationToken) + .AsMessage(); + } } } From 547da3e41b85e17ca28cf254f9d6d3d09c02996d Mon Sep 17 00:00:00 2001 From: Jericho Date: Sat, 16 May 2020 22:34:22 -0400 Subject: [PATCH 10/17] Methods to manage webinar registrants --- Source/ZoomNet/Resources/IWebinars.cs | 107 +++++++++++++++- Source/ZoomNet/Resources/Webinars.cs | 177 +++++++++++++++++++++++++- 2 files changed, 276 insertions(+), 8 deletions(-) diff --git a/Source/ZoomNet/Resources/IWebinars.cs b/Source/ZoomNet/Resources/IWebinars.cs index 34bcc58c..ded1522e 100644 --- a/Source/ZoomNet/Resources/IWebinars.cs +++ b/Source/ZoomNet/Resources/IWebinars.cs @@ -115,7 +115,7 @@ public interface IWebinars /// /// A . /// - Task AddPanelistAsync(long webinarId, string email, string fullName, CancellationToken cancellationToken = default); + Task AddPanelistAsync(long webinarId, string email, string fullName, CancellationToken cancellationToken = default); /// /// Add multiple panelists to a webinar. @@ -126,7 +126,7 @@ public interface IWebinars /// /// The async task. /// - Task AddPanelistsAsync(long webinarId, IEnumerable<(string Email, string FullName)> panelists, CancellationToken cancellationToken = default); + Task AddPanelistsAsync(long webinarId, IEnumerable<(string Email, string FullName)> panelists, CancellationToken cancellationToken = default); /// /// Remove a single panelist from a webinar. @@ -148,5 +148,108 @@ public interface IWebinars /// The async task. /// Task RemoveAllPanelistsAsync(long webinarId, CancellationToken cancellationToken = default); + + /// + /// List the users that have registered for a webinar. + /// + /// The webinar ID. + /// The registrant status. + /// The webinar occurrence id. + /// The number of records returned within a single API call. + /// The current page number of returned records. + /// The cancellation token. + /// + /// An array of . + /// + Task> GetRegistrantsAsync(long webinarId, RegistrantStatus status, string occurrenceId = null, int recordsPerPage = 30, int page = 1, CancellationToken cancellationToken = default); + + /// + /// Add a registrant to a webinar. + /// + /// The webinar ID. + /// A valid email address. + /// User's first name. + /// User's last name. + /// The webinar occurrence id. + /// The cancellation token. + /// + /// A . + /// + Task AddRegistrantAsync(long webinarId, string email, string firstName, string lastName, string occurrenceId = null, CancellationToken cancellationToken = default); + + /// + /// Approve a registration for a webinar. + /// + /// The webinar ID. + /// The registrant ID. + /// The registrant's email address. + /// The webinar occurrence id. + /// The cancellation token. + /// + /// The async task. + /// + Task ApproveRegistrantAsync(long webinarId, string registrantId, string registrantEmail, string occurrenceId = null, CancellationToken cancellationToken = default); + + /// + /// Approve multiple registrations for a webinar. + /// + /// The webinar ID. + /// ID and email for each registrant to be approved. + /// The webinar occurrence id. + /// The cancellation token. + /// + /// The async task. + /// + Task ApproveRegistrantsAsync(long webinarId, IEnumerable<(string RegistrantId, string RegistrantEmail)> registrantsInfo, string occurrenceId = null, CancellationToken cancellationToken = default); + + /// + /// Reject a registration for a webinar. + /// + /// The webinar ID. + /// The registrant ID. + /// The registrant's email address. + /// The webinar occurrence id. + /// The cancellation token. + /// + /// The async task. + /// + Task RejectRegistrantAsync(long webinarId, string registrantId, string registrantEmail, string occurrenceId = null, CancellationToken cancellationToken = default); + + /// + /// Reject multiple registrations for a webinar. + /// + /// The webinar ID. + /// ID and email for each registrant to be rejected. + /// The webinar occurrence id. + /// The cancellation token. + /// + /// The async task. + /// + Task RejectRegistrantsAsync(long webinarId, IEnumerable<(string RegistrantId, string RegistrantEmail)> registrantsInfo, string occurrenceId = null, CancellationToken cancellationToken = default); + + /// + /// Cancel a previously approved registration for a webinar. + /// + /// The webinar ID. + /// The registrant ID. + /// The registrant's email address. + /// The webinar occurrence id. + /// The cancellation token. + /// + /// The async task. + /// + Task CancelRegistrantAsync(long webinarId, string registrantId, string registrantEmail, string occurrenceId = null, CancellationToken cancellationToken = default); + + /// + /// Cancel multiple previously approved registrations for a webinar. + /// + /// The webinar ID. + /// ID and email for each registrant to be cancelled. + /// The webinar occurrence id. + /// The cancellation token. + /// + /// The async task. + /// + Task CancelRegistrantsAsync(long webinarId, IEnumerable<(string RegistrantId, string RegistrantEmail)> registrantsInfo, string occurrenceId = null, CancellationToken cancellationToken = default); } } diff --git a/Source/ZoomNet/Resources/Webinars.cs b/Source/ZoomNet/Resources/Webinars.cs index 8df77cf7..94e7c7cc 100644 --- a/Source/ZoomNet/Resources/Webinars.cs +++ b/Source/ZoomNet/Resources/Webinars.cs @@ -1,3 +1,4 @@ +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Pathoschild.Http.Client; using System; @@ -139,10 +140,10 @@ public Task CreateRecurringWebinarAsync(string userId, string /// Retrieve the details of a webinar. /// /// The webinar ID. - /// The meeting occurrence id. + /// The webinar occurrence id. /// The cancellation token. /// - /// The . + /// The . /// public Task GetAsync(long webinarId, string occurrenceId = null, CancellationToken cancellationToken = default) { @@ -219,9 +220,10 @@ public Task GetPanelistsAsync(long webinarId, CancellationToken canc /// /// The unique identifier of the new panelist. /// - public Task AddPanelistAsync(long webinarId, string email, string fullName, CancellationToken cancellationToken = default) + public async Task AddPanelistAsync(long webinarId, string email, string fullName, CancellationToken cancellationToken = default) { - return AddPanelistsAsync(webinarId, new (string Email, string FullName)[] { (email, fullName) }, cancellationToken); + var panelists = await AddPanelistsAsync(webinarId, new (string Email, string FullName)[] { (email, fullName) }, cancellationToken).ConfigureAwait(false); + return panelists.FirstOrDefault(); } /// @@ -233,7 +235,7 @@ public Task AddPanelistAsync(long webinarId, string email, string fullName, Canc /// /// The async task. /// - public Task AddPanelistsAsync(long webinarId, IEnumerable<(string Email, string FullName)> panelists, CancellationToken cancellationToken = default) + public Task AddPanelistsAsync(long webinarId, IEnumerable<(string Email, string FullName)> panelists, CancellationToken cancellationToken = default) { var data = new JObject(); data.AddPropertyIfValue("panelists", panelists.Select(p => new { email = p.Email, name = p.FullName }).ToArray()); @@ -242,7 +244,7 @@ public Task AddPanelistsAsync(long webinarId, IEnumerable<(string Email, string .PostAsync($"webinars/{webinarId}/panelists") .WithJsonBody(data) .WithCancellationToken(cancellationToken) - .AsMessage(); + .AsObject(); } /// @@ -277,5 +279,168 @@ public Task RemoveAllPanelistsAsync(long webinarId, CancellationToken cancellati .WithCancellationToken(cancellationToken) .AsMessage(); } + + /// + /// List the users that have registered for a webinar. + /// + /// The webinar ID. + /// The registrant status. + /// The webinar occurrence id. + /// The number of records returned within a single API call. + /// The current page number of returned records. + /// The cancellation token. + /// + /// An array of . + /// + public Task> GetRegistrantsAsync(long webinarId, RegistrantStatus status, string occurrenceId = null, int recordsPerPage = 30, int page = 1, CancellationToken cancellationToken = default) + { + if (recordsPerPage < 1 || recordsPerPage > 300) + { + throw new ArgumentOutOfRangeException(nameof(recordsPerPage), "Records per page must be between 1 and 300"); + } + + return _client + .GetAsync($"webinars/{webinarId}/registrants") + .WithArgument("status", JToken.Parse(JsonConvert.SerializeObject(status)).ToString()) + .WithArgument("occurrence_id", occurrenceId) + .WithArgument("page_size", recordsPerPage) + .WithArgument("page", page) + .WithCancellationToken(cancellationToken) + .AsPaginatedResponse("registrants"); + } + + /// + /// Add a registrant to a webinar. + /// + /// The webinar ID. + /// A valid email address. + /// User's first name. + /// User's last name. + /// The webinar occurrence id. + /// The cancellation token. + /// + /// A . + /// + public Task AddRegistrantAsync(long webinarId, string email, string firstName, string lastName, string occurrenceId = null, CancellationToken cancellationToken = default) + { + var data = new JObject(); + data.AddPropertyIfValue("email", email); + data.AddPropertyIfValue("first_name", firstName); + data.AddPropertyIfValue("last_name", lastName); + + return _client + .PostAsync($"webinars/{webinarId}/registrants") + .WithArgument("occurence_id", occurrenceId) + .WithJsonBody(data) + .WithCancellationToken(cancellationToken) + .AsObject(); + } + + /// + /// Approve a registration for a webinar. + /// + /// The webinar ID. + /// The registrant ID. + /// The registrant's email address. + /// The webinar occurrence id. + /// The cancellation token. + /// + /// The async task. + /// + public Task ApproveRegistrantAsync(long webinarId, string registrantId, string registrantEmail, string occurrenceId = null, CancellationToken cancellationToken = default) + { + return ApproveRegistrantsAsync(webinarId, new[] { (registrantId, registrantEmail) }, occurrenceId, cancellationToken); + } + + /// + /// Approve multiple registrations for a webinar. + /// + /// The webinar ID. + /// ID and email for each registrant to be approved. + /// The webinar occurrence id. + /// The cancellation token. + /// + /// The async task. + /// + public Task ApproveRegistrantsAsync(long webinarId, IEnumerable<(string RegistrantId, string RegistrantEmail)> registrantsInfo, string occurrenceId = null, CancellationToken cancellationToken = default) + { + return UpdateRegistrantsStatusAsync(webinarId, registrantsInfo, "approve", occurrenceId, cancellationToken); + } + + /// + /// Reject a registration for a webinar. + /// + /// The webinar ID. + /// The registrant ID. + /// The registrant's email address. + /// The webinar occurrence id. + /// The cancellation token. + /// + /// The async task. + /// + public Task RejectRegistrantAsync(long webinarId, string registrantId, string registrantEmail, string occurrenceId = null, CancellationToken cancellationToken = default) + { + return RejectRegistrantsAsync(webinarId, new[] { (registrantId, registrantEmail) }, occurrenceId, cancellationToken); + } + + /// + /// Reject multiple registrations for a webinar. + /// + /// The webinar ID. + /// ID and email for each registrant to be rejected. + /// The webinar occurrence id. + /// The cancellation token. + /// + /// The async task. + /// + public Task RejectRegistrantsAsync(long webinarId, IEnumerable<(string RegistrantId, string RegistrantEmail)> registrantsInfo, string occurrenceId = null, CancellationToken cancellationToken = default) + { + return UpdateRegistrantsStatusAsync(webinarId, registrantsInfo, "deny", occurrenceId, cancellationToken); + } + + /// + /// Cancel a previously approved registration for a webinar. + /// + /// The webinar ID. + /// The registrant ID. + /// The registrant's email address. + /// The webinar occurrence id. + /// The cancellation token. + /// + /// The async task. + /// + public Task CancelRegistrantAsync(long webinarId, string registrantId, string registrantEmail, string occurrenceId = null, CancellationToken cancellationToken = default) + { + return CancelRegistrantsAsync(webinarId, new[] { (registrantId, registrantEmail) }, occurrenceId, cancellationToken); + } + + /// + /// Cancel multiple previously approved registrations for a webinar. + /// + /// The webinar ID. + /// ID and email for each registrant to be cancelled. + /// The webinar occurrence id. + /// The cancellation token. + /// + /// The async task. + /// + public Task CancelRegistrantsAsync(long webinarId, IEnumerable<(string RegistrantId, string RegistrantEmail)> registrantsInfo, string occurrenceId = null, CancellationToken cancellationToken = default) + { + return UpdateRegistrantsStatusAsync(webinarId, registrantsInfo, "cancel", occurrenceId, cancellationToken); + } + + private Task UpdateRegistrantsStatusAsync(long webinarId, IEnumerable<(string RegistrantId, string RegistrantEmail)> registrantsInfo, string status, string occurrenceId = null, CancellationToken cancellationToken = default) + { + var data = new JObject(); + data.AddPropertyIfValue("action", status); + data.AddPropertyIfValue("registrants", registrantsInfo.Select(ri => new { id = ri.RegistrantId, email = ri.RegistrantEmail }).ToArray()); + + return _client + .PostAsync($"webinars/{webinarId}/registrants/status") + .WithArgument("occurence_id", occurrenceId) + .WithJsonBody(data) + .WithCancellationToken(cancellationToken) + .AsMessage(); + } } } From 00c8fae7afb74edc66581ad997a8f4a1ea44b35b Mon Sep 17 00:00:00 2001 From: Jericho Date: Sat, 16 May 2020 22:40:58 -0400 Subject: [PATCH 11/17] Retrieve all polls for a meeting --- Source/ZoomNet/Resources/IMeetings.cs | 12 +++++++++++- Source/ZoomNet/Resources/Meetings.cs | 18 +++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Source/ZoomNet/Resources/IMeetings.cs b/Source/ZoomNet/Resources/IMeetings.cs index ffe46c97..099ea199 100644 --- a/Source/ZoomNet/Resources/IMeetings.cs +++ b/Source/ZoomNet/Resources/IMeetings.cs @@ -220,6 +220,16 @@ public interface IMeetings /// Task CancelRegistrantsAsync(long meetingId, IEnumerable<(string RegistrantId, string RegistrantEmail)> registrantsInfo, string occurrenceId = null, CancellationToken cancellationToken = default); + /// + /// Retrieve all polls for a meeting. + /// + /// The meeting id. + /// The cancellation token. + /// + /// An array of . + /// + Task GetPollsAsync(long meetingId, CancellationToken cancellationToken = default); + /// /// Create a poll for a meeting. /// @@ -233,7 +243,7 @@ public interface IMeetings Task CreatePoll(long meetingId, string title, IEnumerable questions, CancellationToken cancellationToken = default); /// - /// Retrieve the details of a meeting. + /// Retrieve a poll. /// /// The meeting id. /// The poll id. diff --git a/Source/ZoomNet/Resources/Meetings.cs b/Source/ZoomNet/Resources/Meetings.cs index a1732509..07cef52b 100644 --- a/Source/ZoomNet/Resources/Meetings.cs +++ b/Source/ZoomNet/Resources/Meetings.cs @@ -406,6 +406,22 @@ public Task CancelRegistrantsAsync(long meetingId, IEnumerable<(string Registran return UpdateRegistrantsStatusAsync(meetingId, registrantsInfo, "cancel", occurrenceId, cancellationToken); } + /// + /// Retrieve all polls for a meeting. + /// + /// The meeting id. + /// The cancellation token. + /// + /// An array of . + /// + public Task GetPollsAsync(long meetingId, CancellationToken cancellationToken = default) + { + return _client + .GetAsync($"meetings/{meetingId}/polls") + .WithCancellationToken(cancellationToken) + .AsObject("polls"); + } + /// /// Create a poll for a meeting. /// @@ -432,7 +448,7 @@ public Task CreatePoll(long meetingId, string title, IEnumerable - /// Retrieve the details of a meeting. + /// Retrieve a poll. /// /// The meeting id. /// The poll id. From ebf1eece4ce3e184c7f1dd6c60fdf9b230ec2956 Mon Sep 17 00:00:00 2001 From: Jericho Date: Sun, 17 May 2020 11:59:17 -0400 Subject: [PATCH 12/17] Fix URL in XML comment --- Source/ZoomNet/Resources/IMeetings.cs | 2 +- Source/ZoomNet/Resources/IWebinars.cs | 2 +- Source/ZoomNet/Resources/Meetings.cs | 2 +- Source/ZoomNet/Resources/Webinars.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/ZoomNet/Resources/IMeetings.cs b/Source/ZoomNet/Resources/IMeetings.cs index 099ea199..73940f3a 100644 --- a/Source/ZoomNet/Resources/IMeetings.cs +++ b/Source/ZoomNet/Resources/IMeetings.cs @@ -10,7 +10,7 @@ namespace ZoomNet.Resources /// Allows you to manage meetings. /// /// - /// See Zoom documentation for more information. + /// See Zoom documentation for more information. /// public interface IMeetings { diff --git a/Source/ZoomNet/Resources/IWebinars.cs b/Source/ZoomNet/Resources/IWebinars.cs index ded1522e..299b0d1b 100644 --- a/Source/ZoomNet/Resources/IWebinars.cs +++ b/Source/ZoomNet/Resources/IWebinars.cs @@ -10,7 +10,7 @@ namespace ZoomNet.Resources /// Allows you to manage webinars. /// /// - /// See Zoom documentation for more information. + /// See Zoom documentation for more information. /// public interface IWebinars { diff --git a/Source/ZoomNet/Resources/Meetings.cs b/Source/ZoomNet/Resources/Meetings.cs index 07cef52b..2c585beb 100644 --- a/Source/ZoomNet/Resources/Meetings.cs +++ b/Source/ZoomNet/Resources/Meetings.cs @@ -16,7 +16,7 @@ namespace ZoomNet.Resources /// /// /// - /// See Zoom documentation for more information. + /// See Zoom documentation for more information. /// public class Meetings : IMeetings { diff --git a/Source/ZoomNet/Resources/Webinars.cs b/Source/ZoomNet/Resources/Webinars.cs index 94e7c7cc..31d358fe 100644 --- a/Source/ZoomNet/Resources/Webinars.cs +++ b/Source/ZoomNet/Resources/Webinars.cs @@ -16,7 +16,7 @@ namespace ZoomNet.Resources /// /// /// - /// See Zoom documentation for more information. + /// See Zoom documentation for more information. /// public class Webinars : IWebinars { From 463a06fc9e7c7bedeb1d52c1ba9b825fc9e46356 Mon Sep 17 00:00:00 2001 From: Jericho Date: Sun, 17 May 2020 12:00:33 -0400 Subject: [PATCH 13/17] Methods to manage polls and registration questions. --- Source/ZoomNet/Models/TrackingSource.cs | 37 +++++ Source/ZoomNet/Resources/IWebinars.cs | 100 ++++++++++++++ Source/ZoomNet/Resources/Webinars.cs | 171 ++++++++++++++++++++++++ 3 files changed, 308 insertions(+) create mode 100644 Source/ZoomNet/Models/TrackingSource.cs diff --git a/Source/ZoomNet/Models/TrackingSource.cs b/Source/ZoomNet/Models/TrackingSource.cs new file mode 100644 index 00000000..a730d679 --- /dev/null +++ b/Source/ZoomNet/Models/TrackingSource.cs @@ -0,0 +1,37 @@ +using Newtonsoft.Json; + +namespace ZoomNet.Models +{ + /// + /// Tracking Source. + /// + public class TrackingSource + { + /// + /// Gets or sets the unique identifier. + /// + /// + /// The id. + /// + [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)] + public string Id { get; set; } + + /// + /// Gets or sets the name of the source (platform) where the registration URL was shared. + /// + [JsonProperty(PropertyName = "source_name")] + public string Name { get; set; } + + /// + /// Gets or sets the URL that was shared for the registration. + /// + [JsonProperty(PropertyName = "tracking_url")] + public string TrackingUrl { get; set; } + + /// + /// Gets or sets the number of visitors who visited the registration page from this source. + /// + [JsonProperty(PropertyName = "visitor_count")] + public long VisitorCount { get; set; } + } +} diff --git a/Source/ZoomNet/Resources/IWebinars.cs b/Source/ZoomNet/Resources/IWebinars.cs index 299b0d1b..7d776466 100644 --- a/Source/ZoomNet/Resources/IWebinars.cs +++ b/Source/ZoomNet/Resources/IWebinars.cs @@ -163,6 +163,18 @@ public interface IWebinars /// Task> GetRegistrantsAsync(long webinarId, RegistrantStatus status, string occurrenceId = null, int recordsPerPage = 30, int page = 1, CancellationToken cancellationToken = default); + /// + /// Get details on a specific user who registered for a webinar. + /// + /// The webinar ID. + /// The registrant ID. + /// The webinar occurrence id. + /// The cancellation token. + /// + /// The . + /// + Task GetRegistrantAsync(long webinarId, string registrantId, string occurrenceId = null, CancellationToken cancellationToken = default); + /// /// Add a registrant to a webinar. /// @@ -251,5 +263,93 @@ public interface IWebinars /// The async task. /// Task CancelRegistrantsAsync(long webinarId, IEnumerable<(string RegistrantId, string RegistrantEmail)> registrantsInfo, string occurrenceId = null, CancellationToken cancellationToken = default); + + /// + /// Retrieve all polls for a webinar. + /// + /// The webinar id. + /// The cancellation token. + /// + /// An array of . + /// + Task GetPollsAsync(long webinarId, CancellationToken cancellationToken = default); + + /// + /// Create a poll for a webinar. + /// + /// The webinar ID. + /// Title for the poll. + /// The poll questions. + /// The cancellation token. + /// + /// The async task. + /// + Task CreatePoll(long webinarId, string title, IEnumerable questions, CancellationToken cancellationToken = default); + + /// + /// Retrieve a poll. + /// + /// The webinar id. + /// The poll id. + /// The cancellation token. + /// + /// The . + /// + Task GetPollAsync(long webinarId, long pollId, CancellationToken cancellationToken = default); + + /// + /// Update a poll for a webinar. + /// + /// The webinar ID. + /// The poll id. + /// Title for the poll. + /// The poll questions. + /// The cancellation token. + /// + /// The async task. + /// + Task UpdatePollAsync(long webinarId, long pollId, string title, IEnumerable questions, CancellationToken cancellationToken = default); + + /// + /// Delete a poll for a webinar. + /// + /// The webinar ID. + /// The poll id. + /// The cancellation token. + /// + /// The async task. + /// + Task DeletePollAsync(long webinarId, long pollId, CancellationToken cancellationToken = default); + + /// + /// Retrieve the questions that are to be answered by users while registering for a webinar. + /// + /// The webinar ID. + /// The cancellation token. + /// + /// An array of . + /// + Task GetRegistrationQuestions(long webinarId, CancellationToken cancellationToken = default); + + /// + /// Update the questions that are to be answered by users while registering for a webinar. + /// + /// The webinar ID. + /// The questions to be answered. + /// The cancellation token. + /// + /// The async task. + /// + Task UpdateRegistrationQuestions(long webinarId, IEnumerable customQuestions, CancellationToken cancellationToken = default); + + /// + /// Retrieve all the tracking sources of a webinar. + /// + /// The webinar id. + /// The cancellation token. + /// + /// An array of . + /// + Task GetTrackingSourcesAsync(long webinarId, CancellationToken cancellationToken = default); } } diff --git a/Source/ZoomNet/Resources/Webinars.cs b/Source/ZoomNet/Resources/Webinars.cs index 31d358fe..8fb842ad 100644 --- a/Source/ZoomNet/Resources/Webinars.cs +++ b/Source/ZoomNet/Resources/Webinars.cs @@ -309,6 +309,25 @@ public Task> GetRegistrantsAsync(long webinarId, R .AsPaginatedResponse("registrants"); } + /// + /// Get details on a specific user who registered for a webinar. + /// + /// The webinar ID. + /// The registrant ID. + /// The webinar occurrence id. + /// The cancellation token. + /// + /// The . + /// + public Task GetRegistrantAsync(long webinarId, string registrantId, string occurrenceId = null, CancellationToken cancellationToken = default) + { + return _client + .GetAsync($"webinars/{webinarId}/registrants/{registrantId}") + .WithArgument("occurrence_id", occurrenceId) + .WithCancellationToken(cancellationToken) + .AsObject(); + } + /// /// Add a registrant to a webinar. /// @@ -429,6 +448,158 @@ public Task CancelRegistrantsAsync(long webinarId, IEnumerable<(string Registran return UpdateRegistrantsStatusAsync(webinarId, registrantsInfo, "cancel", occurrenceId, cancellationToken); } + /// + /// Retrieve all polls for a webinar. + /// + /// The webinar id. + /// The cancellation token. + /// + /// An array of . + /// + public Task GetPollsAsync(long webinarId, CancellationToken cancellationToken = default) + { + return _client + .GetAsync($"webinars/{webinarId}/polls") + .WithCancellationToken(cancellationToken) + .AsObject("polls"); + } + + /// + /// Create a poll for a webinar. + /// + /// The webinar ID. + /// Title for the poll. + /// The poll questions. + /// The cancellation token. + /// + /// The async task. + /// + public Task CreatePoll(long webinarId, string title, IEnumerable questions, CancellationToken cancellationToken = default) + { + var data = new JObject() + { + { "title", title } + }; + data.AddPropertyIfValue("questions", questions); + + return _client + .PostAsync($"webinars/{webinarId}/polls") + .WithJsonBody(data) + .WithCancellationToken(cancellationToken) + .AsObject(); + } + + /// + /// Retrieve a poll. + /// + /// The webinar id. + /// The poll id. + /// The cancellation token. + /// + /// The . + /// + public Task GetPollAsync(long webinarId, long pollId, CancellationToken cancellationToken = default) + { + return _client + .GetAsync($"webinars/{webinarId}/polls/{pollId}") + .WithCancellationToken(cancellationToken) + .AsObject(); + } + + /// + /// Update a poll for a webinar. + /// + /// The webinar ID. + /// The poll id. + /// Title for the poll. + /// The poll questions. + /// The cancellation token. + /// + /// The async task. + /// + public Task UpdatePollAsync(long webinarId, long pollId, string title, IEnumerable questions, CancellationToken cancellationToken = default) + { + var data = new JObject(); + data.AddPropertyIfValue("title", title); + data.AddPropertyIfValue("questions", questions); + + return _client + .PutAsync($"webinars/{webinarId}/polls/{pollId}") + .WithJsonBody(data) + .WithCancellationToken(cancellationToken) + .AsMessage(); + } + + /// + /// Delete a poll for a webinar. + /// + /// The webinar ID. + /// The poll id. + /// The cancellation token. + /// + /// The async task. + /// + public Task DeletePollAsync(long webinarId, long pollId, CancellationToken cancellationToken = default) + { + return _client + .DeleteAsync($"webinars/{webinarId}/polls/{pollId}") + .WithCancellationToken(cancellationToken) + .AsMessage(); + } + + /// + /// Retrieve the questions that are to be answered by users while registering for a webinar. + /// + /// The webinar ID. + /// The cancellation token. + /// + /// An array of . + /// + public Task GetRegistrationQuestions(long webinarId, CancellationToken cancellationToken = default) + { + return _client + .GetAsync($"webinars/{webinarId}/registrants/questions") + .WithCancellationToken(cancellationToken) + .AsObject("custom_questions"); + } + + /// + /// Update the questions that are to be answered by users while registering for a webinar. + /// + /// The webinar ID. + /// The questions to be answered. + /// The cancellation token. + /// + /// The async task. + /// + public Task UpdateRegistrationQuestions(long webinarId, IEnumerable customQuestions, CancellationToken cancellationToken = default) + { + var data = new JObject(); + data.AddPropertyIfValue("custom_questions", customQuestions); + + return _client + .PutAsync($"webinars/{webinarId}/registrants/questions") + .WithJsonBody(data) + .WithCancellationToken(cancellationToken) + .AsMessage(); + } + + /// + /// Retrieve all the tracking sources of a webinar. + /// + /// The webinar id. + /// The cancellation token. + /// + /// An array of . + /// + public Task GetTrackingSourcesAsync(long webinarId, CancellationToken cancellationToken = default) + { + return _client + .GetAsync($"webinars/{webinarId}/tracking_sources") + .WithCancellationToken(cancellationToken) + .AsObject("tracking_sources"); + } + private Task UpdateRegistrantsStatusAsync(long webinarId, IEnumerable<(string RegistrantId, string RegistrantEmail)> registrantsInfo, string status, string occurrenceId = null, CancellationToken cancellationToken = default) { var data = new JObject(); From 484188526adbbab0d6830fd5d9e420fc36b63f57 Mon Sep 17 00:00:00 2001 From: Jericho Date: Sun, 17 May 2020 12:02:18 -0400 Subject: [PATCH 14/17] Fix xml comments --- Source/ZoomNet/Resources/IPastMeetings.cs | 2 +- Source/ZoomNet/Resources/PastMeetings.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/ZoomNet/Resources/IPastMeetings.cs b/Source/ZoomNet/Resources/IPastMeetings.cs index 4ef2a512..a96f7709 100644 --- a/Source/ZoomNet/Resources/IPastMeetings.cs +++ b/Source/ZoomNet/Resources/IPastMeetings.cs @@ -40,7 +40,7 @@ public interface IPastMeetings /// The meeting identifier. /// The cancellation token. /// - /// An array of . + /// An array of . /// Task GetInstancesAsync(long meetingId, CancellationToken cancellationToken = default); diff --git a/Source/ZoomNet/Resources/PastMeetings.cs b/Source/ZoomNet/Resources/PastMeetings.cs index a21eab35..a24354ac 100644 --- a/Source/ZoomNet/Resources/PastMeetings.cs +++ b/Source/ZoomNet/Resources/PastMeetings.cs @@ -74,7 +74,7 @@ public Task> GetParticipantsAsync(string /// The meeting identifier. /// The cancellation token. /// - /// An array of . + /// An array of . /// public Task GetInstancesAsync(long meetingId, CancellationToken cancellationToken = default) { From f6463391cdde539a71cbab650d204802fdf8ef86 Mon Sep 17 00:00:00 2001 From: Jericho Date: Sun, 17 May 2020 12:09:07 -0400 Subject: [PATCH 15/17] Methods to manage webinars that occured in the past --- Source/ZoomNet/IZoomClient.cs | 8 ++ ...PastMeetingInstance.cs => PastInstance.cs} | 10 +- Source/ZoomNet/Resources/IPastMeetings.cs | 2 +- Source/ZoomNet/Resources/IPastWebinars.cs | 70 ++++++++++ Source/ZoomNet/Resources/PastMeetings.cs | 4 +- Source/ZoomNet/Resources/PastWebinars.cs | 121 ++++++++++++++++++ Source/ZoomNet/ZoomClient.cs | 9 ++ 7 files changed, 216 insertions(+), 8 deletions(-) rename Source/ZoomNet/Models/{PastMeetingInstance.cs => PastInstance.cs} (65%) create mode 100644 Source/ZoomNet/Resources/IPastWebinars.cs create mode 100644 Source/ZoomNet/Resources/PastWebinars.cs diff --git a/Source/ZoomNet/IZoomClient.cs b/Source/ZoomNet/IZoomClient.cs index 172da59a..e9a44ebf 100644 --- a/Source/ZoomNet/IZoomClient.cs +++ b/Source/ZoomNet/IZoomClient.cs @@ -23,6 +23,14 @@ public interface IZoomClient /// IPastMeetings PastMeetings { get; } + /// + /// Gets the resource which allows you to manage webinars that occured in the past. + /// + /// + /// The past webinars resource. + /// + IPastWebinars PastWebinars { get; } + /// /// Gets the resource which allows you to manage webinars. /// diff --git a/Source/ZoomNet/Models/PastMeetingInstance.cs b/Source/ZoomNet/Models/PastInstance.cs similarity index 65% rename from Source/ZoomNet/Models/PastMeetingInstance.cs rename to Source/ZoomNet/Models/PastInstance.cs index 848ca17f..d9d5ab9a 100644 --- a/Source/ZoomNet/Models/PastMeetingInstance.cs +++ b/Source/ZoomNet/Models/PastInstance.cs @@ -4,12 +4,12 @@ namespace ZoomNet.Models { /// - /// A meeting instance that occured in the past. + /// A meeting or webinar instance that occured in the past. /// - public class PastMeetingInstance + public class PastInstance { /// - /// Gets or sets the meeting uuid. + /// Gets or sets the uuid. /// /// /// The uuid. @@ -18,9 +18,9 @@ public class PastMeetingInstance public string Uuid { get; set; } /// - /// Gets or sets the date and time when the meeting instance started. + /// Gets or sets the date and time when the instance started. /// - /// The meeting start time. + /// The start time. [JsonProperty(PropertyName = "start_time", NullValueHandling = NullValueHandling.Ignore)] public DateTime StartedOn { get; set; } } diff --git a/Source/ZoomNet/Resources/IPastMeetings.cs b/Source/ZoomNet/Resources/IPastMeetings.cs index a96f7709..52b70764 100644 --- a/Source/ZoomNet/Resources/IPastMeetings.cs +++ b/Source/ZoomNet/Resources/IPastMeetings.cs @@ -42,7 +42,7 @@ public interface IPastMeetings /// /// An array of . /// - Task GetInstancesAsync(long meetingId, CancellationToken cancellationToken = default); + Task GetInstancesAsync(long meetingId, CancellationToken cancellationToken = default); /// /// Get a list of poll results for a meeting that occured in the past. diff --git a/Source/ZoomNet/Resources/IPastWebinars.cs b/Source/ZoomNet/Resources/IPastWebinars.cs new file mode 100644 index 00000000..f80e1eed --- /dev/null +++ b/Source/ZoomNet/Resources/IPastWebinars.cs @@ -0,0 +1,70 @@ +using System.Threading; +using System.Threading.Tasks; +using ZoomNet.Models; + +namespace ZoomNet.Resources +{ + /// + /// Allows you to manage webinars that occured in the past. + /// + /// + /// See Zoom documentation for more information. + /// + public interface IPastWebinars + { + /// + /// List absentees of a webinar that occured in the past. + /// + /// The webinar UUID. + /// The number of records to return. + /// The page token. + /// The cancellation token. + /// + /// An array of . + /// + Task> GetAbsenteesAsync(string uuid, int recordsPerPage = 30, string pageToken = null, CancellationToken cancellationToken = default); + + /// + /// Get a list of ended webinar instance. + /// + /// The webinar identifier. + /// The cancellation token. + /// + /// An array of . + /// + Task GetInstancesAsync(long webinarId, CancellationToken cancellationToken = default); + + /// + /// Get a list of poll results for a webinar that occured in the past. + /// + /// The webinar identifier. + /// The cancellation token. + /// + /// An array of . + /// + Task GetPollResultsAsync(long webinarId, CancellationToken cancellationToken = default); + + /// + /// Get a list of Q&A results for a webinar that occured in the past. + /// + /// The webinar identifier. + /// The cancellation token. + /// + /// An array of . + /// + Task GetQuestionsAndAnswersResultsAsync(long webinarId, CancellationToken cancellationToken = default); + + /// + /// Get a list of files sent via in-webinar chat during a webinar. + /// + /// + /// The in-webinar files are deleted after 24 hours of the webinar completion time. + /// + /// The webinar identifier. + /// The cancellation token. + /// + /// An array of . + /// + Task GetFilesAsync(long webinarId, CancellationToken cancellationToken = default); + } +} diff --git a/Source/ZoomNet/Resources/PastMeetings.cs b/Source/ZoomNet/Resources/PastMeetings.cs index a24354ac..de5d5e11 100644 --- a/Source/ZoomNet/Resources/PastMeetings.cs +++ b/Source/ZoomNet/Resources/PastMeetings.cs @@ -76,12 +76,12 @@ public Task> GetParticipantsAsync(string /// /// An array of . /// - public Task GetInstancesAsync(long meetingId, CancellationToken cancellationToken = default) + public Task GetInstancesAsync(long meetingId, CancellationToken cancellationToken = default) { return _client .GetAsync($"past_meetings/{meetingId}/instances") .WithCancellationToken(cancellationToken) - .AsObject("meetings"); + .AsObject("meetings"); } /// diff --git a/Source/ZoomNet/Resources/PastWebinars.cs b/Source/ZoomNet/Resources/PastWebinars.cs new file mode 100644 index 00000000..2bbeea96 --- /dev/null +++ b/Source/ZoomNet/Resources/PastWebinars.cs @@ -0,0 +1,121 @@ +using Pathoschild.Http.Client; +using System; +using System.Threading; +using System.Threading.Tasks; +using ZoomNet.Models; +using ZoomNet.Utilities; + +namespace ZoomNet.Resources +{ + /// + /// Allows you to manage webinars that occured in the past. + /// + /// + /// See Zoom documentation for more information. + /// + public class PastWebinars : IPastWebinars + { + private readonly Pathoschild.Http.Client.IClient _client; + + /// + /// Initializes a new instance of the class. + /// + /// The HTTP client. + internal PastWebinars(Pathoschild.Http.Client.IClient client) + { + _client = client; + } + + /// + /// List absentees of a webinar that occured in the past. + /// + /// The webinar UUID. + /// The number of records to return. + /// The page token. + /// The cancellation token. + /// + /// An array of . + /// + public Task> GetAbsenteesAsync(string uuid, int recordsPerPage = 30, string pageToken = null, CancellationToken cancellationToken = default) + { + if (recordsPerPage < 1 || recordsPerPage > 300) + { + throw new ArgumentOutOfRangeException(nameof(recordsPerPage), "Records per page must be between 1 and 300"); + } + + return _client + .GetAsync($"past_webinars/{uuid}/absentees") + .WithArgument("page_size", recordsPerPage) + .WithArgument("next_page_token", pageToken) + .WithCancellationToken(cancellationToken) + .AsPaginatedResponseWithToken("registrants"); + } + + /// + /// Get a list of ended webinar instance. + /// + /// The webinar identifier. + /// The cancellation token. + /// + /// An array of . + /// + public Task GetInstancesAsync(long webinarId, CancellationToken cancellationToken = default) + { + return _client + .GetAsync($"past_webinars/{webinarId}/instances") + .WithCancellationToken(cancellationToken) + .AsObject("webinars"); + } + + /// + /// Get a list of poll results for a webinar that occured in the past. + /// + /// The webinar identifier. + /// The cancellation token. + /// + /// An array of . + /// + public Task GetPollResultsAsync(long webinarId, CancellationToken cancellationToken = default) + { + return _client + .GetAsync($"past_webinars/{webinarId}/polls") + .WithCancellationToken(cancellationToken) + .AsObject("questions"); + } + + /// + /// Get a list of Q&A results for a webinar that occured in the past. + /// + /// The webinar identifier. + /// The cancellation token. + /// + /// An array of . + /// + public Task GetQuestionsAndAnswersResultsAsync(long webinarId, CancellationToken cancellationToken = default) + { + return _client + .GetAsync($"past_webinars/{webinarId}/qa") + .WithCancellationToken(cancellationToken) + .AsObject("questions"); + } + + /// + /// Get a list of files sent via in-webinar chat during a webinar. + /// + /// + /// The in-webinar files are deleted after 24 hours of the webinar completion time. + /// + /// The webinar identifier. + /// The cancellation token. + /// + /// An array of . + /// + public Task GetFilesAsync(long webinarId, CancellationToken cancellationToken = default) + { + return _client + .GetAsync($"past_webinars/{webinarId}/files") + .WithCancellationToken(cancellationToken) + .AsObject("in_meeting_files"); + } + } +} diff --git a/Source/ZoomNet/ZoomClient.cs b/Source/ZoomNet/ZoomClient.cs index 2152da41..258b93d1 100644 --- a/Source/ZoomNet/ZoomClient.cs +++ b/Source/ZoomNet/ZoomClient.cs @@ -71,6 +71,14 @@ public static string Version /// public IPastMeetings PastMeetings { get; private set; } + /// + /// Gets the resource which allows you to manage webinars that occured in the past. + /// + /// + /// The past webinars resource. + /// + public IPastWebinars PastWebinars { get; private set; } + /// /// Gets the resource which allows you to manage webinars. /// @@ -165,6 +173,7 @@ private ZoomClient(IConnectionInfo connectionInfo, HttpClient httpClient, bool d Meetings = new Meetings(_fluentClient); PastMeetings = new PastMeetings(_fluentClient); + PastWebinars = new PastWebinars(_fluentClient); Webinars = new Webinars(_fluentClient); } From a87718dd7a7180e73045be060e51f1045352c589 Mon Sep 17 00:00:00 2001 From: Jericho Date: Sat, 23 May 2020 11:44:25 -0400 Subject: [PATCH 16/17] Use cancellation token when pausing --- Source/ZoomNet.IntegrationTests/Tests/Meetings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/ZoomNet.IntegrationTests/Tests/Meetings.cs b/Source/ZoomNet.IntegrationTests/Tests/Meetings.cs index 7bedf653..45358063 100644 --- a/Source/ZoomNet.IntegrationTests/Tests/Meetings.cs +++ b/Source/ZoomNet.IntegrationTests/Tests/Meetings.cs @@ -34,7 +34,7 @@ public async Task RunAsync(string userId, IZoomClient client, TextWriter log, Ca { await client.Meetings.DeleteAsync(userId, oldMeeting.Id, null, cancellationToken).ConfigureAwait(false); await log.WriteLineAsync($"Meeting {oldMeeting.Id} deleted").ConfigureAwait(false); - await Task.Delay(250).ConfigureAwait(false); // Brief pause to ensure Zoom has time to catch up + await Task.Delay(250, cancellationToken).ConfigureAwait(false); // Brief pause to ensure Zoom has time to catch up }); await Task.WhenAll(cleanUpTasks).ConfigureAwait(false); From 242ead709db0cfa7bafcac11ea724f93e672330a Mon Sep 17 00:00:00 2001 From: Jericho Date: Sat, 23 May 2020 15:14:04 -0400 Subject: [PATCH 17/17] Integration tests for Webinars --- .../Tests/Webinars.cs | 68 +++++++++++++++++++ .../ZoomNet.IntegrationTests/TestsRunner.cs | 1 + 2 files changed, 69 insertions(+) create mode 100644 Source/ZoomNet.IntegrationTests/Tests/Webinars.cs diff --git a/Source/ZoomNet.IntegrationTests/Tests/Webinars.cs b/Source/ZoomNet.IntegrationTests/Tests/Webinars.cs new file mode 100644 index 00000000..6e4b0ecc --- /dev/null +++ b/Source/ZoomNet.IntegrationTests/Tests/Webinars.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ZoomNet.Models; + +namespace ZoomNet.IntegrationTests.Tests +{ + public class Webinars : IIntegrationTest + { + public async Task RunAsync(string userId, IZoomClient client, TextWriter log, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) return; + + await log.WriteLineAsync("\n***** WEBINARS *****\n").ConfigureAwait(false); + + // GET ALL THE WEBINARS + var paginatedWebinars = await client.Webinars.GetAllAsync(userId, 30, 1, cancellationToken).ConfigureAwait(false); + await log.WriteLineAsync($"There are {paginatedWebinars.TotalRecords} webinars for user {userId}").ConfigureAwait(false); + + // CLEANUP PREVIOUS INTEGRATION TESTS THAT MIGHT HAVE BEEN INTERRUPTED BEFORE THEY HAD TIME TO CLEANUP AFTER THEMSELVES + var cleanUpTasks = paginatedWebinars.Records + .Union(paginatedWebinars.Records) + .Where(m => m.Topic.StartsWith("ZoomNet Integration Testing:")) + .Select(async oldWebinar => + { + await client.Webinars.DeleteAsync(oldWebinar.Id, null, cancellationToken).ConfigureAwait(false); + await log.WriteLineAsync($"Webinar {oldWebinar.Id} deleted").ConfigureAwait(false); + await Task.Delay(250, cancellationToken).ConfigureAwait(false); // Brief pause to ensure Zoom has time to catch up + }); + await Task.WhenAll(cleanUpTasks).ConfigureAwait(false); + + var settings = new WebinarSettings() + { + ApprovalType = MeetingApprovalType.Manual + }; + var trackingFields = new Dictionary() + { + { "field1", "value1"}, + { "field2", "value2"} + }; + + // Scheduled webinar + var start = DateTime.UtcNow.AddMonths(1); + var duration = 30; + var newScheduledWebinar = await client.Webinars.CreateScheduledWebinarAsync(userId, "ZoomNet Integration Testing: scheduled webinar", "The agenda", start, duration, "p@ss!w0rd", settings, trackingFields, cancellationToken).ConfigureAwait(false); + await log.WriteLineAsync($"Scheduled webinar {newScheduledWebinar.Id} created").ConfigureAwait(false); + + await client.Webinars.DeleteAsync(newScheduledWebinar.Id, null, cancellationToken).ConfigureAwait(false); + await log.WriteLineAsync($"Scheduled webinar {newScheduledWebinar.Id} deleted").ConfigureAwait(false); + + // Recurring webinar + var recurrenceInfo = new RecurrenceInfo() + { + EndTimes = 2, + WeeklyDays = new[] { DayOfWeek.Monday, DayOfWeek.Friday }, + Type = RecurrenceType.Weekly + }; + var newRecurringWebinar = await client.Webinars.CreateRecurringWebinarAsync(userId, "ZoomNet Integration Testing: recurring webinar", "The agenda", start, duration, recurrenceInfo, "p@ss!w0rd", settings, trackingFields, cancellationToken).ConfigureAwait(false); + await log.WriteLineAsync($"Recurring webinar {newRecurringWebinar.Id} created").ConfigureAwait(false); + + await client.Webinars.DeleteAsync(newRecurringWebinar.Id, null, cancellationToken).ConfigureAwait(false); + await log.WriteLineAsync($"Recurring webinar {newRecurringWebinar.Id} deleted").ConfigureAwait(false); + } + } +} diff --git a/Source/ZoomNet.IntegrationTests/TestsRunner.cs b/Source/ZoomNet.IntegrationTests/TestsRunner.cs index e067b69c..249e02ff 100644 --- a/Source/ZoomNet.IntegrationTests/TestsRunner.cs +++ b/Source/ZoomNet.IntegrationTests/TestsRunner.cs @@ -97,6 +97,7 @@ public async Task RunAsync() var integrationTests = new Type[] { typeof(Meetings), + typeof(Webinars), }; // Execute the async tests in parallel (with max degree of parallelism)