diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 0000000..f2b2cc2 --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,46 @@ + + + + 6 + + CS1591;CA5349 + disable + enable + true + true + true + True + true + true + latest + All + true + 4DotNet + Whack-A-Mole Core Library + 4Dotnet Whack-A-Mole Team + (c) 2024 - rights reserved + WAM.Core + Whack-a-mole core + 1.0.0 + 12 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props new file mode 100644 index 0000000..03b69b2 --- /dev/null +++ b/src/Directory.Packages.props @@ -0,0 +1,66 @@ + + + true + false + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + + \ No newline at end of file diff --git a/src/Wam.Core/Configuration/AzureServices.cs b/src/Wam.Core/Configuration/AzureServices.cs index 12ae27a..f3b7ca8 100644 --- a/src/Wam.Core/Configuration/AzureServices.cs +++ b/src/Wam.Core/Configuration/AzureServices.cs @@ -3,8 +3,8 @@ public class AzureServices { public const string SectionName = "AzureServices"; - public string WebPubSubEndpoint { get; set; } - public string WebPubSubHub{ get; set; } - public string UsersStorageAccountName { get; set; } - public string GamesStorageAccountName { get; set; } + public string? WebPubSubEndpoint { get; set; } + public string? WebPubSubHub{ get; set; } + public string? UsersStorageAccountName { get; set; } + public string? GamesStorageAccountName { get; set; } } \ No newline at end of file diff --git a/src/Wam.Core/Configuration/ServiceCollectionExtensions.cs b/src/Wam.Core/Configuration/ServiceCollectionExtensions.cs index 29ce3ed..8cc0e9b 100644 --- a/src/Wam.Core/Configuration/ServiceCollectionExtensions.cs +++ b/src/Wam.Core/Configuration/ServiceCollectionExtensions.cs @@ -2,28 +2,39 @@ using Microsoft.Extensions.Azure; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using System.Diagnostics.CodeAnalysis; using Wam.Core.Identity; namespace Wam.Core.Configuration; public static class ServiceCollectionExtensions { + private const string MissingConfigMessage = "Missing Azure Services configuration"; + public static IServiceCollection AddWamCoreConfiguration( this IServiceCollection services, - IConfiguration configuration, + [NotNull] IConfiguration configuration, bool skipApplicationInsights = false) { - var azureServicesOptions = configuration.GetSection(AzureServices.SectionName).Get(); + var azureServicesOptions = configuration + .GetSection(AzureServices.SectionName) + .Get() + ?? throw new InvalidOperationException(MissingConfigMessage); + services.AddHealthChecks(); - services.AddOptions().Bind(configuration.GetSection(AzureServices.SectionName)); //.ValidateOnStart(); - services.AddOptions().Bind(configuration.GetSection(ServicesConfiguration.SectionName)); //.ValidateOnStart(); + services.AddOptions().Bind(configuration.GetSection(AzureServices.SectionName)); + services.AddOptions().Bind(configuration.GetSection(ServicesConfiguration.SectionName)); + + string webPubSubEndpoint = + azureServicesOptions.WebPubSubEndpoint + ?? throw new InvalidOperationException(MissingConfigMessage); services.AddAzureClients(builder => { builder.AddWebPubSubServiceClient( - new Uri(azureServicesOptions.WebPubSubEndpoint), + new Uri(webPubSubEndpoint), azureServicesOptions.WebPubSubHub, - CloudIdentity.GetCloudIdentity()); + CloudIdentity.GetCloudIdentity); }); if (!skipApplicationInsights) diff --git a/src/Wam.Core/Enums/GameState.cs b/src/Wam.Core/Enums/GameState.cs index 060b942..4fb14c1 100644 --- a/src/Wam.Core/Enums/GameState.cs +++ b/src/Wam.Core/Enums/GameState.cs @@ -4,45 +4,28 @@ namespace Wam.Core.Enums; public abstract class GameState { - - public static readonly GameState New; - public static readonly GameState Current; - public static readonly GameState Started; - public static readonly GameState Finished; - public static readonly GameState Cancelled; - public static readonly GameState[] All; + public static readonly GameState Init = new GameStateInit(); + public static readonly GameState Current = new GameStateCurrent(); + public static readonly GameState Started = new GameStateStarted(); + public static readonly GameState Finished = new GameStateFinished(); + public static readonly GameState Cancelled = new GameStateCancelled(); + public static readonly GameState[] All = [Init, Current, Started, Finished, Cancelled]; public static GameState FromCode(string code) { - var state = All.FirstOrDefault(s => s.Code == code); - if (state == null) - { - throw new InvalidOperationException($"Invalid game state code {code}"); - } - return state; + return Array.Find(All, s => s.Code == code) + ?? throw new InvalidOperationException($"Invalid game state code {code}"); } public abstract string Code { get; } public virtual string TranslationKey => $"Game.States.{Code}"; public virtual bool CanChangeTo(GameState state) => false; - - static GameState() - { - All = new[] - { - New = new GameStateNew() , - Current = new GameStateCurrent(), - Started = new GameStateStarted(), - Finished = new GameStateFinished(), - Cancelled = new GameStateCancelled() - }; - } } -public class GameStateNew : GameState +public class GameStateInit : GameState { - public override string Code => "New"; + public override string Code => "Init"; public override bool CanChangeTo(GameState state) { return state == Current || state == Cancelled; diff --git a/src/Wam.Core/ErrorCodes/WamErrorCode.cs b/src/Wam.Core/ErrorCodes/WamErrorCode.cs index fb366b4..bb8332a 100644 --- a/src/Wam.Core/ErrorCodes/WamErrorCode.cs +++ b/src/Wam.Core/ErrorCodes/WamErrorCode.cs @@ -2,9 +2,7 @@ public abstract class WamErrorCode { - public abstract string Code { get; } - public virtual string Namespace { get; } = "Wam.ErrorCodes"; - public virtual string TranslationKey => $"{Namespace}.{Code}"; - -} \ No newline at end of file + public virtual string RootNamespace { get; } = "Wam.ErrorCodes"; + public virtual string TranslationKey => $"{RootNamespace}.{Code}"; +} diff --git a/src/Wam.Core/Events/RealtimeEvent.cs b/src/Wam.Core/Events/RealtimeEvent.cs index e39ad1a..3771055 100644 --- a/src/Wam.Core/Events/RealtimeEvent.cs +++ b/src/Wam.Core/Events/RealtimeEvent.cs @@ -5,8 +5,8 @@ namespace Wam.Core.Events; public class RealtimeEvent { - public string Message { get; set; } - public T Data { get; set; } + public string Message { get; set; } = string.Empty; + public T Data { get; set; } = default!; public string ToJson() { @@ -19,6 +19,5 @@ public string ToJson() ContractResolver = contractResolver, Formatting = Formatting.None }); - } } \ No newline at end of file diff --git a/src/Wam.Core/Exceptions/WamException.cs b/src/Wam.Core/Exceptions/WamException.cs index 677955a..a7b5850 100644 --- a/src/Wam.Core/Exceptions/WamException.cs +++ b/src/Wam.Core/Exceptions/WamException.cs @@ -2,7 +2,20 @@ namespace Wam.Core.Exceptions; -public class WamException(WamErrorCode error, string message) : Exception(message) +public class WamException(WamErrorCode? error, string message, Exception? innerException) : Exception(message, innerException) { - public WamErrorCode Error { get; } = error; + private const string ErrorCodeIsMissingMessage = "WamErrorCode is required"; + public WamErrorCode Error { get; } = error ?? throw new ArgumentNullException(nameof(error), ErrorCodeIsMissingMessage); + + public WamException() : this(null, "An unknown error occurred.", null) + { + } + + public WamException(string message) : this(null, message, null) + { + } + + public WamException(string message, Exception innerException) : this(null, message, innerException) + { + } } \ No newline at end of file diff --git a/src/Wam.Core/ExtensionMethods/ConfigurationExtensions.cs b/src/Wam.Core/ExtensionMethods/ConfigurationExtensions.cs index ddfbf36..6556a31 100644 --- a/src/Wam.Core/ExtensionMethods/ConfigurationExtensions.cs +++ b/src/Wam.Core/ExtensionMethods/ConfigurationExtensions.cs @@ -6,12 +6,10 @@ public static class ConfigurationExtensions { public static string GetRequiredValue(this IConfiguration configuration, string key, string? description = null) { - string value = configuration.GetValue(key); - if (string.IsNullOrWhiteSpace(value)) - { - throw new InvalidOperationException("Missing setting " + ((description != null) ? ("for " + description) : "") + " : " + key); - } - - return value; + return configuration.GetValue(key) + ?? throw new InvalidOperationException( + $"Missing setting {ForDescription(description)}: {key}"); } + + private static string ForDescription(string? description) => (description != null) ? $"for {description} " : string.Empty; } \ No newline at end of file diff --git a/src/Wam.Core/ExtensionMethods/IntegerExtensions.cs b/src/Wam.Core/ExtensionMethods/IntegerExtensions.cs index 31e5cad..f4883e5 100644 --- a/src/Wam.Core/ExtensionMethods/IntegerExtensions.cs +++ b/src/Wam.Core/ExtensionMethods/IntegerExtensions.cs @@ -2,8 +2,6 @@ public static class IntegerExtensions { - public static bool IsHttpSuccessCode(this int code) => code >= 200 && code <= 299; - } \ No newline at end of file diff --git a/src/Wam.Core/ExtensionMethods/StringExtensions.cs b/src/Wam.Core/ExtensionMethods/StringExtensions.cs index 55b1121..a166baf 100644 --- a/src/Wam.Core/ExtensionMethods/StringExtensions.cs +++ b/src/Wam.Core/ExtensionMethods/StringExtensions.cs @@ -1,21 +1,17 @@ -namespace Wam.Core.ExtensionMethods; +using System.Security.Cryptography; +namespace Wam.Core.ExtensionMethods; public static class StringExtensions { private const string Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - private static readonly Random Random; public static string GenerateGameCode(int length = 6) { var result = new string( - Enumerable.Repeat(Chars, length) - .Select(s => s[Random.Next(s.Length)]) - .ToArray()); + Enumerable + .Repeat(Chars, length) + .Select(s => s[RandomNumberGenerator.GetInt32(s.Length)]) + .ToArray()); return result; } - - static StringExtensions() - { - Random = new Random(); - } } \ No newline at end of file diff --git a/src/Wam.Core/Identity/CloudIdentity.cs b/src/Wam.Core/Identity/CloudIdentity.cs index 4c603be..cdb5e00 100644 --- a/src/Wam.Core/Identity/CloudIdentity.cs +++ b/src/Wam.Core/Identity/CloudIdentity.cs @@ -3,13 +3,10 @@ namespace Wam.Core.Identity; -public class CloudIdentity +public static class CloudIdentity { - public static TokenCredential GetCloudIdentity() - { - return new ChainedTokenCredential( + public static TokenCredential GetCloudIdentity => new ChainedTokenCredential( new ManagedIdentityCredential(), new VisualStudioCredential(), new AzureCliCredential()); - } } \ No newline at end of file diff --git a/src/Wam.Core/Wam.Core.csproj b/src/Wam.Core/Wam.Core.csproj index 1be4346..c194da5 100644 --- a/src/Wam.Core/Wam.Core.csproj +++ b/src/Wam.Core/Wam.Core.csproj @@ -12,17 +12,17 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/src/wam-core-library.sln b/src/wam-core-library.sln index a6b0b3c..b65ff86 100644 --- a/src/wam-core-library.sln +++ b/src/wam-core-library.sln @@ -3,7 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.6.33717.318 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wam.Core", "Wam.Core\Wam.Core.csproj", "{D5F4E98F-FFD3-4448-B17C-56AA61719D59}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wam.Core", "Wam.Core\Wam.Core.csproj", "{D5F4E98F-FFD3-4448-B17C-56AA61719D59}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F83C7DFC-90A3-4D64-98DE-C5F2156E7752}" + ProjectSection(SolutionItems) = preProject + Directory.Build.props = Directory.Build.props + Directory.Packages.props = Directory.Packages.props + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution