Skip to content

Commit 4212ce0

Browse files
irvinesundaybaywet
andauthored
[Breaking] Modify validation rules to add CRUD operations (#1256)
* Add CRUD methods to validation rules * Update PublicApi doc * Adds missing using * Update src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs Co-authored-by: Vincent Biret <[email protected]> * Update src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs Co-authored-by: Vincent Biret <[email protected]> * Type cast validation rule * Update remove method * Update field name * Update/add tests * Remove unused param * Update test; simplify method --------- Co-authored-by: Vincent Biret <[email protected]>
1 parent 89a4137 commit 4212ce0

File tree

7 files changed

+372
-68
lines changed

7 files changed

+372
-68
lines changed

src/Microsoft.OpenApi/Properties/SRResource.Designer.cs

Lines changed: 12 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.OpenApi/Validations/OpenApiValidator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ private void Validate(object item, Type type)
317317
type = typeof(IOpenApiReferenceable);
318318
}
319319

320-
var rules = _ruleSet.FindRules(type);
320+
var rules = _ruleSet.FindRules(type.Name);
321321
foreach (var rule in rules)
322322
{
323323
rule.Evaluate(this as IValidationContext, item);

src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs

Lines changed: 158 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System;
55
using System.Linq;
66
using System.Reflection;
7-
using System.Collections;
87
using System.Collections.Generic;
98
using Microsoft.OpenApi.Exceptions;
109
using Microsoft.OpenApi.Properties;
@@ -15,23 +14,44 @@ namespace Microsoft.OpenApi.Validations
1514
/// <summary>
1615
/// The rule set of the validation.
1716
/// </summary>
18-
public sealed class ValidationRuleSet : IEnumerable<ValidationRule>
17+
public sealed class ValidationRuleSet
1918
{
20-
private readonly IDictionary<Type, IList<ValidationRule>> _rules = new Dictionary<Type, IList<ValidationRule>>();
19+
private readonly IDictionary<string, IList<ValidationRule>> _rulesDictionary = new Dictionary<string, IList<ValidationRule>>();
2120

2221
private static ValidationRuleSet _defaultRuleSet;
2322

2423
private readonly IList<ValidationRule> _emptyRules = new List<ValidationRule>();
2524

2625
/// <summary>
27-
/// Retrieve the rules that are related to a specific type
26+
/// Gets the keys in this rule set.
2827
/// </summary>
29-
/// <param name="type">The type that is to be validated</param>
30-
/// <returns>Either the rules related to the type, or an empty list.</returns>
31-
public IList<ValidationRule> FindRules(Type type)
28+
public ICollection<string> Keys => _rulesDictionary.Keys;
29+
30+
/// <summary>
31+
/// Gets the rules in this rule set.
32+
/// </summary>
33+
public IList<ValidationRule> Rules => _rulesDictionary.Values.SelectMany(v => v).ToList();
34+
35+
/// <summary>
36+
/// Gets the number of elements contained in this rule set.
37+
/// </summary>
38+
public int Count => _rulesDictionary.Count;
39+
40+
/// <summary>
41+
/// Initializes a new instance of the <see cref="ValidationRuleSet"/> class.
42+
/// </summary>
43+
public ValidationRuleSet()
44+
{
45+
}
46+
47+
/// <summary>
48+
/// Retrieve the rules that are related to a specific key.
49+
/// </summary>
50+
/// <param name="key">The key of the rules to search for.</param>
51+
/// <returns>Either the rules related to the given key, or an empty list.</returns>
52+
public IList<ValidationRule> FindRules(string key)
3253
{
33-
IList<ValidationRule> results = null;
34-
_rules.TryGetValue(type, out results);
54+
_rulesDictionary.TryGetValue(key, out var results);
3555
return results ?? _emptyRules;
3656
}
3757

@@ -67,10 +87,22 @@ public static ValidationRuleSet GetEmptyRuleSet()
6787
}
6888

6989
/// <summary>
70-
/// Initializes a new instance of the <see cref="ValidationRuleSet"/> class.
90+
/// Add validation rules to the rule set.
7191
/// </summary>
72-
public ValidationRuleSet()
92+
/// <param name="ruleSet">The rule set to add validation rules to.</param>
93+
/// <param name="rules">The validation rules to be added to the rules set.</param>
94+
/// <exception cref="OpenApiException">Throws a null argument exception if the arguments are null.</exception>
95+
public static void AddValidationRules(ValidationRuleSet ruleSet, IDictionary<string, IList<ValidationRule>> rules)
7396
{
97+
if (ruleSet == null || rules == null)
98+
{
99+
throw new OpenApiException(SRResource.ArgumentNull);
100+
}
101+
102+
foreach (var rule in rules)
103+
{
104+
ruleSet.Add(rule.Key, rule.Value);
105+
}
74106
}
75107

76108
/// <summary>
@@ -86,79 +118,169 @@ public ValidationRuleSet(ValidationRuleSet ruleSet)
86118

87119
foreach (ValidationRule rule in ruleSet)
88120
{
89-
Add(rule);
121+
Add(rule.ElementType.Name, rule);
90122
}
91123
}
92124

