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

Support $ref in requestBody #4917

Merged
merged 5 commits into from
Jul 17, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public CSharpFileTemplateModel(
public bool RequiresFileParameterType =>
_settings.CSharpGeneratorSettings.ExcludedTypeNames?.Contains("FileParameter") != true &&
(_document.Operations.Any(o => o.Operation.ActualParameters.Any(p => p.ActualTypeSchema.IsBinary)) ||
_document.Operations.Any(o => o.Operation?.RequestBody?.Content?.Any(c => c.Value.Schema?.IsBinary == true ||
_document.Operations.Any(o => o.Operation?.ActualRequestBody?.Content?.Any(c => c.Value.Schema?.IsBinary == true ||
c.Value.Schema?.ActualSchema.ActualProperties.Any(p => p.Value.IsBinary ||
p.Value.Item?.IsBinary == true ||
p.Value.Items.Any(i => i.IsBinary)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public IEnumerable<string> ResponseClassNames
public bool RequiresFileParameterInterface =>
!_settings.TypeScriptGeneratorSettings.ExcludedTypeNames.Contains("FileParameter") &&
(_document.Operations.Any(o => o.Operation.ActualParameters.Any(p => p.ActualTypeSchema.IsBinary)) ||
_document.Operations.Any(o => o.Operation?.RequestBody?.Content?.Any(c => c.Value.Schema?.IsBinary == true ||
_document.Operations.Any(o => o.Operation?.ActualRequestBody?.Content?.Any(c => c.Value.Schema?.IsBinary == true ||
c.Value.Schema?.ActualProperties.Any(p => p.Value.IsBinary ||
p.Value.Item?.IsBinary == true ||
p.Value.Items.Any(i => i.IsBinary)
Expand Down
8 changes: 4 additions & 4 deletions src/NSwag.CodeGeneration/Models/OperationModelBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ public TParameterModel ContentParameter
/// <summary>Gets a value indicating whether the operation consumes 'application/x-www-form-urlencoded'.</summary>
public bool ConsumesFormUrlEncoded =>
_operation.ActualConsumes?.Any(c => c == "application/x-www-form-urlencoded") == true ||
_operation.RequestBody?.Content.Any(mt => mt.Key == "application/x-www-form-urlencoded") == true;
_operation.ActualRequestBody?.Content.Any(mt => mt.Key == "application/x-www-form-urlencoded") == true;

/// <summary>Gets the form parameters.</summary>
public IEnumerable<TParameterModel> FormParameters => Parameters.Where(p => p.Kind == OpenApiParameterKind.FormData);
Expand Down Expand Up @@ -259,7 +259,7 @@ public string Consumes
}

return _operation.ActualConsumes?.FirstOrDefault() ??
_operation.RequestBody?.Content.Keys.FirstOrDefault() ??
_operation.ActualRequestBody?.Content.Keys.FirstOrDefault() ??
"application/json";
}
}
Expand Down Expand Up @@ -350,8 +350,8 @@ protected IList<OpenApiParameter> GetActualParameters()
.ToList();

var formDataSchema =
_operation?.RequestBody?.Content?.ContainsKey("multipart/form-data") == true ?
_operation.RequestBody.Content["multipart/form-data"]?.Schema.ActualSchema: null;
_operation?.ActualRequestBody?.Content?.ContainsKey("multipart/form-data") == true ?
_operation.ActualRequestBody.Content["multipart/form-data"]?.Schema.ActualSchema: null;

if (formDataSchema != null && formDataSchema.ActualProperties.Count > 0)
{
Expand Down
9 changes: 9 additions & 0 deletions src/NSwag.Core.Tests/Serialization/ExternalReferenceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ public async Task When_file_contains_path_reference_to_another_file_it_is_loaded
Assert.Equal("External path", document.Paths.First().Value.ActualPathItem.Values.First().Description);
}

[Fact]
public async Task When_file_contains_requestBody_reference_to_another_file_it_is_loaded()
{
var document = await OpenApiDocument.FromFileAsync("TestFiles/requestBody-reference.json");

Assert.NotNull(document);
Assert.Equal("External request body", document.Paths.First().Value.Values.First().RequestBody.ActualRequestBody.Description);
}

[Fact]
public async Task When_file_contains_response_reference_to_another_file_it_is_loaded()
{
Expand Down
13 changes: 13 additions & 0 deletions src/NSwag.Core.Tests/TestFiles/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@
"additionalProperties": true
}
},
"requestBodies": {
"TestRequest": {
"description": "External request body",
"required": true,
"content": {
"text/json": {
"schema": {
"$ref": "#/components/schemas/TestObject"
}
}
}
}
},
"responses": {
"TestResponse": {
"description": "External response",
Expand Down
14 changes: 14 additions & 0 deletions src/NSwag.Core.Tests/TestFiles/requestBody-reference.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"openapi": "3.0.2",
"paths": {
"/test": {
"get": {
"description": "Test path",
"requestBody": {
"$ref": "./common.json#/components/requestBodies/TestRequest"
},
"responses": {}
}
}
}
}
14 changes: 14 additions & 0 deletions src/NSwag.Core/OpenApiComponents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ public OpenApiComponents(OpenApiDocument document)
};
Schemas = schemas;

var requestBodies = new ObservableDictionary<string, OpenApiRequestBody>();
requestBodies.CollectionChanged += (sender, args) =>
{
foreach (var path in RequestBodies.Values)
{
path.Parent = document;
}
};
RequestBodies = requestBodies;

var responses = new ObservableDictionary<string, OpenApiResponse>();
responses.CollectionChanged += (sender, args) =>
{
Expand Down Expand Up @@ -86,6 +96,10 @@ public OpenApiComponents(OpenApiDocument document)
[JsonProperty(PropertyName = "schemas", DefaultValueHandling = DefaultValueHandling.Ignore)]
public IDictionary<string, JsonSchema> Schemas { get; }

/// <summary>Gets or sets the responses which can be used for all operations.</summary>
[JsonProperty(PropertyName = "requestBodies", DefaultValueHandling = DefaultValueHandling.Ignore)]
public IDictionary<string, OpenApiRequestBody> RequestBodies { get; }

/// <summary>Gets or sets the responses which can be used for all operations.</summary>
[JsonProperty(PropertyName = "responses", DefaultValueHandling = DefaultValueHandling.Ignore)]
public IDictionary<string, OpenApiResponse> Responses { get; }
Expand Down
4 changes: 2 additions & 2 deletions src/NSwag.Core/OpenApiMediaType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public JsonSchema Schema
set
{
_schema = value;
Parent?.Parent?.UpdateBodyParameter();
Parent?.ParentOperation?.UpdateBodyParameter();
}
}

Expand All @@ -41,7 +41,7 @@ public object Example
set
{
_example = value;
Parent?.Parent?.UpdateBodyParameter();
Parent?.ParentOperation?.UpdateBodyParameter();
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/NSwag.Core/OpenApiOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ public IReadOnlyList<OpenApiParameter> ActualParameters
[JsonIgnore]
public ICollection<OpenApiSchema> ActualSchemes => Schemes ?? Parent.Parent.Schemes;

/// <summary>Gets the response body and dereferences it if necessary.</summary>
[JsonIgnore]
public OpenApiRequestBody ActualRequestBody => RequestBody?.ActualRequestBody;

/// <summary>Gets the responses from the operation and from the <see cref="OpenApiDocument"/> and dereferences them if necessary.</summary>
[JsonIgnore]
public IReadOnlyDictionary<string, OpenApiResponse> ActualResponses => Responses.ToDictionary(t => t.Key, t => t.Value.ActualResponse);
Expand Down
6 changes: 3 additions & 3 deletions src/NSwag.Core/OpenApiParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ public bool IsXmlBodyParameter
var parent = Parent as OpenApiOperation;
var consumes = parent?.ActualConsumes?.Any() == true ?
parent.ActualConsumes :
parent?.RequestBody?.Content.Keys;
parent?.ActualRequestBody?.Content.Keys;

return consumes?.Any() == true &&
consumes.Any(p => p.Contains("application/xml")) &&
Expand Down Expand Up @@ -256,7 +256,7 @@ public bool IsBinaryBodyParameter
}
else
{
var consumes = parent?.RequestBody?.Content;
var consumes = parent?.ActualRequestBody?.Content;
return (consumes?.Any(p => p.Key == "multipart/form-data") == true ||
consumes?.Any(p => p.Value.Schema?.IsBinary != false) == true) &&
consumes.Any(p => p.Key.Contains("*/*") && p.Value.Schema?.IsBinary != true) == false &&
Expand Down Expand Up @@ -286,7 +286,7 @@ public bool HasBinaryBodyWithMultipleMimeTypes
}
else
{
var consumes = parent?.RequestBody?.Content;
var consumes = parent?.ActualRequestBody?.Content;
return consumes?.Any() == true &&
(consumes.Count() > 1 ||
consumes.Any(p => p.Key.Contains("*")));
Expand Down
32 changes: 25 additions & 7 deletions src/NSwag.Core/OpenApiRequestBody.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@

using System.Collections.Generic;
using Newtonsoft.Json;
using NJsonSchema.References;
using NSwag.Collections;

namespace NSwag
{
/// <summary>The OpenApi request body (OpenAPI only).</summary>
public class OpenApiRequestBody
public class OpenApiRequestBody : JsonReferenceBase<OpenApiRequestBody>, IJsonReference
{
private string _name;
private bool _isRequired;
Expand All @@ -31,13 +32,20 @@ public OpenApiRequestBody()
mediaType.Parent = this;
}

Parent?.UpdateBodyParameter();
ParentOperation?.UpdateBodyParameter();
};
Content = content;
}

[JsonIgnore]
internal OpenApiOperation Parent { get; set; }
internal object Parent { get; set; }

[JsonIgnore]
internal OpenApiOperation ParentOperation => Parent as OpenApiOperation;

/// <summary>Gets the actual request body, either this or the referenced request body.</summary>
[JsonIgnore]
public OpenApiRequestBody ActualRequestBody => Reference ?? this;

/// <summary>Gets or sets the name.</summary>
[JsonProperty(PropertyName = "x-name", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
Expand All @@ -47,7 +55,7 @@ public string Name
set
{
_name = value;
Parent?.UpdateBodyParameter();
ParentOperation?.UpdateBodyParameter();
}
}

Expand All @@ -59,7 +67,7 @@ public string Description
set
{
_description = value;
Parent?.UpdateBodyParameter();
ParentOperation?.UpdateBodyParameter();
}
}

Expand All @@ -75,7 +83,7 @@ public bool IsRequired
set
{
_isRequired = value;
Parent?.UpdateBodyParameter();
ParentOperation?.UpdateBodyParameter();
}
}

Expand All @@ -87,12 +95,22 @@ public int? Position
set
{
_position = value;
Parent?.UpdateBodyParameter();
ParentOperation?.UpdateBodyParameter();
}
}

/// <summary>Gets the actual name of the request body parameter.</summary>
[JsonIgnore]
public string ActualName => string.IsNullOrEmpty(Name) ? "body" : Name;

#region Implementation of IJsonReference

[JsonIgnore]
IJsonReference IJsonReference.ActualObject => ActualRequestBody;

[JsonIgnore]
object IJsonReference.PossibleRoot => ParentOperation?.Parent?.Parent;

#endregion
}
}
Loading