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

Output filename customization #200

Merged
merged 18 commits into from
Oct 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
6bd7c73
Added camel case to JSON serialization settings
christianhelle Oct 12, 2023
3e7b5e5
Remove unnecessary JsonPropertyName attributes
christianhelle Oct 12, 2023
0088ea9
Add OutputFilename property to RefitGeneratorSettings
christianhelle Oct 12, 2023
d8d00b0
Update source generator to use OutputFilename setting
christianhelle Oct 12, 2023
fa2e6f6
Rename auto-generated file for better organization
christianhelle Oct 12, 2023
ef8ef83
Add output path configuration options for RefitGenerator
christianhelle Oct 12, 2023
5b3c1a7
Refactor directory check for outputPath
christianhelle Oct 12, 2023
901f917
Add outputFolder and outputFilename to petstore.refitter
christianhelle Oct 12, 2023
51828fd
Update assertion in generator test
christianhelle Oct 13, 2023
2c3807e
Refactored validation logic into SettingsValidator class
christianhelle Oct 13, 2023
24fc27f
"Added `outputFilename` option to configuration and updated documenta…
christianhelle Oct 24, 2023
b7de4ee
Remove CommandContext from SettingsValidator
christianhelle Oct 26, 2023
37ebd57
Make ValidateFileExistence method static
christianhelle Oct 27, 2023
18a5f17
Make ValidateOperationNameAndUrl method static
christianhelle Oct 27, 2023
1853e6d
Make ValidateFileAndOutputSettings method static
christianhelle Oct 27, 2023
6f3a8cc
Make ValidateFilePath method static
christianhelle Oct 27, 2023
91fceb1
Change SettingsValidator method to static
christianhelle Oct 27, 2023
a213294
Enable continue-on-error for Qodana
christianhelle Oct 27, 2023
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
1 change: 1 addition & 0 deletions .github/workflows/qodana.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2023.2
continue-on-error: true
with:
push-fixes: pull-request
pr-mode: false
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,10 @@ The following is an example `.refitter` file
"useIsoDateFormat": false, // Optional. Default=false
"multipleInterfaces": "ByEndpoint", // Optional. May be one of "ByEndpoint" or "ByTag"
"generateDeprecatedOperations": false, // Optional. Default=true
"operationNameTemplate": "{operationName}Async", // Optional. Must contain {operationName}
"operationNameTemplate": "{operationName}Async", // Optional. Must contain {operationName} when multipleInterfaces != ByEndpoint
"optionalParameters": false, // Optional. Default=false
"outputFolder": "../CustomOutput" // Optional. Default=./Generated
"outputFilename": "RefitInterface.cs", // Optional. Default=Output.cs for CLI tool
"additionalNamespaces": [ // Optional
"Namespace1",
"Namespace2"
Expand Down Expand Up @@ -215,6 +216,7 @@ The following is an example `.refitter` file
- `useIsoDateFormat` - Set to `true` to explicitly format date query string parameters in ISO 8601 standard date format using delimiters (for example: 2023-06-15). Default is `false`
- `multipleInterfaces` - Set to `ByEndpoint` to generate an interface for each endpoint, or `ByTag` to group Endpoints by their Tag (like SwaggerUI groups them).
- `outputFolder` - a string describing a relative path to a desired output folder. Default is `./Generated`
- `outputFilename` - Output filename. Default is `Output.cs` when used from the CLI tool, otherwise its the .refitter filename. So `Petstore.refitter` becomes `Petstore.cs`.
- `additionalNamespaces` - A collection of additional namespaces to include in the generated file. A use case for this is when you want to reuse contracts from a different namespace than the generated code. Default is empty
- `includeTags` - A collection of tags to use a filter for including endpoints that contain this tag.
- `includePathMatches` - A collection of regular expressions used to filter paths.
Expand Down
8 changes: 4 additions & 4 deletions docs/docfx_project/articles/refitter-file-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ The following is an example `.refitter` file
"useIsoDateFormat": false, // Optional. Default=false
"multipleInterfaces": "ByEndpoint", // Optional. May be one of "ByEndpoint" or "ByTag"
"generateDeprecatedOperations": false, // Optional. Default=true
"operationNameTemplate": "{operationName}Async", // Optional. Must contain {operationName}
"operationNameTemplate": "{operationName}Async", // Optional. Must contain {operationName} when multipleInterfaces != ByEndpoint
"optionalParameters": false, // Optional. Default=false
"outputFolder": "../CustomOutput" // Optional. Default=./Generated
"outputFilename": "RefitInterface.cs", // Optional. Default=Output.cs for CLI tool
"additionalNamespaces": [ // Optional
"Namespace1",
"Namespace2"
Expand All @@ -39,7 +40,7 @@ The following is an example `.refitter` file
"^/pet/.*",
"^/store/.*"
],
"dependencyInjectionSettings": {
"dependencyInjectionSettings": { // Optional
"baseUrl": "https://petstore3.swagger.io/api/v3", // Optional. Leave this blank to set the base address manually
"httpMessageHandlers": [ // Optional
"AuthorizationMessageHandler",
Expand Down Expand Up @@ -86,8 +87,6 @@ The following is an example `.refitter` file
}
```

Here are some basic explanations of each property:

- `openApiPath` - points to the OpenAPI Specifications file. This can be the path to a file stored on disk, relative to the `.refitter` file. This can also be a URL to a remote file that will be downloaded over HTTP/HTTPS
- `naming.useOpenApiTitle` - a boolean indicating whether the OpenApi title should be used. Default is `true`
- `naming.interfaceName` - the name of the generated interface. The generated code will automatically prefix this with `I` so if this set to `MyApiClient` then the generated interface is called `IMyApiClient`. Default is `ApiClient`
Expand All @@ -102,6 +101,7 @@ Here are some basic explanations of each property:
- `useIsoDateFormat` - Set to `true` to explicitly format date query string parameters in ISO 8601 standard date format using delimiters (for example: 2023-06-15). Default is `false`
- `multipleInterfaces` - Set to `ByEndpoint` to generate an interface for each endpoint, or `ByTag` to group Endpoints by their Tag (like SwaggerUI groups them).
- `outputFolder` - a string describing a relative path to a desired output folder. Default is `./Generated`
- `outputFilename` - Output filename. Default is `Output.cs` when used from the CLI tool, otherwise its the .refitter filename. So `Petstore.refitter` becomes `Petstore.cs`.
- `additionalNamespaces` - A collection of additional namespaces to include in the generated file. A use case for this is when you want to reuse contracts from a different namespace than the generated code. Default is empty
- `includeTags` - A collection of tags to use a filter for including endpoints that contain this tag.
- `includePathMatches` - A collection of regular expressions used to filter paths.
Expand Down
3 changes: 2 additions & 1 deletion src/Refitter.Core/Serializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ public static class Serializer
{
private static readonly JsonSerializerOptions JsonSerializerOptions = new()
{
PropertyNameCaseInsensitive = true
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};

/// <summary>
Expand Down
9 changes: 1 addition & 8 deletions src/Refitter.Core/Settings/DependencyInjectionSettings.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.Text.Json.Serialization;

namespace Refitter.Core;
namespace Refitter.Core;

/// <summary>
/// Dependency Injection settings describing how the Refit client should be configured.
Expand All @@ -11,31 +9,26 @@ public class DependencyInjectionSettings
/// <summary>
/// Base Address for the HttpClient
/// </summary>
[JsonPropertyName("baseUrl")]
public string? BaseUrl { get; set; }

/// <summary>
/// A collection of HttpMessageHandlers to be added to the HttpClient pipeline.
/// This can be for telemetry logging, authorization, etc.
/// </summary>
[JsonPropertyName("httpMessageHandlers")]
public string[] HttpMessageHandlers { get; set; } = Array.Empty<string>();

/// <summary>
/// Set this to true to use Polly for transient fault handling.
/// </summary>
[JsonPropertyName("usePolly")]
public bool UsePolly { get; set; }

/// <summary>
/// Default max retry count for Polly. Default is 6.
/// </summary>
[JsonPropertyName("pollyMaxRetryCount")]
public int PollyMaxRetryCount { get; set; } = 6;

/// <summary>
/// The median delay to target before the first retry in seconds. Default is 1 second
/// </summary>
[JsonPropertyName("firstBackoffRetryInSeconds")]
public double FirstBackoffRetryInSeconds { get; set; } = 1.0;
}
13 changes: 5 additions & 8 deletions src/Refitter.Core/Settings/MultipleInterfaces.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.Text.Json.Serialization;

namespace Refitter.Core;
namespace Refitter.Core;

/// <summary>
/// Enum representing the different options for generating multiple Refit interfaces.
Expand All @@ -10,17 +8,16 @@ public enum MultipleInterfaces
/// <summary>
/// Do not generate multiple interfaces
/// </summary>
[JsonPropertyName("unset")] Unset,
Unset,

/// <summary>
/// Generate a Refit interface for each endpoint with a single Execute() method.
/// The method name can be customized using the --operation-name-template command line option,
/// or the operationNameTemplate property in the settings file.
/// The method name can be customized using the <see cref="RefitGeneratorSettings.OperationNameTemplate"/> setting.
/// </summary>
[JsonPropertyName("byEndpoint")] ByEndpoint,
ByEndpoint,

/// <summary>
/// Generate a Refit interface for each tag
/// </summary>
[JsonPropertyName("byTag")] ByTag
ByTag
}
3 changes: 0 additions & 3 deletions src/Refitter.Core/Settings/NamingSettings.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;

namespace Refitter.Core;

Expand All @@ -12,12 +11,10 @@ public class NamingSettings
/// <summary>
/// Gets or sets a value indicating whether the OpenApi title should be used. Default is true.
/// </summary>
[JsonPropertyName("useOpenApiTitle")]
public bool UseOpenApiTitle { get; set; } = true;

/// <summary>
/// Gets or sets the name of the Interface. Default is "ApiClient".
/// </summary>
[JsonPropertyName("interfaceName")]
public string InterfaceName { get; set; } = "ApiClient";
}
35 changes: 11 additions & 24 deletions src/Refitter.Core/Settings/RefitGeneratorSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,138 +9,125 @@ namespace Refitter.Core;
[ExcludeFromCodeCoverage]
public class RefitGeneratorSettings
{
public const string DefaultOutputFolder = "./Generated";

/// <summary>
/// Gets or sets the path to the Open API.
/// </summary>
[JsonPropertyName("openApiPath")]
public string OpenApiPath { get; set; } = null!;

/// <summary>
/// Gets or sets the namespace for the generated code. (default: GeneratedCode)
/// </summary>
[JsonPropertyName("namespace")]
public string Namespace { get; set; } = "GeneratedCode";

/// <summary>
/// Gets or sets the naming settings.
/// </summary>
[JsonPropertyName("naming")]
public NamingSettings Naming { get; set; } = new();

/// <summary>
/// Gets or sets a value indicating whether contracts should be generated.
/// </summary>
[JsonPropertyName("generateContracts")]
public bool GenerateContracts { get; set; } = true;

/// <summary>
/// Gets or sets a value indicating whether XML doc comments should be generated.
/// </summary>
[JsonPropertyName("generateXmlDocCodeComments")]
public bool GenerateXmlDocCodeComments { get; set; } = true;

/// <summary>
/// Gets or sets a value indicating whether to add auto-generated header.
/// </summary>
[JsonPropertyName("addAutoGeneratedHeader")]
public bool AddAutoGeneratedHeader { get; set; } = true;

/// <summary>
/// Gets or sets a value indicating whether to add accept headers [Headers("Accept: application/json")].
/// </summary>
[JsonPropertyName("addAcceptHeaders")]
public bool AddAcceptHeaders { get; set; } = true;

/// <summary>
/// Gets or sets a value indicating whether to return <c>IApiResponse</c> objects.
/// </summary>
[JsonPropertyName("returnIApiResponse")]
public bool ReturnIApiResponse { get; set; }

/// <summary>
/// Gets or sets a value indicating whether to generate operation headers.
/// </summary>
[JsonPropertyName("generateOperationHeaders")]
public bool GenerateOperationHeaders { get; set; } = true;

/// <summary>
/// Gets or sets the generated type accessibility. (default: Public)
/// </summary>
[JsonPropertyName("typeAccessibility")]
public TypeAccessibility TypeAccessibility { get; set; } = TypeAccessibility.Public;

/// <summary>
/// Enable or disable the use of cancellation tokens.
/// </summary>
[JsonPropertyName("useCancellationTokens")]
public bool UseCancellationTokens { get; set; }

/// <summary>
/// Set to <c>true</c> to explicitly format date query string parameters
/// in ISO 8601 standard date format using delimiters (for example: 2023-06-15)
/// </summary>
[JsonPropertyName("useIsoDateFormat")]
public bool UseIsoDateFormat { get; set; }

/// <summary>
/// Add additional namespace to generated types
/// </summary>
[JsonPropertyName("additionalNamespaces")]
public string[] AdditionalNamespaces { get; set; } = Array.Empty<string>();

/// <summary>
/// Set to <c>true</c> to Generate a Refit interface for each endpoint
/// </summary>
[JsonPropertyName("multipleInterfaces")]
[JsonConverter(typeof(JsonStringEnumConverter))]
public MultipleInterfaces MultipleInterfaces { get; set; }

/// <summary>
/// Set to <c>true</c> to Generate a Refit interface for each endpoint
/// </summary>
[JsonPropertyName("includePathMatches")]
public string[] IncludePathMatches { get; set; } = Array.Empty<string>();

/// <summary>
/// Set to <c>true</c> to Generate a Refit interface for each endpoint
/// </summary>
[JsonPropertyName("includeTags")]
public string[] IncludeTags { get; set; } = Array.Empty<string>();

/// <summary>
/// Set to <c>true</c> to generate deprecated operations, otherwise <c>false</c>
/// </summary>
[JsonPropertyName("generateDeprecatedOperations")]
public bool GenerateDeprecatedOperations { get; set; } = true;

/// <summary>
/// Generate operation names using pattern.
/// When using <see cref="MultipleInterfaces"/> <see cref="ByEndpoint"/>, this is name of the Execute() method in the interface.
/// When using <see cref="MultipleInterfaces"/> <see cref="Core.MultipleInterfaces.ByEndpoint"/>, this is name of the Execute() method in the interface.
/// </summary>
[JsonPropertyName("operationNameTemplate")]
public string? OperationNameTemplate { get; set; }

/// <summary>
/// Set to <c>true</c> to re-order optional parameters to the end of the parameter list
/// </summary>
[JsonPropertyName("optionalParameters")]
public bool OptionalParameters { get; set; }

/// <summary>
/// Gets or sets the relative path to a folder in which the output files are generated. (default: ./Generated)
/// </summary>
[JsonPropertyName("outputFolder")]
public string OutputFolder { get; set; } = "./Generated";
public string OutputFolder { get; set; } = DefaultOutputFolder;

/// <summary>
/// Gets or sets the filename of the generated code.
/// For the CLI tool, the default is Output.cs
/// For the Source Generator, this is the name of the generated class and the default is [.refitter defined naming OR .refitter filename].g.cs)
/// </summary>
public string? OutputFilename { get; set; }

/// <summary>
/// Gets or sets the settings describing how to register generated interface to the .NET Core DI container
/// </summary>
[JsonPropertyName("dependencyInjectionSettings")]
public DependencyInjectionSettings? DependencyInjectionSettings { get; set; }

/// <summary>
/// Gets or sets the settings describing how to generate types using NSwag
/// </summary>
[JsonPropertyName("codeGeneratorSettings")]
public CodeGeneratorSettings? CodeGeneratorSettings { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"openApiPath": "../Resources/V3/SwaggerPetstore.json",
"namespace": "Refitter.Tests.CustomGenerated",
"outputFolder": "../CustomGenerated",
"outputFileName": "CustomGenerated.cs",
"generateContracts": false,
"additionalNamespaces": ["Refitter.Tests.AdditionalFiles.SingeInterface"],
"naming": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class CustomOutputFolderGeneratorTests
{
[Fact]
public void Can_Create_File_In_Custom_Path() =>
File.Exists("../../../CustomGenerated/SwaggerPetstoreCustomOutputFolder.g.cs").Should().BeTrue();
File.Exists("../../../CustomGenerated/CustomGenerated.cs").Should().BeTrue();

[Fact]
public void Can_Resolve_Refit_Interface() =>
Expand Down
4 changes: 3 additions & 1 deletion src/Refitter.SourceGenerator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ The following is an example `.refitter` file
"operationNameTemplate": "{operationName}Async", // Optional. Must contain {operationName} when multipleInterfaces != ByEndpoint
"optionalParameters": false, // Optional. Default=false
"outputFolder": "../CustomOutput" // Optional. Default=./Generated
"outputFilename": "RefitInterface.cs", // Optional. Default=Output.cs for CLI tool
"additionalNamespaces": [ // Optional
"Namespace1",
"Namespace2"
Expand All @@ -59,7 +60,7 @@ The following is an example `.refitter` file
"^/pet/.*",
"^/store/.*"
],
"dependencyInjectionSettings": {
"dependencyInjectionSettings": { // Optional
"baseUrl": "https://petstore3.swagger.io/api/v3", // Optional. Leave this blank to set the base address manually
"httpMessageHandlers": [ // Optional
"AuthorizationMessageHandler",
Expand Down Expand Up @@ -122,6 +123,7 @@ The following is an example `.refitter` file
- `useIsoDateFormat` - Set to `true` to explicitly format date query string parameters in ISO 8601 standard date format using delimiters (for example: 2023-06-15). Default is `false`
- `multipleInterfaces` - Set to `ByEndpoint` to generate an interface for each endpoint, or `ByTag` to group Endpoints by their Tag (like SwaggerUI groups them).
- `outputFolder` - a string describing a relative path to a desired output folder. Default is `./Generated`
- `outputFilename` - Output filename. Default is `Output.cs` when used from the CLI tool, otherwise its the .refitter filename. So `Petstore.refitter` becomes `Petstore.cs`.
- `additionalNamespaces` - A collection of additional namespaces to include in the generated file. A use case for this is when you want to reuse contracts from a different namespace than the generated code. Default is empty
- `includeTags` - A collection of tags to use a filter for including endpoints that contain this tag.
- `includePathMatches` - A collection of regular expressions used to filter paths.
Expand Down
14 changes: 7 additions & 7 deletions src/Refitter.SourceGenerator/RefitterSourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,6 @@ private static List<Diagnostic> GenerateCode(

try
{
var filename = Path.GetFileName(file.Path).Replace(".refitter", ".g.cs");
if (filename == ".g.cs")
{
filename = "Refitter.g.cs";
}

var content = file.GetText(cancellationToken)!;
var json = content.ToString();
var settings = Serializer.Deserialize<RefitGeneratorSettings>(json);
Expand Down Expand Up @@ -102,7 +96,13 @@ private static List<Diagnostic> GenerateCode(
cancellationToken.ThrowIfCancellationRequested();
try
{
var folder = Path.Combine(Path.GetDirectoryName(file.Path), settings.OutputFolder);
var filename = settings.OutputFilename ?? Path.GetFileName(file.Path).Replace(".refitter", ".g.cs");
if (filename == ".g.cs")
{
filename = "Refitter.g.cs";
}

var folder = Path.Combine(Path.GetDirectoryName(file.Path)!, settings.OutputFolder);
var output = Path.Combine(folder, filename);
if (!Directory.Exists(folder))
{
Expand Down
Loading