Initial
This commit is contained in:
14 files changed
+284
-93
No files matched your search
@@ -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}'");
|
||||
}
|
||||
}
|
||||
@@ -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}.");
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user