Skip to content

Commit eee2f60

Browse files
authored
Merge branch 'master' into perthcharern/ParsingAnyShouldRespectFormatAndType
2 parents 463a3fc + bb960b3 commit eee2f60

32 files changed

+814
-36
lines changed

src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<TargetFrameworks>net46; netstandard2.0</TargetFrameworks>
44
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
@@ -10,7 +10,7 @@
1010
<Company>Microsoft</Company>
1111
<Title>Microsoft.OpenApi.Readers</Title>
1212
<PackageId>Microsoft.OpenApi.Readers</PackageId>
13-
<Version>1.1.0</Version>
13+
<Version>1.1.1</Version>
1414
<Description>OpenAPI.NET Readers for JSON and YAML documents</Description>
1515
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
1616
<PackageTags>OpenAPI .NET</PackageTags>

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license.
33

44
using System;
5+
using System.Globalization;
56
using Microsoft.OpenApi.Any;
67
using Microsoft.OpenApi.Readers.Exceptions;
78
using SharpYaml.Serialization;

src/Microsoft.OpenApi.Readers/Services/OpenApiReferenceResolver.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,14 @@ public override void Visit(OpenApiSecurityRequirement securityRequirement)
108108
{
109109
ResolveObject(scheme, (resolvedScheme) =>
110110
{
111-
// If scheme was unresolved
112-
// copy Scopes and remove old unresolved scheme
113-
var scopes = securityRequirement[scheme];
114-
securityRequirement.Remove(scheme);
115-
securityRequirement.Add(resolvedScheme, scopes);
111+
if (resolvedScheme != null)
112+
{
113+
// If scheme was unresolved
114+
// copy Scopes and remove old unresolved scheme
115+
var scopes = securityRequirement[scheme];
116+
securityRequirement.Remove(scheme);
117+
securityRequirement.Add(resolvedScheme, scopes);
118+
}
116119
});
117120
}
118121
}

src/Microsoft.OpenApi.Readers/V2/OpenApiDocumentDeserializer.cs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,12 @@ internal static partial class OpenApiV2Deserializer
7373

7474
o.Components.RequestBodies = n.CreateMapWithReference(ReferenceType.RequestBody, p =>
7575
{
76-
var parameter = LoadParameter(p, evenBody: true);
77-
if (parameter.In == null)
76+
var parameter = LoadParameter(p, loadRequestBody: true);
77+
if (parameter != null)
7878
{
79-
return CreateRequestBody(n.Context,parameter);
79+
return CreateRequestBody(n.Context, parameter);
8080
}
81+
8182
return null;
8283
}
8384
);
@@ -121,18 +122,26 @@ internal static partial class OpenApiV2Deserializer
121122
{s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))}
122123
};
123124

124-
private static void MakeServers(IList<OpenApiServer> servers, ParsingContext context, Uri defaultUrl)
125+
private static void MakeServers(IList<OpenApiServer> servers, ParsingContext context, RootNode rootNode)
125126
{
126127
var host = context.GetFromTempStorage<string>("host");
127128
var basePath = context.GetFromTempStorage<string>("basePath");
128129
var schemes = context.GetFromTempStorage<List<string>>("schemes");
130+
Uri defaultUrl = rootNode.Context.BaseUrl;
129131

130132
// If nothing is provided, don't create a server
131133
if (host == null && basePath == null && schemes == null)
132134
{
133135
return;
134136
}
135137

138+
//Validate host
139+
if (host != null && !IsHostValid(host))
140+
{
141+
rootNode.Diagnostic.Errors.Add(new OpenApiError(rootNode.Context.GetLocation(), "Invalid host"));
142+
return;
143+
}
144+
136145
// Fill in missing information based on the defaultUrl
137146
if (defaultUrl != null)
138147
{
@@ -225,7 +234,7 @@ public static OpenApiDocument LoadOpenApi(RootNode rootNode)
225234
openApidoc.Servers = new List<OpenApiServer>();
226235
}
227236

228-
MakeServers(openApidoc.Servers, openApiNode.Context, rootNode.Context.BaseUrl);
237+
MakeServers(openApidoc.Servers, openApiNode.Context, rootNode);
229238

230239
FixRequestBodyReferences(openApidoc);
231240
return openApidoc;
@@ -242,6 +251,19 @@ private static void FixRequestBodyReferences(OpenApiDocument doc)
242251
walker.Walk(doc);
243252
}
244253
}
254+
255+
private static bool IsHostValid(string host)
256+
{
257+
//Check if the host contains ://
258+
if (host.Contains(Uri.SchemeDelimiter))
259+
{
260+
return false;
261+
}
262+
263+
//Check if the host (excluding port number) is a valid dns/ip address.
264+
var hostPart = host.Split(':').First();
265+
return Uri.CheckHostName(hostPart) != UriHostNameType.Unknown;
266+
}
245267
}
246268

