Skip to content

Commit

Permalink
Move static properties of ProblemDetails class to global config
Browse files Browse the repository at this point in the history
  • Loading branch information
KateyBee committed May 11, 2024
1 parent 1528518 commit 9feb3c2
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 46 deletions.
2 changes: 1 addition & 1 deletion Src/Directory.Build.props
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>

<Version>5.25.0.4-beta</Version>
<Version>5.25.0.5-beta</Version>

<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
Expand Down
51 changes: 49 additions & 2 deletions Src/Library/Config/ErrorOptions.cs
Expand Up @@ -52,9 +52,56 @@ public sealed class ErrorOptions
/// <summary>
/// change the default error response builder to <see cref="ProblemDetails" /> instead of <see cref="ErrorResponse" />
/// </summary>
public void UseProblemDetails()
/// <param name="config">an action for configuring global settings for <see cref="ProblemDetails" /></param>
public void UseProblemDetails(Action<ProblemDetailsConfig>? config = null)
{
ProducesMetadataType = typeof(ProblemDetails);
ResponseBuilder = ProblemDetails.ResponseBuilder;
ResponseBuilder = ProblemDetailsConf.ResponseBuilder;
config?.Invoke(ProblemDetailsConf);
}

internal ProblemDetailsConfig ProblemDetailsConf { get; } = new();

