Skip to content

Add support for request body path parameters in v2 #922

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion src/Microsoft.OpenApi.Readers/V2/OpenApiPathItemDeserializer.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers.ParseNodes;
Expand Down Expand Up @@ -32,7 +34,7 @@ internal static partial class OpenApiV2Deserializer
{
"parameters", (o, n) =>
{
o.Parameters = n.CreateList(LoadParameter);
LoadPathParameters(o,n);
}
},
};
Expand All @@ -53,5 +55,51 @@ public static OpenApiPathItem LoadPathItem(ParseNode node)

return pathItem;
}

private static void LoadPathParameters(OpenApiPathItem pathItem, ParseNode node)
{
node.Context.SetTempStorage(TempStorageKeys.BodyParameter, null);
node.Context.SetTempStorage(TempStorageKeys.FormParameters, null);

pathItem.Parameters = node.CreateList(LoadParameter);

// Build request body based on information determined while parsing OpenApiOperation
var bodyParameter = node.Context.GetFromTempStorage<OpenApiParameter>(TempStorageKeys.BodyParameter);
if (bodyParameter != null)
{
var requestBody = CreateRequestBody(node.Context, bodyParameter);
foreach(var opPair in pathItem.Operations.Where(x => x.Value.RequestBody is null))
{
switch (opPair.Key)
{
case OperationType.Post:
case OperationType.Put:
case OperationType.Patch:
opPair.Value.RequestBody = requestBody;
break;
}
}
}
else
{
var formParameters = node.Context.GetFromTempStorage<List<OpenApiParameter>>(TempStorageKeys.FormParameters);
if (formParameters != null)
{
var requestBody = CreateFormBody(node.Context, formParameters);
foreach (var opPair in pathItem.Operations.Where(x => x.Value.RequestBody is null))
{
switch (opPair.Key)
{
case OperationType.Post:
case OperationType.Put:
case OperationType.Patch:
opPair.Value.RequestBody = requestBody;
break;
}
}
}
}

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
</PropertyGroup>
<ItemGroup>
<None Remove="V2Tests\Samples\ComponentRootReference.json" />
<None Remove="V2Tests\Samples\OpenApiPathItem\pathItemWithBodyPathParameter.yaml" />
<None Remove="V2Tests\Samples\OpenApiPathItem\pathItemWithFormDataPathParameter.yaml" />
<None Remove="V3Tests\Samples\OpenApiWorkspace\TodoComponents.yaml" />
<None Remove="V3Tests\Samples\OpenApiWorkspace\TodoMain.yaml" />
</ItemGroup>
Expand Down Expand Up @@ -85,6 +87,12 @@
<EmbeddedResource Include="V2Tests\Samples\OpenApiParameter\queryParameter.yaml">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="V2Tests\Samples\OpenApiPathItem\pathItemWithFormDataPathParameter.yaml">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="V2Tests\Samples\OpenApiPathItem\pathItemWithBodyPathParameter.yaml">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="V2Tests\Samples\OpenApiPathItem\basicPathItemWithFormData.yaml">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using FluentAssertions;
using Microsoft.OpenApi.Extensions;
Expand Down Expand Up @@ -253,10 +254,50 @@ public void ParseBasicPathItemWithFormDataShouldSucceed()
}

// Act
var operation = OpenApiV2Deserializer.LoadPathItem(node);
var pathItem = OpenApiV2Deserializer.LoadPathItem(node);

// Assert
operation.Should().BeEquivalentTo(_basicPathItemWithFormData);
pathItem.Should().BeEquivalentTo(_basicPathItemWithFormData);
}

[Fact]
public void ParsePathItemWithFormDataPathParameterShouldSucceed()
{
// Arrange
MapNode node;
using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "pathItemWithFormDataPathParameter.yaml")))
{
node = TestHelper.CreateYamlMapNode(stream);
}

// Act
var pathItem = OpenApiV2Deserializer.LoadPathItem(node);

// Assert
// FormData parameters at in the path level are pushed into Operation request bodies.
Assert.True(pathItem.Operations[OperationType.Put].RequestBody != null);
Assert.True(pathItem.Operations[OperationType.Post].RequestBody != null);
Assert.Equal(2, pathItem.Operations.Count(o => o.Value.RequestBody != null));
}
[Fact]
public void ParsePathItemBodyDataPathParameterShouldSucceed()
{
// Arrange
MapNode node;
using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "pathItemWithBodyPathParameter.yaml")))
{
node = TestHelper.CreateYamlMapNode(stream);
}

// Act
var pathItem = OpenApiV2Deserializer.LoadPathItem(node);

// Assert
// FormData parameters at in the path level are pushed into Operation request bodies.
Assert.True(pathItem.Operations[OperationType.Put].RequestBody != null);
Assert.True(pathItem.Operations[OperationType.Post].RequestBody != null);
Assert.Equal(2, pathItem.Operations.Count(o => o.Value.RequestBody != null));
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
put:
summary: Puts a pet in the store with form data
description: ""
responses:
'200':
description: Pet updated.
'405':
description: Invalid input
post:
summary: Posts a pet in the store with form data
description: ""
responses:
'200':
description: Pet updated.
parameters:
- name: petId
in: path
description: ID of pet that needs to be updated
required: true
schema:
type: string
- name: name
in: body
description: Updated pet body
required: true
type: object
properties:
name:
type: string
status:
type: string
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
put:
summary: Puts a pet in the store with form data
description: ""
responses:
'200':
description: Pet updated.
'405':
description: Invalid input
x-http-tests:
- parameterValues:
petId: 10
name: Milo
status: Happy
expectedRequest:
href: /pathitem-form-parameter/10
headers:
Content-Type: multipart/form-data
content: name=Milo&status=Happy
post:
summary: Posts a pet in the store with form data
description: ""
responses:
'200':
description: Pet updated.
parameters:
- name: petId
in: path
description: ID of pet that needs to be updated
required: true
schema:
type: string
- name: name
in: formData
description: Updated name of the pet
required: true
type: string
- name: status
in: formData
description: Updated status of the pet
required: false
type: string