93125
/// <summary>
94126
/// Initializes a new instance of the <see cref="ValidationRuleSet"/> class.
95127
/// </summary>
96128
/// <param name="rules">Rules to be contained in this ruleset.</param>
97-
public ValidationRuleSet(IEnumerable<ValidationRule> rules)
129+
public ValidationRuleSet(IDictionary<string, IList<ValidationRule>> rules)
98130
{
99131
if (rules == null)
100132
{
101133
return;
102134
}
103135

104-
foreach (ValidationRule rule in rules)
136+
foreach (var rule in rules)
105137
{
106-
Add(rule);
138+
Add(rule.Key, rule.Value);
107139
}
108140
}
109141

110142
/// <summary>
111-
/// Gets the rules in this rule set.
143+
/// Add the new rule into the rule set.
112144
/// </summary>
113-
public IEnumerable<ValidationRule> Rules
145+
/// <param name="key">The key for the rule.</param>
146+
/// <param name="rules">The list of rules.</param>
147+
public void Add(string key, IList<ValidationRule> rules)
114148
{
115-
get
149+
foreach (var rule in rules)
116150
{
117-
return _rules.Values.SelectMany(v => v);
151+
Add(key, rule);
118152
}
119153
}
120154

121155
/// <summary>
122-
/// Add the new rule into the rule set.
156+
/// Add a new rule into the rule set.
123157
/// </summary>
158+
/// <param name="key">The key for the rule.</param>
124159
/// <param name="rule">The rule.</param>
125-
public void Add(ValidationRule rule)
160+
/// <exception cref="OpenApiException">Exception thrown when rule already exists.</exception>
161+
public void Add(string key, ValidationRule rule)
126162
{
127-
if (!_rules.ContainsKey(rule.ElementType))
163+
if (!_rulesDictionary.ContainsKey(key))
128164
{
129-
_rules[rule.ElementType] = new List<ValidationRule>();
165+
_rulesDictionary[key] = new List<ValidationRule>();
130166
}
131167

132-
if (_rules[rule.ElementType].Contains(rule))
168+
if (_rulesDictionary[key].Contains(rule))
133169
{
134170
throw new OpenApiException(SRResource.Validation_RuleAddTwice);
135171
}
136172

137-
_rules[rule.ElementType].Add(rule);
173+
_rulesDictionary[key].Add(rule);
138174
}
139175

