Skip to content

Commit 532fb65

Browse files
committed
- Add validation rules
- Don't throw inside the convertor
1 parent 8624f35 commit 532fb65

File tree

8 files changed

+587
-34
lines changed

8 files changed

+587
-34
lines changed

src/Microsoft.OpenApi.Readers/ParseNodes/OpenApiAnyConverter.cs

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,6 @@ public static IOpenApiAny GetSpecificOpenApiAny(IOpenApiAny openApiAny, OpenApiS
199199
{
200200
return new OpenApiDouble(doubleValue);
201201
}
202-
else
203-
{
204-
throw new OpenApiException("The value is not compatible with the given type and format.");
205-
}
206202
}
207203

208204
if (type == "number")
@@ -211,10 +207,6 @@ public static IOpenApiAny GetSpecificOpenApiAny(IOpenApiAny openApiAny, OpenApiS
211207
{
212208
return new OpenApiDouble(doubleValue);
213209
}
214-
else
215-
{
216-
throw new OpenApiException("The value is not compatible with the given type and format.");
217-
}
218210
}
219211

220212
if (type == "string" && format == "byte")
@@ -223,10 +215,6 @@ public static IOpenApiAny GetSpecificOpenApiAny(IOpenApiAny openApiAny, OpenApiS
223215
{
224216
return new OpenApiByte(byteValue);
225217
}
226-
else
227-
{
228-
throw new OpenApiException("The value is not compatible with the given type and format.");
229-
}
230218
}
231219

232220
// TODO: Parse byte array to OpenApiBinary type.
@@ -237,10 +225,6 @@ public static IOpenApiAny GetSpecificOpenApiAny(IOpenApiAny openApiAny, OpenApiS
237225
{
238226
return new OpenApiDate(dateValue.Date);
239227
}
240-
else
241-
{
242-
throw new OpenApiException("The value is not compatible with the given type and format.");
243-
}
244228
}
245229

246230
if (type == "string" && format == "date-time")
@@ -249,10 +233,6 @@ public static IOpenApiAny GetSpecificOpenApiAny(IOpenApiAny openApiAny, OpenApiS
249233
{
250234
return new OpenApiDateTime(dateTimeValue);
251235
}
252-
else
253-
{
254-
throw new OpenApiException("The value is not compatible with the given type and format.");
255-
}
256236
}
257237

258238
if (type == "string" && format == "password")
@@ -271,13 +251,12 @@ public static IOpenApiAny GetSpecificOpenApiAny(IOpenApiAny openApiAny, OpenApiS
271251
{
272252
return new OpenApiBoolean(booleanValue);
273253
}
274-
else
275-
{
276-
throw new OpenApiException("The value is not compatible with the given type and format.");
277-
}
278254
}
279255

280-
throw new OpenApiException("The specified type is not supported.");
256+
// If data conflicts with the given type, return a string.
257+
// This converter is used in the parser, so it does not perform any validations,
258+
// but the validator can be used to validate whether the data and given type conflicts.
259+
return new OpenApiString(value);
281260
}
282261
}
283262
}

src/Microsoft.OpenApi/Validations/IValidationContext.cs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