247269
internal class RequestBodyReferenceFixer : OpenApiVisitorBase

src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Globalization;
7-
using Microsoft.OpenApi.Any;
87
using Microsoft.OpenApi.Extensions;
98
using Microsoft.OpenApi.Models;
109
using Microsoft.OpenApi.Readers.ParseNodes;
@@ -17,7 +16,7 @@ namespace Microsoft.OpenApi.Readers.V2
1716
/// </summary>
1817
internal static partial class OpenApiV2Deserializer
1918
{
20-
private static ParameterLocation? _in;
19+
private static bool _isBodyOrFormData;
2120

2221
private static readonly FixedFieldMap<OpenApiParameter> _parameterFixedFields =
2322
new FixedFieldMap<OpenApiParameter>
@@ -223,9 +222,11 @@ private static void ProcessIn(OpenApiParameter o, ParseNode n)
223222
switch (value)
224223
{
225224
case "body":
225+
_isBodyOrFormData = true;
226226
n.Context.SetTempStorage(TempStorageKeys.BodyParameter, o);
227227
break;
228228
case "formData":
229+
_isBodyOrFormData = true;
229230
var formParameters = n.Context.GetFromTempStorage<List<OpenApiParameter>>("formParameters");
230231
if (formParameters == null)
231232
{
@@ -235,9 +236,13 @@ private static void ProcessIn(OpenApiParameter o, ParseNode n)
235236

236237
formParameters.Add(o);
237238
break;
239+
case "query":
240+
case "header":
241+
case "path":
242+
o.In = value.GetEnumFromDisplayName<ParameterLocation>();
243+
break;
238244
default:
239-
_in = value.GetEnumFromDisplayName<ParameterLocation>();
240-
o.In = _in;
245+
o.In = null;
241246
break;
242247
}
243248
}
@@ -247,10 +252,10 @@ public static OpenApiParameter LoadParameter(ParseNode node)
247252
return LoadParameter(node, false);
248253
}
249254

250-
public static OpenApiParameter LoadParameter(ParseNode node, bool evenBody)
255+
public static OpenApiParameter LoadParameter(ParseNode node, bool loadRequestBody)
251256
{
252257
// Reset the local variables every time this method is called.
253-
_in = null;
258+
_isBodyOrFormData = false;
254259

255260
var mapNode = node.CheckMapNode("parameter");
256261

@@ -275,9 +280,14 @@ public static OpenApiParameter LoadParameter(ParseNode node, bool evenBody)
275280
node.Context.SetTempStorage("schema", null);
276281
}
277282

278-
if (_in == null && !evenBody)
283+
if (_isBodyOrFormData && !loadRequestBody)
284+
{
285+
return null; // Don't include Form or Body parameters when normal parameters are loaded.
286+
}
287+
288+
if ( loadRequestBody && !_isBodyOrFormData )
279289
{
280-
return null; // Don't include Form or Body parameters in OpenApiOperation.Parameters list
290+
return null; // Don't include non-Body or non-Form parameters when request bodies are loaded.
281291
}
282292

283293
return parameter;

src/Microsoft.OpenApi.Readers/V3/OpenApiParameterDeserializer.cs

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

4-
using System.Collections.Generic;
4+
using System;
55
using System.Linq;
66
using Microsoft.OpenApi.Extensions;
77
using Microsoft.OpenApi.Models;
@@ -27,7 +27,18 @@ internal static partial class OpenApiV3Deserializer
2727
{
2828
"in", (o, n) =>
2929
{
30-
o.In = n.GetScalarValue().GetEnumFromDisplayName<ParameterLocation>();
30+
var inString = n.GetScalarValue();
31+
32+
if ( Enum.GetValues(typeof(ParameterLocation)).Cast<ParameterLocation>()
33+
.Select( e => e.GetDisplayName() )
34+
.Contains(inString) )
35+
{
36+
o.In = n.GetScalarValue().GetEnumFromDisplayName<ParameterLocation>();
37+
}
38+
else
39+
{
40+
o.In = null;
41+
}
3142
}
3243
},
3344
{
@@ -115,7 +126,6 @@ public static OpenApiParameter LoadParameter(ParseNode node)
115126
}
116127

