refactor(client): replace hardcoded API URLs with ApiRoutes constants

Centralizes all API endpoint strings in ApiRoutes for consistency and
easier maintenance. Adds Hubs class for SignalR endpoints. Removes
completed plan files.
This commit is contained in:
Joseph Doherty
2026-01-28 08:33:48 -05:00
parent 1e21e33ade
commit daaeba2004
9 changed files with 22 additions and 1368 deletions
File diff suppressed because it is too large Load Diff
@@ -1,329 +0,0 @@
# Remove Pipeline Viewer Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Remove the pipeline viewer feature from the web UI and clean up all supporting code (controllers, DTOs, client services, routes).
**Architecture:** Delete files in dependency order (DTOs/interfaces first would break compilation), so we delete leaf components first (views, client), then API layer, then shared contracts/DTOs. Modify DI registrations and navigation.
**Tech Stack:** Blazor WebAssembly, ASP.NET Core API, C#
---
## Task 1: Remove Client Components (Blazor Pages)
**Files:**
- Delete: `src/JdeScoping.Client/Pages/Admin/PipelineViewer.razor`
- Delete: `src/JdeScoping.Client/Components/Admin/PipelineScheduleSection.razor`
- Delete: `src/JdeScoping.Client/Components/Admin/SqlQueryModal.razor`
**Step 1: Delete PipelineViewer.razor**
```bash
rm src/JdeScoping.Client/Pages/Admin/PipelineViewer.razor
```
**Step 2: Delete PipelineScheduleSection.razor**
```bash
rm src/JdeScoping.Client/Components/Admin/PipelineScheduleSection.razor
```
**Step 3: Delete SqlQueryModal.razor (if exists)**
```bash
rm -f src/JdeScoping.Client/Components/Admin/SqlQueryModal.razor
```
**Step 4: Verify files deleted**
```bash
ls src/JdeScoping.Client/Pages/Admin/ | grep -i pipeline || echo "No pipeline pages remain"
ls src/JdeScoping.Client/Components/Admin/ | grep -i pipeline || echo "No pipeline components remain"
```
Expected: No pipeline-related files in Admin folders.
---
## Task 2: Remove Navigation Link
**Files:**
- Modify: `src/JdeScoping.Client/Layout/MainLayout.razor:16`
**Step 1: Remove the pipeline viewer nav link**
In `MainLayout.razor`, delete this line (around line 16):
```razor
<NavLink class="nav-link" href="/admin/pipeline-viewer">Pipeline Viewer</NavLink>
```
**Step 2: Verify the change**
```bash
grep -n "pipeline-viewer" src/JdeScoping.Client/Layout/MainLayout.razor || echo "Nav link removed"
```
Expected: "Nav link removed"
---
## Task 3: Remove Client Service
**Files:**
- Delete: `src/JdeScoping.Client/Services/PipelineApiClient.cs`
- Modify: `src/JdeScoping.Client/Program.cs:58`
**Step 1: Delete PipelineApiClient.cs**
```bash
rm src/JdeScoping.Client/Services/PipelineApiClient.cs
```
**Step 2: Remove DI registration from Program.cs**
In `src/JdeScoping.Client/Program.cs`, delete this line (around line 58):
```csharp
builder.Services.AddScoped<IPipelineApiClient, PipelineApiClient>();
```
**Step 3: Verify the changes**
```bash
grep -n "PipelineApiClient" src/JdeScoping.Client/Program.cs || echo "DI registration removed"
ls src/JdeScoping.Client/Services/PipelineApiClient.cs 2>/dev/null || echo "File deleted"
```
Expected: Both checks show removal confirmed.
---
## Task 4: Remove API Controller and Mapper
**Files:**
- Delete: `src/JdeScoping.Api/Controllers/PipelineController.cs`
- Delete: `src/JdeScoping.Api/Mapping/PipelineMapper.cs`
- Delete: `src/JdeScoping.Api/Mapping/IPipelineMapper.cs`
- Modify: `src/JdeScoping.Api/DependencyInjection.cs:43`
**Step 1: Delete PipelineController.cs**
```bash
rm src/JdeScoping.Api/Controllers/PipelineController.cs
```
**Step 2: Delete PipelineMapper.cs**
```bash
rm src/JdeScoping.Api/Mapping/PipelineMapper.cs
```
**Step 3: Delete IPipelineMapper.cs**
```bash
rm src/JdeScoping.Api/Mapping/IPipelineMapper.cs
```
**Step 4: Remove DI registration from DependencyInjection.cs**
In `src/JdeScoping.Api/DependencyInjection.cs`, delete this line (around line 43):
```csharp
services.AddSingleton<IPipelineMapper, PipelineMapper>();
```
**Step 5: Verify the changes**
```bash
ls src/JdeScoping.Api/Controllers/PipelineController.cs 2>/dev/null || echo "Controller deleted"
ls src/JdeScoping.Api/Mapping/*Pipeline* 2>/dev/null || echo "Mapper files deleted"
grep -n "PipelineMapper" src/JdeScoping.Api/DependencyInjection.cs || echo "DI registration removed"
```
Expected: All three checks confirm removal.
---
## Task 5: Remove Core DTOs and Interface
**Files:**
- Delete: `src/JdeScoping.Core/Models/Pipelines/` (entire folder)
- Delete: `src/JdeScoping.Core/ApiContracts/IPipelineApiClient.cs`
**Step 1: Delete the Pipelines DTO folder**
```bash
rm -rf src/JdeScoping.Core/Models/Pipelines/
```
**Step 2: Delete IPipelineApiClient.cs**
```bash
rm src/JdeScoping.Core/ApiContracts/IPipelineApiClient.cs
```
**Step 3: Verify deletions**
```bash
ls src/JdeScoping.Core/Models/Pipelines/ 2>/dev/null || echo "Pipelines folder deleted"
ls src/JdeScoping.Core/ApiContracts/IPipelineApiClient.cs 2>/dev/null || echo "Interface deleted"
```
Expected: Both checks confirm deletion.
---
## Task 6: Remove API Routes
**Files:**
- Modify: `src/JdeScoping.Core/ApiContracts/ApiRoutes.cs:155-188`
**Step 1: Remove the Pipelines class from ApiRoutes.cs**
Delete the entire `Pipelines` static class (lines 155-188):
```csharp
/// <summary>
/// Routes for pipeline configuration API endpoints.
/// </summary>
public static class Pipelines
{
/// <summary>Base route for pipeline endpoints.</summary>
public const string Base = "api/pipelines";
/// <summary>Route template for getting a pipeline by name.</summary>
public const string ByName = "{name}";
/// <summary>Route template for getting pipeline status.</summary>
public const string Status = "{name}/status";
/// <summary>Route template for getting pipeline executions.</summary>
public const string Executions = "{name}/executions";
/// <summary>Builds the route to get a specific pipeline config.</summary>
/// <param name="name">The pipeline name to URL-encode.</param>
/// <returns>The formatted route.</returns>
public static string GetByName(string name) => $"api/pipelines/{Uri.EscapeDataString(name)}";
/// <summary>Builds the route to get pipeline status.</summary>
/// <param name="name">The pipeline name to URL-encode.</param>
/// <returns>The formatted route.</returns>
public static string GetStatus(string name) => $"api/pipelines/{Uri.EscapeDataString(name)}/status";
/// <summary>Builds the route to get pipeline executions.</summary>
/// <param name="name">The pipeline name to URL-encode.</param>
/// <param name="count">The number of recent executions to retrieve.</param>
/// <returns>The formatted route.</returns>
public static string GetExecutions(string name, int count = 10) =>
$"api/pipelines/{Uri.EscapeDataString(name)}/executions?count={count}";
}
```
**Step 2: Verify the change**
```bash
grep -n "Pipelines" src/JdeScoping.Core/ApiContracts/ApiRoutes.cs || echo "Pipelines routes removed"
```
Expected: "Pipelines routes removed"
---
## Task 7: Delete Plan Documents
**Files:**
- Delete: `docs/plans/2026-01-07-pipeline-viewer-design.md`
- Delete: `docs/plans/2026-01-07-pipeline-viewer-implementation.md`
**Step 1: Delete design document**
```bash
rm docs/plans/2026-01-07-pipeline-viewer-design.md
```
**Step 2: Delete implementation plan**
```bash
rm docs/plans/2026-01-07-pipeline-viewer-implementation.md
```
**Step 3: Verify deletions**
```bash
ls docs/plans/*pipeline-viewer* 2>/dev/null || echo "Plan documents deleted"
```
Expected: "Plan documents deleted"
---
## Task 8: Build and Verify
**Step 1: Build the solution**
```bash
cd /Users/dohertj2/Desktop/JdeScopingTool/NEW && dotnet build
```
Expected: Build succeeds with no errors.
**Step 2: Search for any remaining pipeline viewer references**
```bash
grep -r "PipelineViewer\|PipelineApiClient\|IPipelineApiClient\|PipelineController\|IPipelineMapper\|PipelineMapper\|pipeline-viewer" src/ --include="*.cs" --include="*.razor" || echo "No references remain"
```
Expected: "No references remain"
**Step 3: Run tests**
```bash
dotnet test
```
Expected: All tests pass.
---
## Task 9: Commit
**Step 1: Stage all changes**
```bash
git add -A
```
**Step 2: Commit**
```bash
git commit -m "$(cat <<'EOF'
refactor(webui): remove pipeline viewer feature
Remove the read-only pipeline viewer from the web UI:
- Delete PipelineViewer.razor page and supporting components
- Delete PipelineController and PipelineMapper from API
- Delete Pipeline DTOs from Core
- Delete PipelineApiClient from Client
- Remove navigation link and DI registrations
- Delete obsolete plan documents
The ConfigManager utility retains pipeline editing capabilities.
EOF
)"
```
---
## Summary
| Task | Action | Files Affected |
|------|--------|----------------|
| 1 | Delete Blazor components | 3 files deleted |
| 2 | Remove nav link | 1 file modified |
| 3 | Remove client service | 1 deleted, 1 modified |
| 4 | Remove API layer | 3 deleted, 1 modified |
| 5 | Remove Core DTOs | 5 files deleted |
| 6 | Remove API routes | 1 file modified |
| 7 | Delete plan docs | 2 files deleted |
| 8 | Build and verify | - |
| 9 | Commit | - |
**Total:** 14 files deleted, 4 files modified
@@ -1,5 +1,6 @@
using System.Net.Http.Json;
using System.Security.Claims;
using JdeScoping.Core.ApiContracts;
using JdeScoping.Core.Models.Auth;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Extensions.Logging;
@@ -63,7 +64,7 @@ public class AuthStateProvider : AuthenticationStateProvider, IAuthStateProvider
{
try
{
var response = await _httpClient.GetAsync("api/auth/me");
var response = await _httpClient.GetAsync(ApiRoutes.Auth.Me);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadFromJsonAsync<UserInfoDto>();
@@ -1,4 +1,5 @@
using System.Net.Http.Json;
using JdeScoping.Core.ApiContracts;
using JdeScoping.Core.ViewModels;
using Microsoft.AspNetCore.Components;
using Radzen;
@@ -88,7 +89,7 @@ public partial class NewSyncRequestDialog : ComponentBase
SyncType = _selectedSyncType!
};
var response = await HttpClient.PostAsJsonAsync("api/manual-sync", createDto);
var response = await HttpClient.PostAsJsonAsync(ApiRoutes.ManualSync.Base, createDto);
if (response.IsSuccessStatusCode)
{
@@ -1,5 +1,6 @@
using System.Net.Http.Json;
using JdeScoping.Client.Auth;
using JdeScoping.Core.ApiContracts;
using JdeScoping.Core.Models.Auth;
namespace JdeScoping.Client.Services;
@@ -43,7 +44,7 @@ public class AuthService : IAuthService
var request = new EncryptedLoginRequest(encryptedData);
// Send encrypted request
var response = await _httpClient.PostAsJsonAsync("api/auth/login", request);
var response = await _httpClient.PostAsJsonAsync(ApiRoutes.Auth.Login, request);
var result = await response.Content.ReadFromJsonAsync<LoginResultModel>();
if (result is null)
@@ -72,7 +73,7 @@ public class AuthService : IAuthService
{
try
{
await _httpClient.PostAsync("api/auth/logout", null);
await _httpClient.PostAsync(ApiRoutes.Auth.Logout, null);
}
catch
{
@@ -1,5 +1,6 @@
using System.Net.Http.Json;
using System.Text.Json;
using JdeScoping.Core.ApiContracts;
using JdeScoping.Core.Models.Auth;
using Microsoft.JSInterop;
@@ -57,7 +58,7 @@ public class CryptoService : ICryptoService, IAsyncDisposable
if (_cachedPublicKeyPem is not null)
return _cachedPublicKeyPem;
var response = await _httpClient.GetFromJsonAsync<PublicKeyResponse>("api/auth/public-key")
var response = await _httpClient.GetFromJsonAsync<PublicKeyResponse>(ApiRoutes.Auth.PublicKey)
?? throw new InvalidOperationException("Failed to fetch public key from server");
_cachedPublicKeyPem = response.PublicKeyPem;
@@ -1,3 +1,4 @@
using JdeScoping.Core.ApiContracts;
using JdeScoping.Core.ViewModels;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.SignalR.Client;
@@ -50,7 +51,7 @@ public class HubConnectionService : IHubConnectionService, IAsyncDisposable
// In Blazor WebAssembly, the browser automatically sends cookies with requests
// to the same origin, so we don't need to configure any special auth options
_hubConnection = new HubConnectionBuilder()
.WithUrl(_navigationManager.ToAbsoluteUri("/hubs/status"))
.WithUrl(_navigationManager.ToAbsoluteUri(ApiRoutes.Hubs.Status))
.WithAutomaticReconnect([
TimeSpan.Zero,
TimeSpan.FromSeconds(2),
@@ -1,4 +1,5 @@
using System.Net.Http.Json;
using JdeScoping.Core.ApiContracts;
using JdeScoping.Core.Models.Infrastructure;
namespace JdeScoping.Client.Services;
@@ -30,10 +31,8 @@ public class RefreshStatusService : IRefreshStatusService
{
try
{
var minDtStr = minDt.ToString("yyyy-MM-dd");
var maxDtStr = maxDt.ToString("yyyy-MM-dd");
var result = await _httpClient.GetFromJsonAsync<List<DataUpdateDto>>(
$"api/refresh-status?minDT={minDtStr}&maxDT={maxDtStr}");
ApiRoutes.RefreshStatus.Get(minDt, maxDt));
return result ?? [];
}
catch (Exception ex)
@@ -192,4 +192,13 @@ public static class ApiRoutes
/// <summary>Route to reload pipelines.</summary>
public const string Reload = "api/pipelines/reload";
}
/// <summary>
/// Routes for SignalR hub endpoints.
/// </summary>
public static class Hubs
{
/// <summary>Route for the status hub (real-time search updates).</summary>
public const string Status = "/hubs/status";
}
}