tareqimbasher / NetPad

A cross-platform C# editor and playground.
MIT License
1.09k stars 56 forks source link

Enhancement: XMLDOC generation features #189

Open rkttu opened 3 months ago

rkttu commented 3 months ago

I've been playing around with the ASP.NET Core runtime capabilities added to NetPad. It's been a very pleasant update and I'm happy to be using it. :-D

Personally, I thought it would be great to be able to render ASP.NET Web APIs using NetPad, so I wrote the following code using Swashbuckle and Swagger.

However, the XMLDOC wasn't being processed as I expected, so I couldn't complete the scenario configuration because I didn't have the XMLDOC file needed to configure Swashbuckle. I couldn't find any options related to XMLDOC generation, so I'm wondering if there is a workaround or if you could add an option in the next version.

Since I am currently using Monaco and Roslyn, which are grammatically capable of recognizing and autocompleting XMLDOCs, I feel this suggestion is appropriate and I am opening an additional issue.

using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder();
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddEndpointsApiExplorer();

builder.Services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo
    {
        Version = "v1",
        Title = "ToDo API",
        Description = "An ASP.NET Core Web API for managing ToDo items",
        TermsOfService = new Uri("https://example.com/terms"),
        Contact = new OpenApiContact
        {
            Name = "Example Contact",
            Url = new Uri("https://example.com/contact")
        },
        License = new OpenApiLicense
        {
            Name = "Example License",
            Url = new Uri("https://example.com/license")
        }
    });

    // using System.Reflection;
    var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
});

var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI(options =>
{
    options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
    options.RoutePrefix = string.Empty;
});

app.MapGet("/todoitems", async (TodoDb db) =>
    await db.Todos.ToListAsync());

app.MapGet("/todoitems/complete", async (TodoDb db) =>
    await db.Todos.Where(t => t.Done).ToListAsync());

app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
    await db.Todos.FindAsync(id)
        is Todo todo
            ? Results.Ok(todo)
            : Results.NotFound());

app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
    db.Todos.Add(todo);
    await db.SaveChangesAsync();

    return Results.Created($"/todoitems/{todo.Id}", todo);
});

app.MapPut("/todoitems/{id}", async (int id, Todo inputTodo, TodoDb db) =>
{
    var todo = await db.Todos.FindAsync(id);

    if (todo is null) return Results.NotFound();

    todo.Name = inputTodo.Name;
    todo.Done = inputTodo.Done;

    await db.SaveChangesAsync();

    return Results.NoContent();
});

app.MapDelete("/todoitems/{id}", async (int id, TodoDb db) =>
{
    if (await db.Todos.FindAsync(id) is Todo todo)
    {
        db.Todos.Remove(todo);
        await db.SaveChangesAsync();
        return Results.NoContent();
    }

    return Results.NotFound();
});

await app.RunAsync("http://localhost:5678");

/* Database Model */

/// <summary>
/// To Do Item
/// </summary>
public class Todo
{
    /// <summary>
    /// Unique ID
    /// </summary>
    public int Id { get; set; }

    /// <summary>
    /// Item Title
    /// </summary>
    public string? Name { get; set; }

    /// <summary>
    /// Done?
    /// </summary>
    public bool Done { get; set; }
}

public class TodoDb : DbContext
{
    public TodoDb(DbContextOptions<TodoDb> options)
        : base(options) { }

    public DbSet<Todo> Todos => Set<Todo>();
}
tareqimbasher commented 2 months ago

Good suggestion. Will add it to a future release.