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

Commit 93d8a47

Browse files
committed
add new v32 properties for Path Items
1 parent 9bba39f commit 93d8a47

12 files changed

Lines changed: 712 additions & 8 deletions

File tree

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-

2-
using System.Collections.Generic;
1+
using System.Collections.Generic;
32
using System.Net.Http;
43

54
namespace Microsoft.OpenApi;
@@ -25,4 +24,15 @@ public interface IOpenApiPathItem : IOpenApiDescribedElement, IOpenApiSummarized
2524
/// These parameters can be overridden at the operation level, but cannot be removed there.
2625
/// </summary>
2726
public IList<IOpenApiParameter>? Parameters { get; }
27+
28+
/// <summary>
29+
/// Gets the query operation for this path (OpenAPI 3.2).
30+
/// </summary>
31+
public OpenApiOperation? Query { get; }
32+
33+
/// <summary>
34+
/// Gets the additional operations for this path (OpenAPI 3.2).
35+
/// A map of additional operations that are not one of the standard HTTP methods (GET, PUT, POST, DELETE, OPTIONS, HEAD, PATCH, TRACE).
36+
/// </summary>
37+
public IDictionary<string, OpenApiOperation>? AdditionalOperations { get; }
2838
}

src/Microsoft.OpenApi/Models/OpenApiConstants.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,16 @@ public static class OpenApiConstants
205205
/// </summary>
206206
public const string RequestBody = "requestBody";
207207

208+
/// <summary>
209+
/// Field: Query (OpenAPI 3.2)
210+
/// </summary>
211+
public const string Query = "query";
212+
213+
/// <summary>
214+
/// Field: AdditionalOperations (OpenAPI 3.2)
215+
/// </summary>
216+
public const string AdditionalOperations = "additionalOperations";
217+
208218
/// <summary>
209219
/// Field: ExtensionFieldNamePrefix
210220
/// </summary>

src/Microsoft.OpenApi/Models/OpenApiPathItem.cs

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ public class OpenApiPathItem : IOpenApiExtensible, IOpenApiPathItem
2727
/// <inheritdoc/>
2828
public IList<IOpenApiParameter>? Parameters { get; set; }
2929

30+
/// <inheritdoc/>
31+
public OpenApiOperation? Query { get; set; }
32+
33+
/// <inheritdoc/>
34+
public IDictionary<string, OpenApiOperation>? AdditionalOperations { get; set; }
35+
3036
/// <inheritdoc/>
3137
public IDictionary<string, IOpenApiExtension>? Extensions { get; set; }
3238

@@ -57,6 +63,8 @@ internal OpenApiPathItem(IOpenApiPathItem pathItem)
5763
Operations = pathItem.Operations != null ? new Dictionary<HttpMethod, OpenApiOperation>(pathItem.Operations) : null;
5864
Servers = pathItem.Servers != null ? [.. pathItem.Servers] : null;
5965
Parameters = pathItem.Parameters != null ? [.. pathItem.Parameters] : null;
66+
Query = pathItem.Query != null ? new OpenApiOperation(pathItem.Query) : null;
67+
AdditionalOperations = pathItem.AdditionalOperations != null ? new Dictionary<string, OpenApiOperation>(pathItem.AdditionalOperations) : null;
6068
Extensions = pathItem.Extensions != null ? new Dictionary<string, IOpenApiExtension>(pathItem.Extensions) : null;
6169
}
6270

@@ -73,15 +81,15 @@ public virtual void SerializeAsV32(IOpenApiWriter writer)
7381
/// </summary>
7482
public virtual void SerializeAsV31(IOpenApiWriter writer)
7583
{
76-
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_1, (writer, element) => element.SerializeAsV31(writer));
84+
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_1, (writer, element) => element.SerializeAsV31(writer), downgradeFrom32: true);
7785
}
7886

7987
/// <summary>
8088
/// Serialize <see cref="OpenApiPathItem"/> to Open Api v3.0
8189
/// </summary>
8290
public virtual void SerializeAsV3(IOpenApiWriter writer)
8391
{
84-
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_0, (writer, element) => element.SerializeAsV3(writer));
92+
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_0, (writer, element) => element.SerializeAsV3(writer), downgradeFrom32: true);
8593
}
8694

8795
/// <summary>
@@ -120,14 +128,17 @@ public virtual void SerializeAsV2(IOpenApiWriter writer)
120128
OpenApiConstants.ExtensionFieldNamePrefix + OpenApiConstants.Description,
121129
Description);
122130

131+
// Write Query and AdditionalOperations as extensions when downgrading to v2
132+
WriteV32FieldsAsExtensions(writer);
133+
123134
// specification extensions
124135
writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi2_0);
125136

126137
writer.WriteEndObject();
127138
}
128139

129140
internal virtual void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
130-
Action<IOpenApiWriter, IOpenApiSerializable> callback)
141+
Action<IOpenApiWriter, IOpenApiSerializable> callback, bool downgradeFrom32 = false)
131142
{
132143
Utils.CheckArgumentNull(writer);
133144

@@ -151,6 +162,21 @@ internal virtual void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersio
151162
}
152163
}
153164

