From 6bd7c73e2f5a58eb7c5f4ce33b9f79eb44802bb4 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Thu, 12 Oct 2023 11:58:32 +0200 Subject: [PATCH 01/18] Added camel case to JSON serialization settings In the JsonSerializer class, I've added the setting `PropertyNamingPolicy = JsonNamingPolicy.CamelCase` to the JsonSerializerOptions object. The change is to ensure the serialized JSON adheres to the camel case naming convention. This convention is a common standard in JSON data exchange and makes the serialized data more manageable in client apps, such as JavaScript which typically use camel case naming. --- src/Refitter.Core/Serializer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Refitter.Core/Serializer.cs b/src/Refitter.Core/Serializer.cs index c08684f0..6ad778e1 100644 --- a/src/Refitter.Core/Serializer.cs +++ b/src/Refitter.Core/Serializer.cs @@ -12,7 +12,8 @@ public static class Serializer { private static readonly JsonSerializerOptions JsonSerializerOptions = new() { - PropertyNameCaseInsensitive = true + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; /// From 3e7b5e536f3f7c7e191db70f143cfc8be5b663c9 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Thu, 12 Oct 2023 12:22:37 +0200 Subject: [PATCH 02/18] Remove unnecessary JsonPropertyName attributes Removed JsonPropertyName attributes from properties in various settings classes. These attributes were unnecessary because the Newtonsoft.Json package automatically infers the JsonPropertyName from the property name and they were adding redundancy. --- .../Settings/DependencyInjectionSettings.cs | 9 +------ .../Settings/MultipleInterfaces.cs | 13 ++++------ src/Refitter.Core/Settings/NamingSettings.cs | 3 --- .../Settings/RefitGeneratorSettings.cs | 24 +------------------ 4 files changed, 7 insertions(+), 42 deletions(-) diff --git a/src/Refitter.Core/Settings/DependencyInjectionSettings.cs b/src/Refitter.Core/Settings/DependencyInjectionSettings.cs index e700493d..6c141583 100644 --- a/src/Refitter.Core/Settings/DependencyInjectionSettings.cs +++ b/src/Refitter.Core/Settings/DependencyInjectionSettings.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace Refitter.Core; +namespace Refitter.Core; /// /// Dependency Injection settings describing how the Refit client should be configured. @@ -11,31 +9,26 @@ public class DependencyInjectionSettings /// /// Base Address for the HttpClient /// - [JsonPropertyName("baseUrl")] public string? BaseUrl { get; set; } /// /// A collection of HttpMessageHandlers to be added to the HttpClient pipeline. /// This can be for telemetry logging, authorization, etc. /// - [JsonPropertyName("httpMessageHandlers")] public string[] HttpMessageHandlers { get; set; } = Array.Empty(); /// /// Set this to true to use Polly for transient fault handling. /// - [JsonPropertyName("usePolly")] public bool UsePolly { get; set; } /// /// Default max retry count for Polly. Default is 6. /// - [JsonPropertyName("pollyMaxRetryCount")] public int PollyMaxRetryCount { get; set; } = 6; /// /// The median delay to target before the first retry in seconds. Default is 1 second /// - [JsonPropertyName("firstBackoffRetryInSeconds")] public double FirstBackoffRetryInSeconds { get; set; } = 1.0; } \ No newline at end of file diff --git a/src/Refitter.Core/Settings/MultipleInterfaces.cs b/src/Refitter.Core/Settings/MultipleInterfaces.cs index e95ab9a7..f2d449c9 100644 --- a/src/Refitter.Core/Settings/MultipleInterfaces.cs +++ b/src/Refitter.Core/Settings/MultipleInterfaces.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace Refitter.Core; +namespace Refitter.Core; /// /// Enum representing the different options for generating multiple Refit interfaces. @@ -10,17 +8,16 @@ public enum MultipleInterfaces /// /// Do not generate multiple interfaces /// - [JsonPropertyName("unset")] Unset, + Unset, /// /// 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 setting. /// - [JsonPropertyName("byEndpoint")] ByEndpoint, + ByEndpoint, /// /// Generate a Refit interface for each tag /// - [JsonPropertyName("byTag")] ByTag + ByTag } \ No newline at end of file diff --git a/src/Refitter.Core/Settings/NamingSettings.cs b/src/Refitter.Core/Settings/NamingSettings.cs index 0554ff2d..c0d891a5 100644 --- a/src/Refitter.Core/Settings/NamingSettings.cs +++ b/src/Refitter.Core/Settings/NamingSettings.cs @@ -1,5 +1,4 @@ using System.Diagnostics.CodeAnalysis; -using System.Text.Json.Serialization; namespace Refitter.Core; @@ -12,12 +11,10 @@ public class NamingSettings /// /// Gets or sets a value indicating whether the OpenApi title should be used. Default is true. /// - [JsonPropertyName("useOpenApiTitle")] public bool UseOpenApiTitle { get; set; } = true; /// /// Gets or sets the name of the Interface. Default is "ApiClient". /// - [JsonPropertyName("interfaceName")] public string InterfaceName { get; set; } = "ApiClient"; } \ No newline at end of file diff --git a/src/Refitter.Core/Settings/RefitGeneratorSettings.cs b/src/Refitter.Core/Settings/RefitGeneratorSettings.cs index 0d50fcf2..9194e6b5 100644 --- a/src/Refitter.Core/Settings/RefitGeneratorSettings.cs +++ b/src/Refitter.Core/Settings/RefitGeneratorSettings.cs @@ -12,135 +12,113 @@ public class RefitGeneratorSettings /// /// Gets or sets the path to the Open API. /// - [JsonPropertyName("openApiPath")] public string OpenApiPath { get; set; } = null!; /// /// Gets or sets the namespace for the generated code. (default: GeneratedCode) /// - [JsonPropertyName("namespace")] public string Namespace { get; set; } = "GeneratedCode"; /// /// Gets or sets the naming settings. /// - [JsonPropertyName("naming")] public NamingSettings Naming { get; set; } = new(); /// /// Gets or sets a value indicating whether contracts should be generated. /// - [JsonPropertyName("generateContracts")] public bool GenerateContracts { get; set; } = true; /// /// Gets or sets a value indicating whether XML doc comments should be generated. /// - [JsonPropertyName("generateXmlDocCodeComments")] public bool GenerateXmlDocCodeComments { get; set; } = true; /// /// Gets or sets a value indicating whether to add auto-generated header. /// - [JsonPropertyName("addAutoGeneratedHeader")] public bool AddAutoGeneratedHeader { get; set; } = true; /// /// Gets or sets a value indicating whether to add accept headers [Headers("Accept: application/json")]. /// - [JsonPropertyName("addAcceptHeaders")] public bool AddAcceptHeaders { get; set; } = true; /// /// Gets or sets a value indicating whether to return IApiResponse objects. /// - [JsonPropertyName("returnIApiResponse")] public bool ReturnIApiResponse { get; set; } /// /// Gets or sets a value indicating whether to generate operation headers. /// - [JsonPropertyName("generateOperationHeaders")] public bool GenerateOperationHeaders { get; set; } = true; /// /// Gets or sets the generated type accessibility. (default: Public) /// - [JsonPropertyName("typeAccessibility")] public TypeAccessibility TypeAccessibility { get; set; } = TypeAccessibility.Public; /// /// Enable or disable the use of cancellation tokens. /// - [JsonPropertyName("useCancellationTokens")] public bool UseCancellationTokens { get; set; } /// /// Set to true to explicitly format date query string parameters /// in ISO 8601 standard date format using delimiters (for example: 2023-06-15) /// - [JsonPropertyName("useIsoDateFormat")] public bool UseIsoDateFormat { get; set; } /// /// Add additional namespace to generated types /// - [JsonPropertyName("additionalNamespaces")] public string[] AdditionalNamespaces { get; set; } = Array.Empty(); /// /// Set to true to Generate a Refit interface for each endpoint /// - [JsonPropertyName("multipleInterfaces")] [JsonConverter(typeof(JsonStringEnumConverter))] public MultipleInterfaces MultipleInterfaces { get; set; } /// /// Set to true to Generate a Refit interface for each endpoint /// - [JsonPropertyName("includePathMatches")] public string[] IncludePathMatches { get; set; } = Array.Empty(); /// /// Set to true to Generate a Refit interface for each endpoint /// - [JsonPropertyName("includeTags")] public string[] IncludeTags { get; set; } = Array.Empty(); /// /// Set to true to generate deprecated operations, otherwise false /// - [JsonPropertyName("generateDeprecatedOperations")] public bool GenerateDeprecatedOperations { get; set; } = true; /// /// Generate operation names using pattern. - /// When using , this is name of the Execute() method in the interface. + /// When using , this is name of the Execute() method in the interface. /// - [JsonPropertyName("operationNameTemplate")] public string? OperationNameTemplate { get; set; } /// /// Set to true to re-order optional parameters to the end of the parameter list /// - [JsonPropertyName("optionalParameters")] public bool OptionalParameters { get; set; } /// /// Gets or sets the relative path to a folder in which the output files are generated. (default: ./Generated) /// - [JsonPropertyName("outputFolder")] public string OutputFolder { get; set; } = "./Generated"; /// /// Gets or sets the settings describing how to register generated interface to the .NET Core DI container /// - [JsonPropertyName("dependencyInjectionSettings")] public DependencyInjectionSettings? DependencyInjectionSettings { get; set; } /// /// Gets or sets the settings describing how to generate types using NSwag /// - [JsonPropertyName("codeGeneratorSettings")] public CodeGeneratorSettings? CodeGeneratorSettings { get; set; } } \ No newline at end of file From 0088ea9084f97d4db4f654842cd2079130816b71 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Thu, 12 Oct 2023 12:42:13 +0200 Subject: [PATCH 03/18] Add OutputFilename property to RefitGeneratorSettings This commit introduces a new property, OutputFilename, to the RefitGeneratorSettings class. This allows users to optionally set the name of the file generated by the Refit library. This flexibility was added to meet diverse user preferences and requirements for file naming. --- src/Refitter.Core/Settings/RefitGeneratorSettings.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Refitter.Core/Settings/RefitGeneratorSettings.cs b/src/Refitter.Core/Settings/RefitGeneratorSettings.cs index 9194e6b5..aeab0a5e 100644 --- a/src/Refitter.Core/Settings/RefitGeneratorSettings.cs +++ b/src/Refitter.Core/Settings/RefitGeneratorSettings.cs @@ -112,6 +112,13 @@ public class RefitGeneratorSettings /// public string OutputFolder { get; set; } = "./Generated"; + /// + /// 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) + /// + public string? OutputFilename { get; set; } + /// /// Gets or sets the settings describing how to register generated interface to the .NET Core DI container /// From d8d00b0b34d0c357e2f3e8cfb32437234615f1a6 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Thu, 12 Oct 2023 13:43:08 +0200 Subject: [PATCH 04/18] Update source generator to use OutputFilename setting The filename in the RefitterSourceGenerator has been moved from the initial file read into the subsequent try block. The change improves the code readability and flow, by keeping the file output manipulation logic in one place. It also allows the filename to leverage the OutputFilename setting if it's provided, providing more flexible file naming. --- .../RefitterSourceGenerator.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Refitter.SourceGenerator/RefitterSourceGenerator.cs b/src/Refitter.SourceGenerator/RefitterSourceGenerator.cs index e23c420f..465bd71a 100644 --- a/src/Refitter.SourceGenerator/RefitterSourceGenerator.cs +++ b/src/Refitter.SourceGenerator/RefitterSourceGenerator.cs @@ -65,12 +65,6 @@ private static List 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(json); @@ -102,7 +96,13 @@ private static List 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)) { From fa2e6f6c97e1385e1f0e3328bdac93f2ed1c0bed Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Thu, 12 Oct 2023 13:54:28 +0200 Subject: [PATCH 05/18] Rename auto-generated file for better organization The SwaggerPetstoreCustomOutputFolder.g.cs file was deleted and recreated as CustomGenerated.cs. This change was made to provide better clarity and organization to the auto-generated files structure in the CustomGenerated directory. The SwaggerPetstoreCustomOutputFolder.refitter configuration file was also updated to reflect this new output file name. --- .../AdditionalFiles/SwaggerPetstoreCustomOutputFolder.refitter | 1 + ...SwaggerPetstoreCustomOutputFolder.g.cs => CustomGenerated.cs} | 0 2 files changed, 1 insertion(+) rename src/Refitter.SourceGenerator.Tests/CustomGenerated/{SwaggerPetstoreCustomOutputFolder.g.cs => CustomGenerated.cs} (100%) diff --git a/src/Refitter.SourceGenerator.Tests/AdditionalFiles/SwaggerPetstoreCustomOutputFolder.refitter b/src/Refitter.SourceGenerator.Tests/AdditionalFiles/SwaggerPetstoreCustomOutputFolder.refitter index 70b81cff..adb78a0e 100644 --- a/src/Refitter.SourceGenerator.Tests/AdditionalFiles/SwaggerPetstoreCustomOutputFolder.refitter +++ b/src/Refitter.SourceGenerator.Tests/AdditionalFiles/SwaggerPetstoreCustomOutputFolder.refitter @@ -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": { diff --git a/src/Refitter.SourceGenerator.Tests/CustomGenerated/SwaggerPetstoreCustomOutputFolder.g.cs b/src/Refitter.SourceGenerator.Tests/CustomGenerated/CustomGenerated.cs similarity index 100% rename from src/Refitter.SourceGenerator.Tests/CustomGenerated/SwaggerPetstoreCustomOutputFolder.g.cs rename to src/Refitter.SourceGenerator.Tests/CustomGenerated/CustomGenerated.cs From ef8ef83cb7712acb1dd705857ccdb8884ca60667 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Thu, 12 Oct 2023 16:47:29 +0200 Subject: [PATCH 06/18] Add output path configuration options for RefitGenerator The code was modified to allow users more control over output path configuration in RefitGenerator. Now, users can define the output folder and filename either directly with --output or via 'outputFolder' and 'outputFilename' settings in the file. A validation error will be returned if both routes are attempted simultaneously. This change offers greater flexibility and security to avoid path-related problems. Default values have also been shifted from being hard coded, to being set as constants within Settings and RefitGeneratorSettings classes. --- .../Settings/RefitGeneratorSettings.cs | 4 ++- src/Refitter/GenerateCommand.cs | 29 +++++++++++++++++-- src/Refitter/Settings.cs | 4 ++- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/Refitter.Core/Settings/RefitGeneratorSettings.cs b/src/Refitter.Core/Settings/RefitGeneratorSettings.cs index aeab0a5e..8ef6dfba 100644 --- a/src/Refitter.Core/Settings/RefitGeneratorSettings.cs +++ b/src/Refitter.Core/Settings/RefitGeneratorSettings.cs @@ -9,6 +9,8 @@ namespace Refitter.Core; [ExcludeFromCodeCoverage] public class RefitGeneratorSettings { + public const string DefaultOutputFolder = "./Generated"; + /// /// Gets or sets the path to the Open API. /// @@ -110,7 +112,7 @@ public class RefitGeneratorSettings /// /// Gets or sets the relative path to a folder in which the output files are generated. (default: ./Generated) /// - public string OutputFolder { get; set; } = "./Generated"; + public string OutputFolder { get; set; } = DefaultOutputFolder; /// /// Gets or sets the filename of the generated code. diff --git a/src/Refitter/GenerateCommand.cs b/src/Refitter/GenerateCommand.cs index 15f7dc1b..7434a384 100644 --- a/src/Refitter/GenerateCommand.cs +++ b/src/Refitter/GenerateCommand.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using System.Text.Json; using Microsoft.OpenApi.Models; @@ -36,12 +35,21 @@ public override ValidationResult Validate(CommandContext context, Settings setti var json = File.ReadAllText(settings.SettingsFilePath); var refitGeneratorSettings = Serializer.Deserialize(json); settings.OpenApiPath = refitGeneratorSettings.OpenApiPath; - + if (string.IsNullOrWhiteSpace(refitGeneratorSettings.OpenApiPath)) return ValidationResult.Error( "The 'openApiPath' in settings file is required when " + "URL or file path to OpenAPI Specification file " + "is not specified in command line argument"); + + if (!string.IsNullOrWhiteSpace(settings.OutputPath) && + settings.OutputPath != Settings.DefaultOutputPath && + (!string.IsNullOrWhiteSpace(refitGeneratorSettings.OutputFolder) || + !string.IsNullOrWhiteSpace(refitGeneratorSettings.OutputFilename))) + return ValidationResult.Error( + "You should either specify an output path directly from --output " + + "or use specify it in 'outputFolder' and 'outputFilename' from the settings file, " + + "not both"); } if (!string.IsNullOrWhiteSpace(settings.OperationNameTemplate) && @@ -112,7 +120,7 @@ public override async Task ExecuteAsync(CommandContext context, Settings se Directory.CreateDirectory(directory); } - var outputPath = settings.OutputPath ?? "Output.cs"; + var outputPath = GetOutputPath(settings, refitGeneratorSettings); AnsiConsole.MarkupLine($"[green]Output: {Path.GetFullPath(outputPath)}[/]"); await File.WriteAllTextAsync(outputPath, code); await Analytics.LogFeatureUsage(settings); @@ -134,6 +142,21 @@ public override async Task ExecuteAsync(CommandContext context, Settings se } } + private static string GetOutputPath(Settings settings, RefitGeneratorSettings refitGeneratorSettings) + { + var outputPath = settings.OutputPath != Settings.DefaultOutputPath && !string.IsNullOrWhiteSpace(settings.OutputPath) + ? settings.OutputPath + : refitGeneratorSettings.OutputFilename ?? "Output.cs"; + + if (!string.IsNullOrWhiteSpace(refitGeneratorSettings.OutputFolder) && + refitGeneratorSettings.OutputFolder != RefitGeneratorSettings.DefaultOutputFolder) + { + outputPath = Path.Combine(refitGeneratorSettings.OutputFolder, outputPath); + } + + return outputPath; + } + private static async Task ValidateOpenApiSpec(Settings settings) { var validationResult = await OpenApiValidator.Validate(settings.OpenApiPath!); diff --git a/src/Refitter/Settings.cs b/src/Refitter/Settings.cs index 713a26f3..917580b7 100644 --- a/src/Refitter/Settings.cs +++ b/src/Refitter/Settings.cs @@ -6,6 +6,8 @@ namespace Refitter; public sealed class Settings : CommandSettings { + public const string DefaultOutputPath = "Output.cs"; + [Description("URL or file path to OpenAPI Specification file")] [CommandArgument(0, "[URL or input file]")] [DefaultValue(null)] @@ -22,7 +24,7 @@ public sealed class Settings : CommandSettings [Description("Path to Output file")] [CommandOption("-o|--output")] - [DefaultValue("Output.cs")] + [DefaultValue(DefaultOutputPath)] public string? OutputPath { get; set; } [Description("Don't add header to output file")] From 5b3c1a7e1d6e7a99af645b81b0d73ba520682241 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Fri, 13 Oct 2023 00:29:26 +0200 Subject: [PATCH 07/18] Refactor directory check for outputPath The check for whether the directory for the output file exists and its creation if it doesn't, was moved. Previously it was checking for a valid directory for settings.OutputPath before calculating the actual output path. Now the directory verification is done after calculating the actual outputPath, ensuring that a directory will always be created for the correct outputPath. --- src/Refitter/GenerateCommand.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Refitter/GenerateCommand.cs b/src/Refitter/GenerateCommand.cs index 7434a384..f46f1c90 100644 --- a/src/Refitter/GenerateCommand.cs +++ b/src/Refitter/GenerateCommand.cs @@ -113,15 +113,13 @@ public override async Task ExecuteAsync(CommandContext context, Settings se var code = generator.Generate().ReplaceLineEndings(); AnsiConsole.MarkupLine($"[green]Length: {code.Length} bytes[/]"); - if (!string.IsNullOrWhiteSpace(settings.OutputPath)) - { - var directory = Path.GetDirectoryName(settings.OutputPath); - if (!string.IsNullOrWhiteSpace(directory) && !Directory.Exists(directory)) - Directory.CreateDirectory(directory); - } - var outputPath = GetOutputPath(settings, refitGeneratorSettings); AnsiConsole.MarkupLine($"[green]Output: {Path.GetFullPath(outputPath)}[/]"); + + var directory = Path.GetDirectoryName(outputPath); + if (!string.IsNullOrWhiteSpace(directory) && !Directory.Exists(directory)) + Directory.CreateDirectory(directory); + await File.WriteAllTextAsync(outputPath, code); await Analytics.LogFeatureUsage(settings); From 901f91772b37edf335b46136478cbdf7652de640 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Fri, 13 Oct 2023 00:30:00 +0200 Subject: [PATCH 08/18] Add outputFolder and outputFilename to petstore.refitter These additional fields were added to petstore.refitter to simplify the refitter configuration. It eliminates the redundancy of specifying the output file path every time in the smoke-tests script. As a result, the refitter call within the smoke-tests script has been adjusted to no longer include the output path. The output file name was also updated in the GenerateAndBuild call to match the petstore.refitter setting. --- test/petstore.refitter | 2 ++ test/smoke-tests.ps1 | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/test/petstore.refitter b/test/petstore.refitter index 60d54a4c..ccfcdaa0 100644 --- a/test/petstore.refitter +++ b/test/petstore.refitter @@ -1,6 +1,8 @@ { "openApiPath": "./OpenAPI/v3.0/petstore.json", "namespace": "Petstore", + "outputFolder": "GeneratedCode", + "outputFilename": "SwaggerPetstoreDirect.cs", "naming": { "useOpenApiTitle": false, "interfaceName": "SwaggerPetstoreDirect" diff --git a/test/smoke-tests.ps1 b/test/smoke-tests.ps1 index cee98fae..0d65394c 100644 --- a/test/smoke-tests.ps1 +++ b/test/smoke-tests.ps1 @@ -33,9 +33,9 @@ function GenerateAndBuild { Get-ChildItem '*.generated.cs' -Recurse | ForEach-Object { Remove-Item -Path $_.FullName } if ($args.Contains("settings-file")) { - Write-Host "refitter --output ./GeneratedCode/$outputPath --no-logging $args" + Write-Host "refitter --no-logging $args" $process = Start-Process "./bin/refitter" ` - -Args "--output ./GeneratedCode/$outputPath --no-logging $args" ` + -Args "--no-logging $args" ` -NoNewWindow ` -PassThru } else { @@ -93,7 +93,7 @@ function RunTests { Write-Host "dotnet publish ../src/Refitter/Refitter.csproj -p:TreatWarningsAsErrors=true -p:PublishReadyToRun=true -o bin" Start-Process "dotnet" -Args "publish ../src/Refitter/Refitter.csproj -p:TreatWarningsAsErrors=true -p:PublishReadyToRun=true -o bin" -NoNewWindow -PassThru | Wait-Process - GenerateAndBuild -format " " -namespace " " -outputPath "SwaggerPetstoreDirect.generated.cs" -args "--settings-file ./petstore.refitter" + GenerateAndBuild -format " " -namespace " " -outputPath "SwaggerPetstoreDirect.cs" -args "--settings-file ./petstore.refitter" "v3.0", "v2.0" | ForEach-Object { $version = $_ From 51828fd4afd79364bdcf374b374fd217d4c99dcd Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Fri, 13 Oct 2023 14:05:07 +0200 Subject: [PATCH 09/18] Update assertion in generator test Changed the file name in the test assertion to match updates in the code generation. This was necessary to reflect new file naming convention which is part of an effort to streamline the code generation process. --- .../CustomOutputFolderGeneratorTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Refitter.SourceGenerator.Tests/CustomOutputFolderGeneratorTests.cs b/src/Refitter.SourceGenerator.Tests/CustomOutputFolderGeneratorTests.cs index cccfce46..93ce9927 100644 --- a/src/Refitter.SourceGenerator.Tests/CustomOutputFolderGeneratorTests.cs +++ b/src/Refitter.SourceGenerator.Tests/CustomOutputFolderGeneratorTests.cs @@ -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() => From 2c3807e4efa36c4357fc92f9f957f54814de128c Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Fri, 13 Oct 2023 14:14:24 +0200 Subject: [PATCH 10/18] Refactored validation logic into SettingsValidator class This commit decomposes the validation logic previously located in the `GenerateCommand` class, and moves it into its own `SettingsValidator` class for better organization and reusability. This provides a cleaner architecture and easier future maintenance, as now settings validation can be altered independent from command generation logic. --- src/Refitter/GenerateCommand.cs | 52 +------------- src/Refitter/SettingsValidator.cs | 116 ++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 50 deletions(-) create mode 100644 src/Refitter/SettingsValidator.cs diff --git a/src/Refitter/GenerateCommand.cs b/src/Refitter/GenerateCommand.cs index f46f1c90..8104ef97 100644 --- a/src/Refitter/GenerateCommand.cs +++ b/src/Refitter/GenerateCommand.cs @@ -19,50 +19,8 @@ public override ValidationResult Validate(CommandContext context, Settings setti if (!settings.NoLogging) Analytics.Configure(); - if (string.IsNullOrWhiteSpace(settings.OpenApiPath) && - string.IsNullOrWhiteSpace(settings.SettingsFilePath)) - return ValidationResult.Error("Input or settings file is required"); - - if (!string.IsNullOrWhiteSpace(settings.OpenApiPath) && - !string.IsNullOrWhiteSpace(settings.SettingsFilePath)) - return ValidationResult.Error( - "You should either specify an input URL/file directly " + - "or use specify it in 'openApiPath' from the settings file, " + - "not both"); - - if (!string.IsNullOrWhiteSpace(settings.SettingsFilePath)) - { - var json = File.ReadAllText(settings.SettingsFilePath); - var refitGeneratorSettings = Serializer.Deserialize(json); - settings.OpenApiPath = refitGeneratorSettings.OpenApiPath; - - if (string.IsNullOrWhiteSpace(refitGeneratorSettings.OpenApiPath)) - return ValidationResult.Error( - "The 'openApiPath' in settings file is required when " + - "URL or file path to OpenAPI Specification file " + - "is not specified in command line argument"); - - if (!string.IsNullOrWhiteSpace(settings.OutputPath) && - settings.OutputPath != Settings.DefaultOutputPath && - (!string.IsNullOrWhiteSpace(refitGeneratorSettings.OutputFolder) || - !string.IsNullOrWhiteSpace(refitGeneratorSettings.OutputFilename))) - return ValidationResult.Error( - "You should either specify an output path directly from --output " + - "or use specify it in 'outputFolder' and 'outputFilename' from the settings file, " + - "not both"); - } - - if (!string.IsNullOrWhiteSpace(settings.OperationNameTemplate) && - !settings.OperationNameTemplate.Contains("{operationName}") && - settings.MultipleInterfaces != MultipleInterfaces.ByEndpoint) - return ValidationResult.Error("'{operationName}' placeholder must be present in operation name template"); - - if (IsUrl(settings.OpenApiPath!)) - return base.Validate(context, settings); - - return File.Exists(settings.OpenApiPath) - ? base.Validate(context, settings) - : ValidationResult.Error($"File not found - {Path.GetFullPath(settings.OpenApiPath!)}"); + return new SettingsValidator() + .Validate(context, settings); } public override async Task ExecuteAsync(CommandContext context, Settings settings) @@ -192,10 +150,4 @@ private static void TryWriteLine( // ignored } } - - private static bool IsUrl(string openApiPath) - { - return Uri.TryCreate(openApiPath, UriKind.Absolute, out var uriResult) && - (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps); - } } \ No newline at end of file diff --git a/src/Refitter/SettingsValidator.cs b/src/Refitter/SettingsValidator.cs new file mode 100644 index 00000000..bc9d07f6 --- /dev/null +++ b/src/Refitter/SettingsValidator.cs @@ -0,0 +1,116 @@ +using Refitter.Core; + +using Spectre.Console; +using Spectre.Console.Cli; + +namespace Refitter; + +public class SettingsValidator +{ + public ValidationResult Validate(CommandContext context, Settings settings) + { + if (BothSettingsFilesAreEmpty(settings) || BothSettingsFilesArePresent(settings)) + { + return GetValidationErrorForSettingsFiles(); + } + + return !string.IsNullOrWhiteSpace(settings.SettingsFilePath) + ? ValidateFilePath(settings) + : ValidateOperationNameAndUrl(settings); + } + + private static bool BothSettingsFilesAreEmpty(Settings settings) + { + return string.IsNullOrWhiteSpace(settings.OpenApiPath) && + string.IsNullOrWhiteSpace(settings.SettingsFilePath); + } + + private static bool BothSettingsFilesArePresent(Settings settings) + { + return !string.IsNullOrWhiteSpace(settings.OpenApiPath) && + !string.IsNullOrWhiteSpace(settings.SettingsFilePath); + } + + private static ValidationResult GetValidationErrorForSettingsFiles() + { + return ValidationResult.Error( + "You should either specify an input URL/file directly " + + "or use specify it in 'openApiPath' from the settings file, " + + "not both"); + } + + private ValidationResult ValidateFilePath(Settings settings) + { + var json = File.ReadAllText(settings.SettingsFilePath!); + var refitGeneratorSettings = Serializer.Deserialize(json); + settings.OpenApiPath = refitGeneratorSettings.OpenApiPath; + + return ValidateFileAndOutputSettings(settings, refitGeneratorSettings); + } + + private ValidationResult ValidateFileAndOutputSettings( + Settings settings, + RefitGeneratorSettings refitGeneratorSettings) + { + if (string.IsNullOrWhiteSpace(refitGeneratorSettings.OpenApiPath)) + { + return GetValidationErrorForOpenApiPath(); + } + + if (!string.IsNullOrWhiteSpace(settings.OutputPath) && + settings.OutputPath != Settings.DefaultOutputPath && + (!string.IsNullOrWhiteSpace(refitGeneratorSettings.OutputFolder) || + !string.IsNullOrWhiteSpace(refitGeneratorSettings.OutputFilename))) + { + return GetValidationErrorForOutputPath(); + } + + return ValidateOperationNameAndUrl(settings); + } + + private static ValidationResult GetValidationErrorForOpenApiPath() + { + return ValidationResult.Error( + "The 'openApiPath' in settings file is required when " + + "URL or file path to OpenAPI Specification file " + + "is not specified in command line argument"); + } + + private static ValidationResult GetValidationErrorForOutputPath() + { + return ValidationResult.Error( + "You should either specify an output path directly from --output " + + "or use specify it in 'outputFolder' and 'outputFilename' from the settings file, " + + "not both"); + } + + private ValidationResult ValidateOperationNameAndUrl(Settings settings) + { + if (!string.IsNullOrWhiteSpace(settings.OperationNameTemplate) && + !settings.OperationNameTemplate.Contains("{operationName}") && + settings.MultipleInterfaces != MultipleInterfaces.ByEndpoint) + { + return GetValidationErrorForOperationName(); + } + + return IsUrl(settings.OpenApiPath!) ? ValidationResult.Success() : ValidateFileExistence(settings); + } + + private static ValidationResult GetValidationErrorForOperationName() + { + return ValidationResult.Error("'{operationName}' placeholder must be present in operation name template"); + } + + private ValidationResult ValidateFileExistence(Settings settings) + { + return File.Exists(settings.OpenApiPath) + ? ValidationResult.Success() + : ValidationResult.Error($"File not found - {Path.GetFullPath(settings.OpenApiPath!)}"); + } + + private static bool IsUrl(string openApiPath) + { + return Uri.TryCreate(openApiPath, UriKind.Absolute, out var uriResult) && + (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps); + } +} \ No newline at end of file From 24fc27f2ee7eef04a0a0c9948d5fd31bed885643 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Tue, 24 Oct 2023 11:03:17 +0200 Subject: [PATCH 11/18] "Added `outputFilename` option to configuration and updated documentation" This commit adds an optional parameter `outputFilename` to the configuration options in the JSON files of `Refitter.SourceGenerator`, `../README` and `Refitter`. The `outputFilename` allows users to specify a name for the output file, with the default being `Output.cs` for the CLI tool. This change was necessary to provide users more flexibility in naming their output files. Additionally, the commit includes updates to the documentation files - `Refitter.SourceGenerator/README`, `../README`, `Refitter/README` and `../docs/docfx_project/articles/refitter-file-format.md` - to explain the new `outputFilename` option. It also adjusts the explanation on `operationNameTemplate` for clarity. Extra explanatory comments were added to `dependencyInjectionSettings` and a new block `codeGeneratorSettings` was included in the `Refitter/README.md` file to give users more control over the generated types and contracts. --- README.md | 4 +- .../articles/refitter-file-format.md | 8 ++-- src/Refitter.SourceGenerator/README.md | 4 +- src/Refitter/README.md | 41 ++++++++++++++++++- 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index bb9ad890..c39f4e49 100644 --- a/README.md +++ b/README.md @@ -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" @@ -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. diff --git a/docs/docfx_project/articles/refitter-file-format.md b/docs/docfx_project/articles/refitter-file-format.md index 56da178d..c6874e7a 100644 --- a/docs/docfx_project/articles/refitter-file-format.md +++ b/docs/docfx_project/articles/refitter-file-format.md @@ -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" @@ -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", @@ -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` @@ -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. diff --git a/src/Refitter.SourceGenerator/README.md b/src/Refitter.SourceGenerator/README.md index 6e3f771d..0f57d376 100644 --- a/src/Refitter.SourceGenerator/README.md +++ b/src/Refitter.SourceGenerator/README.md @@ -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" @@ -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", @@ -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. diff --git a/src/Refitter/README.md b/src/Refitter/README.md index 2b56c642..c5e9f269 100644 --- a/src/Refitter/README.md +++ b/src/Refitter/README.md @@ -93,6 +93,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" @@ -106,7 +107,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", @@ -115,6 +116,41 @@ The following is an example `.refitter` file "usePolly": true, // Optional. Set this to true, to configure Polly with a retry policy that uses a jittered backoff. Default=false "pollyMaxRetryCount": 3, // Optional. Default=6 "firstBackoffRetryInSeconds": 0.5 // Optional. Default=1.0 + }, + "codeGeneratorSettings": { // Optional. Default settings are the values set in this example + "namespace": "GeneratedCode", + "requiredPropertiesMustBeDefined": true, + "generateDataAnnotations": true, + "anyType": "object", + "dateType": "System.DateTimeOffset", + "dateTimeType": "System.DateTimeOffset", + "timeType": "System.TimeSpan", + "timeSpanType": "System.TimeSpan", + "arrayType": "System.Collections.Generic.ICollection", + "dictionaryType": "System.Collections.Generic.IDictionary", + "arrayInstanceType": "System.Collections.ObjectModel.Collection", + "dictionaryInstanceType": "System.Collections.Generic.Dictionary", + "arrayBaseType": "System.Collections.ObjectModel.Collection", + "dictionaryBaseType": "System.Collections.Generic.Dictionary", + "propertySetterAccessModifier": "", + "generateImmutableArrayProperties": false, + "generateImmutableDictionaryProperties": false, + "handleReferences": false, + "jsonSerializerSettingsTransformationMethod": null, + "generateJsonMethods": false, + "enforceFlagEnums": false, + "inlineNamedDictionaries": false, + "inlineNamedTuples": true, + "inlineNamedArrays": false, + "generateOptionalPropertiesAsNullable": false, + "generateNullableReferenceTypes": false, + "generateNativeRecords": false, + "generateDefaultValues": true, + "inlineNamedAny": false, + "excludedTypeNames": [ + "ExcludedTypeFoo", + "ExcludedTypeBar" + ] } } ``` @@ -134,11 +170,12 @@ 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. - `generateDeprecatedOperations` - a boolean indicating whether deprecated operations should be generated or skipped. Default is `true` -- `operationNameTemplate` - Generate operation names using pattern. This must contain the string {operationName}. An example usage of this could be `{operationName}Async` to suffix all method names with Async. When using `"multipleIinterfaces": "ByEndpoint"`, This is name of the Execute() method in the interface +- `operationNameTemplate` - Generate operation names using pattern. This must contain the string {operationName}. An example usage of this could be `{operationName}Async` to suffix all method names with Async - `optionalParameters` - Generate non-required parameters as nullable optional parameters - `dependencyInjectionSettings` - Setting this will generated extension methods to `IServiceCollection` for configuring Refit clients - `baseUrl` - Used as the HttpClient base address. Leave this blank to manually set the base URL From b7de4ee1e0148811d0d90c4d243b1f7aebcd7cd9 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Thu, 26 Oct 2023 16:03:29 +0200 Subject: [PATCH 12/18] Remove CommandContext from SettingsValidator Simplified the Validate method in SettingsValidator by removing the unnecessary CommandContext parameter. This change optimizes the method by improving readability and reducing complexity. Passed Settings directly as an argument. --- src/Refitter/GenerateCommand.cs | 3 +-- src/Refitter/SettingsValidator.cs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Refitter/GenerateCommand.cs b/src/Refitter/GenerateCommand.cs index 8104ef97..5d0d50bf 100644 --- a/src/Refitter/GenerateCommand.cs +++ b/src/Refitter/GenerateCommand.cs @@ -19,8 +19,7 @@ public override ValidationResult Validate(CommandContext context, Settings setti if (!settings.NoLogging) Analytics.Configure(); - return new SettingsValidator() - .Validate(context, settings); + return new SettingsValidator().Validate(settings); } public override async Task ExecuteAsync(CommandContext context, Settings settings) diff --git a/src/Refitter/SettingsValidator.cs b/src/Refitter/SettingsValidator.cs index bc9d07f6..5c2849ec 100644 --- a/src/Refitter/SettingsValidator.cs +++ b/src/Refitter/SettingsValidator.cs @@ -1,13 +1,12 @@ using Refitter.Core; using Spectre.Console; -using Spectre.Console.Cli; namespace Refitter; public class SettingsValidator { - public ValidationResult Validate(CommandContext context, Settings settings) + public ValidationResult Validate(Settings settings) { if (BothSettingsFilesAreEmpty(settings) || BothSettingsFilesArePresent(settings)) { From 37ebd57a687f6d1008c9d0ce5b11e4875f8fdfb2 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Fri, 27 Oct 2023 08:37:39 +0200 Subject: [PATCH 13/18] Make ValidateFileExistence method static The ValidateFileExistence method in SettingsValidator.cs has been made static. This change was made because the method does not access any instance members and does not depend on specific object state. Making methods that satisfy these criteria static can result in minor performance improvements. --- src/Refitter/SettingsValidator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Refitter/SettingsValidator.cs b/src/Refitter/SettingsValidator.cs index 5c2849ec..cc8c9b56 100644 --- a/src/Refitter/SettingsValidator.cs +++ b/src/Refitter/SettingsValidator.cs @@ -100,7 +100,7 @@ private static ValidationResult GetValidationErrorForOperationName() return ValidationResult.Error("'{operationName}' placeholder must be present in operation name template"); } - private ValidationResult ValidateFileExistence(Settings settings) + private static ValidationResult ValidateFileExistence(Settings settings) { return File.Exists(settings.OpenApiPath) ? ValidationResult.Success() From 18a5f1717f947ba511ae9a79ec35d334f991cfbd Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Fri, 27 Oct 2023 08:51:23 +0200 Subject: [PATCH 14/18] Make ValidateOperationNameAndUrl method static The ValidateOperationNameAndUrl method in SettingsValidator.cs was changed to be static. This was done because the method does not use any instance variables and can be called without an instance of the class. This improves performance and reduces memory footprint. --- src/Refitter/SettingsValidator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Refitter/SettingsValidator.cs b/src/Refitter/SettingsValidator.cs index cc8c9b56..6bb45615 100644 --- a/src/Refitter/SettingsValidator.cs +++ b/src/Refitter/SettingsValidator.cs @@ -83,7 +83,7 @@ private static ValidationResult GetValidationErrorForOutputPath() "not both"); } - private ValidationResult ValidateOperationNameAndUrl(Settings settings) + private static ValidationResult ValidateOperationNameAndUrl(Settings settings) { if (!string.IsNullOrWhiteSpace(settings.OperationNameTemplate) && !settings.OperationNameTemplate.Contains("{operationName}") && From 1853e6d6483cbdc432042b5cb66aedfb3bceb25c Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Fri, 27 Oct 2023 08:52:14 +0200 Subject: [PATCH 15/18] Make ValidateFileAndOutputSettings method static The method ValidateFileAndOutputSettings in SettingsValidator.cs was changed to be static. This change was made because we found out during code review that this method does not use any instance data, therefore it makes sense to make it static to improve the readability and maintainability of the code. --- src/Refitter/SettingsValidator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Refitter/SettingsValidator.cs b/src/Refitter/SettingsValidator.cs index 6bb45615..2bf31d23 100644 --- a/src/Refitter/SettingsValidator.cs +++ b/src/Refitter/SettingsValidator.cs @@ -47,7 +47,7 @@ private ValidationResult ValidateFilePath(Settings settings) return ValidateFileAndOutputSettings(settings, refitGeneratorSettings); } - private ValidationResult ValidateFileAndOutputSettings( + private static ValidationResult ValidateFileAndOutputSettings( Settings settings, RefitGeneratorSettings refitGeneratorSettings) { From 6f3a8ccf42094b5101baa30d8c9ad13eabba1f71 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Fri, 27 Oct 2023 08:52:32 +0200 Subject: [PATCH 16/18] Make ValidateFilePath method static The method ValidateFilePath in SettingsValidator.cs was changed to static since it doesn't use or change any instance-level state. This promotes good practice and increased efficiency by avoiding unnecessary object instantiation. --- src/Refitter/SettingsValidator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Refitter/SettingsValidator.cs b/src/Refitter/SettingsValidator.cs index 2bf31d23..d587432f 100644 --- a/src/Refitter/SettingsValidator.cs +++ b/src/Refitter/SettingsValidator.cs @@ -38,7 +38,7 @@ private static ValidationResult GetValidationErrorForSettingsFiles() "not both"); } - private ValidationResult ValidateFilePath(Settings settings) + private static ValidationResult ValidateFilePath(Settings settings) { var json = File.ReadAllText(settings.SettingsFilePath!); var refitGeneratorSettings = Serializer.Deserialize(json); From 91fceb1cb4fc1d930c1f4e2706c9f4dd99f2932b Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Fri, 27 Oct 2023 08:53:49 +0200 Subject: [PATCH 17/18] Change SettingsValidator method to static The Validate method in SettingsValidator class got changed from an instance method to a static method. This was done to avoid creating unnecessary instances of SettingsValidator when validating settings in GenerateCommand.cs. The related call in GenerateCommand has been modified accordingly. --- src/Refitter/GenerateCommand.cs | 2 +- src/Refitter/SettingsValidator.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Refitter/GenerateCommand.cs b/src/Refitter/GenerateCommand.cs index 5d0d50bf..08b48844 100644 --- a/src/Refitter/GenerateCommand.cs +++ b/src/Refitter/GenerateCommand.cs @@ -19,7 +19,7 @@ public override ValidationResult Validate(CommandContext context, Settings setti if (!settings.NoLogging) Analytics.Configure(); - return new SettingsValidator().Validate(settings); + return SettingsValidator.Validate(settings); } public override async Task ExecuteAsync(CommandContext context, Settings settings) diff --git a/src/Refitter/SettingsValidator.cs b/src/Refitter/SettingsValidator.cs index d587432f..4f0df347 100644 --- a/src/Refitter/SettingsValidator.cs +++ b/src/Refitter/SettingsValidator.cs @@ -4,9 +4,9 @@ namespace Refitter; -public class SettingsValidator +public static class SettingsValidator { - public ValidationResult Validate(Settings settings) + public static ValidationResult Validate(Settings settings) { if (BothSettingsFilesAreEmpty(settings) || BothSettingsFilesArePresent(settings)) { From a213294f641febf87eae09ccbc038d55289f8cd1 Mon Sep 17 00:00:00 2001 From: Christian Helle Date: Fri, 27 Oct 2023 15:39:52 +0200 Subject: [PATCH 18/18] Enable continue-on-error for Qodana --- .github/workflows/qodana.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/qodana.yml b/.github/workflows/qodana.yml index aab76427..44b85705 100644 --- a/.github/workflows/qodana.yml +++ b/.github/workflows/qodana.yml @@ -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