The decision to implement were:
- https://github.com/OpenAPITools/openapi-generator – because
- has multiple language implementations
- has a WebAPI docker
- Implement one SDK generator per WebAPI –
- it is simpler to generate commands for each one , rather to have multiple on the same ASPIRE resource
- Can wait for 1 WebAPI to become available, rather for all
This said, the usage is pretty simple:
var apiService = builder.AddProject<Projects.WebAPIDocsExtensions_ApiService>("apiservice")
apiService.AddSDKGeneration_openapitools();
And the result is

genClients – saves all the clients in the specified folder ( wwwroot/docs )
html2 – generates code for HTML 2
clients – shows all the programming languages on the ASPIRE log
The code has had the following problems to be solved:
1. Find the clients to send the command
The WebAPI has a HTPP endpoint call to retrieve the languages ( api/gen/clients ) . The result is a JSON array – and the software needs this JSON array to have download the generated SDK in the language. How to retrieve and send this to the command ? Retrieve – easy – use OnResourceReady .
internal class AnnotationClients : IResourceAnnotation
{
public string Text { get; set; } = string.Empty;
public string[] Data { get; set; } = [];
public string Url { get; set; } = string.Empty;
public AnnotationClients(string text)
{
Text = text;
}
}
resource.OnResourceReady(async (res, evt, ct) =>
{
// code omitted to retrieve endpoints of aspire resourceHttpClient client = new();
client.BaseAddress = new Uri(first);
try
{
var response = await client.GetAsync("api/gen/clients");
response.EnsureSuccessStatusCode();
var text = await response.Content.ReadAsStringAsync();
logger?.LogInformation($"!!!Clients: {text}");
var ann = res.Annotations.FirstOrDefault(it => it is AnnotationClients) as AnnotationClients;
if (ann == null)
{
logger?.LogError($"!!!!!!!!Container {name} has no annotation.");
return;
}
ann.Url = first;
ann.Data = text
.Replace("[", "")
.Replace("]", "")
.Replace("\"", "")
.Split(',', StringSplitOptions.RemoveEmptyEntries);
To send to the command , I use an IResourceAnnotation
.WithCommand("genClients", "genClients", async (ctx) =>
{
//code omited for clarity
var ann = resource.Resource.Annotations.FirstOrDefault(it => it is AnnotationClients) as AnnotationClients;
if (ann == null)
{
return new ExecuteCommandResult() { Success = false, ErrorMessage = "No annotation found" };
}
if (ann.Data.Length == 0)
{
return new ExecuteCommandResult() { Success = false, ErrorMessage = "No clients available" };
}
logger?.LogInformation("Available clients:");
HttpClient httpClient = new();
httpClient.BaseAddress = new Uri(ann.Url);
foreach (var client in ann.Data)
2. The docker with OpenAPITools has no acces to localhost , where the WebAPI works. However, because I added ASPIRE reference of OpenAPITools of the WebAPI, ASPIRE has been nice to add a network – hence this code to retrieve the WebAPI base address
var endPoints = project.Resource.GetEndpoints()?.ToArray() ?? [];
var first = endPoints.First(it => it.Url.Contains("http://")).Url.Replace("localhost", "host.docker.internal");
var http = first.EndsWith("/") ? first : first + "/";
2