Fly.Sdk.Reporting
1.2.3
dotnet add package Fly.Sdk.Reporting --version 1.2.3
NuGet\Install-Package Fly.Sdk.Reporting -Version 1.2.3
<PackageReference Include="Fly.Sdk.Reporting" Version="1.2.3" />
<PackageVersion Include="Fly.Sdk.Reporting" Version="1.2.3" />
<PackageReference Include="Fly.Sdk.Reporting" />
paket add Fly.Sdk.Reporting --version 1.2.3
#r "nuget: Fly.Sdk.Reporting, 1.2.3"
#:package Fly.Sdk.Reporting@1.2.3
#addin nuget:?package=Fly.Sdk.Reporting&version=1.2.3
#tool nuget:?package=Fly.Sdk.Reporting&version=1.2.3
Fly.Sdk.Reporting
Shared wire contract + query executor for FlyOS dashboard reports.
Power-BI–style queryable datasets composable from the desktop shell's Reports Builder.
The wire shape (DatasetSchema, DatasetQuery, RenderResponse) is identical on every
backend that opts into the surface, so the same builder UI works against every app
without per-app frontend code.
Breaking change in 1.0.0:
ChartResponse/ChartSeriesPoint/ChartDataset/ChartWarning/ChartResult/ChartTypeswere renamed toRenderResponse/SeriesPoint/SeriesDataset/RenderWarning/RenderResult/RenderTypes(and theChartTypeproperty toRenderType) to drop "chart" terminology from the reports domain. There is no compat shim — external consumers of this package must update on upgrade.
What's in the box
| Namespace | What it gives you |
|---|---|
Fly.Sdk.Reporting.Wire |
DatasetSchema, FieldSchema, DatasetQuery, DatasetFilter, DatasetDimension, DatasetMeasure, RenderResponse (+ typed factories: Bar / Pie / Doughnut / PolarArea / Radar / Line / Scatter / Table / MultiDataset / Gauge), RenderResult (the IActionResult wrapper), SeriesPoint, TableColumn, SeriesDataset, RenderWarning, plus the closed enum constants (FilterOp, Aggregation, TimeBucket, FieldRole, FieldType, RenderTypes). These are the only types you need to consume or produce a dataset query on the wire. |
Fly.Sdk.Reporting.Execution |
QueryExecutor (validates + runs a DatasetQuery against your DbSet<T>), FieldBinding<TEntity, TValue> (declarative field-to-property mapping), DatasetBindings<TEntity> (per-dataset bundle), QueryValidationException (errors with machine-readable codes), RelativeTime (resolves now-30d-style tokens at execution time). |
Quick start (business app side)
Declare a dataset in your
manifest.jsonunderdashboardDatasets— seeskills/dashboard-reports.mdfor the full schema.Declare bindings:
public static DatasetBindings<Signal> Bindings() => new("signals", [ new FieldBinding<Signal, string?>("source", FieldType.String, s => s.Source), new FieldBinding<Signal, string?>("pestle", FieldType.String, s => s.Pestle), new FieldBinding<Signal, double?>("confidence", FieldType.Number, s => s.Confidence), new FieldBinding<Signal, DateTime>("createdAt", FieldType.Datetime, s => s.CreatedAt), ]);Add the query endpoint:
[HttpPost("datasets/signals/query")] public async Task<IActionResult> Query([FromBody] DatasetQuery q, CancellationToken ct) { // Schema + bindings provided by DI; tenant filter already applied to DbSet via global filter. var result = await _executor.ExecuteAsync(q, _schema, SignalBindings.Bindings(), _db.Signals, ct); return result.ToActionResult(); // <-- sidesteps any inherited Ok<T> override }Why
ToActionResult()and notOk(result)? If your controller inherits from a base class that overridesOk<T>to wrap the value in an envelope (e.g.ApiResponse<T>),return Ok(result)ships the wrapped shape. The Dashboard proxy forwards the body verbatim and the renderer readsresp.seriesdirectly, so an envelope-wrapped body silently breaks every report card with no exception logged.ToActionResult()returns aRenderResult(anIActionResultimplementation) that the framework dispatches directly — noOk()override can interpose.Or use the typed factories when you build the response by hand:
return RenderResponse.Bar("status", series).ToActionResult(); return RenderResponse.Line("month", points, truncated: true, warnings: warnings).ToActionResult(); return RenderResponse.Table("worklist", columns, rows).ToActionResult(); return RenderResponse.MultiDataset(RenderTypes.Bar, "status", datasets).ToActionResult(); return RenderResponse.Scatter("conf-vs-time", xyPoints).ToActionResult(); return RenderResponse.Gauge(42, "total").ToActionResult();The factory enforces shape at the call site —
Scatterrejects points missingX,Tablerejects empty columns — so wrong-shape envelopes never reach the renderer.
Design notes
- Closed-by-default surface. Validators reject any field key not in the schema, any
op not in
FilterOp.All, any agg not in the field's declaredAggslist. There's no way to escape into aRawSqlorInclude. - In-memory aggregation, SQL filtering. Filters push down to EF Core; aggregation happens after the (RowCap-bounded) fetch. Pragmatic v1 trade-off — push-down optimisation for common shapes is a v1.1 follow-up.
- Relative time tokens (
now-30d) resolve at execution time, not save time. A saved "last 30 days" report re-evaluates the window on every render. - Tenant scoping is the caller's responsibility. Pass in your tenant-filtered
IQueryable; the executor doesn't second-guess. - Cerbos authorization is the caller's responsibility. The schema's
Permissionslist is metadata for the builder UI, not enforced server-side.
See skills/dashboard-reports.md for full architecture, security, and rollout guidance.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.EntityFrameworkCore (>= 10.0.9)
- Microsoft.EntityFrameworkCore.Relational (>= 10.0.9)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.2.3 | 88 | 8/4/2026 |