Skip to content

Commit c0d89d1

Browse files
Copilotbaywet
andauthored
feat(library): add V2RequestBodyToBodyParameterPolicy for path conversion
Agent-Logs-Url: https://github.com/microsoft/OpenAPI.NET/sessions/3e249ace-8d41-41c9-af08-288d036a4def Co-authored-by: baywet <7905502+baywet@users.noreply.github.com>
1 parent c46f13c commit c0d89d1

2 files changed

Lines changed: 72 additions & 6 deletions

File tree

src/Microsoft.OpenApi/Services/OpenApiPathHelper.cs

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public static class OpenApiPathHelper
2121
[
2222
// Order matters: null policies first, then transformations.
2323
new V2UnsupportedPathPolicy(),
24+
new V2RequestBodyToBodyParameterPolicy(),
2425
new V2ComponentRenamePolicy(),
2526
new V2ResponseContentUnwrappingPolicy(),
2627
new V2HeaderSchemaUnwrappingPolicy(),
@@ -187,7 +188,6 @@ internal sealed class V2UnsupportedPathPolicy : IOpenApiPathRepresentationPolicy
187188
OpenApiConstants.Servers,
188189
OpenApiConstants.Callbacks,
189190
OpenApiConstants.Links,
190-
OpenApiConstants.RequestBody,
191191
};
192192

193193
public bool TryGetVersionedPath(string[] segments, out string? result)
@@ -238,6 +238,69 @@ public bool TryGetVersionedPath(string[] segments, out string? result)
238238
}
239239
}
240240

241+
/// <summary>
242+
/// Maps v3 <c>requestBody</c> paths to their v2 <c>in:body</c> parameter equivalents.
243+
/// <list type="bullet">
244+
/// <item><c>.../requestBody</c> → <c>.../parameters/0</c></item>
245+
/// <item><c>.../requestBody/content/{mediaType}/schema/**</c> → <c>.../parameters/0/schema/**</c></item>
246+
/// <item><c>.../requestBody/{prop}</c> → <c>.../parameters/0/{prop}</c></item>
247+
/// </list>
248+
/// </summary>
249+
internal sealed class V2RequestBodyToBodyParameterPolicy : IOpenApiPathRepresentationPolicy
250+
{
251+
public bool TryGetVersionedPath(string[] segments, out string? result)
252+
{
253+
result = null;
254+
255+
// Find requestBody segment
256+
var requestBodyIndex = -1;
257+
for (var i = 0; i < segments.Length; i++)
258+
{
259+
if (string.Equals(segments[i], OpenApiConstants.RequestBody, StringComparison.Ordinal))
260+
{
261+
requestBodyIndex = i;
262+
break;
263+
}
264+
}
265+
266+
if (requestBodyIndex < 0)
267+
{
268+
return false;
269+
}
270+
271+
// +1 because requestBody (1 segment) is replaced by parameters/0 (2 segments)
272+
var buffer = new string[segments.Length + 1];
273+
var written = 0;
274+
275+
// Copy segments before requestBody
276+
for (var i = 0; i < requestBodyIndex; i++)
277+
{
278+
buffer[written++] = segments[i];
279+
}
280+
281+
// Replace requestBody with parameters/0
282+
buffer[written++] = OpenApiConstants.Parameters;
283+
buffer[written++] = "0";
284+
285+
// Skip content/{mediaType} if both segments immediately follow requestBody
286+
var next = requestBodyIndex + 1;
287+
if (next + 1 < segments.Length &&
288+
string.Equals(segments[next], OpenApiConstants.Content, StringComparison.Ordinal))
289+
{
290+
next += 2; // skip "content" and "{mediaType}"
291+
}
292+
293+
// Copy remaining segments
294+
for (var i = next; i < segments.Length; i++)
295+
{
296+
buffer[written++] = segments[i];
297+
}
298+
299+
result = OpenApiPathHelper.BuildPath(buffer, written);
300+
return true;
301+
}
302+
}
303+
241304
/// <summary>
242305
/// Returns null for paths that have no equivalent in OpenAPI v3.0.
243306
/// Covers: webhooks (added in v3.1).

test/Microsoft.OpenApi.Tests/Services/OpenApiPathHelperTests.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,15 @@ public void V2_Links_ReturnsNull(string path)
137137
}
138138

139139
[Theory]
140-
[InlineData("#/paths/~1items/post/requestBody")]
141-
[InlineData("#/paths/~1items/post/requestBody/content/application~1json/schema")]
142-
public void V2_InlineRequestBody_ReturnsNull(string path)
140+
[InlineData("#/paths/~1items/post/requestBody", "#/paths/~1items/post/parameters/0")]
141+
[InlineData("#/paths/~1items/post/requestBody/content/application~1json/schema", "#/paths/~1items/post/parameters/0/schema")]
142+
[InlineData("#/paths/~1items/post/requestBody/content/application~1json/schema/properties/id", "#/paths/~1items/post/parameters/0/schema/properties/id")]
143+
[InlineData("#/paths/~1items/post/requestBody/description", "#/paths/~1items/post/parameters/0/description")]
144+
[InlineData("#/paths/~1items/post/requestBody/required", "#/paths/~1items/post/parameters/0/required")]
145+
public void V2_InlineRequestBody_MappedToBodyParameter(string input, string expected)
143146
{
144-
var result = OpenApiPathHelper.GetVersionedPath(path, OpenApiSpecVersion.OpenApi2_0);
145-
Assert.Null(result);
147+
var result = OpenApiPathHelper.GetVersionedPath(input, OpenApiSpecVersion.OpenApi2_0);
148+
Assert.Equal(expected, result);
146149
}
147150

148151
[Theory]

0 commit comments

Comments
 (0)