4-
using System;
5-
using System.Collections.Generic;
6-
using System.Linq;
7-
using Microsoft.OpenApi.Models;
8-
using Microsoft.OpenApi.Services;
9-
using Microsoft.OpenApi.Validations.Rules;
10-
114
namespace Microsoft.OpenApi.Validations
125
{
136
/// <summary>
@@ -21,7 +14,6 @@ public interface IValidationContext
2114
/// <param name="error">Error to register.</param>
2215
void AddError(OpenApiValidatorError error);
2316

24-
2517
/// <summary>
2618
/// Allow Rule to indicate validation error occured at a deeper context level.
2719
/// </summary>
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
using System;
5+
using Microsoft.OpenApi.Any;
6+
using Microsoft.OpenApi.Models;
7+
using Microsoft.OpenApi.Properties;
8+
9+
namespace Microsoft.OpenApi.Validations.Rules
10+
{
11+
/// <summary>
12+
/// The validation rules for <see cref="OpenApiSchema"/>.
13+
/// </summary>
14+
[OpenApiRule]
15+
public static class OpenApiSchemaRules
16+
{
17+
internal const string DataTypeMismatchedErrorMessage = "Data and type mismatch found in \"default\". ";
18+
19+
private static void ValidateDataTypeMismatch(IValidationContext context, IOpenApiAny value, OpenApiSchema schema)
20+
{
21+
if (schema == null)
22+
{
23+
return;
24+
}
25+
26+
var type = schema.Type;
27+
var format = schema.Format;
28+
29+
if (type == "object")
30+
{
31+
if (!(value is OpenApiObject))
32+
{
33+
context.CreateError(
34+
nameof(SchemaMismatchedDataType),
35+
DataTypeMismatchedErrorMessage);
36+
return;
37+
}
38+
39+
var anyObject = (OpenApiObject)value;
40+
41+
foreach (var key in anyObject.Keys)
42+
{
43+
context.Enter(key);
44+
45+
if (schema.Properties != null && schema.Properties.ContainsKey(key))
46+
{
47+
ValidateDataTypeMismatch(context, anyObject[key], schema.Properties[key]);
48+
}
49+
else
50+
{
51+
ValidateDataTypeMismatch(context, anyObject[key], schema.AdditionalProperties);
52+
}
53+
54+
context.Exit();
55+
}
56+
57+
return;
58+
}
59+
60+
if (type == "array")
61+
{
62+
if (!(value is OpenApiArray))
63+
{
64+
context.CreateError(
65+
nameof(SchemaMismatchedDataType),
66+
DataTypeMismatchedErrorMessage);
67+
return;
68+
}
69+
70+
var anyArray = (OpenApiArray)value;
71+
72+
for (int i = 0; i < anyArray.Count; i++)
73+
{
74+
context.Enter(i.ToString());
75+
76+
ValidateDataTypeMismatch(context, anyArray[i], schema.Items);
77+
78+
context.Exit();
79+
}
80+
81+
return;
82+
}
83+
84+
if (type == "integer" && format == "int32" )
85+
{
86+
if (!(value is OpenApiInteger))
87+
{
88+
context.CreateError(
89+
nameof(SchemaMismatchedDataType),
90+
DataTypeMismatchedErrorMessage);
91+
}
92+
93+
return;
94+
}
95+
96+
if (type == "integer" && format == "int64")
97+
{
98+
if (!(value is OpenApiLong))
99+
{
100+
context.CreateError(
101+
nameof(SchemaMismatchedDataType),
102+
DataTypeMismatchedErrorMessage);
103+
}
104+
105+
return;
106+
}
107+
108+
if (type == "integer" && !(value is OpenApiInteger))
109+
{
110+
if (!(value is OpenApiInteger))
111+
{
112+
context.CreateError(
113+
nameof(SchemaMismatchedDataType),
114+
DataTypeMismatchedErrorMessage);
115+
}
116+
117+
return;
118+
}
119+
120+
if (type == "number" && format == "float")
121+
{
122+
if (!(value is OpenApiFloat))
123+
{
124+
context.CreateError(
125+
nameof(SchemaMismatchedDataType),
126+
DataTypeMismatchedErrorMessage);
127+
}
128+
129+
return;
130+
}
131+
132+
if (type == "number" && format == "double")
133+
{
134+
if (!(value is OpenApiDouble))
135+
{
136+
context.CreateError(
137+
nameof(SchemaMismatchedDataType),
138+
DataTypeMismatchedErrorMessage);
139+
}
140+
141+
return;
142+
}
143+
144+
if (type == "number")
145+
{
146+
if (!(value is OpenApiDouble))
147+
{
148+
context.CreateError(
149+
nameof(SchemaMismatchedDataType),
150+
DataTypeMismatchedErrorMessage);
151+
}
152+
153+
return;
154+
}
155+
156+
if (type == "string" && format == "byte")
157+
{
158+
if (!(value is OpenApiByte))
159+
{
160+
context.CreateError(
161+
nameof(SchemaMismatchedDataType),
162+
DataTypeMismatchedErrorMessage);
163+
}
164+
165+
return;
166+
}
167+
168+
if (type == "string" && format == "date")
169+
{
170+
if (!(value is OpenApiDate))
171+
{
172+
context.CreateError(
173+
nameof(SchemaMismatchedDataType),
174+
DataTypeMismatchedErrorMessage);
175+
}
176+
177+
return;
178+
}
179+
180+
if (type == "string" && format == "date-time")
181+
{
182+
if (!(value is OpenApiDateTime))
183+
{
184+
context.CreateError(
185+
nameof(SchemaMismatchedDataType),
186+
DataTypeMismatchedErrorMessage);
187+
}
188+
189+
return;
190+
}
191+
192+
if (type == "string" && format == "password")
193+
{
194+
if (!(value is OpenApiPassword))
195+
{
196+
context.CreateError(
197+
nameof(SchemaMismatchedDataType),
198+
DataTypeMismatchedErrorMessage);
199+
}
200+
201+
return;
202+
}
203+
204+
if (type == "string")
205+
{
206+
if (!(value is OpenApiString))
207+
{
208+
context.CreateError(
209+
nameof(SchemaMismatchedDataType),
210+
DataTypeMismatchedErrorMessage);
211+
}
212+
213+
return;
214+
}
215+
216+
if (type == "boolean" )
217+
{
218+
if (!(value is OpenApiBoolean))
219+
{
220+
context.CreateError(
221+
nameof(SchemaMismatchedDataType),
222+
DataTypeMismatchedErrorMessage);
223+
}
224+
225+
return;
226+
}
227+
}
228+
229+
/// <summary>
230+
/// Validate the field is required.
231+
/// </summary>
232+
public static ValidationRule<OpenApiSchema> SchemaMismatchedDataType =>
233+
new ValidationRule<OpenApiSchema>(
234+
(context, schema) =>
235+
{
236+
// default
237+
context.Enter("default");
238+
239+
if (schema.Default != null)
240+
{
241+
ValidateDataTypeMismatch(context, schema.Default, schema);
242+
}
243+
244+
context.Exit();
245+
246+
// example
247+
context.Enter("example");
248+
249+
if (schema.Example != null)
250+
{
251+
ValidateDataTypeMismatch(context, schema.Example, schema);
252+
}
253+
254+
context.Exit();
255+
256+
257+
// enum
258+
context.Enter("enum");
259+
260+
if (schema.Enum != null)
261+
{
262+
for (int i = 0; i < schema.Enum.Count; i++)
263+
{
264+
context.Enter(i.ToString());
265+
ValidateDataTypeMismatch(context, schema.Enum[i], schema);
266+
context.Exit();
267+
}
268+
}
269+
270+
context.Exit();
271+
});
272+
273+
// add more rule.
274+
}
275+
}

test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@
4646
<EmbeddedResource Include="V2Tests\Samples\OpenApiParameter\formDataParameter.yaml">
4747
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
4848
</EmbeddedResource>
49+
<EmbeddedResource Include="V2Tests\Samples\OpenApiParameter\headerParameterWithIncorrectDataType.yaml">
50+
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
51+
</EmbeddedResource>
4952
<EmbeddedResource Include="V2Tests\Samples\OpenApiParameter\parameterWithNoLocation.yaml">
5053
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
5154
</EmbeddedResource>

0 commit comments

Comments
 (0)