Skip to content

Commit bf04c72

Browse files
committed
# Changes in 3.2.0:
* FluentValidation fix version to [8.3.0, 9) * Swashbuckle.AspNetCore fix version to [5.2.0, 6) * Base type for numeric switched to decimal to match type change in OpenApi. Fixes floating numbers with nines after period. * More smart MinLength, MaxLength, Minimum, Maximum that allows to combine rules without override values. * More strict limits will be used for min and max values that was set more then once in other rules
1 parent cc4ea02 commit bf04c72

File tree

11 files changed

+312
-82
lines changed

11 files changed

+312
-82
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# Changes in 3.2.0:
2+
* FluentValidation fix version to [8.3.0, 9)
3+
* Swashbuckle.AspNetCore fix version to [5.2.0, 6)
4+
* Base type for numeric switched to decimal to match type change in OpenApi. Fixes floating numbers with nines after period.
5+
* More smart MinLength, MaxLength, Minimum, Maximum that allows to combine rules without override values.
6+
* More strict limits will be used for min and max values that was set more then once in other rules
7+
18
# Changes in 3.1.1:
29
* Mark required properties as not nullable (PR#58 by @manne) Fixes: #55, #57
310

samples/SampleWebApi/Controllers/SampleApiController.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System.Collections.Generic;
2+
using FluentValidation;
23
using Microsoft.AspNetCore.Mvc;
34
using SampleWebApi.Contracts;
5+
using SampleWebApi.Validators;
46

57
namespace SampleWebApi.Controllers
68
{
@@ -71,5 +73,25 @@ public IActionResult AddSampleWithNoRequired([FromBody] SampleWithNoRequired cus
7173

7274
return Ok();
7375
}
76+
77+
[HttpGet("[action]")]
78+
public IActionResult Get(GetRequest query) => Ok();
79+
80+
public class GetRequest
81+
{
82+
[FromQuery]
83+
public string Id { get; set; }
84+
}
85+
86+
public class BasicRequestValidator : AbstractValidator<SampleApiController.GetRequest>
87+
{
88+
public BasicRequestValidator()
89+
{
90+
RuleFor(x => x.Id).NotEmpty().MaximumLength(3);
91+
}
92+
}
93+
7494
}
95+
96+
7597
}

src/MicroElements.Swashbuckle.FluentValidation/Extensions.cs

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Linq.Expressions;
5+
using System.Reflection;
6+
using System.Runtime.CompilerServices;
47
using FluentValidation;
58
using FluentValidation.Internal;
69
using FluentValidation.Validators;
710
using JetBrains.Annotations;
11+
using Microsoft.OpenApi.Models;
12+
13+
[assembly: InternalsVisibleTo("MicroElements.Swashbuckle.FluentValidation.Tests")]
814