/// <summary>
/// global settings for <see cref="ProblemDetails" /> error responses.
/// </summary>
public sealed class ProblemDetailsConfig
{
/// <summary>
/// the built-in function for transforming validation errors to a RFC7807 compatible problem details error response dto.
/// </summary>
public Func<List<ValidationFailure>, HttpContext, int, object> ResponseBuilder { internal get; set; }
= (failures, ctx, statusCode)
=> new ProblemDetails(failures, ctx.Request.Path, ctx.TraceIdentifier, statusCode);

/// <summary>
/// globally sets the value of <see cref="ProblemDetails.Type" />.
/// </summary>
public string TypeValue { internal get; set; } = "https://www.rfc-editor.org/rfc/rfc7231#section-6.5.1";

/// <summary>
/// globally sets the value of <see cref="ProblemDetails.Title" />.
/// </summary>
public string TitleValue { internal get; set; } = "One or more validation errors occurred.";

/// <summary>
/// sets a function that will be called per instance/response that allows customization of the <see cref="ProblemDetails.Title" /> value.
/// </summary>
public Func<ProblemDetails, string> TitleTransformer { internal get; set; } = _ => Cfg.ErrOpts.ProblemDetailsConf.TitleValue;

/// <summary>
/// controls whether duplicate errors with the same name should be allowed for <see cref="ProblemDetails.Errors" />.
/// </summary>
public bool AllowDuplicateErrors { internal get; set; }

/// <summary>
/// if set to true, the <see cref="ValidationFailure.ErrorCode" /> value of the failure will be serialized to the response.
/// </summary>
public bool IndicateErrorCode { get; set; } = false;

/// <summary>
/// if set to true, the <see cref="FluentValidation.Severity" /> value of the failure will be serialized to the response.
/// </summary>
public bool IndicateErrorSeverity { get; set; } = false;
}
}
47 changes: 5 additions & 42 deletions Src/Library/DTOs/ProblemDetails.cs
Expand Up @@ -24,39 +24,12 @@ public sealed class ProblemDetails : IResult, IEndpointMetadataProvider
public sealed class ProblemDetails : IResult
#endif
{
/// <summary>
/// the built-in function for transforming validation errors to a RFC7807 compatible problem details error response dto.
/// </summary>
public static Func<List<ValidationFailure>, HttpContext, int, object> ResponseBuilder { get; }
= (failures, ctx, statusCode)
=> new ProblemDetails(failures, ctx.Request.Path, ctx.TraceIdentifier, statusCode);

/// <summary>
/// controls whether duplicate errors with the same name should be allowed.
/// </summary>
public static bool AllowDuplicates { private get; set; }

/// <summary>
/// globally sets the 'Type' value of the problem details dto.
/// </summary>
public static string TypeValue { private get; set; } = "https://www.rfc-editor.org/rfc/rfc7231#section-6.5.1";

/// <summary>
/// globally sets the 'Title' value of the problem details dto.
/// </summary>
public static string TitleValue { private get; set; } = "One or more validation errors occurred.";

/// <summary>
/// sets a function that will be called per instance/response that allows customization of the <see cref="Title" /> value.
/// </summary>
public static Func<ProblemDetails, string> TitleTransformer { private get; set; } = _ => TitleValue;

#pragma warning disable CA1822
[DefaultValue("https://www.rfc-editor.org/rfc/rfc7231#section-6.5.1")]
public string Type => TypeValue;
public string Type => Cfg.ErrOpts.ProblemDetailsConf.TypeValue;

[DefaultValue("One or more validation errors occurred.")]
public string Title => TitleTransformer(this);
public string Title => Cfg.ErrOpts.ProblemDetailsConf.TitleTransformer(this);
#pragma warning restore CA1822

[DefaultValue(400)]
Expand Down Expand Up @@ -94,7 +67,7 @@ void Initialize(IReadOnlyList<ValidationFailure> failures, string instance, stri
Instance = instance;
TraceId = traceId;

if (AllowDuplicates)
if (Cfg.ErrOpts.ProblemDetailsConf.AllowDuplicateErrors)
Errors = failures.Select(f => new Error(f));
else
{
Expand Down Expand Up @@ -141,16 +114,6 @@ public sealed class Error
{
internal static readonly Comparer EqComparer = new();

/// <summary>
/// if set to true, the <see cref="ValidationFailure.ErrorCode" /> value of the failure will be serialized to the response.
/// </summary>
public static bool IndicateErrorCode { get; set; } = false;

/// <summary>
/// if set to true, the <see cref="FluentValidation.Severity" /> value of the failure will be serialized to the response.
/// </summary>
public static bool IndicateSeverity { get; set; } = false;

/// <summary>
/// the name of the error or property of the dto that caused the error
/// </summary>
Expand Down Expand Up @@ -181,8 +144,8 @@ public Error(ValidationFailure failure)
{
Name = Cfg.SerOpts.Options.PropertyNamingPolicy?.ConvertName(failure.PropertyName) ?? failure.PropertyName;
Reason = failure.ErrorMessage;
Code = IndicateErrorCode ? failure.ErrorCode : null;
Severity = IndicateSeverity ? failure.Severity.ToString() : null;
Code = Cfg.ErrOpts.ProblemDetailsConf.IndicateErrorCode ? failure.ErrorCode : null;
Severity = Cfg.ErrOpts.ProblemDetailsConf.IndicateErrorSeverity ? failure.Severity.ToString() : null;
}

internal sealed class Comparer : IEqualityComparer<Error>
Expand Down
27 changes: 26 additions & 1 deletion Src/Library/changelog.md
Expand Up @@ -50,4 +50,29 @@ Please the [documentation](https://fast-endpoints.com//docs/integration-unit-tes

[//]: # (## Fixes 🪲)

[//]: # (## Breaking Changes ⚠️)
## Minor Breaking Changes ⚠️

<details><summary>Move static properties of 'ProblemDetails' class to global config</summary>

Static configuration properties that used to be on the `ProblemDetails` class will have to be set from the global configuration going forward like so:

```csharp
app.UseFastEndpoints(
c => c.Errors.UseProblemDetails(
x =>
{
x.AllowDuplicateErrors = true; //allows duplicate errors for the same error name
x.IndicateErrorCode = true; //serializes the fluentvalidation error code
x.IndicateErrorSeverity = true; //serializes the fluentvalidation error severity
x.TypeValue = "https://www.rfc-editor.org/rfc/rfc7231#section-6.5.1";
x.TitleValue = "One or more validation errors occurred.";
x.TitleTransformer = pd => pd.Status switch
{
400 => "Validation Error",
404 => "Not Found",
_ => "One or more errors occurred!"
};
}));
```

</details>

0 comments on commit 9feb3c2

Please sign in to comment.