-
Notifications
You must be signed in to change notification settings - Fork 0
16 02 Protocol Components Reference
Complete reference for all 15 Protocol components in FractalDataWorks.UI.Components.Blazor.Protocols.
Protocol components are headless Blazor components that encapsulate domain logic, state management, and API communication without rendering any HTML. They follow the RenderFragment<T> pattern to pass themselves as context to child visual components, enabling the same logic to power MudBlazor, Tailwind CSS, or any other rendering framework.
For the architectural rationale and layering diagram, see 13-01 Headless UI Pattern.
Every Protocol component has exactly the same rendering structure:
@if (ChildContent != null)
{
@ChildContent(this)
}
@code {
[Parameter] public RenderFragment<ConnectionProvider>? ChildContent { get; set; }
}The component passes this (itself) as the context value. Child markup receives it via the Context directive and can access all public state properties and methods.
All Protocol components share a common state pattern:
| Property | Type | Purpose |
|---|---|---|
IsLoading |
bool |
True while any async operation is in progress |
ErrorMessage |
string? |
Set to the exception message on failure, null on success |
SearchString |
string |
Client-side filter text (on components that support filtering) |
State transitions follow a consistent lifecycle:
- Set
IsLoading = true,ErrorMessage = null - Call
StateHasChanged()to show loading indicator - Await API client call(s)
- Update state properties with results
- Set
IsLoading = falseinfinallyblock - Call
StateHasChanged()to re-render with data
Protocol components receive typed API clients via Blazor's [Inject] attribute. These clients are registered in DI by the consuming application and handle HTTP communication, serialization, and base URL routing.
[Inject] private ConnectionApiClient ConnectionApi { get; set; } = default!;@* MudBlazor example *@
<ConnectionProvider @ref="_provider">
<ChildContent Context="logic">
@if (logic.IsLoading)
{
<MudProgressLinear Indeterminate="true" />
}
else if (logic.ErrorMessage != null)
{
<MudAlert Severity="Severity.Error">@logic.ErrorMessage</MudAlert>
}
else
{
<MudTable Items="@logic.FilteredConnections">
<RowTemplate>
<MudTd>@context.Name</MudTd>
<MudTd>@context.ConnectionType</MudTd>
</RowTemplate>
</MudTable>
}
</ChildContent>
</ConnectionProvider>
@code {
private ConnectionProvider? _provider;
protected override async Task OnInitializedAsync()
{
if (_provider != null)
await _provider.LoadData();
}
}Manages Connection CRUD operations, connection type discovery, and connectivity testing.
Injected Clients:
-
ConnectionApiClient-- Connection CRUD and test operations -
ConfigurationApiClient-- Connection type metadata browsing
State Properties:
| Property | Type | Description |
|---|---|---|
Connections |
List<ConnectionDto> |
All loaded connections |
ConnectionTypes |
List<ConfigurationTypeSummary> |
Available connection type definitions |
IsLoading |
bool |
Loading indicator |
ErrorMessage |
string? |
Last error message |
SearchString |
string |
Filter text for FilteredConnections
|
FilteredConnections |
IEnumerable<ConnectionDto> |
Connections filtered by name or type (computed) |
Operations:
| Method | Returns | Description |
|---|---|---|
LoadData(ct) |
Task |
Loads connections and connection types concurrently via Task.WhenAll
|
GetConnectionDetails(name, ct) |
Task<ConnectionDetailResponse?> |
Fetches detailed configuration for a single connection |
CreateConnection(request, ct) |
Task<ConnectionDetailResponse?> |
Creates a connection and reloads the list on success |
UpdateConnection(name, request, ct) |
Task<ConnectionDetailResponse?> |
Updates a connection and reloads the list on success |
DeleteConnection(name, ct) |
Task<bool> |
Deletes a connection and removes it from the local list |
TestConnection(name, ct) |
Task<TestConnectionResponse?> |
Tests connectivity and updates IsHealthy/LastTestedAt on the local DTO |
Manages DataStore CRUD operations and container discovery for schema introspection.
Injected Clients:
-
DataStoreApiClient-- DataStore CRUD and container discovery
State Properties:
| Property | Type | Description |
|---|---|---|
DataStores |
List<DataStoreSummaryDto> |
All loaded data stores |
IsLoading |
bool |
Loading indicator |
ErrorMessage |
string? |
Last error message |
SearchString |
string |
Filter text for FilteredDataStores
|
FilteredDataStores |
IEnumerable<DataStoreSummaryDto> |
Data stores filtered by name or connection name (computed) |
Operations:
| Method | Returns | Description |
|---|---|---|
LoadDataStores(ct) |
Task |
Loads all data stores |
GetDataStoreDetails(name, ct) |
Task<DataStoreDetailDto?> |
Fetches detailed configuration for a single data store |
CreateDataStore(request, ct) |
Task<DataStoreDetailDto?> |
Creates a data store with path configuration and reloads |
UpdateDataStore(name, request, ct) |
Task<DataStoreDetailDto?> |
Updates a data store and reloads |
DeleteDataStore(name, ct) |
Task<bool> |
Deletes a data store and removes it from the local list |
DiscoverContainers(connectionName, path, ct) |
Task<IReadOnlyList<ContainerDiscoveryResult>?> |
Discovers available containers (tables/schemas) at a connection path |
Manages DataSet CRUD operations with support for a detail-view "current" item.
Injected Clients:
-
DataSetApiClient-- DataSet CRUD operations
State Properties:
| Property | Type | Description |
|---|---|---|
DataSets |
List<DataSetSummaryDto> |
All loaded data sets |
CurrentDataSet |
DataSetDetailDto? |
The currently selected data set's detail view |
IsLoading |
bool |
Loading indicator |
ErrorMessage |
string? |
Last error message |
SearchString |
string |
Filter text for FilteredDataSets
|
FilteredDataSets |
IEnumerable<DataSetSummaryDto> |
Data sets filtered by name or category (computed) |
Operations:
| Method | Returns | Description |
|---|---|---|
LoadDataSets(ct) |
Task |
Loads all data sets (summary list) |
LoadDataSet(name, ct) |
Task |
Loads a single data set's details into CurrentDataSet
|
CreateDataSet(request, ct) |
Task<DataSetDetailDto?> |
Creates a data set and reloads the list |
UpdateDataSet(name, request, ct) |
Task<DataSetDetailDto?> |
Updates a data set, reloads the list, and refreshes CurrentDataSet if it matches |
DeleteDataSet(name, ct) |
Task<bool> |
Deletes a data set, removes it from the list, and clears CurrentDataSet if it matches |
Manages Pipeline CRUD, execution triggering, and pipeline status tracking.
Injected Clients:
-
IPipelineClient-- Pipeline configuration CRUD (targets ApiSolution) -
IPipelineJobClient-- Job execution trigger and status (targets EtlServer)
State Properties:
| Property | Type | Description |
|---|---|---|
Pipelines |
List<PipelineDto> |
All loaded pipelines with status |
IsLoading |
bool |
Loading indicator |
ErrorMessage |
string? |
Last error message |
SearchString |
string |
Filter text for FilteredPipelines
|
FilteredPipelines |
IEnumerable<PipelineDto> |
Pipelines filtered by name (computed) |
Operations:
| Method | Returns | Description |
|---|---|---|
LoadPipelines(ct) |
Task |
Loads pipeline list and maps raw API responses to rich PipelineDto objects with status |
GetPipelineDetails(name, ct) |
Task<PipelineDetailResponse?> |
Fetches detailed configuration for a single pipeline |
ExecutePipeline(name, ct) |
Task<ExecutePipelineResponse?> |
Sets status to Running, triggers execution, and reloads on completion (sets Failed on error) |
CreatePipeline(request, ct) |
Task<CreatePipelineResponse?> |
Creates a pipeline and reloads the list |
UpdatePipeline(name, request, ct) |
Task<UpdatePipelineResponse?> |
Updates a pipeline and reloads the list |
DeletePipeline(name, ct) |
Task<bool> |
Deletes a pipeline and removes it from the local list |
Manages Schedule CRUD with pause/resume toggling and pipeline list cross-referencing.
Injected Clients:
-
ScheduleApiClient-- Schedule CRUD and toggle operations -
IPipelineClient-- Pipeline list for schedule-to-pipeline association
State Properties:
| Property | Type | Description |
|---|---|---|
Schedules |
List<ScheduleDto> |
All loaded schedules |
AvailablePipelines |
List<PipelineInfo> |
Pipeline names/types for schedule assignment dropdowns |
IsLoading |
bool |
Loading indicator |
ErrorMessage |
string? |
Last error message |
SearchString |
string |
Filter text for FilteredSchedules
|
FilteredSchedules |
IEnumerable<ScheduleDto> |
Schedules filtered by name or pipeline name (computed) |
Operations:
| Method | Returns | Description |
|---|---|---|
LoadData(ct) |
Task |
Loads schedules and available pipelines concurrently via Task.WhenAll
|
GetScheduleDetails(name, ct) |
Task<ScheduleInfo?> |
Fetches detailed configuration for a single schedule |
PauseSchedule(name, ct) |
Task<bool> |
Pauses a schedule and updates local status to Paused
|
ResumeSchedule(name, ct) |
Task<bool> |
Resumes a schedule and updates local status to Active
|
CreateSchedule(request, ct) |
Task<bool> |
Creates a schedule and reloads all data |
UpdateSchedule(name, request, ct) |
Task<UpdateScheduleResponse?> |
Updates a schedule and reloads all data |
DeleteSchedule(name, ct) |
Task<bool> |
Deletes a schedule and removes it from the local list |
Manages User CRUD with integrated role synchronization logic.
Injected Clients:
-
UserApiClient-- User CRUD, role assignment, and role revocation
State Properties:
| Property | Type | Description |
|---|---|---|
Users |
List<UserSummaryDto> |
All loaded users |
IsLoading |
bool |
Loading indicator |
ErrorMessage |
string? |
Last error message |
SearchString |
string |
Filter text for FilteredUsers
|
FilteredUsers |
IEnumerable<UserSummaryDto> |
Users filtered by username (computed) |
Operations:
| Method | Returns | Description |
|---|---|---|
LoadUsers(ct) |
Task |
Loads all users |
CreateUser(request, ct) |
Task<CreateUserResponse?> |
Creates a user and reloads the list |
UpdateUser(id, request, selectedRoles, initialRoles, ct) |
Task<bool> |
Updates a user and synchronizes role assignments -- computes diff between selectedRoles and initialRoles, then calls AssignUserRole for additions and RevokeUserRole for removals |
DeleteUser(id, ct) |
Task<bool> |
Deletes a user and removes from the local list |
The UpdateUser method is notable for its role synchronization logic: it diffs the current and initial role sets and issues individual assign/revoke API calls for each change, keeping the Protocol as the single source of role management logic.
Manages Role definitions and permission matrix configuration.
Injected Clients:
-
RoleApiClient-- Role CRUD and permission management
State Properties:
| Property | Type | Description |
|---|---|---|
Roles |
List<RoleSummaryDto> |
All loaded roles, ordered by SortOrder
|
PermissionGroups |
List<PermissionGroupDto> |
Permission definitions grouped by category |
IsLoading |
bool |
Loading indicator |
ErrorMessage |
string? |
Last error message |
Operations:
| Method | Returns | Description |
|---|---|---|
LoadRoles(ct) |
Task |
Loads all roles sorted by SortOrder
|
LoadPermissionGroups(ct) |
Task |
Loads permission definitions grouped by category |
GetRoleDetails(name, ct) |
Task<RoleDetailDto?> |
Fetches a role with its assigned permissions |
CreateRole(request, ct) |
Task<RoleDetailDto?> |
Creates a role and reloads the list |
UpdateRole(name, request, ct) |
Task<RoleDetailDto?> |
Updates a role and reloads the list |
DeleteRole(name, ct) |
Task<bool> |
Deletes a role and removes it from the local list |
SavePermissions(roleName, permissionNames, ct) |
Task<bool> |
Replaces all permissions for a role via SetRolePermissions
|
Provides cross-domain configuration instance browsing and management.
Injected Clients:
-
ConfigurationApiClient-- Configuration instance and type metadata operations
State Properties:
| Property | Type | Description |
|---|---|---|
Instances |
List<ConfigurationInstanceSummary> |
All loaded configuration instances |
Types |
List<ConfigurationTypeSummary> |
Available configuration type definitions |
IsLoading |
bool |
Loading indicator |
ErrorMessage |
string? |
Last error message |
SearchString |
string |
Filter text for FilteredInstances
|
FilteredInstances |
IEnumerable<ConfigurationInstanceSummary> |
Instances filtered by name or service type (computed) |
Operations:
| Method | Returns | Description |
|---|---|---|
LoadInstances(category?, ct) |
Task |
Loads configuration instances, optionally filtered by category |
LoadTypes(category, ct) |
Task |
Loads available configuration types for a category (fails silently) |
GetInstanceDetails(category, name, ct) |
Task<ConfigurationInstanceDetail?> |
Fetches detailed configuration for a single instance |
CreateInstance(category, request, ct) |
Task<ConfigurationInstanceDetail?> |
Creates a configuration instance and reloads |
UpdateInstance(category, name, request, ct) |
Task<ConfigurationInstanceDetail?> |
Updates a configuration instance and reloads |
DeleteInstance(category, name, ct) |
Task<bool> |
Deletes a configuration instance and removes from the local list |
Provides schema introspection and data preview capabilities for schema-capable connections.
Injected Clients:
-
SchemaApiClient-- Schema introspection and data preview
State Properties:
| Property | Type | Description |
|---|---|---|
CapableConnections |
List<SchemaCapableConnectionDto> |
Connections that support schema introspection |
IntrospectionResult |
SchemaIntrospectionResponse? |
Result of the last schema introspection |
PreviewResult |
DataPreviewResponse? |
Result of the last data preview query |
IsLoading |
bool |
Loading indicator |
ErrorMessage |
string? |
Last error message |
Operations:
| Method | Returns | Description |
|---|---|---|
LoadCapableConnections(ct) |
Task |
Loads connections that support schema discovery |
Introspect(connectionName, ct) |
Task |
Runs schema introspection on a connection and stores result in IntrospectionResult
|
PreviewData(request, ct) |
Task |
Executes a data preview query and stores result in PreviewResult
|
Aggregates metrics from multiple domain APIs into a unified dashboard view.
Injected Clients:
-
IPipelineClient-- Pipeline counts -
ScheduleApiClient-- Schedule counts and active/inactive status -
ConnectionApiClient-- Connection counts -
AnalyticsApiClient-- Time-range analytics data
State Properties:
| Property | Type | Description |
|---|---|---|
Summary |
DashboardSummaryDto? |
Aggregated counts (pipelines, connections, schedules, active schedules) |
Activities |
List<ActivityEntryDto> |
Recent activity feed (placeholder pending history API migration) |
AnalyticsData |
AnalyticsResponse? |
Analytics data for the requested time range |
IsLoading |
bool |
Loading indicator |
ErrorMessage |
string? |
Last error message |
Operations:
| Method | Returns | Description |
|---|---|---|
LoadDashboard(start, end, ct) |
Task |
Loads pipelines, schedules, connections, and analytics concurrently via Task.WhenAll, then aggregates into Summary
|
This is the most complex Protocol component because it orchestrates four API clients in parallel and performs aggregation logic to compute the DashboardSummaryDto.
Provides analytics data loading with configurable date range.
Injected Clients:
-
AnalyticsApiClient-- Analytics query and retrieval
State Properties:
| Property | Type | Description |
|---|---|---|
Data |
AnalyticsResponse? |
Current analytics response |
IsLoading |
bool |
Loading indicator |
ErrorMessage |
string? |
Last error message |
CurrentRequest |
AnalyticsRequest |
The current request parameters (mutable, includes date range) |
Operations:
| Method | Returns | Description |
|---|---|---|
LoadAnalytics(ct) |
Task |
Loads analytics using CurrentRequest parameters |
UpdatePeriod(start, end, ct) |
Task |
Updates CurrentRequest date range and reloads analytics |
Manages Calculation definitions with formula validation.
Injected Clients:
-
CalculationApiClient-- Calculation CRUD and formula validation
State Properties:
| Property | Type | Description |
|---|---|---|
Calculations |
List<CalculationSummaryDto> |
All loaded calculations |
IsLoading |
bool |
Loading indicator |
ErrorMessage |
string? |
Last error message |
SearchString |
string |
Filter text for FilteredCalculations
|
FilteredCalculations |
IEnumerable<CalculationSummaryDto> |
Calculations filtered by name or target data set (computed) |
Operations:
| Method | Returns | Description |
|---|---|---|
LoadCalculations(ct) |
Task |
Loads all calculation definitions |
GetCalculationDetails(id, ct) |
Task<CalculationDetailDto?> |
Fetches detailed configuration for a calculation by Guid |
CreateCalculation(request, ct) |
Task<CalculationDetailDto?> |
Creates a calculation and reloads the list |
UpdateCalculation(id, request, ct) |
Task<CalculationDetailDto?> |
Updates a calculation and reloads the list |
DeleteCalculation(id, ct) |
Task<bool> |
Deletes a calculation and removes from the local list |
ValidateFormula(formula, dataSet, ct) |
Task<PreviewFormulaResponse?> |
Validates a calculation formula against a target data set without saving |
Provides dataflow graph visualization, lineage traversal, and impact analysis.
Injected Clients:
-
DataflowApiClient-- Dataflow graph, lineage, and impact analysis
State Properties:
| Property | Type | Description |
|---|---|---|
Graph |
DataflowGraphResponse? |
The full dataflow graph |
CurrentLineage |
DataSetLineageResponse? |
Lineage tree for a specific data set |
LastImpactAnalysis |
ImpactAnalysisResponse? |
Result of the last impact analysis |
IsLoading |
bool |
Loading indicator |
ErrorMessage |
string? |
Last error message |
Operations:
| Method | Returns | Description |
|---|---|---|
LoadGraph(ct) |
Task |
Loads the complete dataflow graph |
LoadLineage(datasetName, ct) |
Task |
Loads lineage for a specific data set |
AnalyzeImpact(targetType, targetName, ct) |
Task |
Runs impact analysis for a target entity and stores result in LastImpactAnalysis
|
Provides entity-level and column-level data lineage traversal.
Injected Clients:
-
LineageApiClient-- Entity and column lineage queries
State Properties:
| Property | Type | Description |
|---|---|---|
CurrentGraph |
LineageGraphDto? |
The current lineage graph |
IsLoading |
bool |
Loading indicator |
ErrorMessage |
string? |
Last error message |
Operations:
| Method | Returns | Description |
|---|---|---|
LoadLineage(entityType, entityName, ct) |
Task |
Loads entity-level lineage graph |
LoadColumnLineage(entityType, entityName, fieldName, ct) |
Task |
Loads column-level lineage for a specific field |
Manages UI themes and default theme selection.
Injected Clients:
-
ThemeApiClient-- Theme CRUD and default theme management
State Properties:
| Property | Type | Description |
|---|---|---|
Themes |
List<ThemeSummaryDto> |
All available themes |
CurrentTheme |
ThemeConfiguration? |
The currently active default theme |
IsLoading |
bool |
Loading indicator |
ErrorMessage |
string? |
Last error message |
Operations:
| Method | Returns | Description |
|---|---|---|
LoadThemes(ct) |
Task |
Loads all available themes |
LoadDefaultTheme(ct) |
Task |
Loads the current default theme (fails silently) |
SetDefaultTheme(name, ct) |
Task<bool> |
Sets a theme as default and reloads both themes and default |
DeleteTheme(name, ct) |
Task<bool> |
Deletes a theme and removes from the local list |
To add a new Protocol component, follow this step-by-step pattern.
The API client lives in a per-domain .Clients package and inherits from ApiClientBase:
// In FractalDataWorks.Services.{Domain}.Clients
public class {Domain}ApiClient : ApiClientBase
{
public {Domain}ApiClient(HttpClient httpClient) : base(httpClient) { }
public Task<IGenericResult<IReadOnlyList<{Domain}Dto>>> GetItems(CancellationToken ct = default)
=> GetAsync<IReadOnlyList<{Domain}Dto>>("/{domain}", ct);
}Create a .razor file in Protocols/:
@namespace FractalDataWorks.UI.Components.Blazor.Protocols
@using Microsoft.AspNetCore.Components
@using System.Threading
@using System.Threading.Tasks
@using System.Collections.Generic
@using System.Linq
@using FractalDataWorks.Services.{Domain}.Clients
@using FractalDataWorks.Services.{Domain}.Clients.Models
@using FractalDataWorks.Results
@*
Headless component that provides {Domain} Management logic and state.
*@
@if (ChildContent != null)
{
@ChildContent(this)
}
@code {
[Parameter] public RenderFragment<{Domain}Provider>? ChildContent { get; set; }
// State
public List<{Domain}Dto> Items { get; private set; } = new();
public bool IsLoading { get; private set; }
public string? ErrorMessage { get; private set; }
public string SearchString { get; set; } = "";
[Inject] private {Domain}ApiClient {Domain}Api { get; set; } = default!;
public async Task LoadItems(CancellationToken ct = default)
{
IsLoading = true;
ErrorMessage = null;
StateHasChanged();
try
{
var result = await {Domain}Api.GetItems(ct);
Items = result.Value?.ToList() ?? new();
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
}
finally
{
IsLoading = false;
StateHasChanged();
}
}
public IEnumerable<{Domain}Dto> FilteredItems =>
string.IsNullOrWhiteSpace(SearchString)
? Items
: Items.Where(i => i.Name.Contains(SearchString, StringComparison.OrdinalIgnoreCase));
}In the consuming application's Program.cs:
builder.Services.AddHttpClient<{Domain}ApiClient>(client =>
{
client.BaseAddress = new Uri(builder.Configuration["ApiBaseUrl"]!);
});<{Domain}Provider @ref="_provider">
<ChildContent Context="logic">
@if (logic.IsLoading)
{
<MudProgressLinear Indeterminate="true" />
}
else
{
@foreach (var item in logic.FilteredItems)
{
<MudText>@item.Name</MudText>
}
}
</ChildContent>
</{Domain}Provider>Protocol components are testable by mocking their injected API clients. Since they are pure logic components with no rendering dependencies, tests focus on state transitions and API orchestration.
public class ConnectionProviderTests
{
[Fact]
public async Task LoadDataSetsIsLoadingDuringFetch()
{
// Arrange - mock the API client
var mockApi = Substitute.For<ConnectionApiClient>();
mockApi.GetConnections(Arg.Any<CancellationToken>())
.Returns(GenericResult<IReadOnlyList<ConnectionDto>>.Success(new List<ConnectionDto>()));
var provider = new ConnectionProvider();
// Inject mock via reflection or test helper
// Act
await provider.LoadData(TestContext.Current.CancellationToken);
// Assert
provider.IsLoading.ShouldBeFalse();
provider.ErrorMessage.ShouldBeNull();
provider.Connections.ShouldNotBeNull();
}
}| Concern | Test |
|---|---|
| Loading state |
IsLoading is true during async operation, false after |
| Error handling |
ErrorMessage is set when API throws |
| Data mapping | API response is correctly mapped to state properties |
| Filtering |
FilteredItems respects SearchString with case-insensitive matching |
| Optimistic updates | Local list is updated before reload (e.g., DeleteConnection removes from Connections) |
| Concurrent loading |
Task.WhenAll calls complete correctly (e.g., DashboardProvider) |
- 13-01 Headless UI Pattern -- Architecture overview and package relationships
- 13-02 Creating Consumer Packages -- Building endpoint closures that Protocol components call
- 11-01 Management UI Overview -- Three reference UI implementations using these Protocol components