140176
/// <summary>
141-
/// Get the enumerator.
177+
/// Updates an existing rule with a new one.
142178
/// </summary>
143-
/// <returns>The enumerator.</returns>
144-
public IEnumerator<ValidationRule> GetEnumerator()
179+
/// <param name="key">The key of the existing rule.</param>
180+
/// <param name="newRule">The new rule.</param>
181+
/// <param name="oldRule">The old rule.</param>
182+
/// <returns>true, if the update was successful; otherwise false.</returns>
183+
public bool Update(string key, ValidationRule newRule, ValidationRule oldRule)
145184
{
146-
foreach (var ruleList in _rules.Values)
185+
if (_rulesDictionary.TryGetValue(key, out var currentRules))
147186
{
148-
foreach (var rule in ruleList)
149-
{
150-
yield return rule;
151-
}
187+
currentRules.Add(newRule);
188+
return currentRules.Remove(oldRule);
152189
}
190+
return false;
191+
}
192+
193+
/// <summary>
194+
/// Removes a collection of rules.
195+
/// </summary>
196+
/// <param name="key">The key of the collection of rules to be removed.</param>
197+
/// <returns>true if the collection of rules with the provided key is removed; otherwise, false.</returns>
198+
public bool Remove(string key)
199+
{
200+
return _rulesDictionary.Remove(key);
201+
}
202+
203+
/// <summary>
204+
/// Removes a rule by key.
205+
/// </summary>
206+
/// <param name="key">The key of the rule to be removed.</param>
207+
/// <param name="rule">The rule to be removed.</param>
208+
/// <returns>true if the rule is successfully removed; otherwise, false.</returns>
209+
public bool Remove(string key, ValidationRule rule)
210+
{
211+
if (_rulesDictionary.TryGetValue(key, out IList<ValidationRule> validationRules))
212+
{
213+
return validationRules.Remove(rule);
214+
}
215+
216+
return false;
217+
}
218+
219+
/// <summary>
220+
/// Removes the first rule that matches the provided rule from the list of rules.
221+
/// </summary>
222+
/// <param name="rule">The rule to be removed.</param>
223+
/// <returns>true if the rule is successfully removed; otherwise, false.</returns>
224+
public bool Remove(ValidationRule rule)
225+
{
226+
return _rulesDictionary.Values.FirstOrDefault(x => x.Remove(rule)) is not null;
227+
}
228+
229+
/// <summary>
230+
/// Clears all rules in this rule set.
231+
/// </summary>
232+
public void Clear()
233+
{
234+
_rulesDictionary.Clear();
235+
}
236+
237+
/// <summary>
238+
/// Determines whether the rule set contains an element with the specified key.
239+
/// </summary>
240+
/// <param name="key">The key to locate in the rule set.</param>
241+
/// <returns>true if the rule set contains an element with the key; otherwise, false.</returns>
242+
public bool ContainsKey(string key)
243+
{
244+
return _rulesDictionary.ContainsKey(key);
245+
}
246+
247+
/// <summary>
248+
/// Determines whether the provided rule is contained in the specified key in the rule set.
249+
/// </summary>
250+
/// <param name="key">The key to locate.</param>
251+
/// <param name="rule">The rule to locate.</param>
252+
/// <returns></returns>
253+
public bool Contains(string key, ValidationRule rule)
254+
{
255+
return _rulesDictionary.TryGetValue(key, out IList<ValidationRule> validationRules) && validationRules.Contains(rule);
256+
}
257+
258+
/// <summary>
259+
/// Gets the rules associated with the specified key.
260+
/// </summary>
261+
/// <param name="key">The key whose rules to get.</param>
262+
/// <param name="rules">When this method returns, the rules associated with the specified key, if the
263+
/// key is found; otherwise, an empty <see cref="IList{ValidationRule}"/> object.
264+
/// This parameter is passed uninitialized.</param>
265+
/// <returns>true if the specified key has rules.</returns>
266+
public bool TryGetValue(string key, out IList<ValidationRule> rules)
267+
{
268+
return _rulesDictionary.TryGetValue(key, out rules);
153269
}
154270

155271
/// <summary>
156272
/// Get the enumerator.
157273
/// </summary>
158274
/// <returns>The enumerator.</returns>
159-
IEnumerator IEnumerable.GetEnumerator()
275+
public IEnumerator<ValidationRule> GetEnumerator()
160276
{
161-
return this.GetEnumerator();
277+
foreach (var ruleList in _rulesDictionary.Values)
278+
{
279+
foreach (var rule in ruleList)
280+
{
281+
yield return rule;
282+
}
283+
}
162284
}
163285

