From c1c477e4e6021f2a090de6d2c64bae4804f06814 Mon Sep 17 00:00:00 2001 From: Jericho Date: Fri, 20 Dec 2024 09:46:04 -0500 Subject: [PATCH 01/14] Remove net6 and net7 Resolves #364 Resolves #365 --- Source/ZoomNet/ZoomNet.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/ZoomNet/ZoomNet.csproj b/Source/ZoomNet/ZoomNet.csproj index a1f2e52e..7cd42597 100644 --- a/Source/ZoomNet/ZoomNet.csproj +++ b/Source/ZoomNet/ZoomNet.csproj @@ -1,7 +1,7 @@ - net48;netstandard2.1;net6.0;net7.0 + net48;netstandard2.1 preview anycpu true From cabde62a10d98f0e1b69878ae6dc168324f522e1 Mon Sep 17 00:00:00 2001 From: Jericho Date: Thu, 26 Dec 2024 14:38:43 -0500 Subject: [PATCH 02/14] Refresh build script --- build.cake | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/build.cake b/build.cake index a38e16db..9bc0f0ff 100644 --- a/build.cake +++ b/build.cake @@ -2,7 +2,7 @@ #tool dotnet:?package=GitVersion.Tool&version=6.1.0 #tool dotnet:?package=coveralls.net&version=4.0.1 #tool nuget:https://f.feedz.io/jericho/jericho/nuget/?package=GitReleaseManager&version=0.17.0-collaborators0008 -#tool nuget:?package=ReportGenerator&version=5.4.1 +#tool nuget:?package=ReportGenerator&version=5.4.2 #tool nuget:?package=xunit.runner.console&version=2.9.2 #tool nuget:?package=CodecovUploader&version=0.8.0 @@ -63,7 +63,7 @@ var sourceFolder = "./Source/"; var outputDir = "./artifacts/"; var codeCoverageDir = $"{outputDir}CodeCoverage/"; var benchmarkDir = $"{outputDir}Benchmark/"; -var coverageFile = $"{codeCoverageDir}coverage.{DefaultFramework}.xml"; +var coverageFile = $"{codeCoverageDir}coverage.{DEFAULT_FRAMEWORK}.xml"; var solutionFile = $"{sourceFolder}{libraryName}.sln"; var sourceProject = $"{sourceFolder}{libraryName}/{libraryName}.csproj"; @@ -96,13 +96,12 @@ var publishingError = false; // - when building source project on Ubuntu // - when running unit tests on Ubuntu // - when calculating code coverage -const string DefaultFramework = "net9.0"; +const string DEFAULT_FRAMEWORK = "net9.0"; var isSingleTfmMode = !IsRunningOnWindows() || target.Equals("Coverage", StringComparison.OrdinalIgnoreCase) || target.Equals("Run-Code-Coverage", StringComparison.OrdinalIgnoreCase) || target.Equals("Generate-Code-Coverage-Report", StringComparison.OrdinalIgnoreCase) || target.Equals("Upload-Coverage-Result", StringComparison.OrdinalIgnoreCase); -var desiredFramework = isSingleTfmMode ? DefaultFramework : null; /////////////////////////////////////////////////////////////////////////////// @@ -186,8 +185,8 @@ Setup(context => foreach(var projectFile in GetFiles("./Source/**/*.csproj")) { Information("Updating TFM in: {0}", projectFile.ToString()); - if (XmlPeek(projectFile, "/Project/PropertyGroup/TargetFramework", peekSettings) != null) XmlPoke(projectFile, "/Project/PropertyGroup/TargetFramework", desiredFramework); - if (XmlPeek(projectFile, "/Project/PropertyGroup/TargetFrameworks", peekSettings) != null) XmlPoke(projectFile, "/Project/PropertyGroup/TargetFrameworks", desiredFramework); + if (XmlPeek(projectFile, "/Project/PropertyGroup/TargetFramework", peekSettings) != null) XmlPoke(projectFile, "/Project/PropertyGroup/TargetFramework", DEFAULT_FRAMEWORK); + if (XmlPeek(projectFile, "/Project/PropertyGroup/TargetFrameworks", peekSettings) != null) XmlPoke(projectFile, "/Project/PropertyGroup/TargetFrameworks", DEFAULT_FRAMEWORK); } } }); @@ -205,11 +204,7 @@ Teardown(context => if (isSingleTfmMode) { Information("Restoring project files that may have been modified during build script setup"); - foreach(var projectFile in GetFiles("./Source/**/*.csproj")) - { - GitCheckout(".", new FilePath[] { projectFile }); - Information(" Restored {0}", projectFile.ToString()); - } + GitCheckout(".", GetFiles("./Source/**/*.csproj").ToArray()); Information(""); } @@ -270,7 +265,7 @@ Task("Build") DotNetBuild(solutionFile, new DotNetBuildSettings { Configuration = configuration, - Framework = desiredFramework, + Framework = isSingleTfmMode ? DEFAULT_FRAMEWORK : null, NoRestore = true, MSBuildSettings = new DotNetMSBuildSettings { @@ -293,7 +288,7 @@ Task("Run-Unit-Tests") NoBuild = true, NoRestore = true, Configuration = configuration, - Framework = desiredFramework + Framework = isSingleTfmMode ? DEFAULT_FRAMEWORK : null }); }); @@ -307,7 +302,7 @@ Task("Run-Code-Coverage") NoBuild = true, NoRestore = true, Configuration = configuration, - Framework = DefaultFramework, + Framework = isSingleTfmMode ? DEFAULT_FRAMEWORK : null, // The following assumes that coverlet.msbuild has been added to the unit testing project ArgumentCustomization = args => args From 55a42051002ebb456518a91ec2085ff520f110a6 Mon Sep 17 00:00:00 2001 From: Jericho Date: Thu, 26 Dec 2024 14:38:57 -0500 Subject: [PATCH 03/14] Formatting --- Source/ZoomNet.IntegrationTests/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/ZoomNet.IntegrationTests/Program.cs b/Source/ZoomNet.IntegrationTests/Program.cs index 1d529b7e..be0006f8 100644 --- a/Source/ZoomNet.IntegrationTests/Program.cs +++ b/Source/ZoomNet.IntegrationTests/Program.cs @@ -34,7 +34,6 @@ private static void ConfigureServices(ServiceCollection services) { services.AddHostedService(); - services .AddLogging(logging => { From 70e893474c7dc85ed514d890ee60f5334a310759 Mon Sep 17 00:00:00 2001 From: Jericho Date: Thu, 26 Dec 2024 14:39:15 -0500 Subject: [PATCH 04/14] Upgrade nuget packages --- Source/ZoomNet.UnitTests/ZoomNet.UnitTests.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/ZoomNet.UnitTests/ZoomNet.UnitTests.csproj b/Source/ZoomNet.UnitTests/ZoomNet.UnitTests.csproj index abd2a6e4..49bdb74f 100644 --- a/Source/ZoomNet.UnitTests/ZoomNet.UnitTests.csproj +++ b/Source/ZoomNet.UnitTests/ZoomNet.UnitTests.csproj @@ -12,7 +12,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all @@ -21,7 +21,7 @@ - + all runtime; build; native; contentfiles; analyzers From 67757a336d4e40fb32cf8c846a34e7e1c4c4bb6d Mon Sep 17 00:00:00 2001 From: Jericho Date: Fri, 10 Jan 2025 18:51:51 -0500 Subject: [PATCH 05/14] Add one more unit test for ParticipantDevice parsing --- Source/ZoomNet.UnitTests/Json/ParticipantDeviceConverter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/ZoomNet.UnitTests/Json/ParticipantDeviceConverter.cs b/Source/ZoomNet.UnitTests/Json/ParticipantDeviceConverter.cs index 9113e071..68488780 100644 --- a/Source/ZoomNet.UnitTests/Json/ParticipantDeviceConverter.cs +++ b/Source/ZoomNet.UnitTests/Json/ParticipantDeviceConverter.cs @@ -76,6 +76,7 @@ public void Write_multiple() [InlineData("win 11", ParticipantDevice.Windows)] [InlineData("Zoom Rooms", ParticipantDevice.ZoomRoom)] [InlineData("win 10+ 17763", ParticipantDevice.Windows)] + [InlineData("Web Browser", ParticipantDevice.Web)] [InlineData("Web Browser Chrome 129", ParticipantDevice.Web)] [InlineData("Web Browser Chrome 130", ParticipantDevice.Web)] [InlineData("Windows 10", ParticipantDevice.Windows)] From df3987b273ca9dae7d478361144203f43e68c039 Mon Sep 17 00:00:00 2001 From: Jericho Date: Fri, 10 Jan 2025 19:41:56 -0500 Subject: [PATCH 06/14] Upgrade the Formitable.BetterStack.Logger packages to 0.1.3-beta0001 (which I created) while I wait for the bug I reported to be resolved. See: https://github.com/Formitable/BetterStack.Logger/issues/1 --- Source/ZoomNet.IntegrationTests/ZoomNet.IntegrationTests.csproj | 2 +- Source/ZoomNet/ZoomNet.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/ZoomNet.IntegrationTests/ZoomNet.IntegrationTests.csproj b/Source/ZoomNet.IntegrationTests/ZoomNet.IntegrationTests.csproj index f79e132b..f688b971 100644 --- a/Source/ZoomNet.IntegrationTests/ZoomNet.IntegrationTests.csproj +++ b/Source/ZoomNet.IntegrationTests/ZoomNet.IntegrationTests.csproj @@ -12,7 +12,7 @@ - + diff --git a/Source/ZoomNet/ZoomNet.csproj b/Source/ZoomNet/ZoomNet.csproj index 7cd42597..e6e6c3dc 100644 --- a/Source/ZoomNet/ZoomNet.csproj +++ b/Source/ZoomNet/ZoomNet.csproj @@ -41,7 +41,7 @@ - + From 8def4c0aa13e23c55a7f286270cbe6a6dbf36f1d Mon Sep 17 00:00:00 2001 From: pvgritsenko-ansible <165666478+pvgritsenko-ansible@users.noreply.github.com> Date: Mon, 13 Jan 2025 17:10:24 +0300 Subject: [PATCH 07/14] Add external contacts endpoints (#385) * Add external contacts endpoints * Remove extra string interpolation operator * Rename new methods --------- Co-authored-by: P. Gritsenko Co-authored-by: Jericho <112710+Jericho@users.noreply.github.com> Co-authored-by: Ivan Pavlik --- Source/ZoomNet/IZoomClient.cs | 5 + .../Json/ZoomNetJsonSerializerContext.cs | 6 +- .../CallHandlingSettings/ExternalContact.cs | 16 ---- Source/ZoomNet/Models/ExternalContact.cs | 23 +++++ .../ZoomNet/Models/ExternalContactDetails.cs | 54 +++++++++++ Source/ZoomNet/Resources/ExternalContacts.cs | 94 +++++++++++++++++++ Source/ZoomNet/Resources/IExternalContacts.cs | 69 ++++++++++++++ Source/ZoomNet/ZoomClient.cs | 4 + 8 files changed, 253 insertions(+), 18 deletions(-) delete mode 100644 Source/ZoomNet/Models/CallHandlingSettings/ExternalContact.cs create mode 100644 Source/ZoomNet/Models/ExternalContact.cs create mode 100644 Source/ZoomNet/Models/ExternalContactDetails.cs create mode 100644 Source/ZoomNet/Resources/ExternalContacts.cs create mode 100644 Source/ZoomNet/Resources/IExternalContacts.cs diff --git a/Source/ZoomNet/IZoomClient.cs b/Source/ZoomNet/IZoomClient.cs index 4bba5eb2..bb0f6fde 100644 --- a/Source/ZoomNet/IZoomClient.cs +++ b/Source/ZoomNet/IZoomClient.cs @@ -57,6 +57,11 @@ public interface IZoomClient [Obsolete("The Data Compliance API is deprecated")] IDataCompliance DataCompliance { get; } + /// + /// Gets the resource which allows you to handle Zoom phone external contacts. + /// + IExternalContacts ExternalContacts { get; } + /// /// Gets the resource that allows you to manage groups. /// diff --git a/Source/ZoomNet/Json/ZoomNetJsonSerializerContext.cs b/Source/ZoomNet/Json/ZoomNetJsonSerializerContext.cs index 4d32b395..c6e8fd2e 100644 --- a/Source/ZoomNet/Json/ZoomNetJsonSerializerContext.cs +++ b/Source/ZoomNet/Json/ZoomNetJsonSerializerContext.cs @@ -34,7 +34,6 @@ namespace ZoomNet.Json [JsonSerializable(typeof(ZoomNet.Models.CallHandlingSettings.CustomHoursChildSubsettings), TypeInfoPropertyName = "CallHandlingSettingsCustomHoursChildSubsettings")] [JsonSerializable(typeof(ZoomNet.Models.CallHandlingSettings.CustomHoursSubsettings), TypeInfoPropertyName = "CallHandlingSettingsCustomHoursSubsettings")] [JsonSerializable(typeof(ZoomNet.Models.CallHandlingSettings.CustomHoursType), TypeInfoPropertyName = "CallHandlingSettingsCustomHoursType")] - [JsonSerializable(typeof(ZoomNet.Models.CallHandlingSettings.ExternalContact), TypeInfoPropertyName = "CallHandlingSettingsExternalContact")] [JsonSerializable(typeof(ZoomNet.Models.CallHandlingSettings.HolidaySubsettings), TypeInfoPropertyName = "CallHandlingSettingsHolidaySubsettings")] [JsonSerializable(typeof(ZoomNet.Models.CallHandlingSettings.RingModeType), TypeInfoPropertyName = "CallHandlingSettingsRingModeType")] [JsonSerializable(typeof(ZoomNet.Models.CallingPlan))] @@ -113,6 +112,8 @@ namespace ZoomNet.Json [JsonSerializable(typeof(ZoomNet.Models.EmailNotificationUserSettings))] [JsonSerializable(typeof(ZoomNet.Models.EmergencyAddress))] [JsonSerializable(typeof(ZoomNet.Models.EncryptionType))] + [JsonSerializable(typeof(ZoomNet.Models.ExternalContact))] + [JsonSerializable(typeof(ZoomNet.Models.ExternalContactDetails))] [JsonSerializable(typeof(ZoomNet.Models.FeatureUserSettings))] [JsonSerializable(typeof(ZoomNet.Models.ImMetric))] [JsonSerializable(typeof(ZoomNet.Models.InstantMeeting))] @@ -353,7 +354,6 @@ namespace ZoomNet.Json [JsonSerializable(typeof(ZoomNet.Models.CallHandlingSettings.CustomHoursChildSubsettings[]), TypeInfoPropertyName = "CallHandlingSettingsCustomHoursChildSubsettingsArray")] [JsonSerializable(typeof(ZoomNet.Models.CallHandlingSettings.CustomHoursSubsettings[]), TypeInfoPropertyName = "CallHandlingSettingsCustomHoursSubsettingsArray")] [JsonSerializable(typeof(ZoomNet.Models.CallHandlingSettings.CustomHoursType[]), TypeInfoPropertyName = "CallHandlingSettingsCustomHoursTypeArray")] - [JsonSerializable(typeof(ZoomNet.Models.CallHandlingSettings.ExternalContact[]), TypeInfoPropertyName = "CallHandlingSettingsExternalContactArray")] [JsonSerializable(typeof(ZoomNet.Models.CallHandlingSettings.HolidaySubsettings[]), TypeInfoPropertyName = "CallHandlingSettingsHolidaySubsettingsArray")] [JsonSerializable(typeof(ZoomNet.Models.CallHandlingSettings.RingModeType[]), TypeInfoPropertyName = "CallHandlingSettingsRingModeTypeArray")] [JsonSerializable(typeof(ZoomNet.Models.CallingPlan[]))] @@ -432,6 +432,8 @@ namespace ZoomNet.Json [JsonSerializable(typeof(ZoomNet.Models.EmailNotificationUserSettings[]))] [JsonSerializable(typeof(ZoomNet.Models.EmergencyAddress[]))] [JsonSerializable(typeof(ZoomNet.Models.EncryptionType[]))] + [JsonSerializable(typeof(ZoomNet.Models.ExternalContact[]))] + [JsonSerializable(typeof(ZoomNet.Models.ExternalContactDetails[]))] [JsonSerializable(typeof(ZoomNet.Models.FeatureUserSettings[]))] [JsonSerializable(typeof(ZoomNet.Models.ImMetric[]))] [JsonSerializable(typeof(ZoomNet.Models.InstantMeeting[]))] diff --git a/Source/ZoomNet/Models/CallHandlingSettings/ExternalContact.cs b/Source/ZoomNet/Models/CallHandlingSettings/ExternalContact.cs deleted file mode 100644 index 8ea29e15..00000000 --- a/Source/ZoomNet/Models/CallHandlingSettings/ExternalContact.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Text.Json.Serialization; - -namespace ZoomNet.Models.CallHandlingSettings -{ - /// - /// External contact object. It's only required for . - /// - public class ExternalContact - { - /// - /// Gets or sets the external contact's ID. - /// - [JsonPropertyName("external_contact_id")] - public string ExternalContactId { get; set; } - } -} diff --git a/Source/ZoomNet/Models/ExternalContact.cs b/Source/ZoomNet/Models/ExternalContact.cs new file mode 100644 index 00000000..6d72538f --- /dev/null +++ b/Source/ZoomNet/Models/ExternalContact.cs @@ -0,0 +1,23 @@ +using System.Text.Json.Serialization; + +namespace ZoomNet.Models +{ + /// + /// Zoom phone external contact model. + /// + public class ExternalContact + { + /// + /// Gets or sets the Zoom-generated external contact Id. + /// Don't set it on external contact creation, it will be generated automatically. + /// + [JsonPropertyName("external_contact_id")] + public string ExternalContactId { get; set; } + + /// + /// Gets or sets the external contact's username or extension display name. + /// + [JsonPropertyName("name")] + public string Name { get; set; } + } +} diff --git a/Source/ZoomNet/Models/ExternalContactDetails.cs b/Source/ZoomNet/Models/ExternalContactDetails.cs new file mode 100644 index 00000000..9163742d --- /dev/null +++ b/Source/ZoomNet/Models/ExternalContactDetails.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace ZoomNet.Models +{ + /// + /// Zoom phone external contact details model. + /// + public class ExternalContactDetails : ExternalContact + { + /// + /// Gets or sets the external contact's description. + /// + [JsonPropertyName("description")] + public string Description { get; set; } + + /// + /// Gets or sets the external contact's email address. + /// + [JsonPropertyName("email")] + public string Email { get; set; } + + /// + /// Gets or sets the external contact's extension number. + /// + [JsonPropertyName("extension_number")] + public string ExtensionNumber { get; set; } + + /// + /// Gets or sets the customer-configured external contact ID. + /// If it is not set id will be generated automatically. + /// + [JsonPropertyName("id")] + public string Id { get; set; } + + /// + /// Gets or sets the external contact's phone numbers. + /// + [JsonPropertyName("phone_numbers")] + public List PhoneNumbers { get; set; } + + /// + /// Gets or sets the external contact's SIP group, to define the call routing path. This is for customers that use SIP trunking. + /// + [JsonPropertyName("routing_path")] + public string RoutingPath { get; set; } + + /// + /// Gets or sets a value indicating whether to allow the automatic call recording. + /// + [JsonPropertyName("auto_call_recorded")] + public bool AutoCallRecorded { get; set; } + } +} diff --git a/Source/ZoomNet/Resources/ExternalContacts.cs b/Source/ZoomNet/Resources/ExternalContacts.cs new file mode 100644 index 00000000..981949dd --- /dev/null +++ b/Source/ZoomNet/Resources/ExternalContacts.cs @@ -0,0 +1,94 @@ +using Pathoschild.Http.Client; +using System; +using System.Threading; +using System.Threading.Tasks; +using ZoomNet; +using ZoomNet.Models; + +namespace ZoomNet.Resources +{ + /// + public class ExternalContacts : IExternalContacts + { + private readonly IClient _client; + + /// + /// Initializes a new instance of the class. + /// + /// The HTTP client. + internal ExternalContacts(IClient client) + { + _client = client; + } + + /// + public Task> GetAllAsync(int pageSize = 30, string nextPageToken = null, CancellationToken cancellationToken = default) + { + if (pageSize < 1 || pageSize > 300) + { + throw new ArgumentOutOfRangeException(nameof(pageSize), "Page size must be between 1 and 300"); + } + + return _client + .GetAsync("phone/external_contacts") + .WithArgument("page_size", pageSize) + .WithArgument("next_page_token", nextPageToken) + .WithCancellationToken(cancellationToken) + .AsPaginatedResponseWithToken("external_contacts"); + } + + /// + public Task GetDetailsAsync(string externalContactId, CancellationToken cancellationToken = default) + { + if (string.IsNullOrEmpty(externalContactId)) + { + throw new ArgumentException(nameof(externalContactId), "External contact id is not set."); + } + + return _client + .GetAsync($"phone/external_contacts/{externalContactId}") + .WithCancellationToken(cancellationToken) + .AsObject(); + } + + /// + public Task AddAsync(ExternalContactDetails externalContact, CancellationToken cancellationToken = default) + { + return _client + .PostAsync("phone/external_contacts") + .WithJsonBody(externalContact) + .WithCancellationToken(cancellationToken) + .AsObject(); + } + + /// + public Task DeleteAsync(string externalContactId, CancellationToken cancellationToken = default) + { + if (string.IsNullOrEmpty(externalContactId)) + { + throw new ArgumentException(nameof(externalContactId), "External contact id is not set."); + } + + return _client + .DeleteAsync($"phone/external_contacts/{externalContactId}") + .WithCancellationToken(cancellationToken) + .AsMessage(); + } + + /// + public Task UpdateAsync( + ExternalContactDetails externalContact, CancellationToken cancellationToken = default) + { + if (string.IsNullOrEmpty(externalContact.ExternalContactId)) + { + throw new ArgumentException(nameof(externalContact.ExternalContactId), "External contact id is not set."); + } + + return _client + .PatchAsync($"phone/external_contacts/{externalContact.ExternalContactId}") + .WithJsonBody(externalContact) + .WithCancellationToken(cancellationToken) + .AsMessage(); + } + } +} diff --git a/Source/ZoomNet/Resources/IExternalContacts.cs b/Source/ZoomNet/Resources/IExternalContacts.cs new file mode 100644 index 00000000..7925a111 --- /dev/null +++ b/Source/ZoomNet/Resources/IExternalContacts.cs @@ -0,0 +1,69 @@ +using System.Threading; +using System.Threading.Tasks; +using ZoomNet.Models; + +namespace ZoomNet.Resources +{ + /// + /// Allows you to access Zoom Phone API endpoints responsible for setting and retrieving data of external contacts. + /// + /// + /// See + /// Zoom API documentation for more information. + /// + public interface IExternalContacts + { + /// + /// Retrieves a list of all of an account's external contacts. + /// + /// The number of records returned from a single API call. Default is 30. + /// + /// The next page token paginates through a large set of results. + /// A next page token is returned whenever the set of available results exceeds the current page size. + /// + /// A cancellation token that can be used to cancel the asynchronous operation. + /// + /// A task representing the asynchronous operation. The task result contains an array of external contacts in type of . + /// + Task> GetAllAsync( + int pageSize = 30, + string nextPageToken = null, + CancellationToken cancellationToken = default); + + /// + /// Gets an external contact's information. + /// + /// The external contact id. + /// A cancellation token that can be used to cancel the asynchronous operation. + /// A task representing the asynchronous operation. The task result contains external contact details. + Task GetDetailsAsync( + string externalContactId, CancellationToken cancellationToken = default); + + /// + /// Adds an external contact. + /// + /// The external contact information. + /// A cancellation token that can be used to cancel the asynchronous operation. + /// A task representing the asynchronous operation. The task result contains external contact details. + Task AddAsync( + ExternalContactDetails externalContact, CancellationToken cancellationToken = default); + + /// + /// Removes an external contact. + /// + /// The external contact id. + /// A cancellation token that can be used to cancel the asynchronous operation. + /// A task representing the asynchronous operation. + Task DeleteAsync( + string externalContactId, CancellationToken cancellationToken = default); + + /// + /// Update an external contact information by . + /// + /// External contact information. + /// A cancellation token that can be used to cancel the asynchronous operation. + /// A task representing the asynchronous operation. + Task UpdateAsync( + ExternalContactDetails externalContact, CancellationToken cancellationToken = default); + } +} diff --git a/Source/ZoomNet/ZoomClient.cs b/Source/ZoomNet/ZoomClient.cs index 3f8812d6..ac16175b 100644 --- a/Source/ZoomNet/ZoomClient.cs +++ b/Source/ZoomNet/ZoomClient.cs @@ -91,6 +91,9 @@ public static string Version [Obsolete("The Data Compliance API is deprecated")] public IDataCompliance DataCompliance { get; private set; } + /// + public IExternalContacts ExternalContacts { get; private set; } + /// public IGroups Groups { get; private set; } @@ -219,6 +222,7 @@ private ZoomClient(IConnectionInfo connectionInfo, HttpClient httpClient, bool d Contacts = new Contacts(_fluentClient); Dashboards = new Dashboards(_fluentClient); DataCompliance = new DataCompliance(_fluentClient); + ExternalContacts = new ExternalContacts(_fluentClient); Groups = new Groups(_fluentClient); Meetings = new Meetings(_fluentClient); PastMeetings = new PastMeetings(_fluentClient); From 249887b3c2fc04db8a78eed741efae2deaea1874 Mon Sep 17 00:00:00 2001 From: Jericho Date: Mon, 13 Jan 2025 09:16:34 -0500 Subject: [PATCH 08/14] Throw ArgumentNullException rather than ArgumentException when a parameter is not provided --- Source/ZoomNet/Resources/ExternalContacts.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/ZoomNet/Resources/ExternalContacts.cs b/Source/ZoomNet/Resources/ExternalContacts.cs index 981949dd..65de9e23 100644 --- a/Source/ZoomNet/Resources/ExternalContacts.cs +++ b/Source/ZoomNet/Resources/ExternalContacts.cs @@ -42,7 +42,7 @@ public Task GetDetailsAsync(string externalContactId, Ca { if (string.IsNullOrEmpty(externalContactId)) { - throw new ArgumentException(nameof(externalContactId), "External contact id is not set."); + throw new ArgumentNullException(nameof(externalContactId)); } return _client @@ -66,7 +66,7 @@ public Task DeleteAsync(string externalContactId, CancellationToken cancellation { if (string.IsNullOrEmpty(externalContactId)) { - throw new ArgumentException(nameof(externalContactId), "External contact id is not set."); + throw new ArgumentNullException(nameof(externalContactId)); } return _client @@ -81,7 +81,7 @@ public Task UpdateAsync( { if (string.IsNullOrEmpty(externalContact.ExternalContactId)) { - throw new ArgumentException(nameof(externalContact.ExternalContactId), "External contact id is not set."); + throw new ArgumentNullException($"{nameof(externalContact)}.{nameof(externalContact.Id)}"); } return _client From 915cb3b1e9cb599e2745a06c1195dacd9302190d Mon Sep 17 00:00:00 2001 From: Jericho Date: Mon, 13 Jan 2025 09:17:21 -0500 Subject: [PATCH 09/14] Integration tests for External Contacts. Unfortunately, it's not working for me because I don't have Phone enabled on my Zoom account. --- .../TestSuites/ApiTestSuite.cs | 1 + .../Tests/ExternalContacts.cs | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 Source/ZoomNet.IntegrationTests/Tests/ExternalContacts.cs diff --git a/Source/ZoomNet.IntegrationTests/TestSuites/ApiTestSuite.cs b/Source/ZoomNet.IntegrationTests/TestSuites/ApiTestSuite.cs index 61dba1ec..87ac3746 100644 --- a/Source/ZoomNet.IntegrationTests/TestSuites/ApiTestSuite.cs +++ b/Source/ZoomNet.IntegrationTests/TestSuites/ApiTestSuite.cs @@ -15,6 +15,7 @@ internal class ApiTestSuite : TestSuite typeof(CloudRecordings), typeof(Contacts), typeof(Dashboards), + typeof(ExternalContacts), typeof(Meetings), typeof(Reports), typeof(Roles), diff --git a/Source/ZoomNet.IntegrationTests/Tests/ExternalContacts.cs b/Source/ZoomNet.IntegrationTests/Tests/ExternalContacts.cs new file mode 100644 index 00000000..a0ad7bd0 --- /dev/null +++ b/Source/ZoomNet.IntegrationTests/Tests/ExternalContacts.cs @@ -0,0 +1,21 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using ZoomNet.Models; + +namespace ZoomNet.IntegrationTests.Tests +{ + public class ExternalContacts : IIntegrationTest + { + public async Task RunAsync(User myUser, string[] myPermissions, IZoomClient client, TextWriter log, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) return; + + await log.WriteLineAsync("\n***** EXTERNAL CONTACTS *****\n").ConfigureAwait(false); + + // GET ALL THE EXTERNAL CONTACTS + var paginatedContacts = await client.ExternalContacts.GetAllAsync(100, null, cancellationToken).ConfigureAwait(false); + await log.WriteLineAsync($"There are {paginatedContacts.TotalRecords} external contacts under the main account").ConfigureAwait(false); + } + } +} From 10703bc921bd0d022405ab75cdf44193b9153fdd Mon Sep 17 00:00:00 2001 From: Jericho Date: Mon, 13 Jan 2025 09:17:39 -0500 Subject: [PATCH 10/14] Refresh build script --- build.cake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.cake b/build.cake index 9bc0f0ff..db655fc8 100644 --- a/build.cake +++ b/build.cake @@ -2,8 +2,8 @@ #tool dotnet:?package=GitVersion.Tool&version=6.1.0 #tool dotnet:?package=coveralls.net&version=4.0.1 #tool nuget:https://f.feedz.io/jericho/jericho/nuget/?package=GitReleaseManager&version=0.17.0-collaborators0008 -#tool nuget:?package=ReportGenerator&version=5.4.2 -#tool nuget:?package=xunit.runner.console&version=2.9.2 +#tool nuget:?package=ReportGenerator&version=5.4.3 +#tool nuget:?package=xunit.runner.console&version=2.9.3 #tool nuget:?package=CodecovUploader&version=0.8.0 // Install addins. From d4f8abc2caf1dbb09975781e3f0aa0589c92e5d7 Mon Sep 17 00:00:00 2001 From: Jericho Date: Mon, 13 Jan 2025 09:19:32 -0500 Subject: [PATCH 11/14] Improve check for null --- Source/ZoomNet/Resources/ExternalContacts.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/ZoomNet/Resources/ExternalContacts.cs b/Source/ZoomNet/Resources/ExternalContacts.cs index 65de9e23..bd7d574e 100644 --- a/Source/ZoomNet/Resources/ExternalContacts.cs +++ b/Source/ZoomNet/Resources/ExternalContacts.cs @@ -79,9 +79,13 @@ public Task DeleteAsync(string externalContactId, CancellationToken cancellation public Task UpdateAsync( ExternalContactDetails externalContact, CancellationToken cancellationToken = default) { - if (string.IsNullOrEmpty(externalContact.ExternalContactId)) + if (externalContact == null) { - throw new ArgumentNullException($"{nameof(externalContact)}.{nameof(externalContact.Id)}"); + throw new ArgumentNullException(nameof(externalContact)); + } + else if (string.IsNullOrEmpty(externalContact.ExternalContactId)) + { + throw new ArgumentNullException($"{nameof(externalContact)}.{nameof(externalContact.ExternalContactId)}"); } return _client From 262939a8bdf88a823f09cc1d12e8d83ac03badd6 Mon Sep 17 00:00:00 2001 From: Jericho Date: Mon, 13 Jan 2025 11:09:31 -0500 Subject: [PATCH 12/14] ZoomNet should allow a range of acceptable versions for certain NuGet packages The goal is to reduce the likelihood of version conflict when developers reference the same packages --- .../ZoomNet.IntegrationTests/ZoomNet.IntegrationTests.csproj | 1 + Source/ZoomNet.UnitTests/ZoomNet.UnitTests.csproj | 1 + Source/ZoomNet/ZoomNet.csproj | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/ZoomNet.IntegrationTests/ZoomNet.IntegrationTests.csproj b/Source/ZoomNet.IntegrationTests/ZoomNet.IntegrationTests.csproj index f688b971..0c15fc19 100644 --- a/Source/ZoomNet.IntegrationTests/ZoomNet.IntegrationTests.csproj +++ b/Source/ZoomNet.IntegrationTests/ZoomNet.IntegrationTests.csproj @@ -15,6 +15,7 @@ + diff --git a/Source/ZoomNet.UnitTests/ZoomNet.UnitTests.csproj b/Source/ZoomNet.UnitTests/ZoomNet.UnitTests.csproj index 49bdb74f..ff7a4c76 100644 --- a/Source/ZoomNet.UnitTests/ZoomNet.UnitTests.csproj +++ b/Source/ZoomNet.UnitTests/ZoomNet.UnitTests.csproj @@ -12,6 +12,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Source/ZoomNet/ZoomNet.csproj b/Source/ZoomNet/ZoomNet.csproj index e6e6c3dc..35184566 100644 --- a/Source/ZoomNet/ZoomNet.csproj +++ b/Source/ZoomNet/ZoomNet.csproj @@ -38,11 +38,11 @@ - + - + From 37a85ecded00481cc3563ddb650890ece75179d2 Mon Sep 17 00:00:00 2001 From: Jericho Date: Mon, 13 Jan 2025 11:18:09 -0500 Subject: [PATCH 13/14] Upgrade NuGet package references --- Source/ZoomNet.UnitTests/ZoomNet.UnitTests.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/ZoomNet.UnitTests/ZoomNet.UnitTests.csproj b/Source/ZoomNet.UnitTests/ZoomNet.UnitTests.csproj index 49bdb74f..376dc6e7 100644 --- a/Source/ZoomNet.UnitTests/ZoomNet.UnitTests.csproj +++ b/Source/ZoomNet.UnitTests/ZoomNet.UnitTests.csproj @@ -8,7 +8,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -20,8 +20,8 @@ - - + + all runtime; build; native; contentfiles; analyzers From c53c3a6024fedd653602297f57d17c49ef8d6911 Mon Sep 17 00:00:00 2001 From: Jericho Date: Mon, 13 Jan 2025 20:03:28 -0500 Subject: [PATCH 14/14] Set the upper limit of the version range Also add a comment so my future self knows not to upgrade these references unless a new major version of ystem.Text.Json or Microsoft.Extensions.Logging is released --- Source/ZoomNet/ZoomNet.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/ZoomNet/ZoomNet.csproj b/Source/ZoomNet/ZoomNet.csproj index 35184566..792d4171 100644 --- a/Source/ZoomNet/ZoomNet.csproj +++ b/Source/ZoomNet/ZoomNet.csproj @@ -38,11 +38,11 @@ - + - +