Skip to content
This repository was archived by the owner on Nov 11, 2025. It is now read-only.

Commit 2e2c3cf

Browse files
Copilotbaywet
andauthored
feat: Add support for response summary in OpenAPI 3.2.0 (#23)
* Initial plan * Add Summary support to OpenApiResponse with serialization/deserialization Co-authored-by: baywet <[email protected]> * Complete implementation with reference support and test fixes Co-authored-by: baywet <[email protected]> * Update IOpenApiResponse to derive from IOpenApiSummarizedElement Co-authored-by: baywet <[email protected]> * Apply suggestions from code review * Apply suggestion from @baywet * chore: copy reference implementation Signed-off-by: Vincent Biret <[email protected]> * chore: adds missing using Signed-off-by: Vincent Biret <[email protected]> * chore: linting Signed-off-by: Vincent Biret <[email protected]> * chore: updates public API export Signed-off-by: Vincent Biret <[email protected]> --------- Signed-off-by: Vincent Biret <[email protected]> Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: baywet <[email protected]> Co-authored-by: Vincent Biret <[email protected]> Co-authored-by: Vincent Biret <[email protected]>
1 parent 6ed161f commit 2e2c3cf

File tree

12 files changed

+215
-10
lines changed

12 files changed

+215
-10
lines changed

src/Microsoft.OpenApi/Models/Interfaces/IOpenApiResponse.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Microsoft.OpenApi;
66
/// Defines the base properties for the response object.
77
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
88
/// </summary>
9-
public interface IOpenApiResponse : IOpenApiDescribedElement, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiResponse>, IOpenApiReferenceable
9+
public interface IOpenApiResponse : IOpenApiDescribedElement, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiResponse>, IOpenApiReferenceable, IOpenApiSummarizedElement
1010
{
1111
/// <summary>
1212
/// Maps a header name to its definition.

src/Microsoft.OpenApi/Models/OpenApiResponse.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ namespace Microsoft.OpenApi
1212
/// </summary>
1313
public class OpenApiResponse : IOpenApiExtensible, IOpenApiResponse
1414
{
15+
/// <inheritdoc/>
16+
public string? Summary { get; set; }
17+
1518
/// <inheritdoc/>
1619
public string? Description { get; set; }
1720

@@ -38,6 +41,7 @@ public OpenApiResponse() { }
3841
internal OpenApiResponse(IOpenApiResponse response)
3942
{
4043
Utils.CheckArgumentNull(response);
44+
Summary = response.Summary ?? Summary;
4145
Description = response.Description ?? Description;
4246
Headers = response.Headers != null ? new Dictionary<string, IOpenApiHeader>(response.Headers) : null;
4347
Content = response.Content != null ? new Dictionary<string, OpenApiMediaType>(response.Content) : null;
@@ -76,6 +80,12 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version
7680

7781
writer.WriteStartObject();
7882

83+
// summary - only for v3.2+
84+
if (version >= OpenApiSpecVersion.OpenApi3_2)
85+
{
86+
writer.WriteProperty(OpenApiConstants.Summary, Summary);
87+
}
88+
7989
// description
8090
writer.WriteRequiredProperty(OpenApiConstants.Description, Description);
8191

@@ -88,6 +98,12 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version
8898
// links
8999
writer.WriteOptionalMap(OpenApiConstants.Links, Links, callback);
90100

101+
// summary as extension for v3.1 and earlier
102+
if (version < OpenApiSpecVersion.OpenApi3_2 && !string.IsNullOrEmpty(Summary))
103+
{
104+
writer.WriteProperty(OpenApiConstants.ExtensionFieldNamePrefix + "oai-" + OpenApiConstants.Summary, Summary);
105+
}
106+
91107
// extension
92108
writer.WriteExtensions(Extensions, version);
93109

src/Microsoft.OpenApi/Models/References/OpenApiResponseReference.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Microsoft.OpenApi
88
/// <summary>
99
/// Response Object Reference.
1010
/// </summary>
11-
public class OpenApiResponseReference : BaseOpenApiReferenceHolder<OpenApiResponse, IOpenApiResponse, OpenApiReferenceWithDescription>, IOpenApiResponse
11+
public class OpenApiResponseReference : BaseOpenApiReferenceHolder<OpenApiResponse, IOpenApiResponse, OpenApiReferenceWithDescriptionAndSummary>, IOpenApiResponse
1212
{
1313
/// <summary>
1414
/// Constructor initializing the reference object.
@@ -32,6 +32,13 @@ private OpenApiResponseReference(OpenApiResponseReference openApiResponseReferen
3232

3333
}
3434

35+
/// <inheritdoc/>
36+
public string? Summary
37+
{
38+
get => string.IsNullOrEmpty(Reference.Summary) ? Target?.Summary : Reference.Summary;
39+
set => Reference.Summary = value;
40+
}
41+
3542
/// <inheritdoc/>
3643
public string? Description
3744
{
@@ -63,9 +70,9 @@ public IOpenApiResponse CreateShallowCopy()
6370
return new OpenApiResponseReference(this);
6471
}
6572
/// <inheritdoc/>
66-
protected override OpenApiReferenceWithDescription CopyReference(OpenApiReferenceWithDescription sourceReference)
73+
protected override OpenApiReferenceWithDescriptionAndSummary CopyReference(OpenApiReferenceWithDescriptionAndSummary sourceReference)
6774
{
68-
return new OpenApiReferenceWithDescription(sourceReference);
75+
return new OpenApiReferenceWithDescriptionAndSummary(sourceReference);
6976
}
7077
}
7178
}

src/Microsoft.OpenApi/Reader/V3/OpenApiResponseDeserializer.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,17 @@ internal static partial class OpenApiV3Deserializer
3434
private static readonly PatternFieldMap<OpenApiResponse> _responsePatternFields =
3535
new()
3636
{
37-
{s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix, StringComparison.OrdinalIgnoreCase), (o, p, n, _) => o.AddExtension(p, LoadExtension(p,n))}
37+
{s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix, StringComparison.OrdinalIgnoreCase), (o, p, n, _) =>
38+
{
39+
if (p.Equals("x-oai-summary", StringComparison.OrdinalIgnoreCase))
40+
{
41+
o.Summary = n.GetScalarValue();
42+
}
43+
else
44+
{
45+
o.AddExtension(p, LoadExtension(p,n));
46+
}
47+
}}
3848
};
3949

4050
public static IOpenApiResponse LoadResponse(ParseNode node, OpenApiDocument hostDocument)

src/Microsoft.OpenApi/Reader/V31/OpenApiResponseDeserializer.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,17 @@ internal static partial class OpenApiV31Deserializer
3939
private static readonly PatternFieldMap<OpenApiResponse> _responsePatternFields =
4040
new()
4141
{
42-
{s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix, StringComparison.OrdinalIgnoreCase), (o, p, n, _) => o.AddExtension(p, LoadExtension(p,n))}
42+
{s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix, StringComparison.OrdinalIgnoreCase), (o, p, n, _) =>
43+
{
44+
if (p.Equals("x-oai-summary", StringComparison.OrdinalIgnoreCase))
45+
{
46+
o.Summary = n.GetScalarValue();
47+
}
48+
else
49+
{
50+
o.AddExtension(p, LoadExtension(p,n));
51+
}
52+
}}
4353
};
4454

