From fedaec289dd5a102475d559c2a790c851f702588 Mon Sep 17 00:00:00 2001 From: James Croft Date: Fri, 15 Apr 2022 18:55:45 +0100 Subject: [PATCH 01/15] Moved HttpContextExceptionHandler extensions to be closer to the code associated with it --- .../HttpContextExceptionHandlerExtensions.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) rename src/MADE.Web/{Extensions => Exceptions}/HttpContextExceptionHandlerExtensions.cs (77%) diff --git a/src/MADE.Web/Extensions/HttpContextExceptionHandlerExtensions.cs b/src/MADE.Web/Exceptions/HttpContextExceptionHandlerExtensions.cs similarity index 77% rename from src/MADE.Web/Extensions/HttpContextExceptionHandlerExtensions.cs rename to src/MADE.Web/Exceptions/HttpContextExceptionHandlerExtensions.cs index 90958542..54c3dce0 100644 --- a/src/MADE.Web/Extensions/HttpContextExceptionHandlerExtensions.cs +++ b/src/MADE.Web/Exceptions/HttpContextExceptionHandlerExtensions.cs @@ -25,6 +25,17 @@ public static IApplicationBuilder UseHttpContextExceptionHandling(this IApplicat return builder; } + /// + /// Adds the default handlers to the service collection. + /// + /// The service collection. + /// The configured service collection. + public static IServiceCollection AddDefaultHttpContextExceptionHandler(this IServiceCollection serviceCollection) + { + serviceCollection.AddHttpContextExceptionHandler(); + return serviceCollection; + } + /// /// Adds a exception handler to the service collection. /// From fb4b32b609f0f1b521d4214673076da65f795f27 Mon Sep 17 00:00:00 2001 From: James Croft Date: Fri, 15 Apr 2022 18:56:07 +0100 Subject: [PATCH 02/15] Added AuthenticatedUser and extensions for registering the user accessor --- src/MADE.Web/Identity/AuthenticatedUser.cs | 67 +++++++++++++++++++ .../Identity/AuthenticatedUserAccessor.cs | 7 +- .../Identity/AuthenticatedUserExtensions.cs | 23 +++++++ .../Identity/IAuthenticatedUserAccessor.cs | 5 ++ 4 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/MADE.Web/Identity/AuthenticatedUser.cs create mode 100644 src/MADE.Web/Identity/AuthenticatedUserExtensions.cs diff --git a/src/MADE.Web/Identity/AuthenticatedUser.cs b/src/MADE.Web/Identity/AuthenticatedUser.cs new file mode 100644 index 00000000..f6238523 --- /dev/null +++ b/src/MADE.Web/Identity/AuthenticatedUser.cs @@ -0,0 +1,67 @@ +namespace MADE.Web.Identity +{ + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Linq; + using System.Security.Claims; + + /// + /// Defines a base model for an authenticated user within the application. + /// + public class AuthenticatedUser + { + /// + /// The value associated with the authenticated user's identity. + /// + public const string SubjectClaimType = "sub"; + + /// + /// The value associated with the authenticated user's preferred email address. + /// + public const string EmailClaimType = "email"; + + /// + /// The value associated with the authenticated user's assigned role(s). + /// + public const string RoleClaimType = "role"; + + /// + /// Initializes a new instance of the class with the claims principal associated with the user and configures the properties based on the claims. + /// + /// The claims principal associated with the user. + public AuthenticatedUser(ClaimsPrincipal claimsPrincipal) + { + this.ClaimsPrincipal = claimsPrincipal; + + this.Subject = claimsPrincipal?.Claims.SingleOrDefault(c => c.Type == SubjectClaimType)?.Value; + this.Email = claimsPrincipal?.Claims.SingleOrDefault(c => c.Type == EmailClaimType)?.Value; + this.Roles = claimsPrincipal?.Claims.Where(x => x.Type == RoleClaimType).Select(x => x.Value); + this.Claims = claimsPrincipal?.Claims.ToImmutableList(); + } + + /// + /// Gets the claims principal associated with the user. + /// + public ClaimsPrincipal ClaimsPrincipal { get; } + + /// + /// Gets the authenticated user's identity. + /// + public string Subject { get; } + + /// + /// Gets the authenticated user's preferred email address. + /// + public string Email { get; } + + /// + /// Gets the collection of the authenticated user's assigned roles. + /// + public IEnumerable Roles { get; } + + /// + /// Gets the collection of the authenticated user's claims. + /// + public IImmutableList Claims { get; } + } +} \ No newline at end of file diff --git a/src/MADE.Web/Identity/AuthenticatedUserAccessor.cs b/src/MADE.Web/Identity/AuthenticatedUserAccessor.cs index 169b3d4b..bb5fa1ad 100644 --- a/src/MADE.Web/Identity/AuthenticatedUserAccessor.cs +++ b/src/MADE.Web/Identity/AuthenticatedUserAccessor.cs @@ -22,6 +22,11 @@ public AuthenticatedUserAccessor(IHttpContextAccessor httpContextAccessor) /// /// Gets the authenticated user's claims principal. /// - public ClaimsPrincipal ClaimsPrincipal => this.httpContextAccessor.HttpContext.User; + public ClaimsPrincipal ClaimsPrincipal => this.httpContextAccessor?.HttpContext?.User; + + /// + /// Gets the authenticated user model for the specified / + /// + public AuthenticatedUser AuthenticatedUser => new(this.ClaimsPrincipal); } } \ No newline at end of file diff --git a/src/MADE.Web/Identity/AuthenticatedUserExtensions.cs b/src/MADE.Web/Identity/AuthenticatedUserExtensions.cs new file mode 100644 index 00000000..627b3296 --- /dev/null +++ b/src/MADE.Web/Identity/AuthenticatedUserExtensions.cs @@ -0,0 +1,23 @@ +namespace MADE.Web.Identity +{ + using Microsoft.AspNetCore.Http; + using Microsoft.Extensions.DependencyInjection; + + /// + /// Defines a collection of extensions for objects. + /// + public static class AuthenticatedUserExtensions + { + /// + /// Adds the and to the specified services collection. + /// + /// The service collection. + /// The configured service collection. + public static IServiceCollection AddAuthenticatedUserAccessor(this IServiceCollection serviceCollection) + { + serviceCollection.AddHttpContextAccessor(); + serviceCollection.AddScoped(); + return serviceCollection; + } + } +} \ No newline at end of file diff --git a/src/MADE.Web/Identity/IAuthenticatedUserAccessor.cs b/src/MADE.Web/Identity/IAuthenticatedUserAccessor.cs index bb5a486a..3cbe6866 100644 --- a/src/MADE.Web/Identity/IAuthenticatedUserAccessor.cs +++ b/src/MADE.Web/Identity/IAuthenticatedUserAccessor.cs @@ -11,5 +11,10 @@ public interface IAuthenticatedUserAccessor /// Gets the authenticated user's claims principal. /// ClaimsPrincipal ClaimsPrincipal { get; } + + /// + /// Gets the authenticated user model for the specified / + /// + AuthenticatedUser AuthenticatedUser { get; } } } \ No newline at end of file From 1ac2f765635557217a3f840ea5217c432cc62e7b Mon Sep 17 00:00:00 2001 From: James Croft Date: Fri, 15 Apr 2022 19:50:00 +0100 Subject: [PATCH 03/15] Added ToDaySuffix date converter --- .gitignore | 51 ++++++++++++++++++- .../Extensions/DateTimeExtensions.cs | 24 +++++++++ .../Tests/DateTimeExtensionsTests.cs | 33 ++++++++++++ 3 files changed, 107 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index dfcfd56f..0bdc52e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore # User-specific files *.rsuser @@ -23,6 +23,7 @@ mono_crash.* [Rr]eleases/ x64/ x86/ +[Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ bld/ @@ -61,6 +62,9 @@ project.lock.json project.fragment.lock.json artifacts/ +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + # StyleCop StyleCopReport.xml @@ -86,6 +90,7 @@ StyleCopReport.xml *.tmp_proj *_wpftmp.csproj *.log +*.tlog *.vspscc *.vssscc .builds @@ -137,6 +142,11 @@ _TeamCity* .axoCover/* !.axoCover/settings.json +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + # Visual Studio code coverage results *.coverage *.coveragexml @@ -284,6 +294,17 @@ node_modules/ # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts @@ -340,6 +361,9 @@ ASALocalRun/ # Local History for Visual Studio .localhistory/ +# Visual Studio History (VSHistory) files +.vshistory/ + # BeatPulse healthcheck temp database healthchecksdb @@ -348,3 +372,28 @@ MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea/ \ No newline at end of file diff --git a/src/MADE.Data.Converters/Extensions/DateTimeExtensions.cs b/src/MADE.Data.Converters/Extensions/DateTimeExtensions.cs index 8abc3a74..34c31ebd 100644 --- a/src/MADE.Data.Converters/Extensions/DateTimeExtensions.cs +++ b/src/MADE.Data.Converters/Extensions/DateTimeExtensions.cs @@ -10,6 +10,30 @@ namespace MADE.Data.Converters.Extensions /// public static class DateTimeExtensions { + /// + /// Gets the day suffix for the specified date, i.e. st, nd, rd, or th. + /// + /// The date to get a day suffix for. + /// The day suffix as a string. + public static string ToDaySuffix(this DateTime dateTime) + { + switch (dateTime.Day) + { + case 1: + case 21: + case 31: + return "st"; + case 2: + case 22: + return "nd"; + case 3: + case 23: + return "rd"; + default: + return "th"; + } + } + /// /// Gets the current age in years based on the specified starting date and today's date. /// diff --git a/tests/MADE.Data.Converters.Tests/Tests/DateTimeExtensionsTests.cs b/tests/MADE.Data.Converters.Tests/Tests/DateTimeExtensionsTests.cs index 3d6aa3b4..4e12dfd3 100644 --- a/tests/MADE.Data.Converters.Tests/Tests/DateTimeExtensionsTests.cs +++ b/tests/MADE.Data.Converters.Tests/Tests/DateTimeExtensionsTests.cs @@ -11,6 +11,39 @@ namespace MADE.Data.Converters.Tests.Tests [TestFixture] public class DateTimeExtensionsTests { + public class WhenConvertingDateToDaySuffix + { + private static object[] TestCases = + { + new object[] {new DateTime(2022, 3, 1), "st"}, new object[] {new DateTime(2022, 3, 2), "nd"}, + new object[] {new DateTime(2022, 3, 3), "rd"}, new object[] {new DateTime(2022, 3, 4), "th"}, + new object[] {new DateTime(2022, 3, 5), "th"}, new object[] {new DateTime(2022, 3, 6), "th"}, + new object[] {new DateTime(2022, 3, 7), "th"}, new object[] {new DateTime(2022, 3, 8), "th"}, + new object[] {new DateTime(2022, 3, 9), "th"}, new object[] {new DateTime(2022, 3, 10), "th"}, + new object[] {new DateTime(2022, 3, 11), "th"}, new object[] {new DateTime(2022, 3, 12), "th"}, + new object[] {new DateTime(2022, 3, 13), "th"}, new object[] {new DateTime(2022, 3, 14), "th"}, + new object[] {new DateTime(2022, 3, 15), "th"}, new object[] {new DateTime(2022, 3, 16), "th"}, + new object[] {new DateTime(2022, 3, 17), "th"}, new object[] {new DateTime(2022, 3, 18), "th"}, + new object[] {new DateTime(2022, 3, 19), "th"}, new object[] {new DateTime(2022, 3, 20), "th"}, + new object[] {new DateTime(2022, 3, 21), "st"}, new object[] {new DateTime(2022, 3, 22), "nd"}, + new object[] {new DateTime(2022, 3, 23), "rd"}, new object[] {new DateTime(2022, 3, 24), "th"}, + new object[] {new DateTime(2022, 3, 25), "th"}, new object[] {new DateTime(2022, 3, 26), "th"}, + new object[] {new DateTime(2022, 3, 27), "th"}, new object[] {new DateTime(2022, 3, 28), "th"}, + new object[] {new DateTime(2022, 3, 29), "th"}, new object[] {new DateTime(2022, 3, 30), "th"}, + new object[] {new DateTime(2022, 3, 31), "st"}, + }; + + [TestCaseSource(nameof(TestCases))] + public void ShouldReturnCorrectDaySuffix(DateTime dateTime, string expected) + { + // Act + var result = dateTime.ToDaySuffix(); + + // Assert + result.ShouldBe(expected); + } + } + public class WhenSettingTime { [Test] From 5d18e1168d4c3b84f94a6776d7e29847ba5fd3c6 Mon Sep 17 00:00:00 2001 From: James Croft Date: Fri, 15 Apr 2022 20:26:02 +0100 Subject: [PATCH 04/15] Added simple string truncate with ellipsis method --- .../Extensions/StringExtensions.cs | 22 +++++++++++++ .../Tests/StringExtensionsTests.cs | 31 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/MADE.Data.Converters/Extensions/StringExtensions.cs b/src/MADE.Data.Converters/Extensions/StringExtensions.cs index 11e0efbf..fff6eef2 100644 --- a/src/MADE.Data.Converters/Extensions/StringExtensions.cs +++ b/src/MADE.Data.Converters/Extensions/StringExtensions.cs @@ -39,6 +39,28 @@ public static string ToTitleCase(this string value) return result.ToString(); } + /// + /// Truncates a string value to the specified length with an ellipsis. + /// + /// The value to truncate. + /// The maximum length of the value. + /// A truncated string with ellipsis if the value's length is greater than the . + public static string Truncate(this string value, int maxLength) + { + if (string.IsNullOrEmpty(value)) + { + return string.Empty; + } + + if (value.Length <= maxLength) + { + return value; + } + + const string suffix = "..."; + return value.Substring(0, maxLength - suffix.Length) + suffix; + } + /// /// Converts a value to default case using the case rules of the invariant culture. /// diff --git a/tests/MADE.Data.Converters.Tests/Tests/StringExtensionsTests.cs b/tests/MADE.Data.Converters.Tests/Tests/StringExtensionsTests.cs index 2c207d3e..adb88cef 100644 --- a/tests/MADE.Data.Converters.Tests/Tests/StringExtensionsTests.cs +++ b/tests/MADE.Data.Converters.Tests/Tests/StringExtensionsTests.cs @@ -9,6 +9,37 @@ namespace MADE.Data.Converters.Tests.Tests [TestFixture] public class StringExtensionsTests { + public class WhenTruncatingStrings + { + [Test] + public void ShouldReturnTruncatedIfGreaterThanMaxLength() + { + // Arrange + const string input = "Hello, World!"; + const int maxLength = 8; + + // Act + var result = input.Truncate(maxLength); + + // Assert + result.ShouldBe("Hello..."); + } + + [Test] + public void ShouldReturnOriginalIfLessThanMaxLength() + { + // Arrange + const string input = "Hello, World!"; + int maxLength = input.Length; + + // Act + var result = input.Truncate(maxLength); + + // Assert + result.ShouldBe(input); + } + } + public class WhenConvertingToTitleCase { [TestCase("", "")] From 5466ed3030cca0213687319601548911175b16a5 Mon Sep 17 00:00:00 2001 From: James Croft Date: Fri, 15 Apr 2022 20:43:31 +0100 Subject: [PATCH 05/15] Added conditional add, add range, and remove range methods for collections --- src/MADE.Collections/CollectionExtensions.cs | 73 +++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/src/MADE.Collections/CollectionExtensions.cs b/src/MADE.Collections/CollectionExtensions.cs index 6bc936c7..27e431af 100644 --- a/src/MADE.Collections/CollectionExtensions.cs +++ b/src/MADE.Collections/CollectionExtensions.cs @@ -12,6 +12,31 @@ namespace MADE.Collections /// public static class CollectionExtensions { + /// + /// Adds the specified item to the collection based on the specified condition being true. + /// + /// The collection to add the item to. + /// The item to add. + /// The condition required to add the item. + /// The type of item within the collection. + public static void AddIf(this IList collection, T item, Func condition) + { + if (collection == null) + { + throw new ArgumentNullException(nameof(collection)); + } + + if (condition == null) + { + throw new ArgumentNullException(nameof(condition)); + } + + if (condition()) + { + collection.Add(item); + } + } + /// /// Updates an item within the collection. /// @@ -114,6 +139,29 @@ public static void AddRange(this ICollection collection, IEnumerable it } } + /// + /// Adds the specified collection of items to the collection based on the specified condition being true. + /// + /// The collection to add the items to. + /// The items to add. + /// The condition required to add the items. + /// The type of item within the collection. + public static void AddRangeIf( + this ICollection collection, + IEnumerable itemsToAdd, + Func condition) + { + if (condition == null) + { + throw new ArgumentNullException(nameof(condition)); + } + + if (condition()) + { + collection.AddRange(itemsToAdd); + } + } + /// /// Removes a collection of items from another. /// @@ -148,6 +196,29 @@ public static void RemoveRange(this ICollection collection, IEnumerable } } + /// + /// Removes the specified collection of items from the collection based on the specified condition being true. + /// + /// The collection to remove the items from. + /// The items to remove. + /// The condition required to remove the items. + /// The type of item within the collection. + public static void RemoveRangeIf( + this ICollection collection, + IEnumerable itemsToRemove, + Func condition) + { + if (condition == null) + { + throw new ArgumentNullException(nameof(condition)); + } + + if (condition()) + { + collection.RemoveRange(itemsToRemove); + } + } + /// /// Determines whether two collections are equivalent, containing all the same items with no regard to order. /// @@ -237,7 +308,7 @@ public static void ForEach(this IEnumerable collection, Action action) public static IEnumerable> Chunk(this IEnumerable source, int chunkSize = 25) { return source - .Select((v, i) => new { Index = i, Value = v }) + .Select((v, i) => new {Index = i, Value = v}) .GroupBy(x => x.Index / chunkSize) .Select(x => x.Select(v => v.Value)); } From 74d40ddfbdc3e8c53395a69ce71266883952da7d Mon Sep 17 00:00:00 2001 From: James Croft Date: Sat, 16 Apr 2022 08:42:38 +0100 Subject: [PATCH 06/15] Added collection shuffle extension method --- src/MADE.Collections/CollectionExtensions.cs | 11 +++ .../Tests/CollectionExtensionsTests.cs | 71 +++++++++++++++---- 2 files changed, 67 insertions(+), 15 deletions(-) diff --git a/src/MADE.Collections/CollectionExtensions.cs b/src/MADE.Collections/CollectionExtensions.cs index 27e431af..4c83c0a2 100644 --- a/src/MADE.Collections/CollectionExtensions.cs +++ b/src/MADE.Collections/CollectionExtensions.cs @@ -350,5 +350,16 @@ public static int PotentialIndexOf(this IList source, T value, Func + /// Shuffles the elements of a sequence randomly. + /// + /// The collection to shuffle. + /// The type of item in the collection. + /// The shuffled collection of items. + public static IEnumerable Shuffle(this IEnumerable source) + { + return source.OrderBy(x => Guid.NewGuid()); + } } } \ No newline at end of file diff --git a/tests/MADE.Collections.Tests/Tests/CollectionExtensionsTests.cs b/tests/MADE.Collections.Tests/Tests/CollectionExtensionsTests.cs index 927dcb43..e0c1fc86 100644 --- a/tests/MADE.Collections.Tests/Tests/CollectionExtensionsTests.cs +++ b/tests/MADE.Collections.Tests/Tests/CollectionExtensionsTests.cs @@ -7,9 +7,7 @@ namespace MADE.Collections.Tests.Tests using System.Linq; using MADE.Collections.Tests.Fakes; using MADE.Testing; - using NUnit.Framework; - using Shouldly; using CollectionExtensions = Collections.CollectionExtensions; @@ -17,6 +15,49 @@ namespace MADE.Collections.Tests.Tests [TestFixture] public class CollectionExtensionsTests { + public class WhenShufflingItems + { + [Test] + public void ShouldShuffleItemOrderRandomly() + { + // Arrange + var items = new List + { + 1, + 2, + 3, + 4, + 5 + }; + + // Act + var shuffledItems = items.Shuffle(); + + // Assert + shuffledItems.ShouldNotBeSameAs(items); + } + + [Test] + public void ShouldContainSameItemsAfterShuffle() + { + // Arrange + var items = new List + { + 1, + 2, + 3, + 4, + 5 + }; + + // Act + var shuffledItems = items.Shuffle(); + + // Assert + shuffledItems.ShouldBeEquivalentTo(items); + } + } + public class WhenUpdatingACollectionItem { [Test] @@ -34,7 +75,7 @@ public void ShouldThrowArgumentNullExceptionIfNullCollection() public void ShouldThrowArgumentNullExceptionIfNullItem() { // Arrange - var list = new List { "Hello" }; + var list = new List {"Hello"}; string item = null; // Act & Assert @@ -48,7 +89,7 @@ public void ShouldReturnTrueIfItemUpdated() TestObject objectToAdd = TestObjectFaker.Create().Generate(); TestObject objectToUpdateWith = TestObjectFaker.Create().Generate(); - var list = new List { objectToAdd }; + var list = new List {objectToAdd}; // Act bool updated = list.Update(objectToUpdateWith, (s, i) => s.Name == objectToAdd.Name); @@ -64,7 +105,7 @@ public void ShouldReturnFalseIfItemToUpdateDoesNotExist() TestObject objectToAdd = TestObjectFaker.Create().Generate(); TestObject objectToUpdateWith = TestObjectFaker.Create().Generate(); - var list = new List { objectToAdd }; + var list = new List {objectToAdd}; // Act bool updated = list.Update(objectToUpdateWith, (s, i) => s.Name == objectToUpdateWith.Name); @@ -90,7 +131,7 @@ public void ShouldThrowArgumentNullExceptionIfNullCollection() public void ShouldThrowArgumentNullExceptionIfNullSource() { // Arrange - var list = new List { "Hello" }; + var list = new List {"Hello"}; // Act & Assert Assert.Throws(() => list.MakeEqualTo(null)); @@ -100,8 +141,8 @@ public void ShouldThrowArgumentNullExceptionIfNullSource() public void ShouldUpdateCollectionToBeEqualOther() { // Arrange - var list = new List { "Hello" }; - var update = new List { "New", "List" }; + var list = new List {"Hello"}; + var update = new List {"New", "List"}; // Act list.MakeEqualTo(update); @@ -170,17 +211,17 @@ public void ShouldReturnFalseForInvalidCases(Collection expected, Collectio private static object[] ValidCases = { - new object[] { (Collection)null, (Collection)null }, - new object[] { new ObservableCollection { 1, 2, 3 }, new ObservableCollection { 1, 2, 3 } }, - new object[] { new ObservableCollection { 1, 2, 3 }, new ObservableCollection { 3, 2, 1 } }, + new object[] {(Collection)null, (Collection)null}, + new object[] {new ObservableCollection {1, 2, 3}, new ObservableCollection {1, 2, 3}}, + new object[] {new ObservableCollection {1, 2, 3}, new ObservableCollection {3, 2, 1}}, }; private static object[] InvalidCases = { - new object[] { (Collection)null, new ObservableCollection() }, - new object[] { new ObservableCollection(), (Collection)null }, - new object[] { new ObservableCollection { 1, 2, 3 }, new ObservableCollection { 4, 5, 6 } }, - new object[] { new ObservableCollection { 1, 2, 3 }, new ObservableCollection { 1, 2, 3, 4 } }, + new object[] {(Collection)null, new ObservableCollection()}, + new object[] {new ObservableCollection(), (Collection)null}, + new object[] {new ObservableCollection {1, 2, 3}, new ObservableCollection {4, 5, 6}}, + new object[] {new ObservableCollection {1, 2, 3}, new ObservableCollection {1, 2, 3, 4}}, }; } } From c31d9cfefb0a81ebd05a2beb7c1d04611414fb1e Mon Sep 17 00:00:00 2001 From: James Croft Date: Sat, 16 Apr 2022 13:34:21 +0100 Subject: [PATCH 07/15] Added String IsLike extension method for comparing string against a wildcard pattern --- .../Extensions/StringExtensions.cs | 38 +++++++++++++++++++ .../Tests/StringExtensionsTests.cs | 25 +++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/MADE.Data.Validation/Extensions/StringExtensions.cs b/src/MADE.Data.Validation/Extensions/StringExtensions.cs index 7a771735..e73e07fe 100644 --- a/src/MADE.Data.Validation/Extensions/StringExtensions.cs +++ b/src/MADE.Data.Validation/Extensions/StringExtensions.cs @@ -5,6 +5,7 @@ namespace MADE.Data.Validation.Extensions { using System; using System.Globalization; + using System.Text.RegularExpressions; /// /// Defines a collection of extensions for string values. @@ -43,6 +44,43 @@ public static bool Contains(this string phrase, string value, CompareOptions com return CultureInfo.CurrentCulture.CompareInfo.IndexOf(phrase, value, compareOption) >= 0; } + /// + /// Compares a string value against a wildcard pattern, similar to the Visual Basic like operator. + /// + /// + /// An example of this in use comparing strings with * wildcard pattern. + /// + /// // result is true + /// bool result = "MyValue".IsLike("My*"); + /// + /// // result is false + /// result = "MyValue".IsLike("Hello"); + /// + /// + /// The value to compare is like. + /// The wildcard like pattern to match on. + /// True if the value is like the pattern; otherwise, false. + public static bool IsLike(this string value, string likePattern) + { + if (value.IsNullOrWhiteSpace() || likePattern.IsNullOrWhiteSpace()) + { + return false; + } + + // Escape any special characters in pattern + var regex = "^" + Regex.Escape(likePattern) + "$"; + + // Replace wildcard characters with regular expression equivalents + regex = regex.Replace(@"\[!", "[^") + .Replace(@"\[", "[") + .Replace(@"\]", "]") + .Replace(@"\?", ".") + .Replace(@"\*", ".*") + .Replace(@"\#", @"\d"); + + return Regex.IsMatch(value, regex); + } + /// /// Checks whether a string value is an integer. /// diff --git a/tests/MADE.Data.Validation.Tests/Tests/StringExtensionsTests.cs b/tests/MADE.Data.Validation.Tests/Tests/StringExtensionsTests.cs index 9a378db5..9b7756f7 100644 --- a/tests/MADE.Data.Validation.Tests/Tests/StringExtensionsTests.cs +++ b/tests/MADE.Data.Validation.Tests/Tests/StringExtensionsTests.cs @@ -2,7 +2,7 @@ namespace MADE.Data.Validation.Tests.Tests { using System.Diagnostics.CodeAnalysis; using System.Globalization; - using Extensions; + using MADE.Data.Validation.Extensions; using NUnit.Framework; using Shouldly; @@ -10,6 +10,27 @@ namespace MADE.Data.Validation.Tests.Tests [TestFixture] public class StringExtensionsTests { + public class WhenValidatingIfStringIsLike + { + [TestCase("*", "abc", true)] + [TestCase("a*", "abc", true)] + [TestCase("a?c", "abc", true)] + [TestCase("[a-z][a-z][a-z]", "abc", true)] + [TestCase("###", "123", true)] + [TestCase("###", "abc", false)] + [TestCase("*###", "123abc", false)] + [TestCase("[a-z][a-z][a-z]", "ABC", false)] + [TestCase("a?c", "aba", false)] + public void ShouldMatchPattern(string pattern, string input, bool expected) + { + // Act + var actual = input.IsLike(pattern); + + // Assert + actual.ShouldBe(expected); + } + } + public class WhenCheckingIfStringContainsValue { [TestCase("Hello, World", "ello", CompareOptions.None)] @@ -141,4 +162,4 @@ public void ShouldReturnFalseIfNotFloat(string value) } } } -} +} \ No newline at end of file From 9570f3937eb1972808f992414b83ed0f14685578 Mon Sep 17 00:00:00 2001 From: James Croft Date: Sat, 16 Apr 2022 14:09:52 +0100 Subject: [PATCH 08/15] Added extension to get domain name from HttpContext --- .../Extensions/HttpContextExtensions.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/MADE.Web/Extensions/HttpContextExtensions.cs diff --git a/src/MADE.Web/Extensions/HttpContextExtensions.cs b/src/MADE.Web/Extensions/HttpContextExtensions.cs new file mode 100644 index 00000000..cbd13977 --- /dev/null +++ b/src/MADE.Web/Extensions/HttpContextExtensions.cs @@ -0,0 +1,23 @@ +// MADE Apps licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MADE.Web.Extensions +{ + using Microsoft.AspNetCore.Http; + + /// + /// Defines a collection of extensions for a object. + /// + public static class HttpContextExtensions + { + /// + /// Gets the domain name of the requesting context. + /// + /// The requesting . + /// The domain part of the request's host. + public static string GetDomain(this HttpContext context) + { + return context.Request.Host.Host; + } + } +} From a28db88a57462d34094c798c0939b1d0f128fb44 Mon Sep 17 00:00:00 2001 From: James Croft Date: Sat, 16 Apr 2022 17:56:56 +0100 Subject: [PATCH 09/15] Added JsonResult and controller base extensions for IActionResult objects --- .../Extensions/ControllerBaseExtensions.cs | 79 ++++++++++++++++++ src/MADE.Web.Mvc/MADE.Web.Mvc.csproj | 8 ++ src/MADE.Web.Mvc/Responses/JsonResult.cs | 82 +++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 src/MADE.Web.Mvc/Extensions/ControllerBaseExtensions.cs create mode 100644 src/MADE.Web.Mvc/Responses/JsonResult.cs diff --git a/src/MADE.Web.Mvc/Extensions/ControllerBaseExtensions.cs b/src/MADE.Web.Mvc/Extensions/ControllerBaseExtensions.cs new file mode 100644 index 00000000..e27fb857 --- /dev/null +++ b/src/MADE.Web.Mvc/Extensions/ControllerBaseExtensions.cs @@ -0,0 +1,79 @@ +namespace MADE.Web.Mvc.Extensions +{ + using System; + using System.Net; + using MADE.Web.Mvc.Responses; + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Mvc; + using Microsoft.AspNetCore.Mvc.ModelBinding; + using Newtonsoft.Json; + using JsonResult = MADE.Web.Mvc.Responses.JsonResult; + + /// + /// Defines a collection of extensions for MVC instances. + /// + public static class ControllerBaseExtensions + { + /// + /// Creates a object from the specified value for a controller response. + /// + /// The controller that is performing the response. + /// The value object to serialize. + /// The expected result HTTP status code. + /// The Json.NET serializer settings for serializing the result. + /// The created for the response. + /// Thrown if the is . + public static IActionResult Json( + this ControllerBase controller, + object value, + HttpStatusCode statusCode = HttpStatusCode.OK, + JsonSerializerSettings serializerSettings = null) + { + if (controller == null) + { + throw new ArgumentNullException(nameof(controller)); + } + + return new JsonResult(value, statusCode, serializerSettings); + } + + /// + /// Creates an that produces a response. + /// + /// The controller that is performing the response. + /// An error object to be returned to the client. + /// The created for the response. + /// Thrown if the is . + public static IActionResult InternalServerError(this ControllerBase controller, object responseContent) + { + if (controller == null) + { + throw new ArgumentNullException(nameof(controller)); + } + + return new InternalServerErrorObjectResult(responseContent); + } + + /// + /// Creates an that produces a response. + /// + /// The controller that is performing the response. + /// The containing errors to be returned to the client. + /// The created for the response. + /// Thrown if the or is . + public static IActionResult InternalServerError(this ControllerBase controller, ModelStateDictionary modelState) + { + if (controller == null) + { + throw new ArgumentNullException(nameof(controller)); + } + + if (modelState == null) + { + throw new ArgumentNullException(nameof(modelState)); + } + + return new InternalServerErrorObjectResult(modelState); + } + } +} \ No newline at end of file diff --git a/src/MADE.Web.Mvc/MADE.Web.Mvc.csproj b/src/MADE.Web.Mvc/MADE.Web.Mvc.csproj index ade129b9..ed1d52e6 100644 --- a/src/MADE.Web.Mvc/MADE.Web.Mvc.csproj +++ b/src/MADE.Web.Mvc/MADE.Web.Mvc.csproj @@ -16,4 +16,12 @@ + + + + + + + + \ No newline at end of file diff --git a/src/MADE.Web.Mvc/Responses/JsonResult.cs b/src/MADE.Web.Mvc/Responses/JsonResult.cs new file mode 100644 index 00000000..ee08297b --- /dev/null +++ b/src/MADE.Web.Mvc/Responses/JsonResult.cs @@ -0,0 +1,82 @@ +namespace MADE.Web.Mvc.Responses +{ + using System; + using System.Net; + using System.Runtime.ExceptionServices; + using System.Threading.Tasks; + using MADE.Web.Extensions; + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Mvc; + using Microsoft.AspNetCore.Mvc.Infrastructure; + using Newtonsoft.Json; + + /// + /// Defines a model for a result of a request that is serialized as JSON using Json.NET. + /// + public class JsonResult : ActionResult, IStatusCodeActionResult + { + /// + /// Initializes a new instance of the class with the object to serialize. + /// + /// The value object to serialize. + /// The expected result HTTP status code. + /// The Json.Net serializer settings for serializing the result. + public JsonResult( + object value, + HttpStatusCode statusCode = HttpStatusCode.OK, + JsonSerializerSettings serializerSettings = default) + { + this.Value = value; + this.StatusCode = (int)statusCode; + this.SerializerSettings = serializerSettings; + } + + /// + /// Gets the value object to serialize. + /// + public object Value { get; } + + /// + /// Gets the expected result HTTP status code. + /// + public int? StatusCode { get; } + + /// + /// Gets the Json.Net serializer settings for serializing the result. + /// + public JsonSerializerSettings SerializerSettings { get; } + + /// + /// Executes the result operation of the action method asynchronously writing the to the response. + /// + /// The context in which the result is executed. + /// An asynchronous operation. + /// Thrown if is . + public override async Task ExecuteResultAsync(ActionContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + HttpResponse response = context.HttpContext.Response; + + ExceptionDispatchInfo exceptionDispatchInfo = null; + try + { + await response.WriteJsonAsync( + this.StatusCode.GetValueOrDefault((int)HttpStatusCode.OK), + this.Value, + this.SerializerSettings); + } + catch (Exception ex) + { + exceptionDispatchInfo = ExceptionDispatchInfo.Capture(ex); + } + finally + { + exceptionDispatchInfo?.Throw(); + } + } + } +} \ No newline at end of file From 7568240ce040a8d4f3d258fd0a44e6d09db91ca5 Mon Sep 17 00:00:00 2001 From: James Croft Date: Sat, 16 Apr 2022 17:57:14 +0100 Subject: [PATCH 10/15] Updated using directives qualified names and ordering --- .../MADE.Collections.Tests/Tests/CollectionExtensionsTests.cs | 2 +- tests/MADE.Data.Converters.Tests/Tests/MathExtensionsTests.cs | 2 +- .../MADE.Data.Converters.Tests/Tests/StringExtensionsTests.cs | 2 +- tests/MADE.Data.Validation.Tests/Tests/AlphaValidatorTests.cs | 2 +- tests/MADE.Data.Validation.Tests/Tests/EmailValidatorTests.cs | 2 +- .../Tests/IpAddressValidatorTests.cs | 2 +- tests/MADE.Data.Validation.Tests/Tests/MathExtensionsTests.cs | 4 +--- 7 files changed, 7 insertions(+), 9 deletions(-) diff --git a/tests/MADE.Collections.Tests/Tests/CollectionExtensionsTests.cs b/tests/MADE.Collections.Tests/Tests/CollectionExtensionsTests.cs index e0c1fc86..a4ea9a34 100644 --- a/tests/MADE.Collections.Tests/Tests/CollectionExtensionsTests.cs +++ b/tests/MADE.Collections.Tests/Tests/CollectionExtensionsTests.cs @@ -9,7 +9,7 @@ namespace MADE.Collections.Tests.Tests using MADE.Testing; using NUnit.Framework; using Shouldly; - using CollectionExtensions = Collections.CollectionExtensions; + using CollectionExtensions = MADE.Collections.CollectionExtensions; [ExcludeFromCodeCoverage] [TestFixture] diff --git a/tests/MADE.Data.Converters.Tests/Tests/MathExtensionsTests.cs b/tests/MADE.Data.Converters.Tests/Tests/MathExtensionsTests.cs index a477746e..cdf78102 100644 --- a/tests/MADE.Data.Converters.Tests/Tests/MathExtensionsTests.cs +++ b/tests/MADE.Data.Converters.Tests/Tests/MathExtensionsTests.cs @@ -2,7 +2,7 @@ namespace MADE.Data.Converters.Tests.Tests { using System; using System.Diagnostics.CodeAnalysis; - using Extensions; + using MADE.Data.Converters.Extensions; using NUnit.Framework; using Shouldly; diff --git a/tests/MADE.Data.Converters.Tests/Tests/StringExtensionsTests.cs b/tests/MADE.Data.Converters.Tests/Tests/StringExtensionsTests.cs index adb88cef..9a87ca96 100644 --- a/tests/MADE.Data.Converters.Tests/Tests/StringExtensionsTests.cs +++ b/tests/MADE.Data.Converters.Tests/Tests/StringExtensionsTests.cs @@ -1,7 +1,7 @@ namespace MADE.Data.Converters.Tests.Tests { using System.Diagnostics.CodeAnalysis; - using Extensions; + using MADE.Data.Converters.Extensions; using NUnit.Framework; using Shouldly; diff --git a/tests/MADE.Data.Validation.Tests/Tests/AlphaValidatorTests.cs b/tests/MADE.Data.Validation.Tests/Tests/AlphaValidatorTests.cs index 6f3fbc9b..8c95d3d7 100644 --- a/tests/MADE.Data.Validation.Tests/Tests/AlphaValidatorTests.cs +++ b/tests/MADE.Data.Validation.Tests/Tests/AlphaValidatorTests.cs @@ -1,9 +1,9 @@ namespace MADE.Data.Validation.Tests.Tests { using System.Diagnostics.CodeAnalysis; + using MADE.Data.Validation.Validators; using NUnit.Framework; using Shouldly; - using Validators; [ExcludeFromCodeCoverage] [TestFixture] diff --git a/tests/MADE.Data.Validation.Tests/Tests/EmailValidatorTests.cs b/tests/MADE.Data.Validation.Tests/Tests/EmailValidatorTests.cs index f4a3a265..448e41e0 100644 --- a/tests/MADE.Data.Validation.Tests/Tests/EmailValidatorTests.cs +++ b/tests/MADE.Data.Validation.Tests/Tests/EmailValidatorTests.cs @@ -1,9 +1,9 @@ namespace MADE.Data.Validation.Tests.Tests { using System.Diagnostics.CodeAnalysis; + using MADE.Data.Validation.Validators; using NUnit.Framework; using Shouldly; - using Validators; [ExcludeFromCodeCoverage] [TestFixture] diff --git a/tests/MADE.Data.Validation.Tests/Tests/IpAddressValidatorTests.cs b/tests/MADE.Data.Validation.Tests/Tests/IpAddressValidatorTests.cs index 3e9142bf..e40c8d1f 100644 --- a/tests/MADE.Data.Validation.Tests/Tests/IpAddressValidatorTests.cs +++ b/tests/MADE.Data.Validation.Tests/Tests/IpAddressValidatorTests.cs @@ -1,9 +1,9 @@ namespace MADE.Data.Validation.Tests.Tests { using System.Diagnostics.CodeAnalysis; + using MADE.Data.Validation.Validators; using NUnit.Framework; using Shouldly; - using Validators; [ExcludeFromCodeCoverage] [TestFixture] diff --git a/tests/MADE.Data.Validation.Tests/Tests/MathExtensionsTests.cs b/tests/MADE.Data.Validation.Tests/Tests/MathExtensionsTests.cs index f33517ed..16161b24 100644 --- a/tests/MADE.Data.Validation.Tests/Tests/MathExtensionsTests.cs +++ b/tests/MADE.Data.Validation.Tests/Tests/MathExtensionsTests.cs @@ -1,10 +1,8 @@ namespace MADE.Data.Validation.Tests.Tests { using System.Diagnostics.CodeAnalysis; - using Extensions; - using MADE.Data.Validation.Exceptions; - + using MADE.Data.Validation.Extensions; using NUnit.Framework; using Shouldly; From 5027d692a929b2da159b53ac56eea63c75d4b3a6 Mon Sep 17 00:00:00 2001 From: James Croft Date: Sat, 16 Apr 2022 18:01:30 +0100 Subject: [PATCH 11/15] Updated entity configurations --- src/MADE.Data.EFCore/EntityBase.cs | 2 ++ .../Extensions/EntityBaseExtensions.cs | 21 ++++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/MADE.Data.EFCore/EntityBase.cs b/src/MADE.Data.EFCore/EntityBase.cs index e4513bc9..05c04fc2 100644 --- a/src/MADE.Data.EFCore/EntityBase.cs +++ b/src/MADE.Data.EFCore/EntityBase.cs @@ -1,6 +1,7 @@ namespace MADE.Data.EFCore { using System; + using System.ComponentModel.DataAnnotations.Schema; /// /// Defines a base definition for an entity. @@ -10,6 +11,7 @@ public abstract class EntityBase : IEntityBase /// /// Gets or sets the identifier of the entity. /// + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid Id { get; set; } /// diff --git a/src/MADE.Data.EFCore/Extensions/EntityBaseExtensions.cs b/src/MADE.Data.EFCore/Extensions/EntityBaseExtensions.cs index bff9bd38..b4e3388c 100644 --- a/src/MADE.Data.EFCore/Extensions/EntityBaseExtensions.cs +++ b/src/MADE.Data.EFCore/Extensions/EntityBaseExtensions.cs @@ -8,18 +8,33 @@ namespace MADE.Data.EFCore.Extensions /// public static class EntityBaseExtensions { + /// + /// Configures the default properties of an entity. + /// + /// The type of entity to configure. + /// The entity type builder associated with the entity. + /// The entity type builder. + public static EntityTypeBuilder Configure(this EntityTypeBuilder builder) + where TEntity : class, IEntityBase + { + builder.HasKey(e => e.Id); + builder.ConfigureDateProperties(); + return builder; + } + /// /// Configures the created and updated date properties of an entity as UTC. /// /// The type of entity to configure. /// The entity type builder associated with the entity. /// The entity type builder. - public static EntityTypeBuilder ConfigureDateProperties(this EntityTypeBuilder builder) - where TEntity : EntityBase + public static EntityTypeBuilder ConfigureDateProperties( + this EntityTypeBuilder builder) + where TEntity : class, IEntityBase { builder.Property(x => x.CreatedDate).IsUtc(); builder.Property(x => x.UpdatedDate).IsUtc(); return builder; } } -} +} \ No newline at end of file From 2a5664dd79d289ec86cc3b02d8ef1d5b91ae3325 Mon Sep 17 00:00:00 2001 From: James Croft Date: Sat, 16 Apr 2022 18:03:05 +0100 Subject: [PATCH 12/15] Updated EF packages --- src/MADE.Data.EFCore/MADE.Data.EFCore.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MADE.Data.EFCore/MADE.Data.EFCore.csproj b/src/MADE.Data.EFCore/MADE.Data.EFCore.csproj index dd6031c4..5359b7a6 100644 --- a/src/MADE.Data.EFCore/MADE.Data.EFCore.csproj +++ b/src/MADE.Data.EFCore/MADE.Data.EFCore.csproj @@ -15,11 +15,11 @@ - + - + From 4b02f8683a7faad59ff83e31ec2f5439d8e655e5 Mon Sep 17 00:00:00 2001 From: James Croft Date: Sat, 16 Apr 2022 18:10:45 +0100 Subject: [PATCH 13/15] Added to/from Base64 string conversion --- .../Extensions/StringExtensions.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/MADE.Data.Converters/Extensions/StringExtensions.cs b/src/MADE.Data.Converters/Extensions/StringExtensions.cs index fff6eef2..09ea8327 100644 --- a/src/MADE.Data.Converters/Extensions/StringExtensions.cs +++ b/src/MADE.Data.Converters/Extensions/StringExtensions.cs @@ -3,6 +3,7 @@ namespace MADE.Data.Converters.Extensions { + using System; using System.Text; /// @@ -84,6 +85,33 @@ public static string ToDefaultCase(this string value) return result; } + /// + /// Converts a value to a Base64 string using the specified encoding. + /// + /// Default encoding is UTF-8. + /// + /// + /// The string value to convert. + /// The encoding to get the value bytes while converting. + /// The Base64 string representing the value. + public static string ToBase64(this string value, Encoding encoding = default) + { + encoding ??= Encoding.UTF8; + return Convert.ToBase64String(encoding.GetBytes(value)); + } + + /// + /// Converts a Base64 string to a value using the specified encoding. + /// + /// The Base64 value to convert. + /// The encoding to get the value string while converting. + /// The string value representing the Base64 string. + public static string FromBase64(this string base64Value, Encoding encoding = default) + { + encoding ??= Encoding.UTF8; + return encoding.GetString(Convert.FromBase64String(base64Value)); + } + /// /// Converts a string value to an integer. /// From de4feecfbdeaf34aabbcf06db86f4d8b162696f0 Mon Sep 17 00:00:00 2001 From: James Croft Date: Sat, 16 Apr 2022 18:17:27 +0100 Subject: [PATCH 14/15] Added Base64 string value converter --- .../Extensions/StringExtensions.cs | 17 +++++++ .../StringToBase64StringValueConverter.cs | 48 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 src/MADE.Data.Converters/StringToBase64StringValueConverter.cs diff --git a/src/MADE.Data.Converters/Extensions/StringExtensions.cs b/src/MADE.Data.Converters/Extensions/StringExtensions.cs index 09ea8327..7e715308 100644 --- a/src/MADE.Data.Converters/Extensions/StringExtensions.cs +++ b/src/MADE.Data.Converters/Extensions/StringExtensions.cs @@ -4,7 +4,9 @@ namespace MADE.Data.Converters.Extensions { using System; + using System.IO; using System.Text; + using System.Threading.Tasks; /// /// Defines a collection of extensions for string values. @@ -112,6 +114,21 @@ public static string FromBase64(this string base64Value, Encoding encoding = def return encoding.GetString(Convert.FromBase64String(base64Value)); } + /// + /// Converts a string value to a . + /// + /// The value to convert. + /// A representing the string value. + public static async Task ToMemoryStreamAsync(this string value) + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream); + await writer.WriteAsync(value); + await writer.FlushAsync(); + stream.Position = 0; + return stream; + } + /// /// Converts a string value to an integer. /// diff --git a/src/MADE.Data.Converters/StringToBase64StringValueConverter.cs b/src/MADE.Data.Converters/StringToBase64StringValueConverter.cs new file mode 100644 index 00000000..5f9173a1 --- /dev/null +++ b/src/MADE.Data.Converters/StringToBase64StringValueConverter.cs @@ -0,0 +1,48 @@ +// MADE Apps licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MADE.Data.Converters +{ + using System.Text; + using MADE.Data.Converters.Extensions; + + /// + /// Defines a value converter from to Base64 with an optional Encoding parameter. + /// + public partial class StringToBase64StringValueConverter : IValueConverter + { + /// + /// Converts the value to the Base64 . + /// + /// + /// The value to convert. + /// + /// + /// The optional parameter used to help with conversion. + /// + /// + /// The converted Base64 object. + /// + public string Convert(string value, object parameter = default) + { + return value.ToBase64(parameter as Encoding ?? Encoding.UTF8); + } + + /// + /// Converts the Base64 value back to the original value. + /// + /// + /// The value to convert. + /// + /// + /// The optional parameter used to help with conversion. + /// + /// + /// The converted object. + /// + public string ConvertBack(string value, object parameter = default) + { + return value.FromBase64(parameter as Encoding ?? Encoding.UTF8); + } + } +} \ No newline at end of file From 6e0e9528268c31754391a441d2005e9dc301a849 Mon Sep 17 00:00:00 2001 From: James Croft Date: Sat, 16 Apr 2022 18:51:47 +0100 Subject: [PATCH 15/15] Added exception documentation --- src/MADE.Collections/CollectionExtensions.cs | 37 ++++++++++++++++++- .../ObservableItemCollection{T}.cs | 5 +++ .../BooleanToStringValueConverter.Windows.cs | 2 +- .../Constants/DateTimeConstants.cs | 5 +++ .../Extensions/DateTimeExtensions.cs | 2 +- .../Extensions/StringExtensions.cs | 6 +-- .../Converters/UtcDateTimeConverter.cs | 2 +- .../Extensions/DbContextExtensions.cs | 6 ++- .../Extensions/DateTimeExtensions.cs | 4 +- .../Extensions/MathExtensions.cs | 26 +++++++------ .../Extensions/StringExtensions.cs | 3 +- .../ValidatorCollection.cs | 2 + .../Validators/IpAddressValidator.cs | 6 ++- .../Validators/RegexValidator.cs | 1 + .../Validators/RequiredValidator.cs | 23 +++++------- src/MADE.Diagnostics/AppDiagnostics.cs | 6 +-- .../Logging/FileEventLogger.cs | 8 ++-- src/MADE.Diagnostics/StopwatchHelper.cs | 2 +- .../Platform/PlatformApiHelper.cs | 6 ++- .../Requests/Json/JsonDeleteNetworkRequest.cs | 6 +-- .../Requests/Json/JsonGetNetworkRequest.cs | 6 +-- .../Requests/Json/JsonPatchNetworkRequest.cs | 6 +-- .../Requests/Json/JsonPostNetworkRequest.cs | 6 +-- .../Requests/Json/JsonPutNetworkRequest.cs | 6 +-- .../Streams/StreamGetNetworkRequest.cs | 6 +-- .../Http/Responses/HttpResponseMessage{T}.cs | 20 +++++----- src/MADE.Runtime/Actions/Chain.cs | 2 +- .../Extensions/ReflectionExtensions.cs | 1 + ...tListener{TInstance,TSource,TEventArgs}.cs | 5 ++- ...ferenceEventListener{TInstance,TSource}.cs | 5 ++- .../CollectionAssertExtensions.cs | 6 ++- src/MADE.Threading/TaskExtensions.cs | 2 + src/MADE.Threading/Timer.cs | 1 + .../HttpContextExceptionsMiddleware.cs | 2 +- .../Responses/IPaginatedResponse{T}.cs | 2 +- .../Tests/CollectionExtensionsTests.cs | 6 +-- 36 files changed, 153 insertions(+), 87 deletions(-) diff --git a/src/MADE.Collections/CollectionExtensions.cs b/src/MADE.Collections/CollectionExtensions.cs index 4c83c0a2..18048c7f 100644 --- a/src/MADE.Collections/CollectionExtensions.cs +++ b/src/MADE.Collections/CollectionExtensions.cs @@ -19,6 +19,8 @@ public static class CollectionExtensions /// The item to add. /// The condition required to add the item. /// The type of item within the collection. + /// Thrown if the or is . + /// Potentially thrown by the delegate callback. public static void AddIf(this IList collection, T item, Func condition) { if (collection == null) @@ -37,6 +39,33 @@ public static void AddIf(this IList collection, T item, Func conditi } } + /// + /// Removes the specified item from the collection based on the specified condition being true. + /// + /// The collection to remove the item from. + /// The item to remove. + /// The condition required to remove the item. + /// The type of item within the collection. + /// Thrown if the or is . + /// Potentially thrown by the delegate callback. + public static void RemoveIf(this IList collection, T item, Func condition) + { + if (collection == null) + { + throw new ArgumentNullException(nameof(collection)); + } + + if (condition == null) + { + throw new ArgumentNullException(nameof(condition)); + } + + if (condition()) + { + collection.Remove(item); + } + } + /// /// Updates an item within the collection. /// @@ -146,6 +175,8 @@ public static void AddRange(this ICollection collection, IEnumerable it /// The items to add. /// The condition required to add the items. /// The type of item within the collection. + /// Thrown if the , or is . + /// Potentially thrown by the delegate callback. public static void AddRangeIf( this ICollection collection, IEnumerable itemsToAdd, @@ -203,6 +234,8 @@ public static void RemoveRange(this ICollection collection, IEnumerable /// The items to remove. /// The condition required to remove the items. /// The type of item within the collection. + /// Thrown if the , or is . + /// Potentially thrown by the delegate callback. public static void RemoveRangeIf( this ICollection collection, IEnumerable itemsToRemove, @@ -290,6 +323,7 @@ public static IEnumerable TakeFrom(this List list, int startingIndex, i /// /// The action to perform. /// + /// Potentially thrown by the delegate callback. public static void ForEach(this IEnumerable collection, Action action) { foreach (T item in collection) @@ -308,7 +342,7 @@ public static void ForEach(this IEnumerable collection, Action action) public static IEnumerable> Chunk(this IEnumerable source, int chunkSize = 25) { return source - .Select((v, i) => new {Index = i, Value = v}) + .Select((v, i) => new { Index = i, Value = v }) .GroupBy(x => x.Index / chunkSize) .Select(x => x.Select(v => v.Value)); } @@ -333,6 +367,7 @@ public static int InsertAtPotentialIndex(this IList source, T value, Func< /// The action to run to determine the position of the item based on the provided and an item in the collection. /// The type of items in the collection. /// The potential index of the item. + /// Potentially thrown by the delegate callback. public static int PotentialIndexOf(this IList source, T value, Func predicate) { var result = 0; diff --git a/src/MADE.Collections/ObjectModel/ObservableItemCollection{T}.cs b/src/MADE.Collections/ObjectModel/ObservableItemCollection{T}.cs index bbe7ae25..4eb1e81d 100644 --- a/src/MADE.Collections/ObjectModel/ObservableItemCollection{T}.cs +++ b/src/MADE.Collections/ObjectModel/ObservableItemCollection{T}.cs @@ -27,6 +27,7 @@ public class ObservableItemCollection : ObservableCollection, IDisposable /// /// Initializes a new instance of the class that is empty and has a default initial capacity. /// + /// Potentially thrown by the callback. public ObservableItemCollection() { base.CollectionChanged += (s, e) => @@ -46,6 +47,7 @@ public ObservableItemCollection() /// The collection whose elements are copied to the new list. /// /// The collection parameter cannot be null. + /// Potentially thrown by the callback. public ObservableItemCollection(IEnumerable collection) : base(collection) { @@ -65,6 +67,7 @@ public ObservableItemCollection(IEnumerable collection) /// The list whose elements are copied to the new list. /// /// The list parameter cannot be null. + /// Potentially thrown by the callback. public ObservableItemCollection(List list) : base(list) { @@ -93,6 +96,7 @@ public ObservableItemCollection(List list) /// /// The objects to add to the end of the collection. /// + /// Potentially thrown by the callback. public void AddRange(IEnumerable items) { this.CheckDisposed(); @@ -117,6 +121,7 @@ public void AddRange(IEnumerable items) /// /// The objects to remove from the collection. /// + /// Potentially thrown by the callback. public void RemoveRange(IEnumerable items) { this.CheckDisposed(); diff --git a/src/MADE.Data.Converters/BooleanToStringValueConverter.Windows.cs b/src/MADE.Data.Converters/BooleanToStringValueConverter.Windows.cs index 156d221b..eb926d81 100644 --- a/src/MADE.Data.Converters/BooleanToStringValueConverter.Windows.cs +++ b/src/MADE.Data.Converters/BooleanToStringValueConverter.Windows.cs @@ -80,7 +80,7 @@ public object Convert(object value, Type targetType, object parameter, string la /// The converted object. public object ConvertBack(object value, Type targetType, object parameter, string language) { - if (!(value is string b)) + if (value is not string b) { return value; } diff --git a/src/MADE.Data.Converters/Constants/DateTimeConstants.cs b/src/MADE.Data.Converters/Constants/DateTimeConstants.cs index 77344619..9e34e717 100644 --- a/src/MADE.Data.Converters/Constants/DateTimeConstants.cs +++ b/src/MADE.Data.Converters/Constants/DateTimeConstants.cs @@ -7,6 +7,11 @@ namespace MADE.Data.Converters.Constants /// public static class DateTimeConstants { + /// + /// Defines the minimum value for a object determined by Unix. + /// + public static readonly DateTime UnixEpoch = new(1970, 1, 1, 0, 0, 0); + /// /// Defines the time at the end of a day. /// diff --git a/src/MADE.Data.Converters/Extensions/DateTimeExtensions.cs b/src/MADE.Data.Converters/Extensions/DateTimeExtensions.cs index 34c31ebd..76631d94 100644 --- a/src/MADE.Data.Converters/Extensions/DateTimeExtensions.cs +++ b/src/MADE.Data.Converters/Extensions/DateTimeExtensions.cs @@ -230,7 +230,7 @@ public static DateTime EndOfYear(this DateTime dateTime) /// public static DateTime? SetTime(this DateTime? dateTime, int hours, int minutes, int seconds, int milliseconds) { - return dateTime == null ? (DateTime?)null : SetTime(dateTime.Value, hours, minutes, seconds, milliseconds); + return dateTime == null ? null : SetTime(dateTime.Value, hours, minutes, seconds, milliseconds); } /// diff --git a/src/MADE.Data.Converters/Extensions/StringExtensions.cs b/src/MADE.Data.Converters/Extensions/StringExtensions.cs index 7e715308..dc9aad44 100644 --- a/src/MADE.Data.Converters/Extensions/StringExtensions.cs +++ b/src/MADE.Data.Converters/Extensions/StringExtensions.cs @@ -166,7 +166,7 @@ public static int ToInt(this string value) } bool parsed = int.TryParse(value, out int intValue); - return parsed ? (int?)intValue : null; + return parsed ? intValue : null; } /// @@ -226,7 +226,7 @@ public static float ToFloat(this string value) } bool parsed = float.TryParse(value, out float floatValue); - return parsed ? (float?)floatValue : null; + return parsed ? floatValue : null; } /// @@ -266,7 +266,7 @@ public static double ToDouble(this string value) } bool parsed = double.TryParse(value, out double doubleValue); - return parsed ? (double?)doubleValue : null; + return parsed ? doubleValue : null; } } } \ No newline at end of file diff --git a/src/MADE.Data.EFCore/Converters/UtcDateTimeConverter.cs b/src/MADE.Data.EFCore/Converters/UtcDateTimeConverter.cs index 209eccec..62d9ea81 100644 --- a/src/MADE.Data.EFCore/Converters/UtcDateTimeConverter.cs +++ b/src/MADE.Data.EFCore/Converters/UtcDateTimeConverter.cs @@ -12,7 +12,7 @@ namespace MADE.Data.EFCore.Converters public static class UtcDateTimeConverter { internal static readonly ValueConverter UtcConverter = - new ValueConverter( + new( value => value, value => DateTime.SpecifyKind(value, DateTimeKind.Utc)); diff --git a/src/MADE.Data.EFCore/Extensions/DbContextExtensions.cs b/src/MADE.Data.EFCore/Extensions/DbContextExtensions.cs index 4ce3f5db..07213721 100644 --- a/src/MADE.Data.EFCore/Extensions/DbContextExtensions.cs +++ b/src/MADE.Data.EFCore/Extensions/DbContextExtensions.cs @@ -26,6 +26,7 @@ public static class DbContextExtensions /// A concurrency violation is encountered while saving to the database. /// A concurrency violation occurs when an unexpected number of rows are affected during save. /// This is usually because the data in the database has been modified since it was loaded into memory. + /// If the is canceled. public static async Task UpdateAsync( this DbContext context, T entity, @@ -61,7 +62,7 @@ public static void SetEntityDates(this DbContext context) .Entries() .Where( entry => entry.Entity is IEntityBase && - (entry.State == EntityState.Added || entry.State == EntityState.Modified)); + entry.State is EntityState.Added or EntityState.Modified); DateTime now = DateTime.UtcNow; @@ -86,6 +87,8 @@ public static void SetEntityDates(this DbContext context) /// /// True if the changes saved successfully; otherwise, false. /// + /// If the is canceled. + /// Potentially thrown by the delegate callback. public static async Task TrySaveChangesAsync( this DbContext context, Action onError = null, @@ -112,6 +115,7 @@ public static async Task TrySaveChangesAsync( /// An exception for handling the exception thrown, for example, event logging. /// The type of data context. /// True if the action ran successfully; otherwise, false. + /// Potentially thrown by the delegate callback. public static async Task TryAsync( this TContext context, Func action, diff --git a/src/MADE.Data.Validation/Extensions/DateTimeExtensions.cs b/src/MADE.Data.Validation/Extensions/DateTimeExtensions.cs index 083b0bf0..9c4d77bc 100644 --- a/src/MADE.Data.Validation/Extensions/DateTimeExtensions.cs +++ b/src/MADE.Data.Validation/Extensions/DateTimeExtensions.cs @@ -26,7 +26,7 @@ public static bool IsInRange(this DateTime date, DateTime from, DateTime to) /// True if the day of week is between Monday and Friday; otherwise, false. public static bool IsWeekday(this DateTime date) { - return date.DayOfWeek >= DayOfWeek.Monday && date.DayOfWeek <= DayOfWeek.Friday; + return date.DayOfWeek is >= DayOfWeek.Monday and <= DayOfWeek.Friday; } /// @@ -36,7 +36,7 @@ public static bool IsWeekday(this DateTime date) /// True if the day of week is Saturday or Sunday; otherwise, false. public static bool IsWeekend(this DateTime date) { - return date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday; + return date.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday; } } } \ No newline at end of file diff --git a/src/MADE.Data.Validation/Extensions/MathExtensions.cs b/src/MADE.Data.Validation/Extensions/MathExtensions.cs index 6fdaebd9..2be9b89f 100644 --- a/src/MADE.Data.Validation/Extensions/MathExtensions.cs +++ b/src/MADE.Data.Validation/Extensions/MathExtensions.cs @@ -3,6 +3,7 @@ namespace MADE.Data.Validation.Extensions { + using System; using MADE.Data.Validation.Exceptions; /// @@ -26,7 +27,7 @@ public static class MathExtensions /// public static bool IsZero(this double value) { - return System.Math.Abs(value) < Epsilon; + return Math.Abs(value) < Epsilon; } /// @@ -40,7 +41,7 @@ public static bool IsZero(this double value) /// public static bool IsZero(this float value) { - return System.Math.Abs(value) < Epsilon; + return Math.Abs(value) < Epsilon; } /// @@ -55,14 +56,15 @@ public static bool IsZero(this float value) /// /// True if the values are close; otherwise, false. /// + /// Thrown if the value equals . public static bool IsCloseTo(this int value, int compare) { - if (System.Math.Abs(value - compare) < 1) + if (Math.Abs(value - compare) < 1) { return true; } - double a = (System.Math.Abs(value) + System.Math.Abs(compare) + 10.0) * Epsilon; + double a = (Math.Abs(value) + Math.Abs(compare) + 10.0) * Epsilon; int b = value - compare; return -a < b && a > b; } @@ -81,12 +83,12 @@ public static bool IsCloseTo(this int value, int compare) /// public static bool IsCloseTo(this double value, double compare) { - if (System.Math.Abs(value - compare) < Epsilon) + if (Math.Abs(value - compare) < Epsilon) { return true; } - double a = (System.Math.Abs(value) + System.Math.Abs(compare) + 10.0) * Epsilon; + double a = (Math.Abs(value) + Math.Abs(compare) + 10.0) * Epsilon; double b = value - compare; return -a < b && a > b; } @@ -110,12 +112,12 @@ public static bool IsCloseTo(this double? value, double? compare) return false; } - if (System.Math.Abs(value.Value - compare.Value) < Epsilon) + if (Math.Abs(value.Value - compare.Value) < Epsilon) { return true; } - double a = (System.Math.Abs(value.Value) + System.Math.Abs(compare.Value) + 10.0) * Epsilon; + double a = (Math.Abs(value.Value) + Math.Abs(compare.Value) + 10.0) * Epsilon; double? b = value - compare; return -a < b && a > b; } @@ -134,12 +136,12 @@ public static bool IsCloseTo(this double? value, double? compare) /// public static bool IsCloseTo(this float value, float compare) { - if (System.Math.Abs(value - compare) < Epsilon) + if (Math.Abs(value - compare) < Epsilon) { return true; } - double a = (System.Math.Abs(value) + System.Math.Abs(compare) + 10.0) * Epsilon; + double a = (Math.Abs(value) + Math.Abs(compare) + 10.0) * Epsilon; float b = value - compare; return -a < b && a > b; } @@ -163,12 +165,12 @@ public static bool IsCloseTo(this float? value, float? compare) return false; } - if (System.Math.Abs(value.Value - compare.Value) < Epsilon) + if (Math.Abs(value.Value - compare.Value) < Epsilon) { return true; } - double a = (System.Math.Abs(value.Value) + System.Math.Abs(compare.Value) + 10.0) * Epsilon; + double a = (Math.Abs(value.Value) + Math.Abs(compare.Value) + 10.0) * Epsilon; float? b = value - compare; return -a < b && a > b; } diff --git a/src/MADE.Data.Validation/Extensions/StringExtensions.cs b/src/MADE.Data.Validation/Extensions/StringExtensions.cs index e73e07fe..7815c96f 100644 --- a/src/MADE.Data.Validation/Extensions/StringExtensions.cs +++ b/src/MADE.Data.Validation/Extensions/StringExtensions.cs @@ -3,7 +3,6 @@ namespace MADE.Data.Validation.Extensions { - using System; using System.Globalization; using System.Text.RegularExpressions; @@ -52,7 +51,6 @@ public static bool Contains(this string phrase, string value, CompareOptions com /// /// // result is true /// bool result = "MyValue".IsLike("My*"); - /// /// // result is false /// result = "MyValue".IsLike("Hello"); /// @@ -60,6 +58,7 @@ public static bool Contains(this string phrase, string value, CompareOptions com /// The value to compare is like. /// The wildcard like pattern to match on. /// True if the value is like the pattern; otherwise, false. + /// Throw if a Regex time-out occurred. public static bool IsLike(this string value, string likePattern) { if (value.IsNullOrWhiteSpace() || likePattern.IsNullOrWhiteSpace()) diff --git a/src/MADE.Data.Validation/ValidatorCollection.cs b/src/MADE.Data.Validation/ValidatorCollection.cs index adb8a686..fca8d774 100644 --- a/src/MADE.Data.Validation/ValidatorCollection.cs +++ b/src/MADE.Data.Validation/ValidatorCollection.cs @@ -3,6 +3,7 @@ namespace MADE.Data.Validation { + using System; using System.Collections.Generic; using System.Linq; using MADE.Data.Validation.Extensions; @@ -66,6 +67,7 @@ public bool IsDirty /// Executes data validation on the provided against the validators provided. /// /// The value to be validated. + /// Potentially thrown by the delegate callback. public void Validate(object value) { this.ForEach(validator => validator.Validate(value)); diff --git a/src/MADE.Data.Validation/Validators/IpAddressValidator.cs b/src/MADE.Data.Validation/Validators/IpAddressValidator.cs index b1cbd4ed..3fee521e 100644 --- a/src/MADE.Data.Validation/Validators/IpAddressValidator.cs +++ b/src/MADE.Data.Validation/Validators/IpAddressValidator.cs @@ -3,6 +3,7 @@ namespace MADE.Data.Validation.Validators { + using System; using System.Linq; using System.Text.RegularExpressions; using MADE.Data.Validation.Extensions; @@ -43,6 +44,7 @@ public string FeedbackMessage /// Executes data validation on the provided . /// /// The value to be validated. + /// The array is multidimensional and contains more than elements. public void Validate(object value) { string str = value?.ToString() ?? string.Empty; @@ -54,7 +56,7 @@ public void Validate(object value) private static bool IsNibbleValid(string nibble) { - if (nibble.Length > 3 || nibble.Length == 0) + if (nibble.Length is > 3 or 0) { return false; } @@ -70,7 +72,7 @@ private static bool IsNibbleValid(string nibble) } int.TryParse(nibble, out int numeric); - return numeric >= 0 && numeric <= 255; + return numeric is >= 0 and <= 255; } } } \ No newline at end of file diff --git a/src/MADE.Data.Validation/Validators/RegexValidator.cs b/src/MADE.Data.Validation/Validators/RegexValidator.cs index 8b645fa1..fd6126fe 100644 --- a/src/MADE.Data.Validation/Validators/RegexValidator.cs +++ b/src/MADE.Data.Validation/Validators/RegexValidator.cs @@ -47,6 +47,7 @@ public virtual string FeedbackMessage /// Executes data validation on the provided . /// /// The value to be validated. + /// Thrown if a Regex time-out occurred. public void Validate(object value) { string str = value?.ToString() ?? string.Empty; diff --git a/src/MADE.Data.Validation/Validators/RequiredValidator.cs b/src/MADE.Data.Validation/Validators/RequiredValidator.cs index 880c86c1..3858b5a1 100644 --- a/src/MADE.Data.Validation/Validators/RequiredValidator.cs +++ b/src/MADE.Data.Validation/Validators/RequiredValidator.cs @@ -34,7 +34,9 @@ public class RequiredValidator : IValidator /// public string FeedbackMessage { - get => this.feedbackMessage.IsNullOrWhiteSpace() ? Resources.RequiredValidator_FeedbackMessage : this.feedbackMessage; + get => this.feedbackMessage.IsNullOrWhiteSpace() + ? Resources.RequiredValidator_FeedbackMessage + : this.feedbackMessage; set => this.feedbackMessage = value; } @@ -50,19 +52,14 @@ public void Validate(object value) private static bool DetermineIsInvalid(object value) { - switch (value) + return value switch { - case null: - return true; - case ICollection collection: - return collection.Count <= 0; - case bool isTrue: - return !isTrue; - case string str: - return str.IsNullOrWhiteSpace(); - default: - return false; - } + null => true, + ICollection collection => collection.Count <= 0, + bool isTrue => !isTrue, + string str => str.IsNullOrWhiteSpace(), + _ => false + }; } } } \ No newline at end of file diff --git a/src/MADE.Diagnostics/AppDiagnostics.cs b/src/MADE.Diagnostics/AppDiagnostics.cs index e3fd638b..fde4474b 100644 --- a/src/MADE.Diagnostics/AppDiagnostics.cs +++ b/src/MADE.Diagnostics/AppDiagnostics.cs @@ -64,7 +64,7 @@ public async Task StartRecordingDiagnosticsAsync() #if WINDOWS_UWP Windows.UI.Xaml.Application.Current.UnhandledException += this.OnAppUnhandledException; #elif NETSTANDARD2_0 || __ANDROID__ || __IOS__ - System.AppDomain.CurrentDomain.UnhandledException += this.OnAppUnhandledException; + AppDomain.CurrentDomain.UnhandledException += this.OnAppUnhandledException; #endif #if __ANDROID__ @@ -92,7 +92,7 @@ public void StopRecordingDiagnostics() #if WINDOWS_UWP Windows.UI.Xaml.Application.Current.UnhandledException -= this.OnAppUnhandledException; #elif NETSTANDARD2_0 || __ANDROID__ || __IOS__ - System.AppDomain.CurrentDomain.UnhandledException -= this.OnAppUnhandledException; + AppDomain.CurrentDomain.UnhandledException -= this.OnAppUnhandledException; #endif #if __ANDROID__ @@ -137,7 +137,7 @@ private void OnAppUnhandledException(object sender, UnhandledExceptionEventArgs "The application is terminating due to an unhandled exception being thrown."); } - if (!(args.ExceptionObject is Exception ex)) + if (args.ExceptionObject is not Exception ex) { return; } diff --git a/src/MADE.Diagnostics/Logging/FileEventLogger.cs b/src/MADE.Diagnostics/Logging/FileEventLogger.cs index 4661daf0..23b5b21a 100644 --- a/src/MADE.Diagnostics/Logging/FileEventLogger.cs +++ b/src/MADE.Diagnostics/Logging/FileEventLogger.cs @@ -15,7 +15,7 @@ public class FileEventLogger : IEventLogger { private const string LogFormat = "{0:G}\tLevel: {1}\tId: {2}\tMessage: '{3}'"; - private readonly SemaphoreSlim fileSemaphore = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim fileSemaphore = new(1, 1); /// /// Gets or sets the full file path to where the current log exists. @@ -240,10 +240,8 @@ private async Task WriteToFileAsync(string line) { try { - using (StreamWriter sw = File.AppendText(this.LogPath)) - { - await sw.WriteLineAsync(line); - } + using StreamWriter sw = File.AppendText(this.LogPath); + await sw.WriteLineAsync(line); } catch (Exception ex) { diff --git a/src/MADE.Diagnostics/StopwatchHelper.cs b/src/MADE.Diagnostics/StopwatchHelper.cs index 45e9959c..4c60007f 100644 --- a/src/MADE.Diagnostics/StopwatchHelper.cs +++ b/src/MADE.Diagnostics/StopwatchHelper.cs @@ -14,7 +14,7 @@ namespace MADE.Diagnostics /// public static class StopwatchHelper { - private static readonly Dictionary Stopwatches = new Dictionary(); + private static readonly Dictionary Stopwatches = new(); /// /// Starts a with the specified and . diff --git a/src/MADE.Foundation/Platform/PlatformApiHelper.cs b/src/MADE.Foundation/Platform/PlatformApiHelper.cs index 6e6aa2ef..93353f8d 100644 --- a/src/MADE.Foundation/Platform/PlatformApiHelper.cs +++ b/src/MADE.Foundation/Platform/PlatformApiHelper.cs @@ -9,9 +9,9 @@ namespace MADE.Foundation.Platform /// public static class PlatformApiHelper { - private static readonly object Lock = new object(); + private static readonly object Lock = new(); - private static readonly Dictionary CheckedTypes = new Dictionary(); + private static readonly Dictionary CheckedTypes = new(); /// /// Indicates whether the specified is supported by the platform. @@ -37,6 +37,7 @@ public static bool IsTypeSupported(Type type) /// The type where the method should be checked. /// The name of the method to check. /// True if supported; otherwise, false. + /// More than one method is found with the specified name. public static bool IsMethodSupported(Type type, string methodName) { var result = IsTypeSupported(type); @@ -54,6 +55,7 @@ public static bool IsMethodSupported(Type type, string methodName) /// The type where the property should be checked. /// The name of the property to check. /// True if supported; otherwise, false. + /// More than one property is found with the specified name. public static bool IsPropertySupported(Type type, string propertyName) { var result = IsTypeSupported(type); diff --git a/src/MADE.Networking/Http/Requests/Json/JsonDeleteNetworkRequest.cs b/src/MADE.Networking/Http/Requests/Json/JsonDeleteNetworkRequest.cs index e3dc5e59..799fdc13 100644 --- a/src/MADE.Networking/Http/Requests/Json/JsonDeleteNetworkRequest.cs +++ b/src/MADE.Networking/Http/Requests/Json/JsonDeleteNetworkRequest.cs @@ -64,7 +64,7 @@ public JsonDeleteNetworkRequest(HttpClient client, string url, Dictionary public override async Task ExecuteAsync(CancellationToken cancellationToken = default) { - string json = await this.GetJsonResponse(cancellationToken); + string json = await this.GetJsonResponseAsync(cancellationToken); return JsonConvert.DeserializeObject(json); } @@ -84,11 +84,11 @@ public override async Task ExecuteAsync( Type expectedResponse, CancellationToken cancellationToken = default) { - string json = await this.GetJsonResponse(cancellationToken); + string json = await this.GetJsonResponseAsync(cancellationToken); return JsonConvert.DeserializeObject(json, expectedResponse); } - private async Task GetJsonResponse(CancellationToken cancellationToken = default) + private async Task GetJsonResponseAsync(CancellationToken cancellationToken = default) { if (this.client == null) { diff --git a/src/MADE.Networking/Http/Requests/Json/JsonGetNetworkRequest.cs b/src/MADE.Networking/Http/Requests/Json/JsonGetNetworkRequest.cs index be2f132c..e3375704 100644 --- a/src/MADE.Networking/Http/Requests/Json/JsonGetNetworkRequest.cs +++ b/src/MADE.Networking/Http/Requests/Json/JsonGetNetworkRequest.cs @@ -64,7 +64,7 @@ public JsonGetNetworkRequest(HttpClient client, string url, Dictionary public override async Task ExecuteAsync(CancellationToken cancellationToken = default) { - string json = await this.GetJsonResponse(cancellationToken); + string json = await this.GetJsonResponseAsync(cancellationToken); return JsonConvert.DeserializeObject(json); } @@ -84,11 +84,11 @@ public override async Task ExecuteAsync( Type expectedResponse, CancellationToken cancellationToken = default) { - string json = await this.GetJsonResponse(cancellationToken); + string json = await this.GetJsonResponseAsync(cancellationToken); return JsonConvert.DeserializeObject(json, expectedResponse); } - private async Task GetJsonResponse(CancellationToken cancellationToken = default) + private async Task GetJsonResponseAsync(CancellationToken cancellationToken = default) { if (this.client == null) { diff --git a/src/MADE.Networking/Http/Requests/Json/JsonPatchNetworkRequest.cs b/src/MADE.Networking/Http/Requests/Json/JsonPatchNetworkRequest.cs index 56699fc0..d1294cd1 100644 --- a/src/MADE.Networking/Http/Requests/Json/JsonPatchNetworkRequest.cs +++ b/src/MADE.Networking/Http/Requests/Json/JsonPatchNetworkRequest.cs @@ -95,7 +95,7 @@ public JsonPatchNetworkRequest( /// public override async Task ExecuteAsync(CancellationToken cancellationToken = default) { - string json = await this.GetJsonResponse(cancellationToken); + string json = await this.GetJsonResponseAsync(cancellationToken); return JsonConvert.DeserializeObject(json); } @@ -115,11 +115,11 @@ public override async Task ExecuteAsync( Type expectedResponse, CancellationToken cancellationToken = default) { - string json = await this.GetJsonResponse(cancellationToken); + string json = await this.GetJsonResponseAsync(cancellationToken); return JsonConvert.DeserializeObject(json, expectedResponse); } - private async Task GetJsonResponse(CancellationToken cancellationToken = default) + private async Task GetJsonResponseAsync(CancellationToken cancellationToken = default) { if (this.client == null) { diff --git a/src/MADE.Networking/Http/Requests/Json/JsonPostNetworkRequest.cs b/src/MADE.Networking/Http/Requests/Json/JsonPostNetworkRequest.cs index 4442b23f..929bb065 100644 --- a/src/MADE.Networking/Http/Requests/Json/JsonPostNetworkRequest.cs +++ b/src/MADE.Networking/Http/Requests/Json/JsonPostNetworkRequest.cs @@ -95,7 +95,7 @@ public JsonPostNetworkRequest( /// public override async Task ExecuteAsync(CancellationToken cancellationToken = default) { - string json = await this.GetJsonResponse(cancellationToken); + string json = await this.GetJsonResponseAsync(cancellationToken); return JsonConvert.DeserializeObject(json); } @@ -115,11 +115,11 @@ public override async Task ExecuteAsync( Type expectedResponse, CancellationToken cancellationToken = default) { - string json = await this.GetJsonResponse(cancellationToken); + string json = await this.GetJsonResponseAsync(cancellationToken); return JsonConvert.DeserializeObject(json, expectedResponse); } - private async Task GetJsonResponse(CancellationToken cancellationToken = default) + private async Task GetJsonResponseAsync(CancellationToken cancellationToken = default) { if (this.client == null) { diff --git a/src/MADE.Networking/Http/Requests/Json/JsonPutNetworkRequest.cs b/src/MADE.Networking/Http/Requests/Json/JsonPutNetworkRequest.cs index 95e121f4..ab917279 100644 --- a/src/MADE.Networking/Http/Requests/Json/JsonPutNetworkRequest.cs +++ b/src/MADE.Networking/Http/Requests/Json/JsonPutNetworkRequest.cs @@ -91,7 +91,7 @@ public JsonPutNetworkRequest(HttpClient client, string url, string jsonData, Dic /// public override async Task ExecuteAsync(CancellationToken cancellationToken = default) { - string json = await this.GetJsonResponse(cancellationToken); + string json = await this.GetJsonResponseAsync(cancellationToken); return JsonConvert.DeserializeObject(json); } @@ -111,11 +111,11 @@ public override async Task ExecuteAsync( Type expectedResponse, CancellationToken cancellationToken = default) { - string json = await this.GetJsonResponse(cancellationToken); + string json = await this.GetJsonResponseAsync(cancellationToken); return JsonConvert.DeserializeObject(json, expectedResponse); } - private async Task GetJsonResponse(CancellationToken cancellationToken = default) + private async Task GetJsonResponseAsync(CancellationToken cancellationToken = default) { if (this.client == null) { diff --git a/src/MADE.Networking/Http/Requests/Streams/StreamGetNetworkRequest.cs b/src/MADE.Networking/Http/Requests/Streams/StreamGetNetworkRequest.cs index f457120a..28c92d25 100644 --- a/src/MADE.Networking/Http/Requests/Streams/StreamGetNetworkRequest.cs +++ b/src/MADE.Networking/Http/Requests/Streams/StreamGetNetworkRequest.cs @@ -62,7 +62,7 @@ public StreamGetNetworkRequest(HttpClient client, string url, Dictionary public override async Task ExecuteAsync(CancellationToken cancellationToken = default) { - return (TResponse)await this.GetStreamResponse(cancellationToken); + return (TResponse)await this.GetStreamResponseAsync(cancellationToken); } /// @@ -81,10 +81,10 @@ public override async Task ExecuteAsync( Type expectedResponse, CancellationToken cancellationToken = default) { - return await this.GetStreamResponse(cancellationToken); + return await this.GetStreamResponseAsync(cancellationToken); } - private async Task GetStreamResponse(CancellationToken cancellationToken = default) + private async Task GetStreamResponseAsync(CancellationToken cancellationToken = default) { if (this.client == null) { diff --git a/src/MADE.Networking/Http/Responses/HttpResponseMessage{T}.cs b/src/MADE.Networking/Http/Responses/HttpResponseMessage{T}.cs index 50cba353..7fa104dc 100644 --- a/src/MADE.Networking/Http/Responses/HttpResponseMessage{T}.cs +++ b/src/MADE.Networking/Http/Responses/HttpResponseMessage{T}.cs @@ -120,17 +120,19 @@ public void Dispose() /// A value indicating whether to release both managed and unmanaged resources. protected virtual void Dispose(bool disposing) { - if (!this.disposed) + if (this.disposed) { - if (disposing) - { - this.response.Dispose(); - } - - this.response = null; - this.DeserializedContent = default; - this.disposed = true; + return; } + + if (disposing) + { + this.response.Dispose(); + } + + this.response = null; + this.DeserializedContent = default; + this.disposed = true; } } } \ No newline at end of file diff --git a/src/MADE.Runtime/Actions/Chain.cs b/src/MADE.Runtime/Actions/Chain.cs index c082cc6e..4524f473 100644 --- a/src/MADE.Runtime/Actions/Chain.cs +++ b/src/MADE.Runtime/Actions/Chain.cs @@ -12,7 +12,7 @@ namespace MADE.Runtime.Actions public class Chain : IChain where T : class { - private readonly List> chain = new List>(); + private readonly List> chain = new(); /// /// Initializes a new instance of the class with an instance. diff --git a/src/MADE.Runtime/Extensions/ReflectionExtensions.cs b/src/MADE.Runtime/Extensions/ReflectionExtensions.cs index cbcd1656..7c65ba8e 100644 --- a/src/MADE.Runtime/Extensions/ReflectionExtensions.cs +++ b/src/MADE.Runtime/Extensions/ReflectionExtensions.cs @@ -15,6 +15,7 @@ public static class ReflectionExtensions /// The name of the property to retrieve a value for. /// The type of expected value. /// The value of the property. + /// More than one property is found with the specified name. public static T GetPropertyValue(this object obj, string property) where T : class { diff --git a/src/MADE.Runtime/WeakReferenceEventListener{TInstance,TSource,TEventArgs}.cs b/src/MADE.Runtime/WeakReferenceEventListener{TInstance,TSource,TEventArgs}.cs index d2644602..7cba82dc 100644 --- a/src/MADE.Runtime/WeakReferenceEventListener{TInstance,TSource,TEventArgs}.cs +++ b/src/MADE.Runtime/WeakReferenceEventListener{TInstance,TSource,TEventArgs}.cs @@ -1,4 +1,4 @@ -namespace MADE.Runtime +namespace MADE.Runtime { using System; @@ -25,6 +25,7 @@ public sealed class WeakReferenceEventListener /// /// The instance. /// + /// Thrown if the is . public WeakReferenceEventListener(TInstance instance) { if (instance == null) @@ -54,6 +55,7 @@ public WeakReferenceEventListener(TInstance instance) /// /// The event arguments. /// + /// Potentially thrown by the delegate callback. public void OnEvent(TSource source, TEventArgs eventArgs) { var target = (TInstance)this.weakInstance.Target; @@ -70,6 +72,7 @@ public void OnEvent(TSource source, TEventArgs eventArgs) /// /// Called when detaching the event listener. /// + /// Potentially thrown by the delegate callback. public void Detach() { var target = (TInstance)this.weakInstance.Target; diff --git a/src/MADE.Runtime/WeakReferenceEventListener{TInstance,TSource}.cs b/src/MADE.Runtime/WeakReferenceEventListener{TInstance,TSource}.cs index 0c70b7d9..ff4a2d40 100644 --- a/src/MADE.Runtime/WeakReferenceEventListener{TInstance,TSource}.cs +++ b/src/MADE.Runtime/WeakReferenceEventListener{TInstance,TSource}.cs @@ -1,4 +1,4 @@ -// MADE Apps licenses this file to you under the MIT license. +// MADE Apps licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. namespace MADE.Runtime @@ -25,6 +25,7 @@ public sealed class WeakReferenceEventListener /// /// The instance. /// + /// Thrown if the is . public WeakReferenceEventListener(TInstance instance) { if (instance == null) @@ -51,6 +52,7 @@ public WeakReferenceEventListener(TInstance instance) /// /// The source of the event. /// + /// Potentially thrown by the delegate callback. public void OnEvent(TSource source) { var target = (TInstance)this.weakReference.Target; @@ -67,6 +69,7 @@ public void OnEvent(TSource source) /// /// Called when detaching the event listener. /// + /// Potentially thrown by the delegate callback. public void Detach() { var target = (TInstance)this.weakReference.Target; diff --git a/src/MADE.Testing/CollectionAssertExtensions.cs b/src/MADE.Testing/CollectionAssertExtensions.cs index c0835688..c2f08ca5 100644 --- a/src/MADE.Testing/CollectionAssertExtensions.cs +++ b/src/MADE.Testing/CollectionAssertExtensions.cs @@ -25,6 +25,7 @@ public static class CollectionAssertExtensions /// /// The second collection to compare. This is the collection produced by the code under test. /// + /// Thrown if the condition of equivalency could not be met. public static void ShouldBeEquivalentTo(this IEnumerable expected, IEnumerable actual) { if ((expected == null) != (actual == null)) @@ -70,6 +71,7 @@ public static void ShouldBeEquivalentTo(this IEnumerable expected, /// /// The second collection to compare. This is the collection produced by the code under test. /// + /// Thrown if the condition of equivalency could not be met. public static void ShouldNotBeEquivalentTo(this IEnumerable expected, IEnumerable actual) { if ((expected == null) != (actual == null)) @@ -131,8 +133,8 @@ public static void ShouldNotBeEquivalentTo(this IEnumerable expect /// True if a mismatched element was found; false otherwise. /// private static bool FindMismatchedElement( - ICollection expected, - ICollection actual, + IEnumerable expected, + IEnumerable actual, out int expectedCount, out int actualCount, out object mismatchedElement) diff --git a/src/MADE.Threading/TaskExtensions.cs b/src/MADE.Threading/TaskExtensions.cs index 4daaf55a..b5aa2114 100644 --- a/src/MADE.Threading/TaskExtensions.cs +++ b/src/MADE.Threading/TaskExtensions.cs @@ -18,6 +18,7 @@ public static class TaskExtensions /// The task to observe for exceptions. /// An action invoked when an exception is caught. /// An asynchronous operation. + /// Potentially thrown by the delegate callback. public static Task AndObserveExceptions(this Task task, Action onException = null) { task?.ContinueWith( @@ -40,6 +41,7 @@ public static Task AndObserveExceptions(this Task task, Action onExce /// The task to observe for exceptions. /// An action invoked when an exception is caught. /// An asynchronous operation. + /// Potentially thrown by the delegate callback. public static Task AndObserveExceptions(this Task task, Action onException = null) { task?.ContinueWith( diff --git a/src/MADE.Threading/Timer.cs b/src/MADE.Threading/Timer.cs index e29c3e39..c24299ff 100644 --- a/src/MADE.Threading/Timer.cs +++ b/src/MADE.Threading/Timer.cs @@ -127,6 +127,7 @@ public void Dispose() /// /// Invokes the event, if attached. /// + /// Potentially thrown by the delegate callback. protected virtual void InvokeTick() { this.Tick?.Invoke(this, EventArgs.Empty); diff --git a/src/MADE.Web/Exceptions/HttpContextExceptionsMiddleware.cs b/src/MADE.Web/Exceptions/HttpContextExceptionsMiddleware.cs index 57acbaaa..85095fa7 100644 --- a/src/MADE.Web/Exceptions/HttpContextExceptionsMiddleware.cs +++ b/src/MADE.Web/Exceptions/HttpContextExceptionsMiddleware.cs @@ -105,7 +105,7 @@ private async Task HandleExceptionAsync(HttpContext context, Exception exception try { - if (!(handleMethod is null)) + if (handleMethod is not null) { await handleMethod.Invoke(exceptionHandler, new object[] { context, exception }); } diff --git a/src/MADE.Web/Responses/IPaginatedResponse{T}.cs b/src/MADE.Web/Responses/IPaginatedResponse{T}.cs index f9235110..edff408d 100644 --- a/src/MADE.Web/Responses/IPaginatedResponse{T}.cs +++ b/src/MADE.Web/Responses/IPaginatedResponse{T}.cs @@ -1,4 +1,4 @@ -// MADE Apps licenses this file to you under the MIT license. +// MADE Apps licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. namespace MADE.Web.Responses diff --git a/tests/MADE.Collections.Tests/Tests/CollectionExtensionsTests.cs b/tests/MADE.Collections.Tests/Tests/CollectionExtensionsTests.cs index a4ea9a34..f434484f 100644 --- a/tests/MADE.Collections.Tests/Tests/CollectionExtensionsTests.cs +++ b/tests/MADE.Collections.Tests/Tests/CollectionExtensionsTests.cs @@ -211,15 +211,15 @@ public void ShouldReturnFalseForInvalidCases(Collection expected, Collectio private static object[] ValidCases = { - new object[] {(Collection)null, (Collection)null}, + new object[] {null, null}, new object[] {new ObservableCollection {1, 2, 3}, new ObservableCollection {1, 2, 3}}, new object[] {new ObservableCollection {1, 2, 3}, new ObservableCollection {3, 2, 1}}, }; private static object[] InvalidCases = { - new object[] {(Collection)null, new ObservableCollection()}, - new object[] {new ObservableCollection(), (Collection)null}, + new object[] {null, new ObservableCollection()}, + new object[] {new ObservableCollection(), null}, new object[] {new ObservableCollection {1, 2, 3}, new ObservableCollection {4, 5, 6}}, new object[] {new ObservableCollection {1, 2, 3}, new ObservableCollection {1, 2, 3, 4}}, };