165+
// OpenAPI 3.2 specific fields
166+
if (version == OpenApiSpecVersion.OpenApi3_2)
167+
{
168+
// query operation
169+
writer.WriteOptionalObject(OpenApiConstants.Query, Query, callback);
170+
171+
// additional operations
172+
writer.WriteOptionalMap(OpenApiConstants.AdditionalOperations, AdditionalOperations, callback);
173+
}
174+
else if (downgradeFrom32)
175+
{
176+
// When downgrading from 3.2 to 3.1/3.0, serialize as extensions
177+
WriteV32FieldsAsExtensions(writer);
178+
}
179+
154180
// servers
155181
writer.WriteOptionalCollection(OpenApiConstants.Servers, Servers, callback);
156182

@@ -163,6 +189,29 @@ internal virtual void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersio
163189
writer.WriteEndObject();
164190
}
165191

192+
/// <summary>
193+
/// Writes OpenAPI 3.2 specific fields as extensions when downgrading to older versions
194+
/// </summary>
195+
private void WriteV32FieldsAsExtensions(IOpenApiWriter writer)
196+
{
197+
if (Query != null)
198+
{
199+
writer.WritePropertyName(OpenApiConstants.ExtensionFieldNamePrefix + "oas-" + OpenApiConstants.Query);
200+
Query.SerializeAsV31(writer);
201+
}
202+
203+
if (AdditionalOperations != null && AdditionalOperations.Count > 0)
204+
{
205+
writer.WritePropertyName(OpenApiConstants.ExtensionFieldNamePrefix + "oas-" + OpenApiConstants.AdditionalOperations);
206+
writer.WriteStartObject();
207+
foreach (var kvp in AdditionalOperations)
208+
{
209+
writer.WriteOptionalObject(kvp.Key, kvp.Value, (w, o) => o.SerializeAsV31(w));
210+
}
211+
writer.WriteEndObject();
212+
}
213+
}
214+
166215
/// <inheritdoc/>
167216
public IOpenApiPathItem CreateShallowCopy()
168217
{

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ public string? Description
5858
/// <inheritdoc/>
5959
public IList<IOpenApiParameter>? Parameters { get => Target?.Parameters; }
6060

61+
/// <inheritdoc/>
62+
public OpenApiOperation? Query { get => Target?.Query; }
63+
64+
/// <inheritdoc/>
65+
public IDictionary<string, OpenApiOperation>? AdditionalOperations { get => Target?.AdditionalOperations; }
66+
6167
/// <inheritdoc/>
6268
public IDictionary<string, IOpenApiExtension>? Extensions { get => Target?.Extensions; }
6369

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ internal static partial class OpenApiV3Deserializer
4141
private static readonly PatternFieldMap<OpenApiPathItem> _pathItemPatternFields =
4242
new()
4343
{
44+
{s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix + "oas-query", StringComparison.OrdinalIgnoreCase), (o, p, n, t) => o.Query = LoadOperation(n, t)},
45+
{s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix + "oas-additionalOperations", StringComparison.OrdinalIgnoreCase), (o, p, n, t) => o.AdditionalOperations = n.CreateMap(LoadOperation, t)},
4446
{s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix, StringComparison.OrdinalIgnoreCase), (o, p, n, _) => o.AddExtension(p, LoadExtension(p,n))}
4547
};
4648

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ internal static partial class OpenApiV31Deserializer
4343
private static readonly PatternFieldMap<OpenApiPathItem> _pathItemPatternFields =
4444
new()
4545
{
46+
{s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix + "oas-query", StringComparison.OrdinalIgnoreCase), (o, p, n, t) => o.Query = LoadOperation(n, t)},
47+
{s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix + "oas-additionalOperations", StringComparison.OrdinalIgnoreCase), (o, p, n, t) => o.AdditionalOperations = n.CreateMap(LoadOperation, t)},
4648
{s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix, StringComparison.OrdinalIgnoreCase), (o, p, n, _) => o.AddExtension(p, LoadExtension(p,n))}
4749
};
4850

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Net.Http;
33

44
namespace Microsoft.OpenApi.Reader.V32
@@ -36,8 +36,10 @@ internal static partial class OpenApiV32Deserializer
3636
{"patch", (o, n, t) => o.AddOperation(new HttpMethod("PATCH"), LoadOperation(n, t))},
3737
#endif
3838
{"trace", (o, n, t) => o.AddOperation(HttpMethod.Trace, LoadOperation(n, t))},
39+
{"query", (o, n, t) => o.Query = LoadOperation(n, t)},
3940
{"servers", (o, n, t) => o.Servers = n.CreateList(LoadServer, t)},
40-
{"parameters", (o, n, t) => o.Parameters = n.CreateList(LoadParameter, t)}
41+
{"parameters", (o, n, t) => o.Parameters = n.CreateList(LoadParameter, t)},
42+
{"additionalOperations", (o, n, t) => o.AdditionalOperations = n.CreateMap(LoadOperation, t)}
4143
};
4244

4345
private static readonly PatternFieldMap<OpenApiPathItem> _pathItemPatternFields =

0 commit comments

Comments
 (0)