915
namespace MicroElements.Swashbuckle.FluentValidation
1016
{
@@ -49,15 +55,10 @@ internal static IEnumerable<PropertyRule> GetPropertyRules(
4955
/// </summary>
5056
internal static bool IsNumeric(this object value) => value is int || value is long || value is float || value is double || value is decimal;
5157

52-
/// <summary>
53-
/// Convert numeric to int.
54-
/// </summary>
55-
internal static int NumericToInt(this object value) => Convert.ToInt32(value);
56-
5758
/// <summary>
5859
/// Convert numeric to double.
5960
/// </summary>
60-
internal static double NumericToDouble(this object value) => Convert.ToDouble(value);
61+
internal static decimal NumericToDecimal(this object value) => Convert.ToDecimal(value);
6162

6263
/// <summary>
6364
/// Converts string to lowerCamelCase.
@@ -124,9 +125,78 @@ internal static bool HasNoCondition(this PropertyRule propertyRule)
124125
/// <param name="left">Left string to compare.</param>
125126
/// <param name="right">Right string to compare.</param>
126127
/// <returns><c>true</c> if input strings are equals in terms of identifier formatting.</returns>
127-
public static bool EqualsIgnoreAll(this string left, string right)
128+
internal static bool EqualsIgnoreAll(this string left, string right)
128129
{
129130
return IgnoreAllStringComparer.Instance.Equals(left, right);
130131
}
132+
133+
/// <summary>
134+
/// Sets Nullable to false if MinLength > 0.
135+
/// </summary>
136+
internal static void SetNotNullableIfMinLengthGreaterThenZero(this OpenApiSchema schemaProperty)
137+
{
138+
var shouldBeNotEmpty = schemaProperty.MinLength.HasValue && schemaProperty.MinLength > 0;
139+
schemaProperty.Nullable = !shouldBeNotEmpty;
140+
}
141+
142+
internal static void SetNewMax(this OpenApiSchema schemaProperty, Expression<Func<OpenApiSchema, int?>> prop, int? newValue)
143+
{
144+
if (newValue.HasValue)
145+
{
146+
var current = prop.Compile()(schemaProperty);
147+
newValue = NewMaxValue(current, newValue.Value);
148+
SetPropertyValue(schemaProperty, prop, newValue);
149+
}
150+
}
151+
152+
internal static void SetNewMax(this OpenApiSchema schemaProperty, Expression<Func<OpenApiSchema, decimal?>> prop, decimal? newValue)
153+
{
154+
if (newValue.HasValue)
155+
{
156+
var current = prop.Compile()(schemaProperty);
157+
newValue = NewMaxValue(current, newValue.Value);
158+
SetPropertyValue(schemaProperty, prop, newValue);
159+
}
160+
}
161+
162+
internal static void SetNewMin(this OpenApiSchema schemaProperty, Expression<Func<OpenApiSchema, int?>> prop, int? newValue)
163+
{
164+
if (newValue.HasValue)
165+
{
166+
var current = prop.Compile()(schemaProperty);
167+
newValue = NewMinValue(current, newValue.Value);
168+
SetPropertyValue(schemaProperty, prop, newValue);
169+
}
170+
}
171+
172+
internal static void SetNewMin(this OpenApiSchema schemaProperty, Expression<Func<OpenApiSchema, decimal?>> prop, decimal? newValue)
173+
{
174+
if (newValue.HasValue)
175+
{
176+
var current = prop.Compile()(schemaProperty);
177+
newValue = NewMinValue(current, newValue.Value);
178+
SetPropertyValue(schemaProperty, prop, newValue);
179+
}
180+
}
181+
182+
private static int NewMaxValue(int? current, int newValue) => current.HasValue ? Math.Min(current.Value, newValue) : newValue;
183+
184+
private static decimal NewMaxValue(decimal? current, decimal newValue) => current.HasValue ? Math.Min(current.Value, newValue) : newValue;
185+
186+
private static int NewMinValue(int? current, int newValue) => current.HasValue ? Math.Max(current.Value, newValue) : newValue;
187+
188+
private static decimal NewMinValue(decimal? current, decimal newValue) => current.HasValue ? Math.Max(current.Value, newValue) : newValue;
189+
190+
private static void SetPropertyValue<T, TValue>(this T target, Expression<Func<T, TValue>> propertyLambda, TValue value)
191+
{
192+
if (propertyLambda.Body is MemberExpression memberSelectorExpression)
193+
{
194+
var property = memberSelectorExpression.Member as PropertyInfo;
195+
if (property != null)
196+
{
197+
property.SetValue(target, value, null);
198+
}
199+
}
200+
}
131201
}
132202
}

