Skip to content

Commit 42fce80

Browse files
committed
Merge remote-tracking branch 'origin/master' into dm/ref-overflow
2 parents f12d221 + c3b0c87 commit 42fce80

File tree

2 files changed

+167
-7
lines changed

2 files changed

+167
-7
lines changed

src/Microsoft.OpenApi/Models/OpenApiSchema.cs

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,32 @@ public void SerializeAsV3WithoutReference(IOpenApiWriter writer)
379379
/// Serialize <see cref="OpenApiSchema"/> to Open Api v2.0
380380
/// </summary>
381381
public void SerializeAsV2(IOpenApiWriter writer)
382+
{
383+
SerializeAsV2(writer: writer, parentRequiredProperties: new List<string>(), propertyName: null);
384+
}
385+
386+
/// <summary>
387+
/// Serialize to OpenAPI V2 document without using reference.
388+
/// </summary>
389+
public void SerializeAsV2WithoutReference(IOpenApiWriter writer)
390+
{
391+
SerializeAsV2WithoutReference(
392+
writer: writer,
393+
parentRequiredProperties: new List<string>(),
394+
propertyName: null);
395+
}
396+
397+
/// <summary>
398+
/// Serialize <see cref="OpenApiSchema"/> to Open Api v2.0 and handles not marking the provided property
399+
/// as readonly if its included in the provided list of required properties of parent schema.
400+
/// </summary>
401+
/// <param name="writer">The open api writer.</param>
402+
/// <param name="parentRequiredProperties">The list of required properties in parent schema.</param>
403+
/// <param name="propertyName">The property name that will be serialized.</param>
404+
internal void SerializeAsV2(
405+
IOpenApiWriter writer,
406+
IList<string> parentRequiredProperties,
407+
string propertyName)
382408
{
383409
if (writer == null)
384410
{
@@ -391,16 +417,28 @@ public void SerializeAsV2(IOpenApiWriter writer)
391417
return;
392418
}
393419

394-
SerializeAsV2WithoutReference(writer);
420+
if (parentRequiredProperties == null)
421+
{
422+
parentRequiredProperties = new List<string>();
423+
}
424+
425+
SerializeAsV2WithoutReference(writer, parentRequiredProperties, propertyName);
395426
}
396427

397428
/// <summary>
398-
/// Serialize to OpenAPI V2 document without using reference.
429+
/// Serialize to OpenAPI V2 document without using reference and handles not marking the provided property
430+
/// as readonly if its included in the provided list of required properties of parent schema.
399431
/// </summary>
400-
public void SerializeAsV2WithoutReference(IOpenApiWriter writer)
432+
/// <param name="writer">The open api writer.</param>
433+
/// <param name="parentRequiredProperties">The list of required properties in parent schema.</param>
434+
/// <param name="propertyName">The property name that will be serialized.</param>
435+
internal void SerializeAsV2WithoutReference(
436+
IOpenApiWriter writer,
437+
IList<string> parentRequiredProperties,
438+
string propertyName)
401439
{
402440
writer.WriteStartObject();
403-
WriteAsSchemaProperties(writer);
441+
WriteAsSchemaProperties(writer, parentRequiredProperties, propertyName);
404442
writer.WriteEndObject();
405443
}
406444

@@ -468,7 +506,10 @@ internal void WriteAsItemsProperties(IOpenApiWriter writer)
468506
writer.WriteExtensions(Extensions);
469507
}
470508

