Skip to content

Commit 3277e46

Browse files
authored
Merge pull request #267 from Microsoft/dm/1.1/fixes
A variety of bug fixes for 1.1 preview
2 parents c9d1bae + 8a76184 commit 3277e46

18 files changed

+410
-44
lines changed

src/Microsoft.OpenApi.Readers/Exceptions/OpenApiUnsupportedSpecVersionException.cs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace Microsoft.OpenApi.Readers.Exceptions
1212
[Serializable]
1313
public class OpenApiUnsupportedSpecVersionException : OpenApiReaderException
1414
{
15-
const string messagePattern = "OpenAPI specification version {0} is not supported.";
15+
const string messagePattern = "OpenAPI specification version '{0}' is not supported.";
1616

1717
/// <summary>
1818
/// Initializes the <see cref="OpenApiUnsupportedSpecVersionException"/> class with a specification version.
@@ -21,11 +21,6 @@ public class OpenApiUnsupportedSpecVersionException : OpenApiReaderException
2121
public OpenApiUnsupportedSpecVersionException(string specificationVersion)
2222
: base(string.Format(CultureInfo.InvariantCulture, messagePattern, specificationVersion))
2323
{
24-
if (string.IsNullOrWhiteSpace(specificationVersion))
25-
{
26-
throw new ArgumentException("Value cannot be null or white space.", nameof(specificationVersion));
27-
}
28-
2924
this.SpecificationVersion = specificationVersion;
3025
}
3126

@@ -38,11 +33,6 @@ public OpenApiUnsupportedSpecVersionException(string specificationVersion)
3833
public OpenApiUnsupportedSpecVersionException(string specificationVersion, Exception innerException)
3934
: base(string.Format(CultureInfo.InvariantCulture, messagePattern, specificationVersion), innerException)
4035
{
41-
if (string.IsNullOrWhiteSpace(specificationVersion))
42-
{
43-
throw new ArgumentException("Value cannot be null or white space.", nameof(specificationVersion));
44-
}
45-
4636
this.SpecificationVersion = specificationVersion;
4737
}
4838

src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,9 @@ public class OpenApiReaderSettings
4949
/// </summary>
5050
public ValidationRuleSet RuleSet { get; set; } = ValidationRuleSet.GetDefaultRuleSet();
5151

52+
/// <summary>
53+
/// URL where relative references should be resolved from if the description does not contain Server definitions
54+
/// </summary>
55+
public Uri BaseUrl { get; set; }
5256
}
5357
}

src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic)
5757

5858
context = new ParsingContext
5959
{
60-
ExtensionParsers = _settings.ExtensionParsers
60+
ExtensionParsers = _settings.ExtensionParsers,
61+
BaseUrl = _settings.BaseUrl
6162
};
6263

6364
OpenApiDocument document = null;

src/Microsoft.OpenApi.Readers/ParsingContext.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class ParsingContext
2828
internal Dictionary<string, Func<IOpenApiAny, IOpenApiExtension>> ExtensionParsers { get; set; } = new Dictionary<string, Func<IOpenApiAny, IOpenApiExtension>>();
2929
internal RootNode RootNode { get; set; }
3030
internal List<OpenApiTag> Tags { get; private set; } = new List<OpenApiTag>();
31+
internal Uri BaseUrl { get; set; }
3132

3233
/// <summary>
3334
/// Initiates the parsing process. Not thread safe and should only be called once on a parsing context

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

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,21 +119,82 @@ internal static partial class OpenApiV2Deserializer
119119
{s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, n.CreateAny())}
120120
};
121121

122-
private static void MakeServers(IList<OpenApiServer> servers, ParsingContext context)
122+
private static void MakeServers(IList<OpenApiServer> servers, ParsingContext context, Uri defaultUrl)
123123
{
124124
var host = context.GetFromTempStorage<string>("host");
125125
var basePath = context.GetFromTempStorage<string>("basePath");
126126
var schemes = context.GetFromTempStorage<List<string>>("schemes");
127127

128+
// If nothing is provided, don't create a server
129+
if (host == null && basePath == null && schemes == null)
130+
{
131+
return;
132+
}
133+
134+
// Fill in missing information based on the defaultUrl
135+
if (defaultUrl != null)
136+
{
137+
host = host ?? defaultUrl.GetComponents(UriComponents.NormalizedHost, UriFormat.SafeUnescaped);
138+
basePath = basePath ?? defaultUrl.GetComponents(UriComponents.Path, UriFormat.SafeUnescaped);
139+
schemes = schemes ?? new List<string> { defaultUrl.GetComponents(UriComponents.Scheme, UriFormat.SafeUnescaped) };
140+
}
141+
else if (String.IsNullOrEmpty(host) && String.IsNullOrEmpty(basePath))
142+
{
143+
return; // Can't make a server object out of just a Scheme
144+
}
145+
146+
// Create the Server objects
128147
if (schemes != null)
129148
{
130149
foreach (var scheme in schemes)
131150
{
132-
var server = new OpenApiServer();
133-
server.Url = scheme + "://" + (host ?? "example.org/") + (basePath ?? "/");
151+
if (String.IsNullOrEmpty(scheme))
152+
{
153+
host = "//" + host; // The double slash prefix creates a relative url where the scheme is defined by the BaseUrl
154+
}
155+
156+
var uriBuilder = new UriBuilder(scheme, host)
157+
{
158+
Path = basePath
159+
};
160+
161+
var server = new OpenApiServer
162+
{
163+
Url = uriBuilder.ToString()
164+
};
165+
134166
servers.Add(server);
135167
}
136168
}
169+
else
170+
{
171+
if (!String.IsNullOrEmpty(host))
172+
{
173+
host = "//" + host; // The double slash prefix creates a relative url where the scheme is defined by the BaseUrl
174+
}
175+
var uriBuilder = new UriBuilder()
176+
{
177+
Scheme = null,
178+
Host = host,
179+
Path = basePath
180+
};
181+
var server = new OpenApiServer
182+
{
183+
Url = uriBuilder.ToString()
184+
};
185+
186+
servers.Add(server);
187+
}
188+
189+
foreach (var server in servers)
190+
{
191+
// Server Urls are always appended to Paths and Paths must start with /
192+
// so removing the slash prevents a double slash.
193+
if (server.Url.EndsWith("/"))
194+
{
195+
server.Url = server.Url.Substring(0, server.Url.Length - 1);
196+
}
197+
}
137198
}
138199

