Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v3.16.0 #96

Merged
merged 3 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

Represents the **NuGet** versions.

## v3.16.0
- *Enhancement*: Added basic [FluentValidator](https://docs.fluentvalidation.net/en/latest/) compatibility to the `CoreEx.Validation` by supporting _key_ (common) named capabilities:
- `AbstractValidator<T>` added as a wrapper for `Validator<T>`; with both supporting `RuleFor` method (wrapper for existing `Property`).
- `NotEmpty`, `NotNull`, `Empty`, `Null`, `InclusiveBetween`, `ExclusiveBetween`, `Equal`, `NotEqual`, `LessThan`, `LessThanOrEqualTo`, `GreaterThan`, `GreaterThanOrEqualTo`, `Matches`, `Length`, `MinimumLength`, `MaximumLength`, `PrecisionScale`, `EmailAddress` and `IsInEnum` extension methods added (invoking existing equivalents).
- `NullRule` and `NotNullRule` added to support the `Null` and `NotNull` capabilities specifically.
- `WithMessage` added to explcitly set the error message for a preceeding `IValueRule` (equivalent to specifying when invoking extension method).
- `ValidatorStrings` have had their fallback texts added to ensure an appropriate text is output where `ITextProvider` is not available.
- _Note:_ The above changes are to achieve a basic level of compatibility, they are not intended to implement the full capabilities of _FluentValidation_; nor, will it ever. The `CoreEx.FluentValidation` enables _FluentValidation_ to be used directly where required; also, the existing `CoreEx.Validation.InteropRule` enables interoperability between the two.
- *Enhancement*: Added `StringSyntaxAttribute` support to improve intellisense for JSON and URI specification.
- *Enhancement*: Added `EventPublisherHealthCheck` that will send an `EventData` message to verify that the `IEventPublisher` is functioning correctly.
- _Note:_ only use where the corresponding subscriber(s)/consumer(s) are aware and can ignore/filter to avoid potential downstream challenges.

## v3.15.0
- *Enhancement*: This is a clean-up version to remove all obsolete code and dependencies. This will result in a number of minor breaking changes, but will ensure that the codebase is up-to-date and maintainable.
- As per [`v3.14.0`](#v3.14.0) the previously obsoleted `TypedHttpClientBase` methods `WithRetry`, `WithTimeout`, `WithCustomRetryPolicy` and `WithMaxRetryDelay` are now removed; including `TypedHttpClientOptions`, `HttpRequestLogger` and related `SettingsBase` capabilities.
Expand Down
2 changes: 1 addition & 1 deletion Common.targets
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>3.15.0</Version>
<Version>3.16.0</Version>
<LangVersion>preview</LangVersion>
<Authors>Avanade</Authors>
<Company>Avanade</Company>
Expand Down
2 changes: 1 addition & 1 deletion samples/My.Hr/My.Hr.Database/My.Hr.Database.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="DbEx.SqlServer" Version="2.5.0" />
<PackageReference Include="DbEx.SqlServer" Version="2.5.1" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public static async Task WriteJson(HttpContext context, HealthReport healthRepor
{
var settings = ExecutionContext.GetService<SettingsBase>();
if (settings is not null && settings.IncludeExceptionInResult)
jsonWriter.WriteString("exception", e.Value.Exception?.ToString());
else
jsonWriter.WriteString("exception", e.Value.Exception?.Message);
}

Expand Down
21 changes: 18 additions & 3 deletions src/CoreEx.Newtonsoft/Json/JsonSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,28 @@ public BinaryData SerializeToBinaryData<T>(T value, JsonWriteFormat? format = nu
}

/// <inheritdoc/>
public object? Deserialize(string json) => Deserialize(BinaryData.FromString(json));
#if NET7_0_OR_GREATER
public object? Deserialize([StringSyntax(StringSyntaxAttribute.Json)] string json)
#else
public object? Deserialize(string json)
#endif
=> Deserialize(BinaryData.FromString(json));

/// <inheritdoc/>
public object? Deserialize(string json, Type type) => Deserialize(BinaryData.FromString(json), type);
#if NET7_0_OR_GREATER
public object? Deserialize([StringSyntax(StringSyntaxAttribute.Json)] string json, Type type)
#else
public object? Deserialize(string json, Type type)
#endif
=> Deserialize(BinaryData.FromString(json), type);

/// <inheritdoc/>
public T? Deserialize<T>(string json) =>Deserialize<T>(BinaryData.FromString(json))!;
#if NET7_0_OR_GREATER
public T? Deserialize<T>([StringSyntax(StringSyntaxAttribute.Json)] string json)
#else
public T? Deserialize<T>(string json)
#endif
=> Deserialize<T>(BinaryData.FromString(json))!;

/// <inheritdoc/>
public object? Deserialize(BinaryData json) => Deserialize<dynamic>(json);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<Import Project="..\..\Common.targets" />

<ItemGroup>
<PackageReference Include="UnitTestEx.NUnit" Version="4.3.0" />
<PackageReference Include="UnitTestEx.NUnit" Version="4.3.1" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/CoreEx.UnitTesting/CoreEx.UnitTesting.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<ItemGroup>
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageReference Include="UnitTestEx" Version="4.3.0" />
<PackageReference Include="UnitTestEx" Version="4.3.1" />
</ItemGroup>

</Project>
31 changes: 30 additions & 1 deletion src/CoreEx.UnitTesting/UnitTestExExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Net.Http;
using System.Net.Mime;
Expand Down Expand Up @@ -102,7 +103,11 @@ public static IServiceScope CreateClientScope<TAgent>(this HttpTesterBase tester
/// <param name="requestUri">The requuest uri.</param>
/// <param name="requestOptions">The optional <see cref="Ceh.HttpRequestOptions"/>.</param>
/// <returns>The <see cref="HttpRequest"/>.</returns>
#if NET7_0_OR_GREATER
public static HttpRequest CreateHttpRequest<TEntryPoint, TSelf>(this FunctionTesterBase<TEntryPoint, TSelf> tester, HttpMethod httpMethod, [StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, Ceh.HttpRequestOptions? requestOptions = null)
#else
public static HttpRequest CreateHttpRequest<TEntryPoint, TSelf>(this FunctionTesterBase<TEntryPoint, TSelf> tester, HttpMethod httpMethod, string? requestUri, Ceh.HttpRequestOptions? requestOptions = null)
#endif
where TEntryPoint : class, new() where TSelf : FunctionTesterBase<TEntryPoint, TSelf>
=> tester.CreateHttpRequest(httpMethod, requestUri).ApplyRequestOptions(requestOptions);

Expand All @@ -115,7 +120,11 @@ public static HttpRequest CreateHttpRequest<TEntryPoint, TSelf>(this FunctionTes
/// <param name="requestOptions">The optional <see cref="Ceh.HttpRequestOptions"/>.</param>
/// <param name="requestModifier">The optional <see cref="HttpRequest"/> modifier.</param>
/// <returns>The <see cref="HttpRequest"/>.</returns>
#if NET7_0_OR_GREATER
public static HttpRequest CreateHttpRequest<TEntryPoint, TSelf>(this FunctionTesterBase<TEntryPoint, TSelf> tester, HttpMethod httpMethod, [StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, Ceh.HttpRequestOptions? requestOptions = null, Action<HttpRequest>? requestModifier = null)
#else
public static HttpRequest CreateHttpRequest<TEntryPoint, TSelf>(this FunctionTesterBase<TEntryPoint, TSelf> tester, HttpMethod httpMethod, string? requestUri, Ceh.HttpRequestOptions? requestOptions = null, Action<HttpRequest>? requestModifier = null)
#endif
where TEntryPoint : class, new() where TSelf : FunctionTesterBase<TEntryPoint, TSelf>
=> tester.CreateHttpRequest(httpMethod, requestUri, requestModifier).ApplyRequestOptions(requestOptions);

Expand All @@ -128,7 +137,11 @@ public static HttpRequest CreateHttpRequest<TEntryPoint, TSelf>(this FunctionTes
/// <param name="body">The optional body content.</param>
/// <param name="requestOptions">The optional <see cref="Ceh.HttpRequestOptions"/>.</param>
/// <returns>The <see cref="HttpRequest"/>.</returns>
#if NET7_0_OR_GREATER
public static HttpRequest CreateHttpRequest<TEntryPoint, TSelf>(this FunctionTesterBase<TEntryPoint, TSelf> tester, HttpMethod httpMethod, [StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, string? body, Ceh.HttpRequestOptions? requestOptions = null)
#else
public static HttpRequest CreateHttpRequest<TEntryPoint, TSelf>(this FunctionTesterBase<TEntryPoint, TSelf> tester, HttpMethod httpMethod, string? requestUri, string? body, Ceh.HttpRequestOptions? requestOptions = null)
#endif
where TEntryPoint : class, new() where TSelf : FunctionTesterBase<TEntryPoint, TSelf>
=> tester.CreateHttpRequest(httpMethod, requestUri, body, null, null).ApplyRequestOptions(requestOptions);

Expand All @@ -142,7 +155,11 @@ public static HttpRequest CreateHttpRequest<TEntryPoint, TSelf>(this FunctionTes
/// <param name="contentType">The content type. Defaults to <see cref="MediaTypeNames.Text.Plain"/>.</param>
/// <param name="requestOptions">The optional <see cref="Ceh.HttpRequestOptions"/>.</param>
/// <returns>The <see cref="HttpRequest"/>.</returns>
#if NET7_0_OR_GREATER
public static HttpRequest CreateHttpRequest<TEntryPoint, TSelf>(this FunctionTesterBase<TEntryPoint, TSelf> tester, HttpMethod httpMethod, [StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, string? body, string? contentType, Ceh.HttpRequestOptions? requestOptions = null)
#else
public static HttpRequest CreateHttpRequest<TEntryPoint, TSelf>(this FunctionTesterBase<TEntryPoint, TSelf> tester, HttpMethod httpMethod, string? requestUri, string? body, string? contentType, Ceh.HttpRequestOptions? requestOptions = null)
#endif
where TEntryPoint : class, new() where TSelf : FunctionTesterBase<TEntryPoint, TSelf>
=> tester.CreateHttpRequest(httpMethod, requestUri, body, contentType, null).ApplyRequestOptions(requestOptions);

Expand All @@ -155,7 +172,11 @@ public static HttpRequest CreateHttpRequest<TEntryPoint, TSelf>(this FunctionTes
/// <param name="value">The value to JSON serialize.</param>
/// <param name="requestOptions">The optional <see cref="Ceh.HttpRequestOptions"/> modifier.</param>
/// <returns>The <see cref="HttpRequest"/>.</returns>
#if NET7_0_OR_GREATER
public static HttpRequest CreateJsonHttpRequest<TEntryPoint, TSelf>(this FunctionTesterBase<TEntryPoint, TSelf> tester, HttpMethod httpMethod, [StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, object? value, Ceh.HttpRequestOptions? requestOptions)
#else
public static HttpRequest CreateJsonHttpRequest<TEntryPoint, TSelf>(this FunctionTesterBase<TEntryPoint, TSelf> tester, HttpMethod httpMethod, string? requestUri, object? value, Ceh.HttpRequestOptions? requestOptions)
#endif
where TEntryPoint : class, new() where TSelf : FunctionTesterBase<TEntryPoint, TSelf>
=> tester.CreateJsonHttpRequest(httpMethod, requestUri, value).ApplyRequestOptions(requestOptions);

Expand All @@ -169,7 +190,11 @@ public static HttpRequest CreateJsonHttpRequest<TEntryPoint, TSelf>(this Functio
/// <param name="requestOptions">The optional <see cref="Ceh.HttpRequestOptions"/> modifier.</param>
/// <param name="requestModifier">The optional <see cref="HttpRequest"/> modifier.</param>
/// <returns>The <see cref="HttpRequest"/>.</returns>
#if NET7_0_OR_GREATER
public static HttpRequest CreateJsonHttpRequest<TEntryPoint, TSelf>(this FunctionTesterBase<TEntryPoint, TSelf> tester, HttpMethod httpMethod, [StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, object? value, Ceh.HttpRequestOptions? requestOptions, Action<HttpRequest>? requestModifier = null)
#else
public static HttpRequest CreateJsonHttpRequest<TEntryPoint, TSelf>(this FunctionTesterBase<TEntryPoint, TSelf> tester, HttpMethod httpMethod, string? requestUri, object? value, Ceh.HttpRequestOptions? requestOptions, Action<HttpRequest>? requestModifier = null)
#endif
where TEntryPoint : class, new() where TSelf : FunctionTesterBase<TEntryPoint, TSelf>
=> tester.CreateJsonHttpRequest(httpMethod, requestUri, value, requestModifier).ApplyRequestOptions(requestOptions);

Expand Down Expand Up @@ -199,7 +224,11 @@ public static ActionResultAssertor AssertETagHeader(this ActionResultAssertor as
/// <param name="assertor">The assertor.</param>
/// <param name="expectedUri">The expected <see cref="Uri"/>.</param>
/// <returns>The <see cref="ActionResultAssertor"/> to support fluent-style method-chaining.</returns>
#if NET7_0_OR_GREATER
public static ActionResultAssertor AssertLocationHeader(this ActionResultAssertor assertor, [StringSyntax(StringSyntaxAttribute.Uri)] Uri expectedUri)
#else
public static ActionResultAssertor AssertLocationHeader(this ActionResultAssertor assertor, Uri expectedUri)
#endif
{
if (assertor.Result != null && assertor.Result is ValueContentResult vcr)
assertor.Owner.Implementor.AssertAreEqual(expectedUri, vcr.Location, $"Expected and Actual {nameof(ValueContentResult.Location)} values are not equal.");
Expand Down Expand Up @@ -256,7 +285,7 @@ public static ActionResultAssertor AssertLocationHeaderContains(this ActionResul
public static ActionResultAssertor AssertLocationHeader<TValue>(this ActionResultAssertor assertor, Func<TValue, string> expected)
=> assertor.AssertLocationHeaderContains(expected.Invoke(assertor.GetValue<TValue>()!));

#endregion
#endregion

#region GenericTesterBase

Expand Down
13 changes: 13 additions & 0 deletions src/CoreEx.Validation/AbstractValidator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/CoreEx

using System;

namespace CoreEx.Validation
{
/// <summary>
/// Represents the base entity validator using <see href="https://docs.fluentvalidation.net/en/latest/">FluentValidation</see> syntax.
/// </summary>
/// <typeparam name="TEntity">The entity <see cref="Type"/>.</typeparam>
/// <remarks>This is a synonym for the <see cref="Validator{TEntity}"/>.</remarks>
public abstract class AbstractValidator<TEntity> : Validator<TEntity> where TEntity : class { }
}
1 change: 0 additions & 1 deletion src/CoreEx.Validation/CollectionValidator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/CoreEx

using CoreEx.Abstractions.Reflection;
using CoreEx.Localization;
using CoreEx.Results;
using CoreEx.Validation.Rules;
Expand Down
1 change: 0 additions & 1 deletion src/CoreEx.Validation/DictionaryValidator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/CoreEx

using CoreEx.Abstractions.Reflection;
using CoreEx.Localization;
using CoreEx.Results;
using CoreEx.Validation.Rules;
Expand Down
5 changes: 5 additions & 0 deletions src/CoreEx.Validation/IPropertyRule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public interface IPropertyRule
/// </summary>
public LText Text { get; set; }

/// <summary>
/// Gets or sets the error message format text (overrides the default) used for all validation errors.
/// </summary>
public LText? ErrorText { get; set; }

/// <summary>
/// Executes the validation for the property value.
/// </summary>
Expand Down
8 changes: 8 additions & 0 deletions src/CoreEx.Validation/IPropertyRuleT2.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/CoreEx

using CoreEx.Localization;
using CoreEx.Validation.Clauses;
using CoreEx.Validation.Rules;
using System;
Expand Down Expand Up @@ -40,5 +41,12 @@ public IPropertyRule<TEntity, TProperty> DependsOn<TDependsProperty>(Expression<
AddClause(new DependsOnClause<TEntity, TDependsProperty>(expression));
return this;
}

/// <summary>
/// Sets the <see cref="ValueRuleBase{TEntity, TProperty}.ErrorText"/> for the last <see cref="AddRule(IValueRule{TEntity, TProperty})">rule</see> added.
/// </summary>
/// <param name="errorText">The error message format text.</param>
/// <returns>The <see cref="PropertyRule{TEntity, TProperty}"/>.</returns>
IPropertyRule<TEntity, TProperty> WithMessage(LText errorText);
}
}
6 changes: 6 additions & 0 deletions src/CoreEx.Validation/PropertyRule.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/CoreEx

using CoreEx.Abstractions.Reflection;
using CoreEx.Localization;
using CoreEx.Validation.Clauses;
using CoreEx.Validation.Rules;
using System;
Expand Down Expand Up @@ -63,6 +64,11 @@ public async Task ValidateAsync(ValidationContext<TEntity> context, Cancellation
return this;
}

/// <summary>
/// Gets or sets the error message format text (overrides the default).
/// </summary>
LText? IValueRule<TEntity, TProperty>.ErrorText { get => throw new NotSupportedException("ErrorText should not bet set directly on a PropertyRule."); set => throw new NotSupportedException("ErrorText should not bet set directly on a PropertyRule."); }

/// <inheritdoc/>
void IValueRule<TEntity, TProperty>.AddClause(IPropertyRuleClause<TEntity> clause) => AddClause(clause);

Expand Down
16 changes: 16 additions & 0 deletions src/CoreEx.Validation/PropertyRuleBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,20 @@ protected PropertyRuleBase(string name, LText? text = null, string? jsonName = n
/// <inheritdoc/>
public LText Text { get; set; }

/// <inheritdoc/>
public LText? ErrorText { get; set; }

/// <inheritdoc/>
IPropertyRule<TEntity, TProperty> IPropertyRule<TEntity, TProperty>.WithMessage(LText errorText)
{
if (_rules.Count == 0)
ErrorText = errorText;
else
_rules.Last().ErrorText = errorText;

return this;
}

/// <inheritdoc/>
IPropertyRule<TEntity, TProperty> IPropertyRule<TEntity, TProperty>.AddRule(IValueRule<TEntity, TProperty> rule) => AddRule(rule);

Expand All @@ -53,6 +67,8 @@ protected PropertyRuleBase(string name, LText? text = null, string? jsonName = n
/// <returns>The <see cref="PropertyRuleBase{TEntity, TProperty}"/>.</returns>
public PropertyRuleBase<TEntity, TProperty> AddRule(IValueRule<TEntity, TProperty> rule)
{
rule.ThrowIfNull(nameof(rule)).ErrorText ??= ErrorText; // Override the rule's error text where not already overridden.

_rules.Add(rule);
return this;
}
Expand Down
Loading
Loading