src/MicroElements.Swashbuckle.FluentValidation/FluentValidationOperationFilter.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ void ApplyInternal(OpenApiOperation operation, OperationFilterContext context)
141141
|| schema.Properties.TryGetValue(schemaPropertyName, out property))
142142
{
143143
parameterSchema.MinLength = property.MinLength;
144+
parameterSchema.Nullable = property.Nullable;
144145
parameterSchema.MaxLength = property.MaxLength;
145146
parameterSchema.Pattern = property.Pattern;
146147
parameterSchema.Minimum = property.Minimum;

src/MicroElements.Swashbuckle.FluentValidation/FluentValidationRules.cs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,9 @@ public static FluentValidationRule[] CreateDefaultRules()
146146
Matches = propertyValidator => propertyValidator is INotEmptyValidator && propertyValidator.HasNoCondition(),
147147
Apply = context =>
148148
{
149-
context.Schema.Properties[context.PropertyKey].MinLength = 1;
149+
var schemaProperty = context.Schema.Properties[context.PropertyKey];
150+
schemaProperty.SetNewMin(p => p.MinLength, 1);
151+
schemaProperty.SetNotNullableIfMinLengthGreaterThenZero();
150152
}
151153
},
152154
new FluentValidationRule("Length")
@@ -155,14 +157,15 @@ public static FluentValidationRule[] CreateDefaultRules()
155157
Apply = context =>
156158
{
157159
var lengthValidator = (ILengthValidator)context.PropertyValidator;
160+
var schemaProperty = context.Schema.Properties[context.PropertyKey];
161+
162+
if (lengthValidator.Max > 0)
163+
schemaProperty.SetNewMax(p => p.MaxLength, lengthValidator.Max);
158164

159-
if(lengthValidator.Max > 0)
160-
context.Schema.Properties[context.PropertyKey].MaxLength = lengthValidator.Max;
165+
if (lengthValidator.Min > 0)
166+
schemaProperty.SetNewMin(p => p.MinLength, lengthValidator.Min);
161167

162-
if (lengthValidator is MinimumLengthValidator
163-
|| lengthValidator is ExactLengthValidator
164-
|| context.Schema.Properties[context.PropertyKey].MinLength == null)
165-
context.Schema.Properties[context.PropertyKey].MinLength = lengthValidator.Min;
168+
schemaProperty.SetNotNullableIfMinLengthGreaterThenZero();
166169
}
167170
},
168171
new FluentValidationRule("Pattern")
@@ -182,25 +185,25 @@ public static FluentValidationRule[] CreateDefaultRules()
182185
var comparisonValidator = (IComparisonValidator)context.PropertyValidator;
183186
if (comparisonValidator.ValueToCompare.IsNumeric())
184187
{
185-
var valueToCompare = comparisonValidator.ValueToCompare.NumericToDouble();
188+
var valueToCompare = comparisonValidator.ValueToCompare.NumericToDecimal();
186189
var schemaProperty = context.Schema.Properties[context.PropertyKey];
187190

188191
if (comparisonValidator.Comparison == Comparison.GreaterThanOrEqual)
189192
{
190-
schemaProperty.Minimum = (decimal?) valueToCompare;
193+
schemaProperty.SetNewMin(p => p.Minimum, valueToCompare);
191194
}
192195
else if (comparisonValidator.Comparison == Comparison.GreaterThan)
193196
{
194-
schemaProperty.Minimum = (decimal?) valueToCompare;
197+
schemaProperty.SetNewMin(p => p.Minimum, valueToCompare);
195198
schemaProperty.ExclusiveMinimum = true;
196199
}
197200
else if (comparisonValidator.Comparison == Comparison.LessThanOrEqual)
198201
{
199-
schemaProperty.Maximum = (decimal?) valueToCompare;
202+
schemaProperty.SetNewMax(p => p.Maximum, valueToCompare);
200203
}
201204
else if (comparisonValidator.Comparison == Comparison.LessThan)
202205
{
203-
schemaProperty.Maximum = (decimal?) valueToCompare;
206+
schemaProperty.SetNewMax(p => p.Maximum, valueToCompare);
204207
schemaProperty.ExclusiveMaximum = true;
205208
}
206209
}
@@ -216,7 +219,7 @@ public static FluentValidationRule[] CreateDefaultRules()
216219

217220
if (betweenValidator.From.IsNumeric())
218221
{
219-
schemaProperty.Minimum = (decimal?) betweenValidator.From.NumericToDouble();
222+
schemaProperty.SetNewMin(p => p.Minimum, betweenValidator.From.NumericToDecimal());
220223

221224
if (betweenValidator is ExclusiveBetweenValidator)
222225
{
@@ -226,7 +229,7 @@ public static FluentValidationRule[] CreateDefaultRules()
226229

227230
if (betweenValidator.To.IsNumeric())
228231
{
229-
schemaProperty.Maximum = (decimal?) betweenValidator.To.NumericToDouble();
232+
schemaProperty.SetNewMax(p => p.Maximum, betweenValidator.To.NumericToDecimal());
230233

231234
if (betweenValidator is ExclusiveBetweenValidator)
232235
{

src/MicroElements.Swashbuckle.FluentValidation/MicroElements.Swashbuckle.FluentValidation.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
</PropertyGroup>
1212

1313
<ItemGroup>
14-
<PackageReference Include="FluentValidation" Version="8.3.0" />
15-
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.2.0" />
14+
<PackageReference Include="FluentValidation" Version="[8.3.0, 9)" />
15+
<PackageReference Include="Swashbuckle.AspNetCore" Version="[5.2.0, 6)" />
1616
</ItemGroup>
1717

1818
</Project>

src/common.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
<PropertyGroup>
55
<Product>MicroElements</Product>
6-
<Copyright>Alexey Petryashev 2019</Copyright>
6+
<Copyright>Alexey Petryashev 2020</Copyright>
77
<Authors>alexey.petriashev;MicroElements</Authors>
88
<PackageIconUrl>https://raw.githubusercontent.com/micro-elements/MicroElements/master/image/logo_rounded.png</PackageIconUrl>
99
<PackageProjectUrl>https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation</PackageProjectUrl>

0 commit comments

Comments
 (0)