Skip to content

Commit 95c4b54

Browse files
authored
Write full objects in components / Fix components missing issue when V2 document is parsed (#128)
- Write full objects in components - Don't write SecurityScheme/Scopes key value pair if SecurityScheme Reference is not present - Unit tests for Callback, Components, Example, Header, Link, SecurityScheme, Document, Header, Parameter, RequestBody, Response - Fix issue of Components being overwritten every time in V2 deserializer
1 parent 6b599f0 commit 95c4b54

30 files changed

+4786
-329
lines changed

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

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
44
// ------------------------------------------------------------
55

6-
using System;
76
using System.Collections.Generic;
87
using Microsoft.OpenApi.Extensions;
98
using Microsoft.OpenApi.Models;
@@ -19,7 +18,11 @@ internal static partial class OpenApiV2Deserializer
1918
{
2019
public static FixedFieldMap<OpenApiDocument> OpenApiFixedFields = new FixedFieldMap<OpenApiDocument>
2120
{
22-
{"swagger", (o, n) => { } /* Version is valid field but we already parsed it */ },
21+
{
22+
"swagger", (o, n) =>
23+
{
24+
} /* Version is valid field but we already parsed it */
25+
},
2326
{"info", (o, n) => o.Info = LoadInfo(n)},
2427
{"host", (o, n) => n.Context.SetTempStorage("host", n.GetScalarValue())},
2528
{"basePath", (o, n) => n.Context.SetTempStorage("basePath", n.GetScalarValue())},
@@ -40,34 +43,54 @@ internal static partial class OpenApiV2Deserializer
4043
{"paths", (o, n) => o.Paths = LoadPaths(n)},
4144
{
4245
"definitions",
43-
(o, n) => {
44-
o.Components = new OpenApiComponents();
45-
o.Components.Schemas = n.CreateMapWithReference(
46-
ReferenceType.Schema,
47-
"#/definitions/",
48-
LoadSchema);
46+
(o, n) =>
47+
{
48+
if (o.Components == null)
49+
{
50+
o.Components = new OpenApiComponents();
4951
}
52+
53+
o.Components.Schemas = n.CreateMapWithReference(
54+
ReferenceType.Schema,
55+
"#/definitions/",
56+
LoadSchema);
57+
}
5058
},
5159
{
5260
"parameters",
53-
(o, n) => {
54-
o.Components = new OpenApiComponents();
55-
o.Components.Parameters = n.CreateMapWithReference(
56-
ReferenceType.Parameter,
57-
"#/parameters/",
58-
LoadParameter);
61+
(o, n) =>
62+
{
63+
if (o.Components == null)
64+
{
65+
o.Components = new OpenApiComponents();
5966
}
67+
68+
o.Components.Parameters = n.CreateMapWithReference(
69+
ReferenceType.Parameter,
70+
"#/parameters/",
71+
LoadParameter);
72+
}
6073
},
6174
{
62-
"responses", (o, n) => {
63-
o.Components = new OpenApiComponents();
64-
o.Components.Responses = n.CreateMap(LoadResponse);
75+
"responses", (o, n) =>
76+
{
77+
if (o.Components == null)
78+
{
79+
o.Components = new OpenApiComponents();
80+
}
81+
82+
o.Components.Responses = n.CreateMap(LoadResponse);
6583
}
6684
},
6785
{
68-
"securityDefinitions", (o, n) => {
69-
o.Components = new OpenApiComponents();
70-
o.Components.SecuritySchemes = n.CreateMap(LoadSecurityScheme);
86+
"securityDefinitions", (o, n) =>
87+
{
88+
if (o.Components == null)
89+
{
90+
o.Components = new OpenApiComponents();
91+
}
92+
93+
o.Components.SecuritySchemes = n.CreateMap(LoadSecurityScheme);
7194
}
7295
},
7396
{"security", (o, n) => o.SecurityRequirements = n.CreateList(LoadSecurityRequirement)},

src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceable.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,28 @@
44
// ------------------------------------------------------------
55

66
using Microsoft.OpenApi.Models;
7+
using Microsoft.OpenApi.Writers;
78

89
namespace Microsoft.OpenApi.Interfaces
910
{
1011
/// <summary>
1112
/// Represents an Open API element is referenceable.
1213
/// </summary>
13-
public interface IOpenApiReferenceable : IOpenApiElement
14+
public interface IOpenApiReferenceable : IOpenApiSerializable
1415
{
1516
/// <summary>
1617
/// Reference object.
1718
/// </summary>
1819
OpenApiReference Reference { get; set; }
20+
21+
/// <summary>
22+
/// Serialize to OpenAPI V3 document without using reference.
23+
/// </summary>
24+
void SerializeAsV3WithoutReference(IOpenApiWriter writer);
25+
26+
/// <summary>
27+
/// Serialize to OpenAPI V2 document without using reference.
28+
/// </summary>
29+
void SerializeAsV2WithoutReference(IOpenApiWriter writer);
1930
}
2031
}

src/Microsoft.OpenApi/Models/OpenApiCallback.cs

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,22 +70,30 @@ public void SerializeAsV3(IOpenApiWriter writer)
7070
if (Reference != null)
7171
{
7272
Reference.SerializeAsV3(writer);
73+
return;
7374
}
74-
else
75-
{
76-
writer.WriteStartObject();
75+
76+
SerializeAsV3WithoutReference(writer);
77+
}
7778

78-
// path items
79-
foreach (var item in PathItems)
80-
{
81-
writer.WriteRequiredObject(item.Key.Expression, item.Value, (w, p) => p.SerializeAsV3(w));
82-
}
79+
/// <summary>
80+
/// Serialize to OpenAPI V3 document without using reference.
81+
/// </summary>
8382

84-
// extensions
85-
writer.WriteExtensions(Extensions);
83+
public void SerializeAsV3WithoutReference(IOpenApiWriter writer)
84+
{
85+
writer.WriteStartObject();
8686

87-
writer.WriteEndObject();
87+
// path items
88+
foreach (var item in PathItems)
89+
{
90+
writer.WriteRequiredObject(item.Key.Expression, item.Value, (w, p) => p.SerializeAsV3(w));
8891
}
92+
93+
// extensions
94+
writer.WriteExtensions(Extensions);
95+
96+
writer.WriteEndObject();
8997
}
9098

9199
/// <summary>
@@ -95,5 +103,14 @@ public void SerializeAsV2(IOpenApiWriter writer)
95103
{
96104
// Callback object does not exist in V2.
97105
}
106+
107+
/// <summary>
108+
/// Serialize to OpenAPI V2 document without using reference.
109+
/// </summary>
110+
111+
public void SerializeAsV2WithoutReference(IOpenApiWriter writer)
112+
{
113+
// Callback object does not exist in V2.
114+
}
98115
}
99116
}

src/Microsoft.OpenApi/Models/OpenApiComponents.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,31 +78,31 @@ public void SerializeAsV3(IOpenApiWriter writer)
7878
writer.WriteStartObject();
7979

8080
// schemas
81-
writer.WriteOptionalMap(OpenApiConstants.Schemas, Schemas, (w, s) => s.SerializeAsV3(w));
81+
writer.WriteOptionalMap(OpenApiConstants.Schemas, Schemas, (w, s) => s.SerializeAsV3WithoutReference(w));
8282

8383
// responses
84-
writer.WriteOptionalMap(OpenApiConstants.Responses, Responses, (w, r) => r.SerializeAsV3(w));
84+
writer.WriteOptionalMap(OpenApiConstants.Responses, Responses, (w, r) => r.SerializeAsV3WithoutReference(w));
8585

8686
// parameters
87-
writer.WriteOptionalMap(OpenApiConstants.Parameters, Parameters, (w, p) => p.SerializeAsV3(w));
87+
writer.WriteOptionalMap(OpenApiConstants.Parameters, Parameters, (w, p) => p.SerializeAsV3WithoutReference(w));
8888

8989
// examples
90-
writer.WriteOptionalMap(OpenApiConstants.Examples, Examples, (w, e) => e.SerializeAsV3(w));
90+
writer.WriteOptionalMap(OpenApiConstants.Examples, Examples, (w, e) => e.SerializeAsV3WithoutReference(w));
9191

9292
// requestBodies
93-
writer.WriteOptionalMap(OpenApiConstants.RequestBodies, RequestBodies, (w, r) => r.SerializeAsV3(w));
93+
writer.WriteOptionalMap(OpenApiConstants.RequestBodies, RequestBodies, (w, r) => r.SerializeAsV3WithoutReference(w));
9494

9595
// headers
96-
writer.WriteOptionalMap(OpenApiConstants.Headers, Headers, (w, h) => h.SerializeAsV3(w));
96+
writer.WriteOptionalMap(OpenApiConstants.Headers, Headers, (w, h) => h.SerializeAsV3WithoutReference(w));
9797

9898
// securitySchemes
99-
writer.WriteOptionalMap(OpenApiConstants.SecuritySchemes, SecuritySchemes, (w, s) => s.SerializeAsV3(w));
99+
writer.WriteOptionalMap(OpenApiConstants.SecuritySchemes, SecuritySchemes, (w, s) => s.SerializeAsV3WithoutReference(w));
100100

101101
// links
102-
writer.WriteOptionalMap(OpenApiConstants.Links, Links, (w, link) => link.SerializeAsV3(w));
102+
writer.WriteOptionalMap(OpenApiConstants.Links, Links, (w, link) => link.SerializeAsV3WithoutReference(w));
103103

104104
// callbacks
105-
writer.WriteOptionalMap(OpenApiConstants.Callbacks, Callbacks, (w, c) => c.SerializeAsV3(w));
105+
writer.WriteOptionalMap(OpenApiConstants.Callbacks, Callbacks, (w, c) => c.SerializeAsV3WithoutReference(w));
106106

107107
// extensions
108108
writer.WriteExtensions(Extensions);

src/Microsoft.OpenApi/Models/OpenApiDocument.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public void SerializeAsV3(IOpenApiWriter writer)
8989
writer.WriteOptionalCollection(OpenApiConstants.Security, SecurityRequirements, (w, s) => s.SerializeAsV3(w));
9090

9191
// tags
92-
writer.WriteOptionalCollection(OpenApiConstants.Tags, Tags, (w, t) => t.SerializeAsV3(w));
92+
writer.WriteOptionalCollection(OpenApiConstants.Tags, Tags, (w, t) => t.SerializeAsV3WithoutReference(w));
9393

9494
// external docs
9595
writer.WriteOptionalObject(OpenApiConstants.ExternalDocs, ExternalDocs, (w, e) => e.SerializeAsV3(w));
@@ -125,22 +125,22 @@ public void SerializeAsV2(IOpenApiWriter writer)
125125
writer.WriteRequiredObject(OpenApiConstants.Paths, Paths, (w, p) => p.SerializeAsV2(w));
126126

127127
// definitions
128-
writer.WriteOptionalMap(OpenApiConstants.Definitions, Components?.Schemas, (w, s) => s.SerializeAsV2(w));
128+
writer.WriteOptionalMap(OpenApiConstants.Definitions, Components?.Schemas, (w, s) => s.SerializeAsV2WithoutReference(w));
129129

130130
// parameters
131-
writer.WriteOptionalMap(OpenApiConstants.Parameters, Components?.Parameters, (w, p) => p.SerializeAsV2(w));
131+
writer.WriteOptionalMap(OpenApiConstants.Parameters, Components?.Parameters, (w, p) => p.SerializeAsV2WithoutReference(w));
132132

133133
// responses
134-
writer.WriteOptionalMap(OpenApiConstants.Responses, Components?.Responses, (w, r) => r.SerializeAsV2(w));
134+
writer.WriteOptionalMap(OpenApiConstants.Responses, Components?.Responses, (w, r) => r.SerializeAsV2WithoutReference(w));
135135

136136
// securityDefinitions
137-
writer.WriteOptionalMap(OpenApiConstants.SecurityDefinitions, Components?.SecuritySchemes, (w, s) => s.SerializeAsV2(w));
137+
writer.WriteOptionalMap(OpenApiConstants.SecurityDefinitions, Components?.SecuritySchemes, (w, s) => s.SerializeAsV2WithoutReference(w));
138138

139139
// security
140140
writer.WriteOptionalCollection(OpenApiConstants.Security, SecurityRequirements, (w, s) => s.SerializeAsV2(w));
141141

142142
// tags
143-
writer.WriteOptionalCollection(OpenApiConstants.Tags, Tags, (w, t) => t.SerializeAsV2(w));
143+
writer.WriteOptionalCollection(OpenApiConstants.Tags, Tags, (w, t) => t.SerializeAsV2WithoutReference(w));
144144

145145
// externalDocs
146146
writer.WriteOptionalObject(OpenApiConstants.ExternalDocs, ExternalDocs, (w, e) => e.SerializeAsV2(w));

src/Microsoft.OpenApi/Models/OpenApiExample.cs

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,16 @@ public class OpenApiExample : IOpenApiSerializable, IOpenApiReferenceable, IOpen
2727
public string Description { get; set; }
2828

2929
/// <summary>
30-
/// Embedded literal example. The value field and externalValue field are mutually
31-
/// exclusive. To represent examples of media types that cannot naturally represented
30+
/// Embedded literal example. The value field and externalValue field are mutually
31+
/// exclusive. To represent examples of media types that cannot naturally represented
3232
/// in JSON or YAML, use a string value to contain the example, escaping where necessary.
3333
/// </summary>
3434
public IOpenApiAny Value { get; set; }
3535

3636
/// <summary>
37-
/// A URL that points to the literal example.
38-
/// This provides the capability to reference examples that cannot easily be
39-
/// included in JSON or YAML documents.
37+
/// A URL that points to the literal example.
38+
/// This provides the capability to reference examples that cannot easily be
39+
/// included in JSON or YAML documents.
4040
/// The value field and externalValue field are mutually exclusive.
4141
/// </summary>
4242
public string ExternalValue { get; set; }
@@ -49,10 +49,7 @@ public class OpenApiExample : IOpenApiSerializable, IOpenApiReferenceable, IOpen
4949
/// <summary>
5050
/// Reference object.
5151
/// </summary>
52-
public OpenApiReference Reference
53-
{
54-
get; set;
55-
}
52+
public OpenApiReference Reference { get; set; }
5653

5754
/// <summary>
5855
/// Serialize <see cref="OpenApiExample"/> to Open Api v3.0
@@ -67,28 +64,35 @@ public void SerializeAsV3(IOpenApiWriter writer)
6764
if (Reference != null)
6865
{
6966
Reference.SerializeAsV3(writer);
67+
return;
7068
}
71-
else
72-
{
73-
writer.WriteStartObject();
7469

75-
// summary
76-
writer.WriteProperty(OpenApiConstants.Summary, Summary);
70+
SerializeAsV3WithoutReference(writer);
71+
}
7772

78-
// description
79-
writer.WriteProperty(OpenApiConstants.Description, Description);
73+
/// <summary>
74+
/// Serialize to OpenAPI V3 document without using reference.
75+
/// </summary>
76+
public void SerializeAsV3WithoutReference(IOpenApiWriter writer)
77+
{
78+
writer.WriteStartObject();
8079

81-
// value
82-
writer.WriteOptionalObject(OpenApiConstants.Value, Value, (w, v) => w.WriteAny(v));
80+
// summary
81+
writer.WriteProperty(OpenApiConstants.Summary, Summary);
8382

84-
// externalValue
85-
writer.WriteProperty(OpenApiConstants.ExternalValue, ExternalValue);
83+
// description
84+
writer.WriteProperty(OpenApiConstants.Description, Description);
8685

87-
// extensions
88-
writer.WriteExtensions(Extensions);
86+
// value
87+
writer.WriteOptionalObject(OpenApiConstants.Value, Value, (w, v) => w.WriteAny(v));
8988

90-
writer.WriteEndObject();
91-
}
89+
// externalValue
90+
writer.WriteProperty(OpenApiConstants.ExternalValue, ExternalValue);
91+
92+
// extensions
93+
writer.WriteExtensions(Extensions);
94+
95+
writer.WriteEndObject();
9296
}
9397

9498
/// <summary>
@@ -100,5 +104,15 @@ public void SerializeAsV2(IOpenApiWriter writer)
100104
// V2 Example object requires knowledge of media type and exists only
101105
// in Response object, so it will be serialized as a part of the Response object.
102106
}
107+
108+
/// <summary>
109+
/// Serialize to OpenAPI V2 document without using reference.
110+
/// </summary>
111+
public void SerializeAsV2WithoutReference(IOpenApiWriter writer)
112+
{
113+
// Example object of this form does not exist in V2.
114+
// V2 Example object requires knowledge of media type and exists only
115+
// in Response object, so it will be serialized as a part of the Response object.
116+
}
103117
}
104-
}
118+
}

0 commit comments

Comments
 (0)