aspire
aspire copied to clipboard
Resource grouping annotation for dashboard
Is there an existing issue for this?
- [X] I have searched the existing issues
Is your feature request related to a problem? Please describe the problem.
I'd like to group my resources together and based on the interest expand/collapse them on the dashboard. Groups could be like - but varies project by project - Frontend, Backend, Control Plane, Application Plane, Supporting Services, etc...
Describe the solution you'd like
I'd like to have a GroupAnnotation or something similar that could act as a visual group for resources, the annotation would be interpreted by the dashboard. When I group resources beside naming the group I'd like to specify an order and to expand or collapse by default when the solution starts. Having an icon assigned would be a plus.
Additional context
No response
@mitchdenny for annotation
@maddymontaquila have you heard similar feedback from other folks?
If we were to do this we'd probably want to figure out whether we see this as an exclusive membership thing (you can only be in one group) and whether it should be single level or hierarchical or whether its more of a tagging mechanism.
We'd probably want to see the visual design for this before we do anything in the app model since this would mostly just be a visual thing.
Related #521
No time in 9.3
I created a little helper for myself that does this:
namespace Aspire.Hosting;
static class AspireHostingExtensions
{
public static IResourceBuilder<Resource> AddGroup(this IDistributedApplicationBuilder builder, string name) =>
builder.AddResource(new GroupResource(name))
.WithInitialState(new()
{
State = new(KnownResourceStates.Running, KnownResourceStateStyles.Success),
ResourceType = "Group",
Properties = []
});
public static IResourceBuilder<T> InGroup<T>(this IResourceBuilder<T> builder, IResourceBuilder<IResource> group)
where T : IResource
{
if (builder.Resource.TryGetAnnotationsOfType<ResourceSnapshotAnnotation>(out var annot))
{
foreach (var snapshot in annot)
{
snapshot.InitialSnapshot.GetType().GetProperty("Properties")?.SetValue(snapshot.InitialSnapshot,
snapshot.InitialSnapshot.Properties.Add(new("resource.parentName", "data")));
}
}
else
{
builder.WithInitialState(new()
{
ResourceType = builder.Resource.GetType().Name ?? "Unknown",
Properties =
[
new("resource.parentName", group.Resource.Name),
]
});
}
return builder;
}
class GroupResource(string name) : Resource(name)
{
}
}
This can then be used like this:
var dataGroup = builder.AddGroup("data");
var dbDir = Path.Combine(Directory.GetCurrentDirectory(), "..", "artifacts", "data");
var db = builder.AddSqlite("db", dbDir, "hybridisms.db")
.InGroup(dataGroup);
var sqliteWeb = db.WithSqliteWeb(b => b.InGroup(dataGroup)); // this one is weird because of the way it was written
And it looks like this:
See #1575 , I think it is the exact same as this issue
@mattleibow @lukedukeus what would you think of this API? Would this serve your needs? https://github.com/dotnet/aspire/pull/9425
@adamint I like it, it works for me.
Here's a few ways I think it could be improved:
- I think the API might be more flexible if it was
child.WithParent(parent)rather thanparent.AddChild(child). If you had to write this code the other way around, it wouldn't flow as well:
var databaseServer = builder.AddPostgres("Postgres")
.WithDataVolume()
.WithLifetime(ContainerLifetime.Persistent);
databaseServer.WithPgAdmin(options =>
{
options.WithParent(databaseServer);
options.WithLifetime(ContainerLifetime.Persistent);
}, containerName: "PgAdmin");
- Parent's logs should be a combination of it's childrens' logs, and its state should be a combination of its childrens' states.
- Having a group node is good, but it should be possible to attach a resource to a parent that is not a group node. If I had a database server, multiple databases, and a db browser, I would arrange it like:
-Postgres
-- Database1
-- Database2
-- PgAdmin
rather than
-Group
-- Postgres
-- Database1
-- Database2
-- PgAdmin
@adamint I like it, it works for me.
Here's a few ways I think it could be improved:
- I think the API might be more flexible if it was
child.WithParent(parent)rather thanparent.AddChild(child). If you had to write this code the other way around, it wouldn't flow as well:var databaseServer = builder.AddPostgres("Postgres") .WithDataVolume() .WithLifetime(ContainerLifetime.Persistent); databaseServer.WithPgAdmin(options => { options.WithParent(databaseServer); options.WithLifetime(ContainerLifetime.Persistent); }, containerName: "PgAdmin");
- Parent's logs should be a combination of it's childrens' logs, and its state should be a combination of its childrens' states.
The goal is more so to create an explicit "group" object separate from a resource at all, so I don't know if this approach would work.
- Having a group node is good, but it should be possible to attach a resource to a parent that is not a group node. If I had a database server, multiple databases, and a db browser, I would arrange it like:
-Postgres -- Database1 -- Database2 -- PgAdminrather than
-Group -- Postgres -- Database1 -- Database2 -- PgAdmin
To achieve that structure, you would arrange like this
var appBuilder = DistributedApplication.CreateBuilder();
// create new groups
var postgresGroup = appBuilder.CreateGroup("postgres");
postgresGroup.AddPostgres(...)
var childGroup = appBuilder.CreateGroup("postgres-dependents", postgresGroup);
childGroup.AddPgAdmin(...)
....
appBuilder.Build();