diff --git a/Directory.Packages.props b/Directory.Packages.props index 350a1c8977..4150948457 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -13,13 +13,13 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 252ee8db7c..5bd996634a 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -141,6 +141,7 @@ const config: UserConfig = { { text: 'Json Serialization', link: '/configuration/json' }, { text: 'Resiliency Policies', link: '/configuration/retries' }, { text: 'Command Line Tooling', link: '/configuration/cli' }, + { text: 'JasperFx Commands in the Aspire Dashboard', link: '/configuration/aspire-commands' }, { text: 'Optimized Development Workflow', link: '/configuration/optimized_artifact_workflow' }, { text: 'Publishing with Native AOT', link: '/configuration/aot-publishing' }, { text: 'Multi-Tenancy with Database per Tenant', link: '/configuration/multitenancy' }, diff --git a/docs/configuration/aspire-commands.md b/docs/configuration/aspire-commands.md new file mode 100644 index 0000000000..5cc25f281f --- /dev/null +++ b/docs/configuration/aspire-commands.md @@ -0,0 +1,129 @@ +# JasperFx Commands in the Aspire Dashboard + +The optional **`JasperFx.Aspire`** package adds Marten's command-line verbs as +clickable **custom commands** on each resource tile in the +[.NET Aspire dashboard](https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/dashboard). +With one extension call in your AppHost project, an operator running the +Aspire dashboard against a local or staging environment can run +**`check-env`**, **`describe`**, **`projections`**, or **`resources`** against +a live Marten service without dropping to a terminal — output streams back +into the dashboard's resource console. + +Marten apps inherit this for free because they build on the shared JasperFx +command layer. See also the page on [Command Line Tooling](/configuration/cli) +for the local-terminal workflow and the +[JasperFx.Aspire package README](https://github.com/JasperFx/jasperfx/tree/master/src/JasperFx.Aspire). + +## Quick start + +Add the package to your **Aspire AppHost** project (not the Marten service +project itself): + +```shell +dotnet add package JasperFx.Aspire +``` + +Then opt in on the Marten service resource: + +```csharp +using JasperFx.Aspire; + +var builder = DistributedApplication.CreateBuilder(args); + +builder.AddProject("api") + .WithJasperFxCommands(); +``` + +That adds the **safe-by-default** command buttons — `check-env`, `describe`, +and `codegen` (preview only) — to the `api` resource tile. Click any of them +in the dashboard, the verb runs against the live service with the same +environment Aspire injects, and the output streams into the resource's log +view. + +## The verbs that matter for Marten users + +- **`check-env`** *(read-only)* — runs every registered + [environment check](/configuration/environment-checks). Confirms the Marten + service can reach its Postgres database, that all projection dependencies + are wired, that required schemas exist, etc. +- **`describe`** *(read-only)* — dumps the resolved Marten `StoreOptions` + (document mappings, event store config, projections, retry policies, + tenancy strategy, …). Useful for verifying composite configuration. +- **`resources`** *(mutating)* — applies / patches Marten's schema objects + (`mt_events`, document tables, indexes, functions, projection tables). + Equivalent to `IDocumentStore.Storage.ApplyAllConfiguredChangesToDatabaseAsync()`. +- **`projections`** *(mutating)* — runs or **rebuilds** async projections. + The rebuild path reprocesses the event store — long-running and + disruptive on a populated store. + +## Opting in to mutating verbs + +Mutating verbs are off by default. Adding them is a one-liner: + +```csharp +builder.AddProject("api") + .WithJasperFxCommands(opts => + { + // Adds resources + projections + codegen-write buttons. + opts.IncludeMutatingCommands = true; + }); +``` + +When `IncludeMutatingCommands = true`, every mutating verb requires an +explicit **confirmation dialog** in the Aspire dashboard before it runs. +The default confirmation copy is generic ("Run `projections` on `api`?"); +customize per-verb when the impact is non-obvious: + +```csharp +builder.AddProject("api") + .WithJasperFxCommands(opts => + { + opts.IncludeMutatingCommands = true; + + opts.For("projections").ConfirmationMessage = + "Rebuild ALL projections for 'api'? This reprocesses the entire event store."; + + opts.For("resources").ConfirmationMessage = + "Apply pending schema changes to the 'api' database?"; + }); +``` + +## Per-verb tweaks + +`opts.For("verb")` returns a `JasperFxCommandRegistration` that lets you +override the dashboard presentation per verb: + +| Property | Use | +| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `DisplayName` | Button label (defaults to a humanized verb name). | +| `DisplayDescription` | Tooltip / extended description. | +| `IconName` | Fluent UI icon name; sensible defaults per verb. | +| `ConfirmationMessage` | Required for mutating verbs; setting this opts a non-mutating verb into confirmation too. | +| `IsHighlighted` | Pins the button to the front of the strip. | +| `IsEnabled` | Predicate over the resource's current `ResourceState` — useful for gating verbs to `Running` (or `Running` + the migration-resource-completed state for `projections`). | + +## Adding a single verb + +When the curated default set isn't quite what you want, register verbs +one-at-a-time with `WithJasperFxCommand` instead of the batch helper: + +```csharp +builder.AddProject("api") + .WithJasperFxCommand("projections", "rebuild MyProjection", registration => + { + registration.DisplayName = "Rebuild MyProjection"; + registration.ConfirmationMessage = + "Rebuild MyProjection for 'api'? This reprocesses the event store."; + registration.IsHighlighted = true; + }); +``` + +The second argument is the verb's fixed argument string — handy for +locking a button down to one specific projection rather than exposing the +full `projections` surface. + +## Constraints + +- **`JasperFx.Aspire` runs at the AppHost project layer**, not inside the Marten service itself. Adding it as a `` of the service project is a no-op. +- The verbs run in a **child process** of the Marten service, with the same environment Aspire injects into the resource. If `check-env`, `resources`, or `projections` fail to reach Aspire-managed dependencies, verify the dashboard shows the resource as `Running` first. +- Buttons require `ApplyJasperFxExtensions()` + `RunJasperFxCommandsAsync(args)` to already be wired in the Marten service's `Program.cs` (see [Command Line Tooling](/configuration/cli)). Without that wiring the verb spawn succeeds but the child process won't recognize the verb. diff --git a/docs/configuration/cli.md b/docs/configuration/cli.md index d817b31cb4..04671de42a 100644 --- a/docs/configuration/cli.md +++ b/docs/configuration/cli.md @@ -6,6 +6,13 @@ libraries. For the full reference of available commands, extensibility points, a see the [JasperFx shared libraries documentation](https://shared-libs.jasperfx.net/). ::: +::: tip +Running an AppHost with .NET Aspire? The optional `JasperFx.Aspire` package +surfaces these same verbs as clickable buttons on each resource tile in the +Aspire dashboard — see +[JasperFx Commands in the Aspire Dashboard](/configuration/aspire-commands). +::: + ::: warning The usage of JasperFx commands shown in this document are only valid for applications bootstrapped with the [generic host builder](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host) with Marten registered in the application's IoC container.