Skip to content

16 02 Protocol Components Reference

Cyberdyne Development edited this page Feb 16, 2026 · 2 revisions

Protocol Components Reference

Complete reference for all 15 Protocol components in FractalDataWorks.UI.Components.Blazor.Protocols.

Overview

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.

How Protocol Components Work

The RenderFragment 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.

State Management

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:

  1. Set IsLoading = true, ErrorMessage = null
  2. Call StateHasChanged() to show loading indicator
  3. Await API client call(s)
  4. Update state properties with results
  5. Set IsLoading = false in finally block
  6. Call StateHasChanged() to re-render with data

API Client Injection

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!;

Usage Pattern in Consuming Pages

@* 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();
    }
}

Protocol Components

ConnectionProvider

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

DataStoreProvider

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

DataSetProvider

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

PipelineProvider

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

ScheduleProvider

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

UserProvider

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.


RoleProvider

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

ConfigurationProvider

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

SchemaProvider

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

DashboardProvider

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.


AnalyticsProvider

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

CalculationProvider

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

DataflowProvider

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

LineageProvider

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

ThemeProvider

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

Creating Custom Protocol Components

To add a new Protocol component, follow this step-by-step pattern.

Step 1: Create the API Client

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);
}

Step 2: Create the Protocol Component

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));
}

Step 3: Register the API Client in DI

In the consuming application's Program.cs:

builder.Services.AddHttpClient<{Domain}ApiClient>(client =>
{
    client.BaseAddress = new Uri(builder.Configuration["ApiBaseUrl"]!);
});

Step 4: Use in a Visual Component

<{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>

Testing Protocol Components

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.

Test Structure

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();
    }
}

What to Test

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)

See Also

Clone this wiki locally