Skip to content
Closed
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
11 changes: 8 additions & 3 deletions src/Refitter.Core/ParameterExtractor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@ public static IEnumerable<string> GetParameters(
continue;
}

if (securityScheme.Type == OpenApiSecuritySchemeType.ApiKey
&& securityScheme.In == OpenApiSecurityApiKeyLocation.Header
&& !operationModel.Parameters.Any(p => p.Kind == OpenApiParameterKind.Header && p.IsHeader && p.Name == securityScheme.Name))
if (ShouldGenerateHeaderParameter(securityScheme, operationModel))
{
headerParameters.Add($"[Header(\"{securityScheme.Name}\")] string {ReplaceUnsafeCharacters(securityScheme.Name)}");
}
Expand Down Expand Up @@ -305,4 +303,11 @@ private static List<string> GetQueryParameters(CSharpOperationModel operationMod

return parameters;
}

private static bool ShouldGenerateHeaderParameter(OpenApiSecurityScheme securityScheme, CSharpOperationModel operationModel)
{
return securityScheme.Type == OpenApiSecuritySchemeType.ApiKey
&& securityScheme.In == OpenApiSecurityApiKeyLocation.Header
&& !operationModel.Parameters.Any(p => p.Kind == OpenApiParameterKind.Header && p.IsHeader && p.Name == securityScheme.Name);
}
}
26 changes: 17 additions & 9 deletions src/Refitter.Core/RefitInterfaceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ namespace Refitter.Core;
internal class RefitInterfaceGenerator : IRefitInterfaceGenerator
{
protected const string Separator = " ";

// HTTP Status Code Constants
private static readonly string[] SuccessStatusCodes = { "200", "201", "203", "206" };
private const string StatusCode2XX = "2XX";
private const string DefaultResponseKey = "default";

// Content Type Constants
private const string MultipartFormDataContentType = "multipart/form-data";
private const string ApplicationJsonContentType = "application/json";
Comment on lines +11 to +19

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

LGTM! Well-organized constants improve maintainability.

The extraction of HTTP status codes and content type literals into clearly-named constants reduces magic strings and improves code maintainability. The grouping with comments enhances readability.

Since these constants represent critical values used in HTTP API generation, let me verify they match common OpenAPI specifications and HTTP standards:


🌐 Web query:

What are the standard HTTP 2xx success status codes and their meanings?

💡 Result:

Standard HTTP 2xx success status codes (common official ones) and meanings:

  • 200 OK — request succeeded; payload depends on method. [1][2]
  • 201 Created — request succeeded and a new resource was created. [1][2]
  • 202 Accepted — request accepted for processing, but not completed yet. [1][2]
  • 203 Non‑Authoritative Information — response is from a transforming proxy; not authoritative. [1][2]
  • 204 No Content — request succeeded; no message body in the response. [1][2]
  • 205 Reset Content — request succeeded; client should reset document/view. [1][2]
  • 206 Partial Content — partial GET response (range requests). [1][2]
  • 207 Multi‑Status — WebDAV multi-status response (multiple independent operations). [1]
  • 208 Already Reported — WebDAV: members of a DAV binding already reported. [1]
  • 226 IM Used — server fulfilled a GET with instance-manipulations (Delta encoding). [1]

Sources: IANA HTTP Status Code Registry (official list) and the HTTP RFCs. [1][2]


Action: Add missing 2xx codes or justify their omission.
The SuccessStatusCodes array in src/Refitter.Core/RefitInterfaceGenerator.cs (lines 11–19) currently contains {"200","201","203","206"} but omits standard 2xx codes 202, 204, 205, 207, 208, 226 (per IANA/RFC); add the missing codes or document/handle intentional omissions to avoid misclassifying successful responses.

🤖 Prompt for AI Agents
In src/Refitter.Core/RefitInterfaceGenerator.cs around lines 11 to 19, the
SuccessStatusCodes array currently lists only "200","201","203","206" but omits
other standard 2xx codes (202, 204, 205, 207, 208, 226); update the array to
include the full set of IANA/RFC 2xx codes or, if certain codes are
intentionally excluded, add a clear comment explaining why and ensure any
status-checking logic treats the full 2xx range ("2XX") as successful (or
explicitly maps excluded codes to non-success) so successful responses are not
misclassified.


protected readonly RefitGeneratorSettings settings;
protected readonly OpenApiDocument document;
Expand Down Expand Up @@ -116,22 +125,21 @@ protected string GetTypeName(OpenApiOperation operation)
}

