This repository contains a library for accessing the SonarQube Web API from .NET applications. It supports limited APIs that are used by SonarLint for Visual Studio and Scanner for MSBuild.
The library automatically calls the best API for the connected SonarQube version.
- Request - SonarQube version-specific implementation of a certain Web API. Each request type consists of an interface and one or more implementations. The request objects' properties are converted to query string parameters.
- RequestFactory - a stateful factory for Request objects that is initialized with the version of the connected SonarQube.
- DefaultConfiguration - registers all supported version-Request pairs with the given RequestFactory.
- SonarQubeService - using a preconfigured RequestFactory, creates, configures and executes Requests.
- Create a new interface for the request in
/Api
, containing all needed parameters (could be subset of the supported parameters of the API). - Inherit from
IRequest<T>
when the response is a single object or non-paged collection, orIPagedRequest<T>
when the response is a paged collection. - Create a class in
Models
that represents the return value of the API. - Continue with a New version-specific implementation
- Create a directory (if it does not exist) for the version you want to support in
/Api
. For example,V5_40
. - Create a class in that directory, name it after the interface.
- Inherit from
RequestBase<T>
for simple requests that return non-paged data orPagedRequestBase<T>
for requests that return paged data. - Override the
Path
property, return the relative path of the API. For example if the actual URL ishttp://localhost:9000/api/issues/search
, the value should be:api/issues/search
. - Implement the interface added in the New Request step. Decorate the properties with
JsonProperty
attributes to specify the corresponding query string parameter names. - Create one or more subclasses representing the JSON objects that are returned from the web API. Use
JsonProperty
attributes. - Override one or more methods of the base class to implement the needed functionality. Usually
ParseResponse
should be enough.
- Inherit from
- Add a new method to
ISonarQubeService
and implement it inSonarQubeService
. Usually the implementation should be something like this:
public async Task<SonarQubeSomething> GetSomethingAsync(string param1, int param2, CancellationToken token) =>
await InvokeRequestAsync<IGetSomethingRequest, SonarQubeSomething>(
request =>
{
request.Param1 = param1;
request.Param2 = param2;
...
},
token);
- Add test class for the new service method. Implement tests for the common cases - API returns valid data, API returns error code, etc.
// Create a new instance of the service:
var service = new SonarQubeService(new HttpClientHandler(), "user-agent-string", logger);
// Connect to SonarQube
await service.ConnectAsync(connectionInfo, token);
// Invoke request
var something = await service.GetSomethingAsync(param1, param2, token);
// Disconnect
service.Disconnect();
The service will throw HttpWebException
when it receives a response that cannot be handled. Make sure those exceptions are caught and correctly handled.
When an unexpected HTTP status code is returned the service will throw an exception of type HttpRequestException
, which contains a WebException
as InnerException
. When the WebException
is HttpWebException
, its Response
property could be cast to HttpWebResponse
and examined for various data such as HttpStatusCode
, etc.
Why exception and not status code? Here is a nice list of pros and cons.
NOTE: Some requests handle HTTP status codes differently and do not always throw exception (for example see IGetNotificationsRequest
).
By default all Request
properties will be serialized as query string parameters.
To ignore a non-virtual property use [JsonIgnore]
. To ignore a virtual property do the following:
[JsonProperty("param3", DefaultValueHandling = DefaultValueHandling.Ignore), DefaultValue(null)]
public virtual string Param3
{
get { return null; }
set { /* not supported in this implementation */ }
}
Note: The JsonIgnore
attribute cannot be undone.
Sometimes the Web API would need an argument that should not be user configurable, or it should depend on the value of another parameter. Add it as a normal property just to the implementation class:
[JsonProperty("param5")]
public virtual bool? Param5 => string.IsNullOrWhiteSpace(Param3) ? (bool?)true : null;
Some APIs return or need Java-specific date format. Use the JavaDateConverter
to serialize such properties:
[JsonProperty("from"), JsonConverter(typeof(JavaDateConverter))]
public virtual DateTimeOffset EventsSince { get; set; }
The SonarQube.Client contains a logging interface - ILogger
. To plug your own logger in you need to create an adapter.
Some APIs return protobuf. See the following request for a working implementation V5_10.GetIssuesRequest
.
Some APIs return XML. See the following request for a working implementation V5_20.GetRoslynExportProfileRequest
.