164286
private static ValidationRuleSet BuildDefaultRuleSet()
@@ -179,7 +301,7 @@ private static ValidationRuleSet BuildDefaultRuleSet()
179301
ValidationRule rule = propertyValue as ValidationRule;
180302
if (rule != null)
181303
{
182-
ruleSet.Add(rule);
304+
ruleSet.Add(rule.ElementType.Name, rule);
183305
}
184306
}
185307

test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,15 +1222,27 @@ namespace Microsoft.OpenApi.Validations
12221222
{
12231223
protected ValidationRule() { }
12241224
}
1225-
public sealed class ValidationRuleSet : System.Collections.Generic.IEnumerable<Microsoft.OpenApi.Validations.ValidationRule>, System.Collections.IEnumerable
1225+
public sealed class ValidationRuleSet
12261226
{
12271227
public ValidationRuleSet() { }
12281228
public ValidationRuleSet(Microsoft.OpenApi.Validations.ValidationRuleSet ruleSet) { }
1229-
public ValidationRuleSet(System.Collections.Generic.IEnumerable<Microsoft.OpenApi.Validations.ValidationRule> rules) { }
1230-
public System.Collections.Generic.IEnumerable<Microsoft.OpenApi.Validations.ValidationRule> Rules { get; }
1231-
public void Add(Microsoft.OpenApi.Validations.ValidationRule rule) { }
1232-
public System.Collections.Generic.IList<Microsoft.OpenApi.Validations.ValidationRule> FindRules(System.Type type) { }
1229+
public ValidationRuleSet(System.Collections.Generic.IDictionary<string, System.Collections.Generic.IList<Microsoft.OpenApi.Validations.ValidationRule>> rules) { }
1230+
public int Count { get; }
1231+
public System.Collections.Generic.ICollection<string> Keys { get; }
1232+
public System.Collections.Generic.IList<Microsoft.OpenApi.Validations.ValidationRule> Rules { get; }
1233+
public void Add(string key, Microsoft.OpenApi.Validations.ValidationRule rule) { }
1234+
public void Add(string key, System.Collections.Generic.IList<Microsoft.OpenApi.Validations.ValidationRule> rules) { }
1235+
public void Clear() { }
1236+
public bool Contains(string key, Microsoft.OpenApi.Validations.ValidationRule rule) { }
1237+
public bool ContainsKey(string key) { }
1238+
public System.Collections.Generic.IList<Microsoft.OpenApi.Validations.ValidationRule> FindRules(string key) { }
12331239
public System.Collections.Generic.IEnumerator<Microsoft.OpenApi.Validations.ValidationRule> GetEnumerator() { }
1240+
public bool Remove(Microsoft.OpenApi.Validations.ValidationRule rule) { }
1241+
public bool Remove(string key) { }
1242+
public bool Remove(string key, Microsoft.OpenApi.Validations.ValidationRule rule) { }
1243+
public bool TryGetValue(string key, out System.Collections.Generic.IList<Microsoft.OpenApi.Validations.ValidationRule> rules) { }
1244+
public bool Update(string key, Microsoft.OpenApi.Validations.ValidationRule newRule, Microsoft.OpenApi.Validations.ValidationRule oldRule) { }
1245+
public static void AddValidationRules(Microsoft.OpenApi.Validations.ValidationRuleSet ruleSet, System.Collections.Generic.IDictionary<string, System.Collections.Generic.IList<Microsoft.OpenApi.Validations.ValidationRule>> rules) { }
12341246
public static Microsoft.OpenApi.Validations.ValidationRuleSet GetDefaultRuleSet() { }
12351247
public static Microsoft.OpenApi.Validations.ValidationRuleSet GetEmptyRuleSet() { }
12361248
}

test/Microsoft.OpenApi.Tests/Services/OpenApiValidatorTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public void ValidateCustomExtension()
108108
{
109109
var ruleset = ValidationRuleSet.GetDefaultRuleSet();
110110

111-
ruleset.Add(
111+
ruleset.Add(typeof(OpenApiAny).Name,
112112
new ValidationRule<OpenApiAny>(
113113
(context, item) =>
114114
{

0 commit comments

Comments
 (0)