Review app (WinForms)¶
src/FdaObservability.App
is a .NET 8 WinForms desktop app for per-interaction drill-down: search the curated triple, review the full
question → generated/executed DAX → answer for any row, and configure scope. It authenticates with the
reviewer's own AAD identity (interactive MSAL — no secret on the client) and reads the Eventhouse through the
stored KQL functions.
App vs dashboard
The app is for deep, single-interaction review (full DAX text, every query in the turn, errors). The
Real-Time Dashboard is for fleet-level trends. Same FdaInteractions table behind both.
Build & run¶
- Open
src/FdaObservability.slnin Visual Studio (ordotnet build), targeting .NET 8 / Windows. - Run. On first launch (or incomplete config) the Configuration dialog appears — fill in identity, Eventhouse, and scope, then Test connection and Save.
- The main review window opens and runs an initial search over the default lookback window.
Requires the .NET 8 SDK and Windows. The reviewer needs Viewer on the observability KQL database.
Project layout¶
FdaObservability.App/
├─ Program.cs # entry point; forces config on first run, then opens MainForm
├─ app.manifest # PerMonitorV2 high-DPI, supportedOS
├─ appsettings.sample.json # seed config copied next to the exe
├─ Models/
│ ├─ AppConfig.cs # persisted user/instance config (no secrets)
│ └─ Interaction.cs # one row of FdaInteractions
├─ Services/
│ ├─ AuthService.cs # interactive AAD (MSAL) → Kusto token
│ ├─ ConfigStore.cs # load/save %APPDATA%\FdaObservability\config.json
│ └─ KustoService.cs # queries via stored KQL functions
└─ Forms/
├─ ConfigForm.cs # configuration dialog (code-built UI)
└─ MainForm.cs # filter bar + results grid + detail pane
Dependencies¶
| Package | Version | Role |
|---|---|---|
Microsoft.Identity.Client |
4.61.3 | Interactive AAD (MSAL) token acquisition |
Microsoft.Azure.Kusto.Data |
12.2.3 | Kusto query provider |
Project flags: net8.0-windows, UseWindowsForms, nullable + implicit usings enabled, PerMonitorV2 high-DPI.
Authentication — AuthService¶
PublicClientApplicationBuilder
.Create(cfg.ClientId)
.WithAuthority(AzureCloudInstance.AzurePublic, cfg.TenantId)
.WithDefaultRedirectUri() // http://localhost for desktop
.Build();
GetKustoTokenAsync requests the https://<cluster>.../.default scope derived from the Eventhouse host. It tries
silent acquisition from the cached account first and only falls back to interactive (SelectAccount) on
MsalUiRequiredException — so the reviewer isn't prompted every launch.
Data access — KustoService¶
Builds a Kusto connection with WithAadUserTokenAuthentication(token) and exposes:
| Method | KQL | Used for |
|---|---|---|
PingAsync |
FdaInteractions \| count |
Connectivity + schema-present test (Configuration dialog) |
SearchAsync(...) |
SearchFdaInteractions(...) |
The filtered results grid |
KpisAsync(from,to) |
inline summarize |
The window KPI line (total / with DAX / errors / unmatched) |
All user-supplied strings are escaped via a single-quote Lit() helper before being interpolated into KQL.
MapRow defensively reads each column by name so a schema addition won't throw.
Configuration — AppConfig / ConfigStore¶
AppConfig is persisted to %APPDATA%\FdaObservability\config.json (no secrets — interactive AAD only). On
first run ConfigStore.Load seeds from appsettings.sample.json shipped next to the exe, if present. IsComplete
(Tenant + Client + Query URI + Database) gates whether the Configuration dialog is forced at startup. Full field
list: Configuration reference.
UI¶
Configuration dialog (ConfigForm)¶
Code-built (no designer) for a self-contained project. Sections: Identity (public-client tenant/client),
Observability Eventhouse (query URI/database), Scope (capacity/workspace/agent), Defaults (lookback /
max rows). Test connection runs PingAsync against the entered values; Save validates IsComplete before
closing.
Main review window (MainForm)¶
flowchart TD
bar["Filter bar — search · from/to · user · status · matched-only · Search · ⚙ Config · KPI line"] --> grid
grid["Results grid — When · User · Host · Question · Status · ms · Match (colour-coded)"] --> detail
detail["Detail pane — meta line · Question · Generated DAX · Executed DAX (all turn queries) · Answer"]
- The filter bar drives
SearchFdaInteractions; Enter in the search box runs the query. A KPI line summarizes the window. - The grid colour-codes
MatchConfidence(Exact green, Windowed amber, Unmatched red) and tintsErrorrows. - The detail pane shows the full triple. Generated/Executed DAX render in a monospace font; when the turn had
multiple executions, all are listed from the
DaxQueriesarray with per-query duration and status. - ⚙ Config reopens the dialog; saving rebuilds the auth + Kusto services and re-runs the search.
Security notes¶
- No secrets are stored client-side; tokens are cached in MSAL's encrypted token cache.
- The app reads only through the stored
FDAfunctions and an inline KPI summarize — all read-only. - Restrict the KQL database (and therefore the app) to authorized reviewers, since prompts/answers may contain sensitive data.