471-
internal void WriteAsSchemaProperties(IOpenApiWriter writer)
509+
internal void WriteAsSchemaProperties(
510+
IOpenApiWriter writer,
511+
IList<string> parentRequiredProperties,
512+
string propertyName)
472513
{
473514
if (writer == null)
474515
{
@@ -542,7 +583,8 @@ internal void WriteAsSchemaProperties(IOpenApiWriter writer)
542583
writer.WriteOptionalCollection(OpenApiConstants.AllOf, AllOf, (w, s) => s.SerializeAsV2(w));
543584

544585
// properties
545-
writer.WriteOptionalMap(OpenApiConstants.Properties, Properties, (w, s) => s.SerializeAsV2(w));
586+
writer.WriteOptionalMap(OpenApiConstants.Properties, Properties, (w, key, s) =>
587+
s.SerializeAsV2(w, Required, key));
546588

547589
// additionalProperties
548590
writer.WriteOptionalObject(
@@ -554,7 +596,12 @@ internal void WriteAsSchemaProperties(IOpenApiWriter writer)
554596
writer.WriteProperty(OpenApiConstants.Discriminator, Discriminator?.PropertyName);
555597

556598
// readOnly
557-
writer.WriteProperty(OpenApiConstants.ReadOnly, ReadOnly, false);
599+
// In V2 schema if a property is part of required properties of parent schema,
600+
// it cannot be marked as readonly.
601+
if (!parentRequiredProperties.Contains(propertyName))
602+
{
603+
writer.WriteProperty(name: OpenApiConstants.ReadOnly, value: ReadOnly, defaultValue: false);
604+
}
558605

559606
// xml
560607
writer.WriteOptionalObject(OpenApiConstants.Xml, Xml, (w, s) => s.SerializeAsV2(w));

test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,60 @@ public class OpenApiSchemaTests
160160
}
161161
};
162162

163+
public static OpenApiSchema AdvancedSchemaWithRequiredPropertiesObject = new OpenApiSchema
164+
{
165+
Title = "title1",
166+
Required = new List<string>(){ "property1" },
167+
Properties = new Dictionary<string, OpenApiSchema>
168+
{
169+
["property1"] = new OpenApiSchema
170+
{
171+
Required = new List<string>() { "property3" },
172+
Properties = new Dictionary<string, OpenApiSchema>
173+
{
174+
["property2"] = new OpenApiSchema
175+
{
176+
Type = "integer"
177+
},
178+
["property3"] = new OpenApiSchema
179+
{
180+
Type = "string",
181+
MaxLength = 15,
182+
ReadOnly = true
183+
}
184+
},
185+
ReadOnly = true,
186+
},
187+
["property4"] = new OpenApiSchema
188+
{
189+
Properties = new Dictionary<string, OpenApiSchema>
190+
{
191+
["property5"] = new OpenApiSchema
192+
{
193+
Properties = new Dictionary<string, OpenApiSchema>
194+
{
195+
["property6"] = new OpenApiSchema
196+
{
197+
Type = "boolean"
198+
}
199+
}
200+
},
201+
["property7"] = new OpenApiSchema
202+
{
203+
Type = "string",
204+
MinLength = 2
205+
}
206+
},
207+
ReadOnly = true,
208+
},
209+
},
210+
Nullable = true,
211+
ExternalDocs = new OpenApiExternalDocs
212+
{
213+
Url = new Uri("http://example.com/externalDocs")
214+
}
215+
};
216+
163217
private readonly ITestOutputHelper _output;
164218

165219
public OpenApiSchemaTests(ITestOutputHelper output)
@@ -363,5 +417,64 @@ public void SerializeReferencedSchemaAsV3JsonWorks()
363417
expected = expected.MakeLineBreaksEnvironmentNeutral();
364418
actual.Should().Be(expected);
365419
}
420+
421+
[Fact]
422+
public void SerializeSchemaWRequiredPropertiesAsV2JsonWorks()
423+
{
424+
// Arrange
425+
var outputStringWriter = new StringWriter();
426+
var writer = new OpenApiJsonWriter(outputStringWriter);
427+
var expected = @"{
428+
""title"": ""title1"",
429+
""required"": [
430+
""property1""
431+
],
432+
""properties"": {
433+
""property1"": {
434+
""required"": [
435+
""property3""
436+
],
437+
""properties"": {
438+
""property2"": {
439+
""type"": ""integer""
440+
},
441+
""property3"": {
442+
""maxLength"": 15,
443+
""type"": ""string""
444+
}
445+
}
446+
},
447+
""property4"": {
448+
""properties"": {
449+
""property5"": {
450+
""properties"": {
451+
""property6"": {
452+
""type"": ""boolean""
453+
}
454+
}
455+
},
456+
""property7"": {
457+
""minLength"": 2,
458+
""type"": ""string""
459+
}
460+
},
461+
""readOnly"": true
462+
}
463+
},
464+
""externalDocs"": {
465+
""url"": ""http://example.com/externalDocs""
466+
}
467+
}";
468+
469+
// Act
470+
AdvancedSchemaWithRequiredPropertiesObject.SerializeAsV2(writer);
471+
writer.Flush();
472+
var actual = outputStringWriter.GetStringBuilder().ToString();
473+
474+
// Assert
475+
actual = actual.MakeLineBreaksEnvironmentNeutral();
476+
expected = expected.MakeLineBreaksEnvironmentNeutral();
477+
actual.Should().Be(expected);
478+
}
366479
}
367480
}

0 commit comments

Comments
 (0)