Skip to content
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

Add support for request body path parameters in v2 #922

Merged
merged 7 commits into from
Jul 4, 2022
Merged
55 changes: 54 additions & 1 deletion src/Microsoft.OpenApi.Readers/V2/OpenApiPathItemDeserializer.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System.Collections.Generic;
baywet marked this conversation as resolved.
Show resolved Hide resolved
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers.ParseNodes;
Expand Down Expand Up @@ -32,7 +33,7 @@ internal static partial class OpenApiV2Deserializer
{
"parameters", (o, n) =>
{
o.Parameters = n.CreateList(LoadParameter);
LoadPathParameters(o,n);
}
},
};
Expand All @@ -53,5 +54,57 @@ 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)
darrelmiller marked this conversation as resolved.
Show resolved Hide resolved
{
if (opPair.Value.RequestBody == null)
{
switch (opPair.Key)
{
case OperationType.Post:
case OperationType.Put:
case OperationType.Patch:
opPair.Value.RequestBody = requestBody;
break;
}
}
darrelmiller marked this conversation as resolved.
Show resolved Hide resolved
}
Fixed Show fixed Hide fixed
}
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)
darrelmiller marked this conversation as resolved.
Show resolved Hide resolved
{
if (opPair.Value.RequestBody == null)
{
switch (opPair.Key)
{
case OperationType.Post:
case OperationType.Put:
case OperationType.Patch:
opPair.Value.RequestBody = requestBody;
break;
}
}
darrelmiller marked this conversation as resolved.
Show resolved Hide resolved
}
Fixed Show fixed Hide fixed
}
}

}
}
}
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