117128
var parameter = new OpenApiParameter();
118-
var required = new List<string> {"name", "in"};
119129

120130
ParseMap(mapNode, parameter, _parameterFixedFields, _parameterPatternFields);
121131

src/Microsoft.OpenApi/Microsoft.OpenApi.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<Company>Microsoft</Company>
1111
<Title>Microsoft.OpenApi</Title>
1212
<PackageId>Microsoft.OpenApi</PackageId>
13-
<Version>1.1.0</Version>
13+
<Version>1.1.1</Version>
1414
<Description>.NET models with JSON and YAML writers for OpenAPI specification</Description>
1515
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
1616
<PackageTags>OpenAPI .NET</PackageTags>

src/Microsoft.OpenApi/Models/OpenApiParameter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ public void SerializeAsV3WithoutReference(IOpenApiWriter writer)
159159
writer.WriteProperty(OpenApiConstants.Name, Name);
160160

161161
// in
162-
writer.WriteProperty(OpenApiConstants.In, In.GetDisplayName());
162+
writer.WriteProperty(OpenApiConstants.In, In?.GetDisplayName());
163163

164164
// description
165165
writer.WriteProperty(OpenApiConstants.Description, Description);
@@ -237,7 +237,7 @@ public void SerializeAsV2WithoutReference(IOpenApiWriter writer)
237237
}
238238
else
239239
{
240-
writer.WriteProperty(OpenApiConstants.In, In.GetDisplayName());
240+
writer.WriteProperty(OpenApiConstants.In, In?.GetDisplayName());
241241
}
242242

243243
// name

src/Microsoft.OpenApi/Validations/OpenApiValidator.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ public void AddError(OpenApiValidatorError error)
113113
/// <param name="item">The object to be validated</param>
114114
public override void Visit(OpenApiTag item) => Validate(item);
115115

116+
/// <summary>
117+
/// Execute validation rules against an <see cref="OpenApiParameter"/>
118+
/// </summary>
119+
/// <param name="item">The object to be validated</param>
120+
public override void Visit(OpenApiParameter item) => Validate(item);
121+
116122
/// <summary>
117123
/// Execute validation rules against an <see cref="OpenApiSchema"/>
118124
/// </summary>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
using System;
5+
using Microsoft.OpenApi.Models;
6+
using Microsoft.OpenApi.Properties;
7+
8+
namespace Microsoft.OpenApi.Validations.Rules
9+
{
10+
/// <summary>
11+
/// The validation rules for <see cref="OpenApiParameter"/>.
12+
/// </summary>
13+
[OpenApiRule]
14+
public static class OpenApiParameterRules
15+
{
16+
/// <summary>
17+
/// Validate the field is required.
18+
/// </summary>
19+
public static ValidationRule<OpenApiParameter> ParameterRequiredFields =>
20+
new ValidationRule<OpenApiParameter>(
21+
(context, item) =>
22+
{
23+
// name
24+
context.Enter("name");
25+
if (item.Name == null)
26+
{
27+
context.CreateError(nameof(ParameterRequiredFields),
28+
String.Format(SRResource.Validation_FieldIsRequired, "name", "parameter"));
29+
}
30+
context.Exit();
31+
32+
// in
33+
context.Enter("in");
34+
if (item.In == null)
35+
{
36+
context.CreateError(nameof(ParameterRequiredFields),
37+
String.Format(SRResource.Validation_FieldIsRequired, "in", "parameter"));
38+
}
39+
context.Exit();
40+
});
41+
42+
/// <summary>
43+
/// Validate the "required" field is true when "in" is path.
44+
/// </summary>
45+
public static ValidationRule<OpenApiParameter> RequiredMustBeTrueWhenInIsPath =>
46+
new ValidationRule<OpenApiParameter>(
47+
(context, item) =>
48+
{
49+
// required
50+
context.Enter("required");
51+
if ( item.In == ParameterLocation.Path && !item.Required )
52+
{
53+
context.CreateError(
54+
nameof(RequiredMustBeTrueWhenInIsPath),
55+
"\"required\" must be true when parameter location is \"path\"");
56+
}
57+
58+
context.Exit();
59+
});
60+
61+
// add more rule.
62+
}
63+
}

0 commit comments

Comments
 (0)