// First check for explicit success status codes
var successCodes = new[] { "200", "201", "203", "206" };
var returnTypeParameter = successCodes
var returnTypeParameter = SuccessStatusCodes
.Where(operation.Responses.ContainsKey)
.Select(code => GetTypeName(code, operation))
.FirstOrDefault();

// If no explicit success codes found, check for 2XX range
if (returnTypeParameter == null && operation.Responses.ContainsKey("2XX"))
if (returnTypeParameter == null && operation.Responses.ContainsKey(StatusCode2XX))
{
returnTypeParameter = GetTypeName("2XX", operation);
returnTypeParameter = GetTypeName(StatusCode2XX, operation);
}

// If no success codes or ranges found, check for default response
if (returnTypeParameter == null && operation.Responses.ContainsKey("default"))
if (returnTypeParameter == null && operation.Responses.ContainsKey(DefaultResponseKey))
{
returnTypeParameter = GetTypeName("default", operation);
returnTypeParameter = GetTypeName(DefaultResponseKey, operation);
}

return GetReturnType(returnTypeParameter);
Expand Down Expand Up @@ -180,7 +188,7 @@ protected string GenerateOperationName(

protected static void GenerateForMultipartFormData(CSharpOperationModel operationModel, StringBuilder code)
{
if (operationModel.Consumes.Contains("multipart/form-data"))
if (operationModel.Consumes.Contains(MultipartFormDataContentType))
{
code.AppendLine($"{Separator}{Separator}[Multipart]");
}
Expand Down Expand Up @@ -216,10 +224,10 @@ protected void GenerateHeaders(
{
var uniqueContentTypes = operations.Value.RequestBody?.Content.Keys ?? Array.Empty<string>();
var contentType =
uniqueContentTypes.FirstOrDefault(c => c.Equals("application/json", StringComparison.OrdinalIgnoreCase)) ??
uniqueContentTypes.FirstOrDefault(c => c.Equals(ApplicationJsonContentType, StringComparison.OrdinalIgnoreCase)) ??
uniqueContentTypes.FirstOrDefault();

if (!string.IsNullOrWhiteSpace(contentType) && !operationModel.Consumes.Contains("multipart/form-data"))
if (!string.IsNullOrWhiteSpace(contentType) && !operationModel.Consumes.Contains(MultipartFormDataContentType))
{
headers.Add($"\"Content-Type: {contentType}\"");
}
Expand Down
20 changes: 11 additions & 9 deletions src/Refitter.Core/RefitMultipleInterfaceByTagGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,18 @@ public override IEnumerable<GeneratedCode> GenerateCode()
: settings.Naming.InterfaceName;
ungroupedTitle = ungroupedTitle.CapitalizeFirstCharacter();

var byGroup = document.Paths
.SelectMany(x => x.Value, (k, v) => (PathItem: k, Operation: v))
.GroupBy(x => GetGroupName(x.Operation.Value, ungroupedTitle), (k, v) => new { Key = k, Combined = v });
var pathOperations = document.Paths
.SelectMany(x => x.Value, (pathItem, operation) => new { PathItem = pathItem, Operation = operation });

var byGroup = pathOperations
.GroupBy(x => GetGroupName(x.Operation.Value, ungroupedTitle));

Dictionary<string, StringBuilder> interfacesByGroup = new();
Dictionary<string, string> interfacesNamesByGroup = new();

foreach (var kv in byGroup)
foreach (var group in byGroup)
{
foreach (var op in kv.Combined)
foreach (var op in group)
{
var operations = op.Operation;
var operation = operations.Value;
Expand All @@ -46,18 +48,18 @@ public override IEnumerable<GeneratedCode> GenerateCode()
var verb = operations.Key.CapitalizeFirstCharacter();

string interfaceName = null!;
if (!interfacesByGroup.TryGetValue(kv.Key, out var sb))
if (!interfacesByGroup.TryGetValue(group.Key, out var sb))
{
interfacesByGroup[kv.Key] = sb = new StringBuilder();
interfacesByGroup[group.Key] = sb = new StringBuilder();
this.docGenerator.AppendInterfaceDocumentation(operation, sb);

interfaceName = GetInterfaceName(kv.Key);
interfaceName = GetInterfaceName(group.Key);
sb.AppendLine($$"""
{{GenerateInterfaceDeclaration(interfaceName)}}
{{Separator}}{
""");

interfacesNamesByGroup[kv.Key] = interfaceName;
interfacesNamesByGroup[group.Key] = interfaceName;
}

var operationName = GetOperationName(interfaceName, op.PathItem.Key, operations.Key, operation);
Expand Down
Loading
Loading