Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/LightQueryProfiler.JsonRpc/JsonRpcServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -429,4 +429,35 @@ public async Task SaveRecentConnectionAsync(
throw;
}
}

/// <summary>
/// Deletes a recent connection by its unique identifier.
/// </summary>
/// <remarks>
/// If no row with the given <paramref name="request"/> Id exists the operation
/// completes silently — SQLite DELETE is a no-op when no rows match.
/// </remarks>
[JsonRpcMethod("DeleteRecentConnectionAsync", UseSingleObjectParameterDeserialization = true)]
public async Task DeleteRecentConnectionAsync(
DeleteRecentConnectionRequest request,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);
cancellationToken.ThrowIfCancellationRequested();

try
{
await _connectionRepository.Delete(request.Id).ConfigureAwait(false);

if (_logger.IsEnabled(LogLevel.Information))
{
_logger.LogInformation("Recent connection deleted: Id={Id}", request.Id);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to delete recent connection: {Id}", request.Id);
throw;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace LightQueryProfiler.JsonRpc.Models;

/// <summary>
/// Request model for deleting a recent connection by its unique identifier.
/// </summary>
public record DeleteRecentConnectionRequest
{
/// <summary>Gets the unique identifier of the connection to delete.</summary>
public required int Id { get; init; }
}
45 changes: 45 additions & 0 deletions tests/LightQueryProfiler.JsonRpc.Tests/JsonRpcServerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,51 @@ public async Task GetRecentConnectionsAsync_WhenConnectionStringModeRow_ReturnsD
Assert.Equal("mydb", dto.InitialCatalog);
}

// ─── DeleteRecentConnectionAsync ────────────────────────────────────────────

[Fact]
public async Task DeleteRecentConnectionAsync_WhenRequestIsNull_ThrowsArgumentNullException()
{
// Arrange
var mockRepo = new Mock<IConnectionRepository>();
var server = new JsonRpcServer(_mockLogger.Object, mockRepo.Object);

// Act & Assert
await Assert.ThrowsAsync<ArgumentNullException>(() =>
server.DeleteRecentConnectionAsync(null!, TestContext.Current.CancellationToken));
}

[Fact]
public async Task DeleteRecentConnectionAsync_WhenValidId_CallsRepositoryDelete()
{
// Arrange
var mockRepo = new Mock<IConnectionRepository>();
mockRepo.Setup(r => r.Delete(It.IsAny<int>())).Returns(Task.CompletedTask);
var server = new JsonRpcServer(_mockLogger.Object, mockRepo.Object);
var request = new DeleteRecentConnectionRequest { Id = 42 };

// Act
await server.DeleteRecentConnectionAsync(request, TestContext.Current.CancellationToken);

// Assert
mockRepo.Verify(r => r.Delete(42), Times.Once);
}

[Fact]
public async Task DeleteRecentConnectionAsync_WhenRepositoryThrows_PropagatesException()
{
// Arrange
var mockRepo = new Mock<IConnectionRepository>();
mockRepo.Setup(r => r.Delete(It.IsAny<int>()))
.ThrowsAsync(new InvalidOperationException("DB error"));
var server = new JsonRpcServer(_mockLogger.Object, mockRepo.Object);
var request = new DeleteRecentConnectionRequest { Id = 99 };

// Act & Assert
await Assert.ThrowsAsync<InvalidOperationException>(() =>
server.DeleteRecentConnectionAsync(request, TestContext.Current.CancellationToken));
}

[Fact]
public async Task StartProfilingAsync_WhenEngineTypeIsZero_IsValidInput()
{
Expand Down
15 changes: 15 additions & 0 deletions vscode-extension/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ All notable changes to the Light Query Profiler extension will be documented in
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.4.0] - 2026-04-21

### Added

- **Recent Connections – Start Profiling button**: Each connection row in the Recent
Connections panel now has a **▶ Start** button. Clicking it opens the profiler panel,
fills the connection form, and starts profiling automatically — no extra click required.
- **Recent Connections – Delete button**: Each connection row now has a **✕ Delete** button
that permanently removes the entry from the local database and refreshes the list in place.
- New `DeleteRecentConnectionAsync` JSON-RPC endpoint in the .NET backend that delegates to
the existing `ConnectionRepository.Delete(int id)` implementation.
- New `deleteRecentConnection(id)` method on `ProfilerClient` for TypeScript consumers.
- New `startProfilingWithConnection(connection)` public method on `ProfilerPanelProvider`
to support programmatic connection-fill-and-start from external providers.

## [1.3.0] - 2026-04-xx

### Added
Expand Down
2 changes: 1 addition & 1 deletion vscode-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "light-query-profiler",
"displayName": "Light Query Profiler",
"description": "SQL Server and Azure SQL Database query profiler for VS Code",
"version": "1.3.0",
"version": "1.4.0",
"publisher": "brandochn",
"author": {
"name": "Hildebrando Chávez",
Expand Down
Loading
Loading