An unofficial .NET client library for SonarQube Server and SonarQube Cloud APIs. Provides version-aware client functionality with automatic API version detection, comprehensive endpoint coverage, and robust error handling. This project is not affiliated with or endorsed by Sonar.
$ dotnet add package SQ.ApiA comprehensive .NET library for interacting with SonarQube Server and SonarQube Cloud APIs. This library provides version-aware client functionality that automatically detects server capabilities and routes requests to the appropriate API endpoints.
| Version | Support Level | V1 API | V2 API | Edition Req | Notes |
|---|---|---|---|---|---|
| SonarQube 8.9 LTS | ✅ Full | ✅ | Community | EOL (End of Life) | |
| SonarQube 9.0-9.8 | ✅ Full | ✅ | Community | EOL | |
| SonarQube 9.9 LTS | ✅ Full | ✅ | Community | Supported until 2027 | |
| SonarQube 10.0-10.1 | ✅ Full | ✅ Basic | Community | Basic V2 endpoints | |
| SonarQube 10.2-10.3 | ✅ Full | ✅ Extended | Community | Extended V2 endpoints | |
| SonarQube 10.4+ | ✅ Full | ✅ Advanced | Community | Advanced V2 endpoints | |
| SonarQube 2025.1 LTA | ✅ Full | ✅ Advanced | Community | Long Term Active | |
| SonarQube 2025.4 | ✅ Full | ✅ Advanced | Community | Latest features | |
| SonarQube 2026.1 LTA | ✅ Full | ✅ Advanced | Community | Current release | |
| SonarQube Cloud | ✅ Full | ✅ All | Cloud | Always latest |
| Endpoint Group | Minimum Version | Edition Required | Available In |
|---|---|---|---|
| V2 System | 10.0+ | Community | All 10.x+ and 202x+ |
| V2 Users | 10.0+ | Community | All 10.x+ and 202x+ |
| V2 Groups | 10.0+ | Community | All 10.x+ and 202x+ |
| V2 Issues | 10.2+ | Enterprise | 10.2+ Enterprise, SonarQube Cloud |
| Feature | Community | Developer | Enterprise | Data Center | SonarQube Cloud |
|---|---|---|---|---|---|
| V1 API Endpoints | ✅ | ✅ | ✅ | ✅ | ✅ |
| V2 System/Users/Groups | ✅ | ✅ | ✅ | ✅ | ✅ |
| V2 Issues | ❌ | ❌ | ✅ | ✅ | ✅ |
Install-Package SQ.Api
dotnet add package SQ.Api
<PackageReference Include="SQ.Api" Version="1.0.0" />
using Microsoft.Extensions.DependencyInjection;
using SonarQube.API.Extensions;
var services = new ServiceCollection();
// Add required services
services.AddLogging();
services.AddHttpClient();
// Add SonarQube API
services.AddSonarQubeApi("https://sonarcloud.io", "your-token-here");
var serviceProvider = services.BuildServiceProvider();
var client = serviceProvider.GetRequiredService<ISonarQubeClient>();
// Test connection
var isConnected = await client.TestConnectionAsync();
Console.WriteLine($"Connected: {isConnected}");
// Detect version
var versionResult = await client.DetectVersionAsync();
if (versionResult.IsSuccess)
{
Console.WriteLine($"Version: {versionResult.Data.Version}");
Console.WriteLine($"Supports V2 API: {versionResult.Data.Version.SupportsV2Api()}");
}
services.AddSonarQubeApiFactory();
var factory = serviceProvider.GetRequiredService<ISonarQubeClientFactory>();
var options = new SonarQubeClientOptions
{
BaseUrl = "https://sonar.example.com",
Authentication = new AuthenticationOptions { Token = "your-token" }
};
using var client = factory.CreateClient(options);
services.AddSonarQubeApi(options =>
{
options.BaseUrl = "https://sonar.example.com";
options.Authentication.Token = "your-token";
options.TimeoutSeconds = 30;
options.MaxRetryAttempts = 3;
options.VerboseLogging = true;
});
// Automatic version detection
var versionResult = await client.DetectVersionAsync();
if (versionResult.IsSuccess)
{
var version = versionResult.Data;
Console.WriteLine($"Server: {version.ServerUrl}");
Console.WriteLine($"Version: {version.Version.ToVersionString()}");
Console.WriteLine($"Raw Version: {version.RawVersion}");
Console.WriteLine($"Is SonarCloud: {version.IsSonarCloud}");
}
// Use auto-selected endpoints (recommended)
var statusResult = await client.Auto.System.GetStatusAsync();
// Use specific API versions
if (client.V2 != null)
{
var v2StatusResult = await client.V2.System.GetStatusAsync();
}
if (client.V1 != null)
{
var v1StatusResult = await client.V1.System.GetStatusAsync();
}
try
{
var result = await client.Auto.System.GetStatusAsync();
if (result.IsSuccess)
{
// Handle success
var status = result.Data;
}
else
{
// Handle API error
Console.WriteLine($"API Error: {result.ErrorMessage}");
}
}
catch (SonarQubeApiException ex)
{
// Handle HTTP/network errors
Console.WriteLine($"HTTP Error: {ex.Message}");
Console.WriteLine($"Status Code: {ex.StatusCode}");
}
The library automatically validates version and edition requirements before making API calls:
using SonarQube.API.Common.Exceptions;
using SonarQube.API.Common.Version;
try
{
// This endpoint requires 10.2+ Enterprise edition
var result = await client.V2.Issues.GetFixSuggestionsAsync(request);
Console.WriteLine($"Fix suggestions: {result.Data}");
}
catch (VersionNotSupportedException ex)
{
Console.WriteLine($"Version Error: {ex.Message}");
Console.WriteLine($"Current: {ex.CurrentVersion.ToVersionString()}");
Console.WriteLine($"Required: {ex.RequiredVersion.ToVersionString()}");
Console.WriteLine("Upgrade your SonarQube instance to use this feature.");
}
catch (EditionNotSupportedException ex)
{
Console.WriteLine($"Edition Error: {ex.Message}");
Console.WriteLine($"Current: {ex.CurrentEdition.ToEditionString()}");
Console.WriteLine($"Required: {ex.RequiredEdition.ToEditionString()}");
Console.WriteLine("Upgrade to Enterprise Edition to use this feature.");
}
Checking Server Capabilities:
// Detect server version and capabilities
var versionResult = await client.DetectVersionAsync();
if (versionResult.IsSuccess)
{
var info = versionResult.Data;
Console.WriteLine($"Version: {info.Version.ToVersionString()}");
Console.WriteLine($"Edition: {info.Edition.ToEditionString()}");
Console.WriteLine($"Supports V1 API: {info.SupportsV1Api()}");
Console.WriteLine($"Supports V2 API: {info.SupportsV2Api()}");
}
public class SonarQubeClientOptions
{
/// <summary>The base URL of the SonarQube instance (required)</summary>
public string BaseUrl { get; set; }
/// <summary>Authentication settings (required)</summary>
public AuthenticationOptions Authentication { get; set; }
/// <summary>HTTP timeout in seconds (default: 30)</summary>
public int TimeoutSeconds { get; set; } = 30;
/// <summary>Maximum retry attempts for failed requests (default: 3)</summary>
public int MaxRetryAttempts { get; set; } = 3;
/// <summary>User agent string for HTTP requests</summary>
public string UserAgent { get; set; } = "SonarQube.API";
/// <summary>Enable verbose request/response logging</summary>
public bool VerboseLogging { get; set; } = false;
/// <summary>Validate SSL certificates (default: true)</summary>
public bool ValidateSslCertificate { get; set; } = true;
/// <summary>Retry policy settings</summary>
public RetryPolicySettings RetryPolicy { get; set; } = new();
}
public class AuthenticationOptions
{
/// <summary>Authentication token for SonarQube Server/SonarQube Cloud</summary>
public string Token { get; set; }
/// <summary>Username for basic authentication (legacy)</summary>
public string? Username { get; set; }
/// <summary>Password for basic authentication (legacy)</summary>
public string? Password { get; set; }
}
public class RetryPolicySettings
{
/// <summary>Maximum number of retry attempts (default: 3)</summary>
public int MaxRetryAttempts { get; set; } = 3;
/// <summary>Base delay between retries in milliseconds (default: 1000)</summary>
public int BaseDelayMilliseconds { get; set; } = 1000;
/// <summary>Maximum delay between retries in milliseconds (default: 30000)</summary>
public int MaxDelayMilliseconds { get; set; } = 30000;
/// <summary>Exponential backoff multiplier (default: 2.0)</summary>
public double BackoffMultiplier { get; set; } = 2.0;
}
services.AddHttpClient<ISonarQubeClient>(client =>
{
client.Timeout = TimeSpan.FromSeconds(60);
client.DefaultRequestHeaders.Add("Custom-Header", "Value");
});
services.AddSonarQubeApi(options =>
{
options.BaseUrl = "https://sonar.example.com";
options.Authentication.Token = "your-token";
});
// Check server version and capabilities
var versionResult = await client.DetectVersionAsync();
if (versionResult.IsSuccess)
{
var version = versionResult.Data;
if (version.Version.SupportsV2Api())
{
// Use V2 endpoints for enhanced features
var fixSuggestions = await client.V2.Issues.GetFixSuggestionsAsync(request);
}
else
{
// Fall back to V1 endpoints
var issues = await client.V1.Issues.SearchAsync(request);
}
}
// Manual pagination
var request = new ProjectSearchRequest
{
PageIndex = 1,
PageSize = 100
};
var result = await client.V1.Projects.SearchAsync(request);
if (result.IsSuccess)
{
var projects = result.Data.Components;
var paging = result.Data.Paging;
Console.WriteLine($"Page {paging.PageIndex} of {paging.Total / paging.PageSize + 1}");
}
using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
try
{
var result = await client.V1.Projects.SearchAsync(request, cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Operation was cancelled");
}
# Run all tests
dotnet test tests/SQ.Api.Tests/
# Run specific test category
dotnet test tests/SQ.Api.Tests/ --filter Category=Integration
# Run with verbose output
dotnet test tests/SQ.Api.Tests/ --logger "console;verbosity=detailed"
The library includes comprehensive integration tests that run against multiple SonarQube versions:
# Run API integration tests against all SonarQube versions
./scripts/run-api-tests-with-coverage.sh
# Run against a specific version
./scripts/run-api-tests-with-coverage.sh --version 2026-1
# Run all tests (unit + API + CLI integration + scenario)
./scripts/run-all-tests-with-coverage.sh
We welcome contributions! This library follows these established patterns:
# Clone the repository
git clone https://github.com/jonlipsky/sonarqube-cli.git
cd sonarqube-cli
# Build the solution
dotnet build
# Run unit tests
dotnet test tests/SQ.Cli.Tests
# Run integration tests (requires Docker and SonarQube license)
./scripts/run-api-tests-with-coverage.sh
This project is licensed under the MIT License - see the LICENSE file for details.