Skip to content
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 @@ -195,7 +195,7 @@ public string JsonConvertersArrayCode
private bool RequiresJsonExceptionConverter =>
_settings.CSharpGeneratorSettings.JsonLibrary == CSharpJsonLibrary.NewtonsoftJson &&
_settings.CSharpGeneratorSettings.ExcludedTypeNames?.Contains("JsonExceptionConverter") != true &&
_document.Operations.Any(o => o.Operation.ActualResponses.Any(r => r.Value.Schema?.InheritsSchema(_exceptionSchema) == true));
_document.Operations.Any(o => o.Operation.HasActualResponse((_, response) => response.Schema?.InheritsSchema(_exceptionSchema) == true));

private static readonly string[] jsonExceptionConverterArray = ["JsonExceptionConverter"];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public CSharpFileTemplateModel(
public string ExceptionModelClass => JsonExceptionTypes.FirstOrDefault(t => t != "Exception") ?? "Exception";

private IEnumerable<string> JsonExceptionTypes => _document.Operations
.SelectMany(o => o.Operation.ActualResponses.Where(r => r.Value.Schema?.InheritsSchema(_resolver.ExceptionSchema) == true).Select(r => new { o.Operation, Response = r.Value }))
.SelectMany(o => o.Operation.GetActualResponses((_, response) => response.Schema?.InheritsSchema(_resolver.ExceptionSchema) == true).Select(r => new { o.Operation, Response = r.Value }))
.Select(t => _generator.GetTypeName(t.Response.Schema, t.Response.IsNullable(_settings.CSharpGeneratorSettings.SchemaType), "Response"));

/// <summary>Gets a value indicating whether the generated code requires the FileParameter type.</summary>
Expand All @@ -99,7 +99,7 @@ public CSharpFileTemplateModel(
/// <summary>Gets a value indicating whether [generate file response class].</summary>
public bool GenerateFileResponseClass =>
_settings.CSharpGeneratorSettings.ExcludedTypeNames?.Contains("FileResponse") != true &&
_document.Operations.Any(o => o.Operation.ActualResponses.Any(r => r.Value.IsBinary(o.Operation)));
_document.Operations.Any(o => o.Operation.HasActualResponse((_, response) => response.IsBinary(o.Operation)));

/// <summary>Gets or sets a value indicating whether to generate exception classes (default: true).</summary>
public bool GenerateExceptionClasses => _settings is CSharpClientGeneratorSettings { GenerateExceptionClasses: true };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,14 +143,14 @@ public override string ExceptionType
{
get
{
if (_operation.ActualResponses.Count(r => !HttpUtilities.IsSuccessStatusCode(r.Key)) != 1)
var response = _operation.GetActualResponse((code, _) => !HttpUtilities.IsSuccessStatusCode(code));
if (response == null)
{
return "System.Exception";
}

var response = _operation.ActualResponses.Single(r => !HttpUtilities.IsSuccessStatusCode(r.Key));
var isNullable = response.Value.IsNullable(_settings.CodeGeneratorSettings.SchemaType);
return _generator.GetTypeName(response.Value.Schema, isNullable, "Exception");
var isNullable = response.IsNullable(_settings.CodeGeneratorSettings.SchemaType);
return _generator.GetTypeName(response.Schema, isNullable, "Exception");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

<ItemGroup>
<EmbeddedResource Include="Templates\*.liquid" />
<Compile Include="..\NSwag.Core\Polyfills.cs" Link="Polyfills.cs" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public IEnumerable<string> ResponseClassNames
public bool RequiresFileResponseInterface =>
!Framework.IsJQuery &&
!_settings.TypeScriptGeneratorSettings.ExcludedTypeNames.Contains("FileResponse") &&
_document.Operations.Any(o => o.Operation.ActualResponses.Any(r => r.Value.IsBinary(o.Operation)));
_document.Operations.Any(o => o.Operation.HasActualResponse((_, response) => response.IsBinary(o.Operation)));

/// <summary>Gets a value indicating whether the client functions are required.</summary>
public bool RequiresClientFunctions =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,13 @@ public override string ExceptionType
{
get
{
if (_operation.ActualResponses.All(r => HttpUtilities.IsSuccessStatusCode(r.Key)))
var actualResponses = _operation.ActualResponses;
if (actualResponses.All(static r => HttpUtilities.IsSuccessStatusCode(r.Key)))
{
return "string";
}

return string.Join(" | ", _operation.ActualResponses
return string.Join(" | ", actualResponses
.Where(r => !HttpUtilities.IsSuccessStatusCode(r.Key) && r.Value.Schema != null)
.Select(r => _generator.GetTypeName(r.Value.Schema, r.Value.IsNullable(_settings.CodeGeneratorSettings.SchemaType), "Exception"))
.Concat(["string"]));
Expand Down
31 changes: 3 additions & 28 deletions src/NSwag.CodeGeneration/Models/OperationModelBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ protected OperationModelBase(JsonSchema exceptionSchema, OpenApiOperation operat
_generator = generator;
_settings = settings;

var responses = _operation.ActualResponses
var responses = _operation.GetActualResponses(static (_, _) => true)
.Select(response => CreateResponseModel(operation, response.Key, response.Value, exceptionSchema, generator, resolver, settings))
.ToList();

Expand Down Expand Up @@ -295,21 +295,7 @@ public string Produces
}

/// <summary>Gets a value indicating whether a file response is expected from one of the responses.</summary>
public bool IsFile
{
get
{
foreach (var r in _operation.ActualResponses)
{
if (r.Value.IsBinary(_operation))
{
return true;
}
}

return false;
}
}
public bool IsFile => _operation.HasActualResponse((_, response) => response.IsBinary(_operation));

/// <summary>Gets a value indicating whether to wrap the response of this operation.</summary>
public bool WrapResponse => _settings.WrapResponses && (
Expand All @@ -324,18 +310,7 @@ public bool IsFile
/// <returns>The response.</returns>
protected KeyValuePair<string, OpenApiResponse> GetSuccessResponse()
{
if (_operation.ActualResponses.TryGetValue("200", out var response200))
{
return new KeyValuePair<string, OpenApiResponse>("200", response200);
}

var response = _operation.ActualResponses.FirstOrDefault(static r => HttpUtilities.IsSuccessStatusCode(r.Key));
if (response.Value != null)
{
return new KeyValuePair<string, OpenApiResponse>(response.Key, response.Value);
}

return new KeyValuePair<string, OpenApiResponse>("default", _operation.ActualResponses.FirstOrDefault(r => r.Key == "default").Value);
return _operation.GetSuccessResponse();
}

/// <summary>Gets the name of the parameter variable.</summary>
Expand Down
9 changes: 9 additions & 0 deletions src/NSwag.Core/NSwag.Core.csproj
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net462;netstandard2.0</TargetFrameworks>
<RootNamespace>NSwag</RootNamespace>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="NJsonSchema" Version="11.2.0" />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="NSwag.CodeGeneration, PublicKey=0024000004800000940000000602000000240000525341310004000001000100eba55a5211cd3198b5ba4b96c5cb70484b376ce083664d47dcb7439a97c3368a26ea54de3ec6d90d2899d39c4e3b65cb4ac7ef8ba5c9ded7c8aa6538757b31291624e96f374c23fdbeeaa85dfe841ab6afffbd3593d2a40c96a0f0888f25d7bd9361611db9450041b57776d33e3acb90794254c428251ddd63aa329d86ec809f" />
<InternalsVisibleTo Include="NSwag.CodeGeneration.CSharp, PublicKey=0024000004800000940000000602000000240000525341310004000001000100eba55a5211cd3198b5ba4b96c5cb70484b376ce083664d47dcb7439a97c3368a26ea54de3ec6d90d2899d39c4e3b65cb4ac7ef8ba5c9ded7c8aa6538757b31291624e96f374c23fdbeeaa85dfe841ab6afffbd3593d2a40c96a0f0888f25d7bd9361611db9450041b57776d33e3acb90794254c428251ddd63aa329d86ec809f" />
<InternalsVisibleTo Include="NSwag.CodeGeneration.TypeScript, PublicKey=0024000004800000940000000602000000240000525341310004000001000100eba55a5211cd3198b5ba4b96c5cb70484b376ce083664d47dcb7439a97c3368a26ea54de3ec6d90d2899d39c4e3b65cb4ac7ef8ba5c9ded7c8aa6538757b31291624e96f374c23fdbeeaa85dfe841ab6afffbd3593d2a40c96a0f0888f25d7bd9361611db9450041b57776d33e3acb90794254c428251ddd63aa329d86ec809f" />
</ItemGroup>

</Project>
12 changes: 6 additions & 6 deletions src/NSwag.Core/OpenApiDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -268,9 +268,9 @@ public void GenerateOperationIds()
{
if (group.Count() > 1)
{
var collections = group.Where(o => o.Operation.ActualResponses.Any(r =>
HttpUtilities.IsSuccessStatusCode(r.Key) &&
r.Value.Schema?.ActualSchema.Type == JsonObjectType.Array));
var collections = group.Where(o => o.Operation.HasActualResponse(static (code, response) =>
HttpUtilities.IsSuccessStatusCode(code) &&
response.Schema?.ActualSchema.Type == JsonObjectType.Array));
// if we have just collections, adding All will not help in discrimination
if (collections.Count() == group.Count())
{
Expand All @@ -279,9 +279,9 @@ public void GenerateOperationIds()

foreach (var o in group)
{
var isCollection = o.Operation.ActualResponses.Any(r =>
HttpUtilities.IsSuccessStatusCode(r.Key) &&
r.Value.Schema?.ActualSchema.Type == JsonObjectType.Array);
var isCollection = o.Operation.HasActualResponse(static (code, response) =>
HttpUtilities.IsSuccessStatusCode(code) &&
response.Schema?.ActualSchema.Type == JsonObjectType.Array);

if (isCollection)
{
Expand Down
85 changes: 80 additions & 5 deletions src/NSwag.Core/OpenApiOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Runtime.CompilerServices;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using NJsonSchema;
Expand All @@ -24,6 +25,8 @@ public class OpenApiOperation : JsonExtensionObject
private bool _disableRequestBodyUpdate;
private bool _disableBodyParameterUpdate;

private readonly ObservableDictionary<string, OpenApiResponse> _responses;

/// <summary>Initializes a new instance of the <see cref="OpenApiPathItem"/> class.</summary>
public OpenApiOperation()
{
Expand All @@ -42,14 +45,14 @@ public OpenApiOperation()
Parameters = parameters;

var responses = new ObservableDictionary<string, OpenApiResponse>();
_responses = responses;
responses.CollectionChanged += (sender, args) =>
{
foreach (var response in Responses.Values)
foreach (var pair in _responses)
{
response.Parent = this;
pair.Value.Parent = this;
}
};
Responses = responses;
}

/// <summary>Gets the parent operations list.</summary>
Expand Down Expand Up @@ -132,7 +135,7 @@ public IReadOnlyList<OpenApiParameter> ActualParameters

/// <summary>Gets or sets the HTTP Status Code/Response pairs.</summary>
[JsonProperty(PropertyName = "responses", Order = 10, Required = Required.Always, DefaultValueHandling = DefaultValueHandling.Ignore)]
public IDictionary<string, OpenApiResponse> Responses { get; }
public IDictionary<string, OpenApiResponse> Responses => _responses;

/// <summary>Gets or sets the schemes.</summary>
[JsonProperty(PropertyName = "schemes", Order = 11, DefaultValueHandling = DefaultValueHandling.Ignore, ItemConverterType = typeof(StringEnumConverter))]
Expand Down Expand Up @@ -172,7 +175,79 @@ public IReadOnlyList<OpenApiParameter> ActualParameters

/// <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);
public IReadOnlyDictionary<string, OpenApiResponse> ActualResponses
{
get
{
var dictionary = new Dictionary<string, OpenApiResponse>(_responses.Count);
foreach (var response in _responses)
{
dictionary.Add(response.Key, response.Value.ActualResponse);
}
return dictionary;
}
}

// helpers to avoid extra allocations
internal IEnumerable<KeyValuePair<string, OpenApiResponse>> GetActualResponses(Func<string, OpenApiResponse, bool> predicate)
{
foreach (var pair in _responses)
{
if (predicate(pair.Key, pair.Value.ActualResponse))
{
yield return new KeyValuePair<string, OpenApiResponse>(pair.Key, pair.Value.ActualResponse);
}
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool HasActualResponse(Func<string, OpenApiResponse, bool> predicate) => GetActualResponse(predicate) != null;

internal OpenApiResponse GetActualResponse(Func<string, OpenApiResponse, bool> predicate)
{
foreach (var pair in _responses)
{
if (predicate(pair.Key, pair.Value.ActualResponse))
{
return pair.Value.ActualResponse;
}
}

return null;
}

internal KeyValuePair<string,OpenApiResponse> GetSuccessResponse()
{
KeyValuePair<string, OpenApiResponse> firstOtherSuccessResponse = new(null, null);
OpenApiResponse defaultResponse = null;
foreach (var pair in _responses)
{
var code = pair.Key;
var actualResponse = pair.Value.ActualResponse;

if (code == "200")
{
// 200 is the default response
return new KeyValuePair<string, OpenApiResponse>(code, actualResponse);
}

if (firstOtherSuccessResponse.Key == null && HttpUtilities.IsSuccessStatusCode(code))
{
firstOtherSuccessResponse = new KeyValuePair<string, OpenApiResponse>(code, actualResponse);
}
else if (code == "default")
{
defaultResponse = actualResponse;
}
}

if (firstOtherSuccessResponse.Key != null)
{
return firstOtherSuccessResponse;
}

return new KeyValuePair<string, OpenApiResponse>("default", defaultResponse);
}

/// <summary>Gets the actual security description, either from the operation or from the <see cref="OpenApiDocument"/>.</summary>
[JsonIgnore]
Expand Down
Loading