diff --git a/src/Nullinside.Api.Common.AspNetCore/.editorconfig b/src/Nullinside.Api.Common.AspNetCore/.editorconfig index 6850b93..c50c605 100644 --- a/src/Nullinside.Api.Common.AspNetCore/.editorconfig +++ b/src/Nullinside.Api.Common.AspNetCore/.editorconfig @@ -25,7 +25,6 @@ end_of_line = lf insert_final_newline = false #### .NET Coding Conventions #### -[*.{cs,vb}] # Organize usings dotnet_separate_import_directive_groups = true @@ -78,7 +77,6 @@ dotnet_code_quality_unused_parameters = all:suggestion dotnet_remove_unnecessary_suppression_exclusions = none #### C# Coding Conventions #### -[*.cs] # var preferences csharp_style_var_elsewhere = false:silent @@ -175,7 +173,6 @@ csharp_preserve_single_line_blocks = true csharp_preserve_single_line_statements = true #### Naming styles #### -[*.{cs,vb}] # Naming rules @@ -229,19 +226,19 @@ dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelca dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields -dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields -dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields -dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields -dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums @@ -365,3 +362,7 @@ dotnet_naming_style.s_camelcase.required_suffix = dotnet_naming_style.s_camelcase.word_separator = dotnet_naming_style.s_camelcase.capitalization = camel_case +dotnet_naming_style.all_upper.required_prefix = +dotnet_naming_style.all_upper.required_suffix = +dotnet_naming_style.all_upper.word_separator = _ +dotnet_naming_style.all_upper.capitalization = all_upper diff --git a/src/Nullinside.Api.Common.AspNetCore/Middleware/BasicAuthenticationHandler.cs b/src/Nullinside.Api.Common.AspNetCore/Middleware/BasicAuthenticationHandler.cs index ca1b877..3699959 100644 --- a/src/Nullinside.Api.Common.AspNetCore/Middleware/BasicAuthenticationHandler.cs +++ b/src/Nullinside.Api.Common.AspNetCore/Middleware/BasicAuthenticationHandler.cs @@ -75,8 +75,8 @@ protected override async Task HandleAuthenticateAsync() { try { //auth logic List claims = [ - new Claim(ClaimTypes.Email, dbUser.Email ?? string.Empty), - new Claim(ClaimTypes.UserData, dbUser.Id.ToString()) + new(ClaimTypes.Email, dbUser.Email ?? string.Empty), + new(ClaimTypes.UserData, dbUser.Id.ToString()) ]; if (null != dbUser.Roles) { diff --git a/src/Nullinside.Api.Common.AspNetCore/Nullinside.Api.Common.AspNetCore.csproj b/src/Nullinside.Api.Common.AspNetCore/Nullinside.Api.Common.AspNetCore.csproj index f1506b1..015a055 100644 --- a/src/Nullinside.Api.Common.AspNetCore/Nullinside.Api.Common.AspNetCore.csproj +++ b/src/Nullinside.Api.Common.AspNetCore/Nullinside.Api.Common.AspNetCore.csproj @@ -21,9 +21,9 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + + + diff --git a/src/Nullinside.Api.Common/.editorconfig b/src/Nullinside.Api.Common/.editorconfig index 6850b93..c50c605 100644 --- a/src/Nullinside.Api.Common/.editorconfig +++ b/src/Nullinside.Api.Common/.editorconfig @@ -25,7 +25,6 @@ end_of_line = lf insert_final_newline = false #### .NET Coding Conventions #### -[*.{cs,vb}] # Organize usings dotnet_separate_import_directive_groups = true @@ -78,7 +77,6 @@ dotnet_code_quality_unused_parameters = all:suggestion dotnet_remove_unnecessary_suppression_exclusions = none #### C# Coding Conventions #### -[*.cs] # var preferences csharp_style_var_elsewhere = false:silent @@ -175,7 +173,6 @@ csharp_preserve_single_line_blocks = true csharp_preserve_single_line_statements = true #### Naming styles #### -[*.{cs,vb}] # Naming rules @@ -229,19 +226,19 @@ dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelca dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields -dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields -dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields -dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields -dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums @@ -365,3 +362,7 @@ dotnet_naming_style.s_camelcase.required_suffix = dotnet_naming_style.s_camelcase.word_separator = dotnet_naming_style.s_camelcase.capitalization = camel_case +dotnet_naming_style.all_upper.required_prefix = +dotnet_naming_style.all_upper.required_suffix = +dotnet_naming_style.all_upper.word_separator = _ +dotnet_naming_style.all_upper.capitalization = all_upper diff --git a/src/Nullinside.Api.Common/Desktop/GithubLatestReleaseJson.cs b/src/Nullinside.Api.Common/Desktop/GithubLatestReleaseJson.cs index 671c4c0..8dcceb6 100644 --- a/src/Nullinside.Api.Common/Desktop/GithubLatestReleaseJson.cs +++ b/src/Nullinside.Api.Common/Desktop/GithubLatestReleaseJson.cs @@ -1,4 +1,6 @@ -using System.Diagnostics.CodeAnalysis; +// ReSharper disable All + +using System.Diagnostics.CodeAnalysis; namespace Nullinside.Api.Common.Desktop; diff --git a/src/Nullinside.Api.Common/Nullinside.Api.Common.csproj b/src/Nullinside.Api.Common/Nullinside.Api.Common.csproj index d67f6f3..a1c5ba0 100644 --- a/src/Nullinside.Api.Common/Nullinside.Api.Common.csproj +++ b/src/Nullinside.Api.Common/Nullinside.Api.Common.csproj @@ -17,8 +17,8 @@ - - + + diff --git a/src/Nullinside.Api.Common/Twitch/Json/TwitchModeratedChannelsResponse.cs b/src/Nullinside.Api.Common/Twitch/Json/TwitchModeratedChannelsResponse.cs index 94c958e..e3b8987 100644 --- a/src/Nullinside.Api.Common/Twitch/Json/TwitchModeratedChannelsResponse.cs +++ b/src/Nullinside.Api.Common/Twitch/Json/TwitchModeratedChannelsResponse.cs @@ -1,4 +1,6 @@ -using System.Diagnostics.CodeAnalysis; +// ReSharper disable All + +using System.Diagnostics.CodeAnalysis; namespace Nullinside.Api.Common.Twitch.Json; diff --git a/src/Nullinside.Api.Common/Twitch/Support/TwitchBotLoginErrors.cs b/src/Nullinside.Api.Common/Twitch/Support/TwitchBotLoginErrors.cs index 5822b9b..dead9b5 100644 --- a/src/Nullinside.Api.Common/Twitch/Support/TwitchBotLoginErrors.cs +++ b/src/Nullinside.Api.Common/Twitch/Support/TwitchBotLoginErrors.cs @@ -7,15 +7,15 @@ public enum TwitchBotLoginErrors { /// /// An error with the token that twitch provided us. /// - TwitchErrorWithToken, + TWITCH_ERROR_WITH_TOKEN, /// /// The twitch account doesn't have an email address associated with it. /// - TwitchAccountHasNoEmail, + TWITCH_ACCOUNT_HAS_NO_EMAIL, /// /// An internal error in the web server having nothing to do with the outside world. /// - InternalError + INTERNAL_ERROR } \ No newline at end of file diff --git a/src/Nullinside.Api.Common/Twitch/TwitchApiProxy.cs b/src/Nullinside.Api.Common/Twitch/TwitchApiProxy.cs index 9e5b63b..25adbdc 100644 --- a/src/Nullinside.Api.Common/Twitch/TwitchApiProxy.cs +++ b/src/Nullinside.Api.Common/Twitch/TwitchApiProxy.cs @@ -27,7 +27,7 @@ public class TwitchApiProxy : ITwitchApiProxy { /// /// The logger. /// - private static readonly ILog Log = LogManager.GetLogger(typeof(TwitchApiProxy)); + private static readonly ILog LOG = LogManager.GetLogger(typeof(TwitchApiProxy)); /// /// Initializes a new instance of the class. @@ -213,14 +213,14 @@ public virtual async Task> BanChannelUsers(string channe } bannedUsers.AddRange(response.Data); - Log.Info($"Banned {user.Username} ({user.Id}) in {channelId}: {reason}"); + LOG.Info($"Banned {user.Username} ({user.Id}) in {channelId}: {reason}"); } catch (HttpResponseException ex) { string exceptionReason = await ex.HttpResponse.Content.ReadAsStringAsync(token); - Log.Debug($"Failed to ban {user.Username} ({user.Id}) in {channelId}: {exceptionReason}", ex); + LOG.Debug($"Failed to ban {user.Username} ({user.Id}) in {channelId}: {exceptionReason}", ex); } catch (Exception ex) { - Log.Debug($"Failed to ban {user.Username} ({user.Id}) in {channelId}", ex); + LOG.Debug($"Failed to ban {user.Username} ({user.Id}) in {channelId}", ex); } } diff --git a/src/Nullinside.Api.Common/Twitch/TwitchClientProxy.cs b/src/Nullinside.Api.Common/Twitch/TwitchClientProxy.cs index b25db58..7a73b0c 100644 --- a/src/Nullinside.Api.Common/Twitch/TwitchClientProxy.cs +++ b/src/Nullinside.Api.Common/Twitch/TwitchClientProxy.cs @@ -27,7 +27,7 @@ public class TwitchClientProxy : ITwitchClientProxy { /// /// The singleton instance of the class. /// - private static TwitchClientProxy? instance; + private static TwitchClientProxy? s_instance; /// /// The list of chats we attempted to join with the bot. @@ -36,42 +36,42 @@ public class TwitchClientProxy : ITwitchClientProxy { /// We need to keep track of this separately so that we can make sure we re-join channels /// in the method. /// - private readonly HashSet joinedChannels; + private readonly HashSet _joinedChannels; /// - /// A timer used to re-connect the Twitch chat client. + /// The callback(s) to invoke when a channel receives a chat message. /// - private readonly Timer twitchChatClientReconnect; + private readonly Dictionary?> _onMessageReceived = new(); /// - /// The lock to prevent mutual exclusion on the object. + /// The callback(s) to invoke when a channel is raided. /// - private readonly object twitchClientLock = new(); + private readonly Dictionary?> _onRaid = new(); /// - /// The twitch client to send and receive messages with. + /// The callback(s) to invoke when a channel receives a ban message. /// - private TwitchClient? client; + private readonly Dictionary?> _onUserBanReceived = new(); /// - /// The callback(s) to invoke when a channel receives a chat message. + /// A timer used to re-connect the Twitch chat client. /// - private Dictionary?> _onMessageReceived = new(); + private readonly Timer _twitchChatClientReconnect; /// - /// The callback(s) to invoke when a channel is raided. + /// The lock to prevent mutual exclusion on the object. /// - private Dictionary?> onRaid = new(); + private readonly object _twitchClientLock = new(); /// - /// The callback(s) to invoke when a channel receives a ban message. + /// The twitch client to send and receive messages with. /// - private Dictionary?> onUserBanReceived = new(); + private TwitchClient? _client; /// /// The web socket to connect to twitch chat with. /// - private WebSocketClient? socket; + private WebSocketClient? _socket; private string? _twitchOAuthToken; @@ -79,13 +79,13 @@ public class TwitchClientProxy : ITwitchClientProxy { /// Initializes a new instance of the class. /// protected TwitchClientProxy() { - joinedChannels = new HashSet(); + _joinedChannels = new HashSet(); // The timer for checking to make sure the IRC channel is connected. - twitchChatClientReconnect = new Timer(1000); - twitchChatClientReconnect.AutoReset = false; - twitchChatClientReconnect.Elapsed += TwitchChatClientReconnectOnElapsed; - twitchChatClientReconnect.Start(); + _twitchChatClientReconnect = new Timer(1000); + _twitchChatClientReconnect.AutoReset = false; + _twitchChatClientReconnect.Elapsed += TwitchChatClientReconnectOnElapsed; + _twitchChatClientReconnect.Start(); } /// @@ -93,11 +93,11 @@ protected TwitchClientProxy() { /// public static TwitchClientProxy Instance { get { - if (null == instance) { - instance = new TwitchClientProxy(); + if (null == s_instance) { + s_instance = new TwitchClientProxy(); } - return instance; + return s_instance; } } @@ -109,10 +109,10 @@ public string? TwitchOAuthToken { get => _twitchOAuthToken; set { _twitchOAuthToken = value; - + // If we have a client, try to connect. - if (null != client) { - client.SetConnectionCredentials(new ConnectionCredentials(TwitchUsername, value)); + if (null != _client) { + _client.SetConnectionCredentials(new ConnectionCredentials(TwitchUsername, value)); Connect(); } } @@ -146,17 +146,17 @@ public async Task SendMessage(string channel, string message, uint retryCo } try { - lock (twitchClientLock) { + lock (_twitchClientLock) { // If we are not connected for some reason, we shouldn't have gotten here, so get out. - if (null == client || - !client.IsConnected || - null == client.JoinedChannels.FirstOrDefault(j => + if (null == _client || + !_client.IsConnected || + null == _client.JoinedChannels.FirstOrDefault(j => channel.Equals(j.Channel, StringComparison.InvariantCultureIgnoreCase))) { return false; } LOG.Info($"{channel} Sending: {message}"); - client.SendMessage(channel, message); + _client.SendMessage(channel, message); } } catch { @@ -169,7 +169,7 @@ public async Task SendMessage(string channel, string message, uint retryCo /// public async Task AddMessageCallback(string channel, Action callback) { await JoinChannel(channel); - var channelSan = channel.ToLowerInvariant(); + string channelSan = channel.ToLowerInvariant(); lock (_onMessageReceived) { if (!_onMessageReceived.TryAdd(channelSan, callback)) { _onMessageReceived[channelSan] += callback; @@ -180,10 +180,10 @@ public async Task AddMessageCallback(string channel, Action public void RemoveMessageCallback(string channel, Action callback) { bool shouldRemove = false; - var channelSan = channel.ToLowerInvariant(); + string channelSan = channel.ToLowerInvariant(); lock (_onMessageReceived) { if (_onMessageReceived.ContainsKey(channelSan)) { - var item = _onMessageReceived[channelSan]; + Action? item = _onMessageReceived[channelSan]; item -= callback; if (null == item) { _onMessageReceived.Remove(channelSan); @@ -195,11 +195,11 @@ public void RemoveMessageCallback(string channel, Action } if (shouldRemove) { - client?.LeaveChannel(channelSan); - + _client?.LeaveChannel(channelSan); + // First add the channel to the master list. - lock (joinedChannels) { - joinedChannels.Add(channelSan); + lock (_joinedChannels) { + _joinedChannels.Add(channelSan); } } } @@ -208,15 +208,15 @@ public void RemoveMessageCallback(string channel, Action public async Task AddBannedCallback(string channel, Action callback) { await JoinChannel(channel); - lock (onUserBanReceived) { - onUserBanReceived[channel] = callback; + lock (_onUserBanReceived) { + _onUserBanReceived[channel] = callback; } } /// public void RemoveBannedCallback(string channel, Action callback) { - lock (onUserBanReceived) { - onUserBanReceived.Remove(channel); + lock (_onUserBanReceived) { + _onUserBanReceived.Remove(channel); } } @@ -224,15 +224,15 @@ public void RemoveBannedCallback(string channel, Action callba public async Task AddRaidCallback(string channel, Action callback) { await JoinChannel(channel); - lock (onRaid) { - onRaid[channel] = callback; + lock (_onRaid) { + _onRaid[channel] = callback; } } /// public void RemoveRaidCallback(string channel, Action callback) { - lock (onRaid) { - onRaid.Remove(channel); + lock (_onRaid) { + _onRaid.Remove(channel); } } @@ -249,24 +249,24 @@ public ValueTask DisposeAsync() { /// True if connected and joined, false otherwise. private async Task JoinChannel(string channel) { // First add the channel to the master list. - lock (joinedChannels) { - joinedChannels.Add(channel.ToLowerInvariant()); + lock (_joinedChannels) { + _joinedChannels.Add(channel.ToLowerInvariant()); } // Try to connect. if (!await Connect()) { return false; } - + try { // If we don't have a client, give up. - if (null == client) { + if (null == _client) { return false; } - lock (twitchClientLock) { + lock (_twitchClientLock) { // If we are already in the channel, we are done. - if (null != client.JoinedChannels.FirstOrDefault(c => + if (null != _client.JoinedChannels.FirstOrDefault(c => channel.Equals(c.Channel, StringComparison.InvariantCultureIgnoreCase))) { return true; } @@ -274,7 +274,7 @@ private async Task JoinChannel(string channel) { // Otherwise, join the channel. At one point we waited here on the "OnJoinedChannel" to ensure the // connection before moving onto the next channel. However, it was causing a massive slowdown in // the application, and we've been working fine without it...so for now...we try this... - client.JoinChannel(channel); + _client.JoinChannel(channel); } return true; @@ -295,8 +295,8 @@ private async void TwitchChatClientReconnectOnElapsed(object? sender, ElapsedEve // Pull the master list of channels we should be connected to the stack. string[]? allChannels = null; - lock (joinedChannels) { - allChannels = joinedChannels.ToArray(); + lock (_joinedChannels) { + allChannels = _joinedChannels.ToArray(); } // Join all the channels. @@ -305,7 +305,7 @@ private async void TwitchChatClientReconnectOnElapsed(object? sender, ElapsedEve } // Restart the timer. - twitchChatClientReconnect.Start(); + _twitchChatClientReconnect.Start(); } /// @@ -314,8 +314,8 @@ private async void TwitchChatClientReconnectOnElapsed(object? sender, ElapsedEve /// True if successful, false otherwise. private Task Connect() { // If we're already connected, we are good to go. - lock (twitchClientLock) { - if (client?.IsConnected ?? false) { + lock (_twitchClientLock) { + if (_client?.IsConnected ?? false) { return Task.FromResult(true); } } @@ -327,61 +327,61 @@ private Task Connect() { try { bool isConnected = false; - lock (twitchClientLock) { - if (client?.IsConnected ?? false) { + lock (_twitchClientLock) { + if (_client?.IsConnected ?? false) { return Task.FromResult(true); } // If this is a first time initialization, create a brand-new client. - bool haveNoClient = null == client; + bool haveNoClient = null == _client; if (haveNoClient) { var clientOptions = new ClientOptions { MessagesAllowedInPeriod = 750, ThrottlingPeriod = TimeSpan.FromSeconds(30) }; - socket = new WebSocketClient(clientOptions); - client = new TwitchClient(socket); + _socket = new WebSocketClient(clientOptions); + _client = new TwitchClient(_socket); var credentials = new ConnectionCredentials(TwitchUsername, TwitchOAuthToken); - client.Initialize(credentials); - client.AutoReListenOnException = true; - client.OnMessageReceived += TwitchChatClient_OnMessageReceived; - client.OnUserBanned += TwitchChatClient_OnUserBanned; - client.OnRaidNotification += TwitchChatClient_OnRaidNotification; - client.OnDisconnected += (sender, args) => { + _client.Initialize(credentials); + _client.AutoReListenOnException = true; + _client.OnMessageReceived += TwitchChatClient_OnMessageReceived; + _client.OnUserBanned += TwitchChatClient_OnUserBanned; + _client.OnRaidNotification += TwitchChatClient_OnRaidNotification; + _client.OnDisconnected += (sender, args) => { LOG.Error("Twitch Client Disconnected"); }; - client.OnConnectionError += (sender, args) => { + _client.OnConnectionError += (sender, args) => { LOG.Error($"Twitch Client Connection Error: {args.Error.Message}"); }; - client.OnError += (sender, args) => { + _client.OnError += (sender, args) => { LOG.Error($"Twitch Client Error: {args.Exception.Message}"); }; - client.OnIncorrectLogin += (sender, args) => { + _client.OnIncorrectLogin += (sender, args) => { LOG.Error($"Twitch Client Incorrect Login: {args.Exception.Message}"); }; - client.OnNoPermissionError += (sender, args) => { + _client.OnNoPermissionError += (sender, args) => { LOG.Error("Twitch Client No Permission Error"); }; } try { // If we are not connect, connect. - if (null != client && !client.IsConnected) { + if (null != _client && !_client.IsConnected) { // If this is a new chat client, connect for the first time, otherwise reconnect. - Action connect = haveNoClient ? () => client.Connect() : () => client.Reconnect(); + Action connect = haveNoClient ? () => _client.Connect() : () => _client.Reconnect(); using var connectedEvent = new ManualResetEventSlim(false); EventHandler onConnected = (_, _) => connectedEvent.Set(); EventHandler onReconnect = (_, _) => connectedEvent.Set(); try { - client!.OnConnected += onConnected; - client!.OnReconnected += onReconnect; + _client!.OnConnected += onConnected; + _client!.OnReconnected += onReconnect; connect(); if (!connectedEvent.Wait(30 * 1000)) { return Task.FromResult(false); } } finally { - client.OnConnected -= onConnected; - client.OnReconnected -= onReconnect; + _client.OnConnected -= onConnected; + _client.OnReconnected -= onReconnect; } } } @@ -390,7 +390,7 @@ private Task Connect() { } // Determine if we successfully connected. - isConnected = client?.IsConnected ?? false; + isConnected = _client?.IsConnected ?? false; } return Task.FromResult(isConnected); @@ -407,11 +407,11 @@ private Task Connect() { /// The event arguments. private void TwitchChatClient_OnRaidNotification(object? sender, OnRaidNotificationArgs e) { Action? callback; - var channel = e.Channel.ToLowerInvariant(); - lock (onRaid) { - onRaid.TryGetValue(channel, out callback); + string channel = e.Channel.ToLowerInvariant(); + lock (_onRaid) { + _onRaid.TryGetValue(channel, out callback); } - + Delegate[]? invokeList = callback?.GetInvocationList(); if (null == invokeList) { return; @@ -429,7 +429,7 @@ private void TwitchChatClient_OnRaidNotification(object? sender, OnRaidNotificat /// The event arguments. private void TwitchChatClient_OnMessageReceived(object? sender, OnMessageReceivedArgs e) { Action? callbacks = null; - var channelSan = e.ChatMessage.Channel.ToLowerInvariant(); + string channelSan = e.ChatMessage.Channel.ToLowerInvariant(); lock (_onMessageReceived) { if (_onMessageReceived.TryGetValue(channelSan, out Action? messageReceivedCallback)) { callbacks = messageReceivedCallback; @@ -457,11 +457,11 @@ private void TwitchChatClient_OnMessageReceived(object? sender, OnMessageReceive /// The event arguments. private void TwitchChatClient_OnUserBanned(object? sender, OnUserBannedArgs e) { Action? callback; - var channel = e.UserBan.Channel.ToLowerInvariant(); - lock (onUserBanReceived) { - onUserBanReceived.TryGetValue(channel, out callback); + string channel = e.UserBan.Channel.ToLowerInvariant(); + lock (_onUserBanReceived) { + _onUserBanReceived.TryGetValue(channel, out callback); } - + Delegate[]? invokeList = callback?.GetInvocationList(); if (null == invokeList) { return; @@ -478,11 +478,11 @@ private void TwitchChatClient_OnUserBanned(object? sender, OnUserBannedArgs e) { /// True if called directly, false if called from the destructor. protected virtual void Dispose(bool disposing) { if (disposing) { - client?.Disconnect(); - twitchChatClientReconnect?.Dispose(); - socket?.Dispose(); + _client?.Disconnect(); + _twitchChatClientReconnect?.Dispose(); + _socket?.Dispose(); } - instance = null; + s_instance = null; } } \ No newline at end of file diff --git a/src/Nullinside.Api.Common/UserRoles.cs b/src/Nullinside.Api.Common/UserRoles.cs index a5f29e4..86e5b15 100644 --- a/src/Nullinside.Api.Common/UserRoles.cs +++ b/src/Nullinside.Api.Common/UserRoles.cs @@ -7,15 +7,15 @@ public enum UserRoles { /// /// The role of a currently authenticated user. This role has no other access. It is considered "public" access. /// - User, + USER, /// /// The role of the administrator that has access to exclusive and highly sensitive functions. /// - Admin, + ADMIN, /// /// The roles of a virtual machine administrator that provides access to the docker images hosted by the site. /// - VmAdmin + VM_ADMIN } \ No newline at end of file diff --git a/src/Nullinside.Api.Model/.editorconfig b/src/Nullinside.Api.Model/.editorconfig index 6850b93..c50c605 100644 --- a/src/Nullinside.Api.Model/.editorconfig +++ b/src/Nullinside.Api.Model/.editorconfig @@ -25,7 +25,6 @@ end_of_line = lf insert_final_newline = false #### .NET Coding Conventions #### -[*.{cs,vb}] # Organize usings dotnet_separate_import_directive_groups = true @@ -78,7 +77,6 @@ dotnet_code_quality_unused_parameters = all:suggestion dotnet_remove_unnecessary_suppression_exclusions = none #### C# Coding Conventions #### -[*.cs] # var preferences csharp_style_var_elsewhere = false:silent @@ -175,7 +173,6 @@ csharp_preserve_single_line_blocks = true csharp_preserve_single_line_statements = true #### Naming styles #### -[*.{cs,vb}] # Naming rules @@ -229,19 +226,19 @@ dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelca dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields -dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields -dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields -dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields -dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums @@ -365,3 +362,7 @@ dotnet_naming_style.s_camelcase.required_suffix = dotnet_naming_style.s_camelcase.word_separator = dotnet_naming_style.s_camelcase.capitalization = camel_case +dotnet_naming_style.all_upper.required_prefix = +dotnet_naming_style.all_upper.required_suffix = +dotnet_naming_style.all_upper.word_separator = _ +dotnet_naming_style.all_upper.capitalization = all_upper diff --git a/src/Nullinside.Api.Model/Nullinside.Api.Model.csproj b/src/Nullinside.Api.Model/Nullinside.Api.Model.csproj index f424449..d1c49d0 100644 --- a/src/Nullinside.Api.Model/Nullinside.Api.Model.csproj +++ b/src/Nullinside.Api.Model/Nullinside.Api.Model.csproj @@ -17,13 +17,13 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + diff --git a/src/Nullinside.Api.Model/Shared/DatabaseLock.cs b/src/Nullinside.Api.Model/Shared/DatabaseLock.cs index 64e2e02..9574712 100644 --- a/src/Nullinside.Api.Model/Shared/DatabaseLock.cs +++ b/src/Nullinside.Api.Model/Shared/DatabaseLock.cs @@ -5,21 +5,22 @@ namespace Nullinside.Api.Common; /// -/// Handles acquiring a lock at the database level. +/// Handles acquiring a lock at the database level. /// /// Requires a transaction wrapping its functionality. public class DatabaseLock : IDisposable { /// - /// The database context. + /// The database context. /// - private INullinsideContext _mysqlDbContext; + private readonly INullinsideContext _mysqlDbContext; + /// - /// The lock name used on the previous lock. + /// The lock name used on the previous lock. /// private string? _name; - + /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The database context. /// Must provide parameter. @@ -27,8 +28,14 @@ public DatabaseLock(INullinsideContext mysqlDbContext) { _mysqlDbContext = mysqlDbContext ?? throw new ArgumentNullException(nameof(mysqlDbContext)); } + /// + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + /// - /// Acquires the lock. + /// Acquires the lock. /// /// Waits indefinitely. /// The name of the lock. MUST BE A HARD CODED VALUE @@ -38,17 +45,17 @@ public DatabaseLock(INullinsideContext mysqlDbContext) { if (string.IsNullOrWhiteSpace(name)) { return false; } - + // This is only used with hard coded names. #pragma warning disable EF1002 - await _mysqlDbContext.Database.ExecuteSqlRawAsync($"SELECT GET_LOCK('{name}', -1)", cancellationToken: cancellationToken); + await _mysqlDbContext.Database.ExecuteSqlRawAsync($"SELECT GET_LOCK('{name}', -1)", cancellationToken); #pragma warning restore EF1002 _name = name; return true; } - + /// - /// Releases the lock. + /// Releases the lock. /// /// The name of the lock. MUST BE A HARD CODED VALUE /// The cancellation token. @@ -56,37 +63,31 @@ public DatabaseLock(INullinsideContext mysqlDbContext) { if (!string.IsNullOrWhiteSpace(name) && !name.Equals(_name, StringComparison.InvariantCultureIgnoreCase)) { // This is only used with hard coded names. #pragma warning disable EF1002 - await _mysqlDbContext.Database.ExecuteSqlRawAsync($"SELECT RELEASE_LOCK('{_name}')", cancellationToken: cancellationToken); + await _mysqlDbContext.Database.ExecuteSqlRawAsync($"SELECT RELEASE_LOCK('{_name}')", cancellationToken); #pragma warning restore EF1002 } - + // This is only used with hard coded names. #pragma warning disable EF1002 - await _mysqlDbContext.Database.ExecuteSqlRawAsync($"SELECT RELEASE_LOCK('{name}')", cancellationToken: cancellationToken); + await _mysqlDbContext.Database.ExecuteSqlRawAsync($"SELECT RELEASE_LOCK('{name}')", cancellationToken); #pragma warning restore EF1002 _name = null; } /// - /// Disposes of resources. + /// Disposes of resources. /// /// True if dispose called, false if finalizer. protected virtual void Dispose(bool disposing) { if (disposing) { if (!string.IsNullOrWhiteSpace(_name)) { - Task.WaitAll(this.ReleaseLock(_name)); + Task.WaitAll(ReleaseLock(_name)); } } } - /// - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } - /// - /// Finalizes instance of the class. + /// Finalizes instance of the class. /// ~DatabaseLock() { Dispose(false); diff --git a/src/Nullinside.Api.Model/Shared/UserHelpers.cs b/src/Nullinside.Api.Model/Shared/UserHelpers.cs index e30d53f..7e62d89 100644 --- a/src/Nullinside.Api.Model/Shared/UserHelpers.cs +++ b/src/Nullinside.Api.Model/Shared/UserHelpers.cs @@ -31,7 +31,7 @@ public static class UserHelpers { if (null == existing && !string.IsNullOrWhiteSpace(twitchUsername)) { existing = await dbContext.Users.FirstOrDefaultAsync(u => u.TwitchUsername == twitchUsername && !u.IsBanned, token); } - + if (null == existing) { dbContext.Users.Add(new User { Email = email, @@ -53,7 +53,7 @@ public static class UserHelpers { } dbContext.UserRoles.Add(new UserRole { - Role = UserRoles.User, + Role = UserRoles.USER, UserId = existing.Id, RoleAdded = DateTime.UtcNow }); diff --git a/src/Nullinside.Api.Tests/.editorconfig b/src/Nullinside.Api.Tests/.editorconfig index 6850b93..c50c605 100644 --- a/src/Nullinside.Api.Tests/.editorconfig +++ b/src/Nullinside.Api.Tests/.editorconfig @@ -25,7 +25,6 @@ end_of_line = lf insert_final_newline = false #### .NET Coding Conventions #### -[*.{cs,vb}] # Organize usings dotnet_separate_import_directive_groups = true @@ -78,7 +77,6 @@ dotnet_code_quality_unused_parameters = all:suggestion dotnet_remove_unnecessary_suppression_exclusions = none #### C# Coding Conventions #### -[*.cs] # var preferences csharp_style_var_elsewhere = false:silent @@ -175,7 +173,6 @@ csharp_preserve_single_line_blocks = true csharp_preserve_single_line_statements = true #### Naming styles #### -[*.{cs,vb}] # Naming rules @@ -229,19 +226,19 @@ dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelca dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields -dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields -dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields -dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields -dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums @@ -365,3 +362,7 @@ dotnet_naming_style.s_camelcase.required_suffix = dotnet_naming_style.s_camelcase.word_separator = dotnet_naming_style.s_camelcase.capitalization = camel_case +dotnet_naming_style.all_upper.required_prefix = +dotnet_naming_style.all_upper.required_suffix = +dotnet_naming_style.all_upper.word_separator = _ +dotnet_naming_style.all_upper.capitalization = all_upper diff --git a/src/Nullinside.Api.Tests/Nullinside.Api.Tests.csproj b/src/Nullinside.Api.Tests/Nullinside.Api.Tests.csproj index 10fa7af..29c817f 100644 --- a/src/Nullinside.Api.Tests/Nullinside.Api.Tests.csproj +++ b/src/Nullinside.Api.Tests/Nullinside.Api.Tests.csproj @@ -15,13 +15,13 @@ - - - + + + - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Nullinside.Api.Tests/Nullinside.Api/Controllers/UserControllerTests.cs b/src/Nullinside.Api.Tests/Nullinside.Api/Controllers/UserControllerTests.cs index 7ac5a7d..04a4ea8 100644 --- a/src/Nullinside.Api.Tests/Nullinside.Api/Controllers/UserControllerTests.cs +++ b/src/Nullinside.Api.Tests/Nullinside.Api/Controllers/UserControllerTests.cs @@ -22,24 +22,24 @@ namespace Nullinside.Api.Tests.Nullinside.Api.Controllers; /// public class UserControllerTests : UnitTestBase { /// - /// The mock configuration. + /// The mock configuration. /// private IConfiguration _configuration; /// - /// The twitch api. + /// The twitch api. /// private Mock _twitchApi; - + /// public override void Setup() { base.Setup(); - + // Setup the config - var config = new Dictionary() { + var config = new Dictionary { { "Api:SiteUrl", string.Empty } }; - + _configuration = new ConfigurationBuilder() .AddInMemoryCollection(config) .Build(); @@ -53,28 +53,28 @@ public override void Setup() { [Test] public async Task PerformGoogleLoginExisting() { // Create an existing user. - this._db.Users.Add(new User { + _db.Users.Add(new User { Id = 1, Email = "hi" }); - await this._db.SaveChangesAsync(); - + await _db.SaveChangesAsync(); + // Make the call and ensure it's successful. var controller = new TestableUserController(_configuration, _db); controller.Email = "hi"; RedirectResult obj = await controller.Login(new GoogleOpenIdToken { credential = "stuff" }); - + // We should have been redirected to the successful route. Assert.That(obj.Url.StartsWith("/user/login?token="), Is.True); - + // No additional users should have been created. - Assert.That(this._db.Users.Count(), Is.EqualTo(1)); - + Assert.That(_db.Users.Count(), Is.EqualTo(1)); + // We should have saved the token in the existing user's database. - Assert.That(obj.Url.EndsWith(this._db.Users.First().Token!), Is.True); + Assert.That(obj.Url.EndsWith(_db.Users.First().Token!), Is.True); } - + /// /// Tests that we can login with google for a new user. /// @@ -84,33 +84,33 @@ public async Task PerformGoogleLoginNewUser() { var controller = new TestableUserController(_configuration, _db); controller.Email = "hi"; RedirectResult obj = await controller.Login(new GoogleOpenIdToken { credential = "stuff" }); - + // We should have been redirected to the successful route. Assert.That(obj.Url.StartsWith("/user/login?token="), Is.True); - + // No additional users should have been created. - Assert.That(this._db.Users.Count(), Is.EqualTo(1)); - + Assert.That(_db.Users.Count(), Is.EqualTo(1)); + // We should have saved the token in the existing user's database. - Assert.That(obj.Url.EndsWith(this._db.Users.First().Token!), Is.True); + Assert.That(obj.Url.EndsWith(_db.Users.First().Token!), Is.True); } - + /// /// Tests that we handle DB errors correctly in the google login. /// [Test] public async Task GoToErrorOnDbException() { - this._db.Users = null!; - + _db.Users = null!; + // Make the call and ensure it's successful. var controller = new TestableUserController(_configuration, _db); controller.Email = "hi"; RedirectResult obj = await controller.Login(new GoogleOpenIdToken { credential = "stuff" }); - + // We should have been redirected to the error. Assert.That(obj.Url.StartsWith("/user/login?error="), Is.True); } - + /// /// Tests that we handle bad gmail responses. /// @@ -120,11 +120,11 @@ public async Task GoToErrorOnBadGmailResponse() { var controller = new TestableUserController(_configuration, _db); controller.Email = null; RedirectResult obj = await controller.Login(new GoogleOpenIdToken { credential = "stuff" }); - + // We should have been redirected to the error. Assert.That(obj.Url.StartsWith("/user/login?error="), Is.True); } - + /// /// Tests that we can login with twitch for a user that already exists. /// @@ -133,33 +133,33 @@ public async Task PerformTwitchLoginExisting() { // Tells us twitch parsed the code successfully. _twitchApi.Setup(a => a.CreateAccessToken(It.IsAny(), It.IsAny())) .Returns(() => Task.FromResult(new TwitchAccessToken())); - + // Gets a matching email address from our database _twitchApi.Setup(a => a.GetUserEmail(It.IsAny())) .Returns(() => Task.FromResult("hi")); - + // Create an existing user. - this._db.Users.Add(new User { + _db.Users.Add(new User { Id = 1, Email = "hi" }); - await this._db.SaveChangesAsync(); - + await _db.SaveChangesAsync(); + // Make the call and ensure it's successful. var controller = new TestableUserController(_configuration, _db); - var obj = await controller.TwitchLogin("things", _twitchApi.Object); - + RedirectResult obj = await controller.TwitchLogin("things", _twitchApi.Object); + // We should have been redirected to the successful route. Assert.That(obj.Url.StartsWith("/user/login?token="), Is.True); - + // No additional users should have been created. - Assert.That(this._db.Users.Count(), Is.EqualTo(1)); - + Assert.That(_db.Users.Count(), Is.EqualTo(1)); + // We should have saved the token in the existing user's database. - Assert.That(obj.Url.EndsWith(this._db.Users.First().Token!), Is.True); + Assert.That(obj.Url.EndsWith(_db.Users.First().Token!), Is.True); } - + /// /// Tests that we can login with twitch for a new user. /// @@ -168,25 +168,25 @@ public async Task PerformTwitchLoginNewUser() { // Tells us twitch parsed the code successfully. _twitchApi.Setup(a => a.CreateAccessToken(It.IsAny(), It.IsAny())) .Returns(() => Task.FromResult(new TwitchAccessToken())); - + // Gets a matching email address from our database _twitchApi.Setup(a => a.GetUserEmail(It.IsAny())) .Returns(() => Task.FromResult("hi")); - + // Make the call and ensure it's successful. var controller = new TestableUserController(_configuration, _db); - var obj = await controller.TwitchLogin("things", _twitchApi.Object); - + RedirectResult obj = await controller.TwitchLogin("things", _twitchApi.Object); + // We should have been redirected to the successful route. Assert.That(obj.Url.StartsWith("/user/login?token="), Is.True); - + // No additional users should have been created. - Assert.That(this._db.Users.Count(), Is.EqualTo(1)); - + Assert.That(_db.Users.Count(), Is.EqualTo(1)); + // We should have saved the token in the existing user's database. - Assert.That(obj.Url.EndsWith(this._db.Users.First().Token!), Is.True); + Assert.That(obj.Url.EndsWith(_db.Users.First().Token!), Is.True); } - + /// /// Tests that we handle a bad response from twitch. /// @@ -195,15 +195,15 @@ public async Task PerformTwitchLoginBadTwitchResponse() { // Tells us twitch thinks it was a bad code. _twitchApi.Setup(a => a.CreateAccessToken(It.IsAny(), It.IsAny())) .Returns(() => Task.FromResult(null)); - + // Make the call and ensure it's successful. var controller = new TestableUserController(_configuration, _db); - var obj = await controller.TwitchLogin("things", _twitchApi.Object); - + RedirectResult obj = await controller.TwitchLogin("things", _twitchApi.Object); + // We should have gone down the bad route Assert.That(obj.Url.StartsWith("/user/login?error="), Is.True); } - + /// /// Tests that we can login with twitch but it has no email associated with the account. /// @@ -212,56 +212,56 @@ public async Task PerformTwitchLoginWithNoEmailAccount() { // Tells us twitch parsed the code successfully. _twitchApi.Setup(a => a.CreateAccessToken(It.IsAny(), It.IsAny())) .Returns(() => Task.FromResult(new TwitchAccessToken())); - + // Make the call and ensure it's successful. var controller = new TestableUserController(_configuration, _db); - var obj = await controller.TwitchLogin("things", _twitchApi.Object); - + RedirectResult obj = await controller.TwitchLogin("things", _twitchApi.Object); + // We should have gone down the bad route because no email was associated with the twitch account. Assert.That(obj.Url.StartsWith("/user/login?error="), Is.True); } - + /// /// Tests that we handle database failures correctly in the twitch login process. /// [Test] public async Task PerformTwitchLoginDbFailure() { _db.Users = null!; - + // Tells us twitch parsed the code successfully. _twitchApi.Setup(a => a.CreateAccessToken(It.IsAny(), It.IsAny())) .Returns(() => Task.FromResult(new TwitchAccessToken())); - + // Gets an email address from twitch _twitchApi.Setup(a => a.GetUserEmail(It.IsAny())) .Returns(() => Task.FromResult("hi")); - + // Make the call and ensure it's successful. var controller = new TestableUserController(_configuration, _db); - var obj = await controller.TwitchLogin("things", _twitchApi.Object); - + RedirectResult obj = await controller.TwitchLogin("things", _twitchApi.Object); + // We should have been redirected to the error route because of an exception in DB processing. Assert.That(obj.Url.StartsWith("/user/login?error="), Is.True); } /// - /// Tests that we get the roles assigned to the user. + /// Tests that we get the roles assigned to the user. /// [Test] public void GetRoles() { // Setup the logged in user - var claims = new List { + var claims = new List { new(ClaimTypes.Role, "candy") }; var identity = new ClaimsIdentity(claims, "icecream"); - + // Make the call and ensure it's successful. var controller = new TestableUserController(_configuration, _db); controller.ControllerContext = new ControllerContext(); controller.ControllerContext.HttpContext = new DefaultHttpContext(); controller.ControllerContext.HttpContext.User = new ClaimsPrincipal(identity); - - var obj = controller.GetRoles(); + + ObjectResult obj = controller.GetRoles(); Assert.That(obj.StatusCode, Is.EqualTo(200)); // Ensure we got the role we put in. @@ -269,67 +269,66 @@ public void GetRoles() { Assert.That(roles!.Count(), Is.EqualTo(1)); Assert.That(roles!.First(), Is.EqualTo("candy")); } - + /// - /// Tests that we can validate a token that exists. + /// Tests that we can validate a token that exists. /// [Test] public async Task ValidateTokenExists() { - this._db.Users.Add(new User { Token = "123" }); - await this._db.SaveChangesAsync(); - + _db.Users.Add(new User { Token = "123" }); + await _db.SaveChangesAsync(); + // Make the call and ensure it's successful. var controller = new TestableUserController(_configuration, _db); - var obj = await controller.Validate(new AuthToken("123")); + IActionResult obj = await controller.Validate(new AuthToken("123")); Assert.That((obj as IStatusCodeActionResult)?.StatusCode, Is.EqualTo(200)); // Ensure we returned that the token was correct. Assert.That((obj as ObjectResult)?.Value, Is.True); } - + /// - /// Tests that we do not validate tokens that do not exist. + /// Tests that we do not validate tokens that do not exist. /// [Test] public async Task ValidateFailWithoutToken() { // Make the call and ensure it fails. var controller = new TestableUserController(_configuration, _db); - var obj = await controller.Validate(new AuthToken("123")); + IActionResult obj = await controller.Validate(new AuthToken("123")); Assert.That((obj as IStatusCodeActionResult)?.StatusCode, Is.EqualTo(401)); } - + /// - /// Tests that unhandled exceptions are performed appropriately. + /// Tests that unhandled exceptions are performed appropriately. /// [Test] public async Task ValidateFailOnDbFailure() { - this._db.Users = null!; - + _db.Users = null!; + // Make the call and ensure it fails. var controller = new TestableUserController(_configuration, _db); - var obj = await controller.Validate(new AuthToken("123")); + IActionResult obj = await controller.Validate(new AuthToken("123")); Assert.That((obj as IStatusCodeActionResult)?.StatusCode, Is.EqualTo(500)); } } /// -/// A testable version of the user controller that removes 3rd party dependencies. +/// A testable version of the user controller that removes 3rd party dependencies. /// public class TestableUserController : UserController { + /// + public TestableUserController(IConfiguration configuration, INullinsideContext dbContext) : base(configuration, dbContext) { + } + /// - /// Gets or sets the email to include in the google payload. + /// Gets or sets the email to include in the google payload. /// public string? Email { get; set; } - - /// - public TestableUserController(IConfiguration configuration, INullinsideContext dbContext) : base(configuration, dbContext) - { - } /// protected override Task GenerateUserObject(GoogleOpenIdToken creds) { if (null != Email) { - return Task.FromResult(new GoogleJsonWebSignature.Payload() { Email = Email }); + return Task.FromResult(new GoogleJsonWebSignature.Payload { Email = Email }); } return Task.FromResult(null); diff --git a/src/Nullinside.Api/.editorconfig b/src/Nullinside.Api/.editorconfig index 6850b93..c50c605 100644 --- a/src/Nullinside.Api/.editorconfig +++ b/src/Nullinside.Api/.editorconfig @@ -25,7 +25,6 @@ end_of_line = lf insert_final_newline = false #### .NET Coding Conventions #### -[*.{cs,vb}] # Organize usings dotnet_separate_import_directive_groups = true @@ -78,7 +77,6 @@ dotnet_code_quality_unused_parameters = all:suggestion dotnet_remove_unnecessary_suppression_exclusions = none #### C# Coding Conventions #### -[*.cs] # var preferences csharp_style_var_elsewhere = false:silent @@ -175,7 +173,6 @@ csharp_preserve_single_line_blocks = true csharp_preserve_single_line_statements = true #### Naming styles #### -[*.{cs,vb}] # Naming rules @@ -229,19 +226,19 @@ dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelca dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields -dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields -dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields -dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields -dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase +dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = all_upper dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums @@ -365,3 +362,7 @@ dotnet_naming_style.s_camelcase.required_suffix = dotnet_naming_style.s_camelcase.word_separator = dotnet_naming_style.s_camelcase.capitalization = camel_case +dotnet_naming_style.all_upper.required_prefix = +dotnet_naming_style.all_upper.required_suffix = +dotnet_naming_style.all_upper.word_separator = _ +dotnet_naming_style.all_upper.capitalization = all_upper diff --git a/src/Nullinside.Api/Controllers/DockerController.cs b/src/Nullinside.Api/Controllers/DockerController.cs index 0393ccb..756994c 100644 --- a/src/Nullinside.Api/Controllers/DockerController.cs +++ b/src/Nullinside.Api/Controllers/DockerController.cs @@ -16,7 +16,7 @@ namespace Nullinside.Api.Controllers; /// /// Provides insights and management options for the virtual machines hosted by the website. /// -[Authorize(nameof(UserRoles.VmAdmin))] +[Authorize(nameof(UserRoles.VM_ADMIN))] [ApiController] [Route("[controller]")] public class DockerController : ControllerBase { @@ -48,7 +48,7 @@ public DockerController(INullinsideContext dbContext, IDockerProxy dockerProxy) /// /// Gets the docker resources that can be configured. /// - [Authorize(nameof(UserRoles.VmAdmin))] + [Authorize(nameof(UserRoles.VM_ADMIN))] [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] @@ -89,7 +89,7 @@ public DockerController(INullinsideContext dbContext, IDockerProxy dockerProxy) /// The id of the docker resource. /// The request to turn on or off a resource. /// The cancellation token. - [Authorize(nameof(UserRoles.VmAdmin))] + [Authorize(nameof(UserRoles.VM_ADMIN))] [HttpPost("{id:int}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] diff --git a/src/Nullinside.Api/Controllers/UserController.cs b/src/Nullinside.Api/Controllers/UserController.cs index 28dbbcc..df547b3 100644 --- a/src/Nullinside.Api/Controllers/UserController.cs +++ b/src/Nullinside.Api/Controllers/UserController.cs @@ -60,7 +60,7 @@ public UserController(IConfiguration configuration, INullinsideContext dbContext public async Task Login([FromForm] GoogleOpenIdToken creds, CancellationToken token = new()) { string? siteUrl = _configuration.GetValue("Api:SiteUrl"); try { - GoogleJsonWebSignature.Payload? credentials = await this.GenerateUserObject(creds); + GoogleJsonWebSignature.Payload? credentials = await GenerateUserObject(creds); if (string.IsNullOrWhiteSpace(credentials?.Email)) { return Redirect($"{siteUrl}/user/login?error=1"); } @@ -78,14 +78,14 @@ public UserController(IConfiguration configuration, INullinsideContext dbContext } /// - /// Converts the credential string we get from google to a representation we read information from. + /// Converts the credential string we get from google to a representation we read information from. /// /// The credentials from Google. /// The user information object. - protected async virtual Task GenerateUserObject(GoogleOpenIdToken creds) { + protected virtual async Task GenerateUserObject(GoogleOpenIdToken creds) { return await GoogleJsonWebSignature.ValidateAsync(creds.credential); } - + /// /// **NOT CALLED BY SITE OR USERS** This endpoint is called by twitch as part of their oauth workflow. It /// redirects users back to the nullinside website. @@ -122,7 +122,7 @@ public async Task TwitchLogin([FromQuery] string code, [FromServ return Redirect($"{siteUrl}/user/login?token={bearerToken}"); } - + /// /// **NOT CALLED BY SITE OR USERS** This endpoint is called by twitch as part of their oauth workflow. It /// redirects users back to the nullinside website. @@ -149,7 +149,7 @@ public async Task TwitchStreamingToolsLogin([FromQuery] string c return Redirect($"{siteUrl}/user/login/desktop?bearer={api.OAuth?.AccessToken}&refresh={api.OAuth?.RefreshToken}&expiresUtc={api.OAuth?.ExpiresUtc?.ToString()}"); } - + /// /// Used to refresh OAuth tokens from the desktop application. /// @@ -166,17 +166,17 @@ public async Task TwitchStreamingToolsLogin([FromQuery] string c [AllowAnonymous] [HttpPost] [Route("twitch-login/twitch-streaming-tools")] - public async Task TwitchStreamingToolsRefreshToken([FromForm]string refreshToken, [FromServices] ITwitchApiProxy api, + public async Task TwitchStreamingToolsRefreshToken([FromForm] string refreshToken, [FromServices] ITwitchApiProxy api, CancellationToken token = new()) { string? siteUrl = _configuration.GetValue("Api:SiteUrl"); - api.OAuth = new() { + api.OAuth = new TwitchAccessToken { AccessToken = null, RefreshToken = refreshToken, ExpiresUtc = DateTime.MinValue }; - + if (null == await api.RefreshAccessToken(token)) { - return this.BadRequest(); + return BadRequest(); } return Ok(new { diff --git a/src/Nullinside.Api/Nullinside.Api.csproj b/src/Nullinside.Api/Nullinside.Api.csproj index cbe222f..136c05e 100644 --- a/src/Nullinside.Api/Nullinside.Api.csproj +++ b/src/Nullinside.Api/Nullinside.Api.csproj @@ -23,19 +23,19 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - - - + + + + + + diff --git a/src/Nullinside.Api/Program.cs b/src/Nullinside.Api/Program.cs index 9d12fcf..cbcadcb 100644 --- a/src/Nullinside.Api/Program.cs +++ b/src/Nullinside.Api/Program.cs @@ -13,7 +13,7 @@ using WebApplicationBuilder = Microsoft.AspNetCore.Builder.WebApplicationBuilder; -const string CORS_KEY = "_customAllowedSpecificOrigins"; +const string corsKey = "_customAllowedSpecificOrigins"; WebApplicationBuilder builder = WebApplication.CreateBuilder(args); builder.Logging.ClearProviders(); @@ -48,7 +48,7 @@ } options.FallbackPolicy = new AuthorizationPolicyBuilder() - .RequireRole(nameof(UserRoles.User)) + .RequireRole(nameof(UserRoles.USER)) .RequireAuthenticatedUser() .Build(); }); @@ -89,7 +89,7 @@ // Add services to the container. builder.Services.AddCors(options => { - options.AddPolicy(CORS_KEY, + options.AddPolicy(corsKey, policyBuilder => { policyBuilder.WithOrigins("https://www.nullinside.com", "https://nullinside.com", "http://localhost:4200", "http://127.0.0.1:4200") @@ -115,7 +115,7 @@ } app.UseHttpsRedirection(); -app.UseCors(CORS_KEY); +app.UseCors(corsKey); app.UseAuthorization(); app.MapControllers(); diff --git a/src/Nullinside.Api/Shared/Json/AuthToken.cs b/src/Nullinside.Api/Shared/Json/AuthToken.cs index c1f7775..f13a2e1 100644 --- a/src/Nullinside.Api/Shared/Json/AuthToken.cs +++ b/src/Nullinside.Api/Shared/Json/AuthToken.cs @@ -1,3 +1,6 @@ +// +// ReSharper disable All + using System.Diagnostics.CodeAnalysis; namespace Nullinside.Api.Shared.Json; diff --git a/src/Nullinside.Api/Shared/Json/BasicServerFailure.cs b/src/Nullinside.Api/Shared/Json/BasicServerFailure.cs index 046e432..8659d47 100644 --- a/src/Nullinside.Api/Shared/Json/BasicServerFailure.cs +++ b/src/Nullinside.Api/Shared/Json/BasicServerFailure.cs @@ -1,3 +1,6 @@ +// +// ReSharper disable All + using System.Diagnostics.CodeAnalysis; namespace Nullinside.Api.Shared.Json; diff --git a/src/Nullinside.Api/Shared/Json/GoogleOpenIdToken.cs b/src/Nullinside.Api/Shared/Json/GoogleOpenIdToken.cs index a329e82..d20de5a 100644 --- a/src/Nullinside.Api/Shared/Json/GoogleOpenIdToken.cs +++ b/src/Nullinside.Api/Shared/Json/GoogleOpenIdToken.cs @@ -1,3 +1,5 @@ +// ReSharper disable All + using System.Diagnostics.CodeAnalysis; namespace Nullinside.Api.Shared.Json; diff --git a/src/Nullinside.Api/Shared/Json/TurnOnOrOffDockerResourcesRequest.cs b/src/Nullinside.Api/Shared/Json/TurnOnOrOffDockerResourcesRequest.cs index e9b7c3b..9574efa 100644 --- a/src/Nullinside.Api/Shared/Json/TurnOnOrOffDockerResourcesRequest.cs +++ b/src/Nullinside.Api/Shared/Json/TurnOnOrOffDockerResourcesRequest.cs @@ -1,3 +1,6 @@ +// +// ReSharper disable All + using System.Diagnostics.CodeAnalysis; namespace Nullinside.Api.Shared.Json; diff --git a/src/global.json b/src/global.json new file mode 100644 index 0000000..eafb435 --- /dev/null +++ b/src/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "rollForward": "latestMajor", + "allowPrerelease": false + } +} \ No newline at end of file