4555
public static IOpenApiResponse LoadResponse(ParseNode node, OpenApiDocument hostDocument)

src/Microsoft.OpenApi/Reader/V32/OpenApiResponseDeserializer.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ internal static partial class OpenApiV32Deserializer
1010
{
1111
private static readonly FixedFieldMap<OpenApiResponse> _responseFixedFields = new()
1212
{
13+
{
14+
"summary", (o, n, _) =>
15+
{
16+
o.Summary = n.GetScalarValue();
17+
}
18+
},
1319
{
1420
"description", (o, n, _) =>
1521
{

test/Microsoft.OpenApi.Readers.Tests/ReferenceService/TryLoadReferenceV2Tests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ public async Task LoadResponseReference()
6767
Assert.Equivalent(
6868
new OpenApiResponse
6969
{
70+
Summary = null,
7071
Description = "Entity not found.",
7172
Content = new Dictionary<string, OpenApiMediaType>()
7273
{

test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiResponseTests.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,29 @@ public async Task ResponseWithReferencedHeaderShouldReferenceComponent()
2424

2525
Assert.Equal(expected.Description, actual.Description);
2626
}
27+
28+
[Fact]
29+
public async Task ResponseWithSummaryV32ShouldDeserializeCorrectly()
30+
{
31+
var result = await OpenApiDocument.LoadAsync(Path.Combine(SampleFolderPath, "responseWithSummary.yaml"), SettingsFixture.ReaderSettings);
32+
33+
var response = result.Document.Components.Responses["SuccessResponse"] as OpenApiResponse;
34+
35+
Assert.NotNull(response);
36+
Assert.Equal("Successful response", response.Summary);
37+
Assert.Equal("A successful response with summary", response.Description);
38+
}
39+
40+
[Fact]
41+
public async Task ResponseWithSummaryExtensionV31ShouldDeserializeCorrectly()
42+
{
43+
var result = await OpenApiDocument.LoadAsync(Path.Combine(SampleFolderPath, "responseWithSummaryExtension.yaml"), SettingsFixture.ReaderSettings);
44+
45+
var response = result.Document.Components.Responses["SuccessResponse"] as OpenApiResponse;
46+
47+
Assert.NotNull(response);
48+
Assert.Equal("Successful response", response.Summary);
49+
Assert.Equal("A successful response with summary extension", response.Description);
50+
}
2751
}
2852
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
openapi: 3.2.0
2+
info:
3+
title: Test API
4+
version: 1.0.0
5+
components:
6+
responses:
7+
SuccessResponse:
8+
summary: Successful response
9+
description: A successful response with summary
10+
content:
11+
application/json:
12+
schema:
13+
type: object
14+
properties:
15+
message:
16+
type: string
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
openapi: 3.1.0
2+
info:
3+
title: Test API
4+
version: 1.0.0
5+
components:
6+
responses:
7+
SuccessResponse:
8+
description: A successful response with summary extension
9+
x-oai-summary: Successful response
10+
content:
11+
application/json:
12+
schema:
13+
type: object
14+
properties:
15+
message:
16+
type: string

0 commit comments

Comments
 (0)