From 4ad8a5380d17bb053cbcbd1139f9765018e39b1c Mon Sep 17 00:00:00 2001 From: Jericho Date: Fri, 6 Dec 2024 09:55:25 -0500 Subject: [PATCH 1/6] Simplify the ChatBot interface --- Source/ZoomNet/Extensions/Public.cs | 38 ++++++++++++++++++++++++++++ Source/ZoomNet/Resources/Chatbot.cs | 12 --------- Source/ZoomNet/Resources/IChatbot.cs | 29 --------------------- 3 files changed, 38 insertions(+), 41 deletions(-) diff --git a/Source/ZoomNet/Extensions/Public.cs b/Source/ZoomNet/Extensions/Public.cs index 6991b196..0f4d052a 100644 --- a/Source/ZoomNet/Extensions/Public.cs +++ b/Source/ZoomNet/Extensions/Public.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using ZoomNet.Models; +using ZoomNet.Models.ChatbotMessage; using ZoomNet.Models.Webhooks; using ZoomNet.Resources; @@ -385,5 +386,42 @@ public static bool HasPermission(this IZoomClient client, string scope) { return client.HasPermissions(new[] { scope }); } + + /// + /// Send a Chatbot message. + /// + /// The chatbot resource. + /// The account ID to which the message was sent. + /// The JID of group channel or user to whom the message should be sent. + /// The robot JID. + /// The simple text message to send. + /// True if the message contains markdown syntax. + /// The cancellation token. + /// + /// The async task. + /// + public static Task SendMessageAsync(this IChatbot chatbotResource, string accountId, string toJId, string robotJId, string message, bool enableMarkdownSupport = false, CancellationToken cancellationToken = default) + { + return chatbotResource.SendMessageAsync(accountId, toJId, robotJId, new ChatbotContent() { Head = new ChatbotHeader(message) }, enableMarkdownSupport, cancellationToken); + } + + /// + /// Edit a Chatbot message. + /// + /// The chatbot resource. + /// The message ID of the message to edit. + /// The account ID to which the message was sent. + /// The JID of group channel or user to whom the message should be sent. + /// The robot JID. + /// The simple text message to send. + /// True if the message contains markdown syntax. + /// The cancellation token. + /// + /// The async task. + /// + public static Task EditMessageAsync(this IChatbot chatbotResource, string messageId, string accountId, string toJId, string robotJId, string message, bool enableMarkdownSupport = false, CancellationToken cancellationToken = default) + { + return chatbotResource.EditMessageAsync(messageId, accountId, toJId, robotJId, new ChatbotContent() { Head = new ChatbotHeader(message) }, enableMarkdownSupport, cancellationToken); + } } } diff --git a/Source/ZoomNet/Resources/Chatbot.cs b/Source/ZoomNet/Resources/Chatbot.cs index d54b6413..2703d67b 100644 --- a/Source/ZoomNet/Resources/Chatbot.cs +++ b/Source/ZoomNet/Resources/Chatbot.cs @@ -33,12 +33,6 @@ public Task DeleteMessageAsync(string messageId, stri .AsObject(); } - /// - public Task SendMessageAsync(string accountId, string toJId, string robotJId, string message, bool enableMarkdownSupport = false, CancellationToken cancellationToken = default) - { - return SendMessageAsync(accountId, toJId, robotJId, new ChatbotContent() { Head = new ChatbotHeader(message) }, enableMarkdownSupport, cancellationToken); - } - /// public Task SendMessageAsync(string accountId, string toJId, string robotJId, ChatbotContent content, bool enableMarkdownSupport = false, CancellationToken cancellationToken = default) { @@ -60,12 +54,6 @@ public Task SendMessageAsync(string accountId, string .AsObject(); } - /// - public Task EditMessageAsync(string messageId, string accountId, string toJId, string robotJId, string message, bool enableMarkdownSupport = false, CancellationToken cancellationToken = default) - { - return EditMessageAsync(messageId, accountId, toJId, robotJId, new ChatbotContent() { Head = new ChatbotHeader(message) }, enableMarkdownSupport, cancellationToken); - } - /// public Task EditMessageAsync(string messageId, string accountId, string toJId, string robotJId, ChatbotContent content, bool enableMarkdownSupport = false, CancellationToken cancellationToken = default) { diff --git a/Source/ZoomNet/Resources/IChatbot.cs b/Source/ZoomNet/Resources/IChatbot.cs index e314696f..e176b368 100644 --- a/Source/ZoomNet/Resources/IChatbot.cs +++ b/Source/ZoomNet/Resources/IChatbot.cs @@ -26,20 +26,6 @@ public interface IChatbot /// public Task DeleteMessageAsync(string messageId, string accountId, string userJId, string robotJId, CancellationToken cancellationToken = default); - /// - /// Send a Chatbot message. - /// - /// The account ID to which the message was sent. - /// The JID of group channel or user to whom the message should be sent. - /// The robot JID. - /// The simple text message to send. - /// True if the message contains markdown syntax. - /// The cancellation token. - /// - /// The async task. - /// - public Task SendMessageAsync(string accountId, string toJId, string robotJId, string message, bool enableMarkdownSupport = false, CancellationToken cancellationToken = default); - /// /// Send a Chatbot message. /// @@ -54,21 +40,6 @@ public interface IChatbot /// public Task SendMessageAsync(string accountId, string toJId, string robotJId, ChatbotContent content, bool enableMarkdownSupport = false, CancellationToken cancellationToken = default); - /// - /// Edit a Chatbot message. - /// - /// The message ID of the message to edit. - /// The account ID to which the message was sent. - /// The JID of group channel or user to whom the message should be sent. - /// The robot JID. - /// The simple text message to send. - /// True if the message contains markdown syntax. - /// The cancellation token. - /// - /// The async task. - /// - public Task EditMessageAsync(string messageId, string accountId, string toJId, string robotJId, string message, bool enableMarkdownSupport = false, CancellationToken cancellationToken = default); - /// /// Edit a Chatbot message. /// From fbda85a28a0c7af5d55aeb8663225a018b3e5366 Mon Sep 17 00:00:00 2001 From: Jericho Date: Sat, 7 Dec 2024 15:52:36 -0500 Subject: [PATCH 2/6] Improve integration tests to better handle ChatBot testing --- .../ZoomNet.IntegrationTests/TestsRunner.cs | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/Source/ZoomNet.IntegrationTests/TestsRunner.cs b/Source/ZoomNet.IntegrationTests/TestsRunner.cs index 5acecd81..38aaa6e0 100644 --- a/Source/ZoomNet.IntegrationTests/TestsRunner.cs +++ b/Source/ZoomNet.IntegrationTests/TestsRunner.cs @@ -49,11 +49,17 @@ public async Task StartAsync(CancellationToken cancellationToken) var connectionType = ConnectionType.OAuthServerToServer; // ----------------------------------------------------------------------------- + // As far as I know, Zoom only supports ClientCredentials when invoking the methods on the ChatBot endpoint + if (testType == TestType.Chatbot && connectionType != ConnectionType.OAuthClientCredentials) + { + throw new Exception("Zoom only support client credentials when invoking the ChatBot endpoint."); + } + // Configure the proxy if desired var proxy = useProxy ? new WebProxy($"http://localhost:{proxyPort}") : null; // Get the connection info and test suite - var connectionInfo = GetConnectionInfo(connectionType); + var connectionInfo = GetConnectionInfo(connectionType, testType); var testSuite = GetTestSuite(connectionInfo, testType, proxy, _loggerFactory); // Run the tests @@ -65,7 +71,7 @@ public Task StopAsync(CancellationToken cancellationToken) return Task.CompletedTask; } - private static IConnectionInfo GetConnectionInfo(ConnectionType connectionType) + private static IConnectionInfo GetConnectionInfo(ConnectionType connectionType, TestType testType) { // Jwt if (connectionType == ConnectionType.Jwt) @@ -76,11 +82,14 @@ private static IConnectionInfo GetConnectionInfo(ConnectionType connectionType) } // OAuth - var clientId = Environment.GetEnvironmentVariable("ZOOM_OAUTH_CLIENTID", EnvironmentVariableTarget.User); - var clientSecret = Environment.GetEnvironmentVariable("ZOOM_OAUTH_CLIENTSECRET", EnvironmentVariableTarget.User); + var clientIdVariableName = testType == TestType.Chatbot ? "ZOOM_CHATBOT_CLIENTID" : "ZOOM_OAUTH_CLIENTID"; + var clientSecretVariableName = testType == TestType.Chatbot ? "ZOOM_CHATBOT_CLIENTSECRET" : "ZOOM_OAUTH_CLIENTSECRET"; + + var clientId = Environment.GetEnvironmentVariable(clientIdVariableName, EnvironmentVariableTarget.User); + var clientSecret = Environment.GetEnvironmentVariable(clientSecretVariableName, EnvironmentVariableTarget.User); - if (string.IsNullOrEmpty(clientId)) throw new Exception("You must set the ZOOM_OAUTH_CLIENTID environment variable before you can run integration tests."); - if (string.IsNullOrEmpty(clientSecret)) throw new Exception("You must set the ZOOM_OAUTH_CLIENTSECRET environment variable before you can run integration tests."); + if (string.IsNullOrEmpty(clientId)) throw new Exception($"You must set the {clientIdVariableName} environment variable before you can run integration tests."); + if (string.IsNullOrEmpty(clientSecret)) throw new Exception($"You must set the {clientSecretVariableName} environment variable before you can run integration tests."); switch (connectionType) { @@ -111,12 +120,13 @@ private static IConnectionInfo GetConnectionInfo(ConnectionType connectionType) } case ConnectionType.OAuthClientCredentials: { - var accessToken = Environment.GetEnvironmentVariable("ZOOM_OAUTH_CLIENTCREDENTIALS_ACCESSTOKEN", EnvironmentVariableTarget.User); + var accessTokenVariableName = testType == TestType.Chatbot ? "ZOOM_OAUTH_CHATBOT_ACCESSTOKEN" : "ZOOM_OAUTH_CLIENTCREDENTIALS_ACCESSTOKEN"; + var accessToken = Environment.GetEnvironmentVariable(accessTokenVariableName, EnvironmentVariableTarget.User); return OAuthConnectionInfo.WithClientCredentials(clientId, clientSecret, accessToken, (newRefreshToken, newAccessToken) => { - Environment.SetEnvironmentVariable("ZOOM_OAUTH_CLIENTCREDENTIALS_ACCESSTOKEN", newAccessToken, EnvironmentVariableTarget.User); + Environment.SetEnvironmentVariable(accessTokenVariableName, newAccessToken, EnvironmentVariableTarget.User); }); } case ConnectionType.OAuthServerToServer: From 22012a2c5c3823e28ecc691127f898a4f6dd85a8 Mon Sep 17 00:00:00 2001 From: ivan-pavlik <132947719+ivan-pavlik@users.noreply.github.com> Date: Thu, 19 Dec 2024 04:20:34 +0300 Subject: [PATCH 3/6] Fix PresenceStatus enum member values (#383) --- Source/ZoomNet/Models/PresenceStatus.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/ZoomNet/Models/PresenceStatus.cs b/Source/ZoomNet/Models/PresenceStatus.cs index 464979b7..b860d843 100644 --- a/Source/ZoomNet/Models/PresenceStatus.cs +++ b/Source/ZoomNet/Models/PresenceStatus.cs @@ -40,7 +40,7 @@ public enum PresenceStatus /// /// In calendar event. /// - [EnumMember(Value = "In_Calendar_Event")] + [EnumMember(Value = "In_A_Calendar_Event")] InEvent, /// @@ -52,13 +52,13 @@ public enum PresenceStatus /// /// In a Zoom meeting. /// - [EnumMember(Value = "In_A_Zoom_Meeting")] + [EnumMember(Value = "In_A_Meeting")] InMeeting, /// /// On a call. /// - [EnumMember(Value = "On_A_Call")] + [EnumMember(Value = "In_A_Call")] OnCall, /// From f501733fe7636251abf3141138fc9c8f629ba411 Mon Sep 17 00:00:00 2001 From: Jericho Date: Wed, 18 Dec 2024 20:23:38 -0500 Subject: [PATCH 4/6] Refresh build script --- build.cake | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/build.cake b/build.cake index 696fde26..0b050b46 100644 --- a/build.cake +++ b/build.cake @@ -1,5 +1,5 @@ // Install tools. -#tool dotnet:?package=GitVersion.Tool&version=6.0.5 +#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 @@ -8,7 +8,7 @@ // Install addins. #addin nuget:?package=Cake.Coveralls&version=4.0.0 -#addin nuget:?package=Cake.Git&version=4.0.0 +#addin nuget:?package=Cake.Git&version=5.0.0 #addin nuget:?package=Cake.Codecov&version=3.0.0 @@ -96,15 +96,13 @@ var publishingError = false; // - when building source project on Ubuntu // - when running unit tests on Ubuntu // - when calculating code coverage -// FYI, this will cause an error if the source project and/or the unit test project are not configured to target this desired framework: -const string DefaultFramework = "net7.0"; -var desiredFramework = ( - !IsRunningOnWindows() || +const string DefaultFramework = "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) - ) ? DefaultFramework : null; + target.Equals("Upload-Coverage-Result", StringComparison.OrdinalIgnoreCase); +var desiredFramework = isSingleTfmMode ? DefaultFramework : null; /////////////////////////////////////////////////////////////////////////////// @@ -180,6 +178,18 @@ Setup(context => Information("Removing benchmark project"); DotNetTool(solutionFile, "sln", $"remove {benchmarkProject.TrimStart(sourceFolder, StringComparison.OrdinalIgnoreCase)}"); } + + // In single TFM mode we want to override the framework(s) with our desired framework + if (isSingleTfmMode) + { + var peekSettings = new XmlPeekSettings { SuppressWarning = true }; + 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); + } + } }); Teardown(context => @@ -188,6 +198,18 @@ Teardown(context => { Information("Restoring projects that may have been removed during build script setup"); GitCheckout(".", new FilePath[] { solutionFile }); + Information(" Restored {0}", solutionFile.ToString()); + Information(""); + } + + 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()); + } Information(""); } From c06b762a8174b0e594ed3b6c21d795a8f8a054d0 Mon Sep 17 00:00:00 2001 From: Jericho Date: Wed, 18 Dec 2024 20:31:53 -0500 Subject: [PATCH 5/6] Switch to net .NET service hosting model Also ship logs to BetterStack --- Source/ZoomNet.IntegrationTests/Program.cs | 84 ++++++++----------- .../ZoomNet.IntegrationTests.csproj | 10 +-- 2 files changed, 38 insertions(+), 56 deletions(-) diff --git a/Source/ZoomNet.IntegrationTests/Program.cs b/Source/ZoomNet.IntegrationTests/Program.cs index 5dc72688..1d529b7e 100644 --- a/Source/ZoomNet.IntegrationTests/Program.cs +++ b/Source/ZoomNet.IntegrationTests/Program.cs @@ -1,11 +1,7 @@ -using Logzio.DotNet.NLog; +using Formitable.BetterStack.Logger.Microsoft; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using NLog; -using NLog.Config; -using NLog.Extensions.Logging; -using NLog.Targets; using System; using System.Linq; using System.Threading; @@ -15,13 +11,10 @@ namespace ZoomNet.IntegrationTests { public class Program { - public static async Task Main(string[] args) + public static async Task Main() { //var serializerContext = GenerateAttributesForSerializerContext(); - var builder = Host.CreateApplicationBuilder(); - builder.Services.AddHostedService(); - // Configure cancellation (this allows you to press CTRL+C or CTRL+Break to stop the integration tests) var cts = new CancellationTokenSource(); Console.CancelKeyPress += (s, e) => @@ -30,16 +23,40 @@ public static async Task Main(string[] args) cts.Cancel(); }; - // Configure logging - builder.Logging.ClearProviders(); // Remove the built-in providers (which include the Console) - builder.Logging.AddNLog(GetNLogConfiguration()); // Add our desired custom providers (which include the Colored Console) + var services = new ServiceCollection(); + ConfigureServices(services); + using var serviceProvider = services.BuildServiceProvider(); + var app = serviceProvider.GetService(); + await app.StartAsync(cts.Token).ConfigureAwait(false); + } + + private static void ConfigureServices(ServiceCollection services) + { + services.AddHostedService(); - // Run the tests - var host = builder.Build(); - await host.StartAsync(cts.Token).ConfigureAwait(false); - // Stop NLog (which has the desirable side-effect of flushing any pending logs) - LogManager.Shutdown(); + services + .AddLogging(logging => + { + var betterStackToken = Environment.GetEnvironmentVariable("BETTERSTACK_TOKEN"); + if (!string.IsNullOrEmpty(betterStackToken)) + { + logging.AddBetterStackLogger(options => + { + options.SourceToken = betterStackToken; + options.Context["source"] = "ZoomNet_integration_tests"; + options.Context["ZoomNet-Version"] = ZoomClient.Version; + }); + } + + logging.AddSimpleConsole(options => + { + options.SingleLine = true; + options.TimestampFormat = "yyyy-MM-dd HH:mm:ss "; + }); + + logging.AddFilter("*", LogLevel.Debug); + }); } private static string GenerateAttributesForSerializerContext() @@ -82,38 +99,5 @@ private static string GenerateAttributesForSerializerContext() var result = string.Join("\r\n\r\n", [simpleAttributes, arrayAttributes, nullableAttributes]); return result; } - - private static LoggingConfiguration GetNLogConfiguration() - { - // Configure logging - var nLogConfig = new LoggingConfiguration(); - - // Send logs to logz.io - var logzioToken = Environment.GetEnvironmentVariable("LOGZIO_TOKEN"); - if (!string.IsNullOrEmpty(logzioToken)) - { - var logzioTarget = new LogzioTarget - { - Name = "Logzio", - Token = logzioToken, - LogzioType = "nlog", - JsonKeysCamelCase = true, - // ProxyAddress = "http://localhost:8888", - }; - logzioTarget.ContextProperties.Add(new TargetPropertyWithContext("Source", "ZoomNet_integration_tests")); - logzioTarget.ContextProperties.Add(new TargetPropertyWithContext("ZoomNet-Version", ZoomNet.ZoomClient.Version)); - - nLogConfig.AddTarget("Logzio", logzioTarget); - nLogConfig.AddRule(NLog.LogLevel.Trace, NLog.LogLevel.Fatal, logzioTarget, "*"); // Send all logs to logz.io, no matther the 'level' - } - - // Send logs to console - var consoleTarget = new ColoredConsoleTarget(); - nLogConfig.AddTarget("ColoredConsole", consoleTarget); - nLogConfig.AddRule(NLog.LogLevel.Debug, NLog.LogLevel.Fatal, consoleTarget, "*"); // Only display logs with 'Debug' level or higher in the console - nLogConfig.AddRule(NLog.LogLevel.Trace, NLog.LogLevel.Fatal, consoleTarget, "ZoomNet.ZoomWebSocketClient"); // Display all logs to console when testing the WebSocket client, no matther the 'level' - - return nLogConfig; - } } } diff --git a/Source/ZoomNet.IntegrationTests/ZoomNet.IntegrationTests.csproj b/Source/ZoomNet.IntegrationTests/ZoomNet.IntegrationTests.csproj index 90b3d9d0..f79e132b 100644 --- a/Source/ZoomNet.IntegrationTests/ZoomNet.IntegrationTests.csproj +++ b/Source/ZoomNet.IntegrationTests/ZoomNet.IntegrationTests.csproj @@ -12,12 +12,10 @@ - - - - - - + + + + From fa260ab099da2ffdba321c2b7bd789572b230956 Mon Sep 17 00:00:00 2001 From: Jericho Date: Thu, 19 Dec 2024 09:19:27 -0500 Subject: [PATCH 6/6] Refresh build script --- build.cake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.cake b/build.cake index 0b050b46..a38e16db 100644 --- a/build.cake +++ b/build.cake @@ -8,7 +8,7 @@ // Install addins. #addin nuget:?package=Cake.Coveralls&version=4.0.0 -#addin nuget:?package=Cake.Git&version=5.0.0 +#addin nuget:?package=Cake.Git&version=5.0.1 #addin nuget:?package=Cake.Codecov&version=3.0.0