This commit is contained in:
tsommer committed 2024-02-17 22:35:45 +01:00
1 parent eff1df2df3
commit c7dda52a49
14 files changed
+284 -93

No files matched your search

+57
View File
@@ -0,0 +1,57 @@
using System.Diagnostics.CodeAnalysis;
using System.Text;
using Cocona;
using NJsonSchema.CodeGeneration.CSharp;
using NSwag;
using NSwag.CodeGeneration.CSharp;
using Terminal.Validation;
namespace Terminal.Commands;
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
public class GenerateCommands
{
[Command(Aliases = ["generate"], Description = "Generate a client from an OpenAPI document")]
public async Task Gen(
[Option(shortName: 'a', Description = "The address of the OpenAPI document, e.g. 'https://petstore.swagger.io/v2/swagger.json'")]
Uri address,
[Option(shortName: 'd', Description = "The destination file, e.g. 'PetStoreClient.cs'. You can use a relative or absolute path and use any environment variables.")]
[ValidPath]
string destinationFile,
[Option(shortName: 'n', Description = "The namespace of the generated client, e.g. 'MyApp.API.PetStoreClient'")]
string nameSpace = "API_Client",
[Option(shortName: 'c', Description = "The class name of the generated client, e.g. 'PetStoreClient'. You might want to use '{controller}' as a placeholder for the controller name.")]
string className = "{controller}Client",
[Option(shortName: 'b', Description = "Expose the base URL as a property in the generated client? (Default: false)")]
bool exposeBaseUrl = false,
[Option(shortName: 'i', Description = "The base interface of the generated client(s), e.g. 'IPetStoreClient'")]
string? baseInterface = null)
{
using var httpClient = new HttpClient();
var result = await httpClient.GetAsync(address);
var openAPI = await OpenApiDocument.FromJsonAsync(await result.Content.ReadAsStringAsync());
var generator = new CSharpClientGenerator(openAPI, new CSharpClientGeneratorSettings
{
UseBaseUrl = exposeBaseUrl,
ClientBaseInterface = baseInterface,
GenerateClientInterfaces = baseInterface is not null,
ClassName = className,
CSharpGeneratorSettings =
{
Namespace = nameSpace,
JsonLibrary = CSharpJsonLibrary.SystemTextJson,
}
});
var apiClientCode = generator.GenerateFile();
destinationFile = Environment.ExpandEnvironmentVariables(destinationFile);
await File.WriteAllTextAsync(destinationFile, apiClientCode, Encoding.UTF8);
Console.WriteLine($"Generated client(s) for '{address}' and saved it to '{destinationFile}'");
}
}
+16
View File
@@ -0,0 +1,16 @@
using System.Diagnostics.CodeAnalysis;
using Cocona;
namespace Terminal.Commands;
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
public class VersionCommands
{
[Command(Aliases = ["ver"], Description = "Show the version of the OpenAPI Client Builder")]
public void Version() => ShowVersion();
public static void ShowVersion()
{
Console.WriteLine($"OpenAPI Client Builder v1.0.0, built using .NET {Environment.Version}.");
}
}
+42
View File
@@ -0,0 +1,42 @@
using Cocona;
using Cocona.Application;
using Cocona.Command;
using Cocona.Command.Features;
using Cocona.Help;
using Terminal.Commands;
var builder = CoconaApp.CreateBuilder(args, options =>
{
options.TreatPublicMethodsAsCommands = false;
options.EnableShellCompletionSupport = true;
});
var app = builder.Build();
app.AddCommands<GenerateCommands>();
app.AddCommands<VersionCommands>();
app.AddCommand(PrimaryCommand);
await app.RunAsync();
return;
//
// We define the primary command here, to show our own version information when
// the `--version` option is used, and to show the help index when no option is
// specified.
//
void PrimaryCommand(
bool version,
[FromService] ICoconaAppContextAccessor appContext,
[FromService] ICoconaCommandProvider commandProvider,
[FromService] ICoconaHelpRenderer helpRenderer,
[FromService] ICoconaCommandHelpProvider commandHelpProvider)
{
if (version)
{
VersionCommands.ShowVersion();
return;
}
var commandStack = appContext.Current!.Features.Get<ICoconaCommandFeature>().CommandStack;
Console.Write(helpRenderer.Render(commandHelpProvider.CreateCommandsIndexHelp(commandProvider.GetCommandCollection(), commandStack)));
}
+23
View File
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<AssemblyName>openClientBuilder</AssemblyName>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<PublishReadyToRun>true</PublishReadyToRun>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>partial</TrimMode>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Cocona" Version="2.2.0" />
<PackageReference Include="NSwag.CodeGeneration.CSharp" Version="14.0.3" />
</ItemGroup>
</Project>
+32
View File
@@ -0,0 +1,32 @@
using System.ComponentModel.DataAnnotations;
namespace Terminal.Validation;
public class ValidPathAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object? value, ValidationContext validationContext)
{
if (value is string path)
{
try
{
path = Environment.ExpandEnvironmentVariables(path);
var info = new FileInfo(path);
if (info.Exists)
return ValidationResult.Success!;
using (var stream = info.Create())
stream.Close();
info.Delete();
return ValidationResult.Success!;
}
catch
{
return new ValidationResult($"The path '{path}' is not valid.");
}
}
return new ValidationResult($"The path '{value}' is not valid.");
}
}