139200
public static OpenApiDocument LoadOpenApi(RootNode rootNode)
@@ -151,7 +212,7 @@ public static OpenApiDocument LoadOpenApi(RootNode rootNode)
151212
openApidoc.Servers = new List<OpenApiServer>();
152213
}
153214

154-
MakeServers(openApidoc.Servers, openApiNode.Context);
215+
MakeServers(openApidoc.Servers, openApiNode.Context, rootNode.Context.BaseUrl);
155216

156217
FixRequestBodyReferences(openApidoc);
157218
return openApidoc;

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,12 @@ internal static OpenApiOperation LoadOperation(ParseNode node)
131131
operation.RequestBody = CreateFormBody(node.Context, formParameters);
132132
}
133133
}
134-
134+
135+
foreach (var response in operation.Responses.Values)
136+
{
137+
ProcessProduces(response, node.Context);
138+
}
139+
135140
return operation;
136141
}
137142

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,22 @@ private static void ProcessProduces(OpenApiResponse response, ParsingContext con
5353
var produces = context.GetFromTempStorage<List<string>>(TempStorageKeys.OperationProduces) ??
5454
context.GetFromTempStorage<List<string>>(TempStorageKeys.GlobalProduces) ?? new List<string>();
5555

56-
response.Content = new Dictionary<string, OpenApiMediaType>();
56+
if (response.Content == null)
57+
{
58+
response.Content = new Dictionary<string, OpenApiMediaType>();
59+
}
60+
5761
foreach (var produce in produces)
5862
{
59-
var mediaType = new OpenApiMediaType
63+
if (!response.Content.ContainsKey(produce))
6064
{
61-
Schema = context.GetFromTempStorage<OpenApiSchema>(TempStorageKeys.ResponseSchema)
62-
};
65+
var mediaType = new OpenApiMediaType
66+
{
67+
Schema = context.GetFromTempStorage<OpenApiSchema>(TempStorageKeys.ResponseSchema)
68+
};
6369

64-
response.Content.Add(produce, mediaType);
70+
response.Content.Add(produce, mediaType);
71+
}
6572
}
6673
}
6774

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ internal static partial class OpenApiV3Deserializer
3333
o.Value = n.CreateAny();
3434
}
3535
},
36+
{
37+
"externalValue", (o, n) =>
38+
{
39+
o.ExternalValue = n.GetScalarValue();
40+
}
41+
},
42+
3643
};
3744

3845
private static readonly PatternFieldMap<OpenApiExample> _examplePatternFields =

src/Microsoft.OpenApi/Extensions/OpenApiSerializableExtensions.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,18 @@ public static void Serialize<T>(this T element, IOpenApiWriter writer, OpenApiSp
114114
writer.Flush();
115115
}
116116

117+
/// <summary>
118+
/// Serializes the <see cref="IOpenApiSerializable"/> to Open API document using the given specification version and writer.
119+
/// </summary>
120+
/// <typeparam name="T">the <see cref="IOpenApiSerializable"/></typeparam>
121+
/// <param name="element">The Open API element.</param>
122+
/// <param name="writer">The output writer.</param>
123+
public static void Serialize<T>(this T element, IOpenApiWriter writer)
124+
where T : IOpenApiSerializable
125+
{
126+
element.Serialize(writer, writer.Settings.SpecVersion);
127+
}
128+
117129
/// <summary>
118130
/// Serializes the <see cref="IOpenApiSerializable"/> to the Open API document as a string in JSON format.
119131
/// </summary>

src/Microsoft.OpenApi/Models/OpenApiDocument.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible
2929
/// <summary>
3030
/// REQUIRED. The available paths and operations for the API.
3131
/// </summary>
32-
public OpenApiPaths Paths { get; set; } = new OpenApiPaths();
32+
public OpenApiPaths Paths { get; set; }
3333

3434
/// <summary>
3535
/// An element to hold various schemas for the specification.
@@ -240,7 +240,10 @@ private static void WriteHostInfoV2(IOpenApiWriter writer, IList<OpenApiServer>
240240
firstServerUrl.GetComponents(UriComponents.Host | UriComponents.Port, UriFormat.SafeUnescaped));
241241

242242
// basePath
243-
writer.WriteProperty(OpenApiConstants.BasePath, firstServerUrl.AbsolutePath);
243+
if (firstServerUrl.AbsolutePath != "/")
244+
{
245+
writer.WriteProperty(OpenApiConstants.BasePath, firstServerUrl.AbsolutePath);
246+
}
244247

245248
// Consider all schemes of the URLs in the server list that have the same
246249
// host, port, and base path as the first server.

0 commit comments

Comments
 (0)