Skip to content
This repository has been archived by the owner on May 9, 2022. It is now read-only.

Commit

Permalink
Document and seal classes
Browse files Browse the repository at this point in the history
  • Loading branch information
nmklotas committed Sep 15, 2017
1 parent db98c2b commit 2d42dd7
Show file tree
Hide file tree
Showing 43 changed files with 281 additions and 83 deletions.
Binary file modified .editorconfig
Binary file not shown.
61 changes: 50 additions & 11 deletions src/GitLabApiClient/GitLabClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,82 @@
using System.Threading.Tasks;
using GitLabApiClient.Http;
using GitLabApiClient.Models.Users;
using GitLabApiClient.Utilities;

namespace GitLabApiClient
{
public class GitLabClient
/// <summary>
/// A client for the GitLab API v4. You can read more about the api here: https://docs.gitlab.com/ce/api/README.html.
/// </summary>
public sealed class GitLabClient
{
private readonly GitLabHttpFacade _httpFacade;

/// <summary>
/// Creates a new instance of the GitLab API v4 client pointing to the specified hostUrl.
/// </summary>
/// <param name="hostUrl">Host address of GitLab instance. For example https://gitlab.example.com or https://gitlab.example.com/api/v4/ </param>
/// <param name="authenticationToken">Personal access token. Obtained from GitLab profile settings.</param>
public GitLabClient(string hostUrl, string authenticationToken = "")
{
if (string.IsNullOrEmpty(hostUrl))
throw new ArgumentException(nameof(hostUrl));

if (authenticationToken == null)
throw new ArgumentNullException(nameof(authenticationToken));
Guard.NotEmpty(hostUrl, nameof(hostUrl));
Guard.NotNull(authenticationToken, nameof(authenticationToken));

_httpFacade = new GitLabHttpFacade(
hostUrl,
PrefixBaseUrl(hostUrl),
authenticationToken);

var projectQueryBuilder = new ProjectsQueryBuilder();
var projectIssuesQueryBuilder = new ProjectIssuesQueryBuilder();
var issuesQueryBuilder = new IssuesQueryBuilder();
var mergeRequestsQueryBuilder = new MergeRequestsQueryBuilder();
var projectMergeRequestsQueryBuilder = new ProjectMergeRequestsQueryBuilder();

Issues = new IssuesClient(_httpFacade, issuesQueryBuilder);
MergeRequests = new MergeRequestsClient(_httpFacade);
Issues = new IssuesClient(_httpFacade, issuesQueryBuilder, projectIssuesQueryBuilder);
MergeRequests = new MergeRequestsClient(_httpFacade, mergeRequestsQueryBuilder, projectMergeRequestsQueryBuilder);
Projects = new ProjectsClient(_httpFacade, projectQueryBuilder);
Users = new UsersClient(_httpFacade);
}

/// <summary>
/// Access GitLab's issues API.
/// </summary>
public IssuesClient Issues { get; }

/// <summary>
/// Access GitLab's merge requests API.
/// </summary>
public MergeRequestsClient MergeRequests { get; }

/// <summary>
/// Access GitLab's projects API.
/// </summary>
public ProjectsClient Projects { get; }

/// <summary>
/// Access GitLab's users API.
/// </summary>
public UsersClient Users { get; }

public Task<Session> LoginAsync(string username, string password) =>
_httpFacade.LoginAsync(username, password);
/// <summary>
/// Authenticates with GitLab API using user credentials.
/// </summary>
public Task<Session> LoginAsync(string username, string password)
{
Guard.NotEmpty(username, nameof(username));
Guard.NotEmpty(password, nameof(password));
return _httpFacade.LoginAsync(username, password);
}

private static string PrefixBaseUrl(string url)
{
if (!url.EndsWith("/", StringComparison.OrdinalIgnoreCase))
url = url + "/";

if (!url.EndsWith("/api/v4", StringComparison.OrdinalIgnoreCase))
url = url + "/api/v4/";

return url;
}
}
}
2 changes: 1 addition & 1 deletion src/GitLabApiClient/GitLabException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace GitLabApiClient
{
[Serializable]
public class GitLabException : Exception
public sealed class GitLabException : Exception
{
public GitLabException()
{
Expand Down
2 changes: 1 addition & 1 deletion src/GitLabApiClient/Http/GitLabApiPagedRequestor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace GitLabApiClient.Http
{
internal class GitLabApiPagedRequestor
internal sealed class GitLabApiPagedRequestor
{
private const int MaxItemsPerPage = 100;

Expand Down
2 changes: 1 addition & 1 deletion src/GitLabApiClient/Http/GitLabHttpFacade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace GitLabApiClient.Http
{
internal class GitLabHttpFacade
internal sealed class GitLabHttpFacade
{
private const string PrivateToken = "PRIVATE-TOKEN";

Expand Down
23 changes: 6 additions & 17 deletions src/GitLabApiClient/Http/GitlabApiRequestor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace GitLabApiClient.Http
{
internal class GitLabApiRequestor
internal sealed class GitLabApiRequestor
{
private readonly HttpClient _client;

Expand All @@ -15,35 +15,35 @@ internal class GitLabApiRequestor
public async Task<T> Put<T>(string url, object data)
{
StringContent content = SerializeToString(data, false);
var responseMessage = await _client.PutAsync(TryFixApiUrl(url), content);
var responseMessage = await _client.PutAsync(url, content);
await EnsureSuccessStatusCode(responseMessage);
return await ReadResponse<T>(responseMessage);
}

public async Task<T> Post<T>(string url, object data = null)
{
StringContent content = SerializeToString(data, true);
var responseMessage = await _client.PostAsync(TryFixApiUrl(url), content);
var responseMessage = await _client.PostAsync(url, content);
await EnsureSuccessStatusCode(responseMessage);
return await ReadResponse<T>(responseMessage);
}

public async Task Delete(string url)
{
var responseMessage = await _client.DeleteAsync(TryFixApiUrl(url));
var responseMessage = await _client.DeleteAsync(url);
await EnsureSuccessStatusCode(responseMessage);
}

public async Task<T> Get<T>(string url)
{
var responseMessage = await _client.GetAsync(TryFixApiUrl(url));
var responseMessage = await _client.GetAsync(url);
await EnsureSuccessStatusCode(responseMessage);
return await ReadResponse<T>(responseMessage);
}

public async Task<Tuple<T, HttpResponseHeaders>> GetWithHeaders<T>(string url)
{
var responseMessage = await _client.GetAsync(TryFixApiUrl(url));
var responseMessage = await _client.GetAsync(url);
await EnsureSuccessStatusCode(responseMessage);
return Tuple.Create(await ReadResponse<T>(responseMessage), responseMessage.Headers);
}
Expand Down Expand Up @@ -80,16 +80,5 @@ private static StringContent SerializeToString(object data, bool ignoreNullValue
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return content;
}

private static string TryFixApiUrl(string url)
{
if (!url.StartsWith("/", StringComparison.OrdinalIgnoreCase))
url = "/" + url;

if (!url.StartsWith("/api/v4", StringComparison.OrdinalIgnoreCase))
url = "/api/v4" + url;

return url;
}
}
}
33 changes: 32 additions & 1 deletion src/GitLabApiClient/IssuesClient.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using GitLabApiClient.Http;
using GitLabApiClient.Models.Issues;

namespace GitLabApiClient
{
/// <summary>
/// Used to query GitLab API to retrieve, modify, create issues.
/// Every call to issues API must be authenticated.
/// <exception cref="GitLabException">Thrown if request to GitLab API does not indicate success</exception>
/// <exception cref="HttpRequestException">Thrown if request to GitLab API fails</exception>
/// </summary>
public sealed class IssuesClient
{
private readonly GitLabHttpFacade _httpFacade;
Expand All @@ -22,9 +29,19 @@ internal IssuesClient(
_projectIssuesQueryBuilder = projectIssuesQueryBuilder;
}

/// <summary>
/// Retrieves project issue.
/// </summary>
public async Task<Issue> GetAsync(int projectId, int issueId) =>
await _httpFacade.Get<Issue>($"/projects/{projectId}/issues/{issueId}");

/// <summary>
/// Retrieves issues from a project.
/// By default retrieves opened issues from all users.
/// </summary>
/// <param name="projectId">Id of the project.</param>
/// <param name="options">Issues retrieval options.</param>
/// <returns>Issues satisfying options.</returns>
public async Task<IList<Issue>> GetAsync(string projectId, Action<ProjectIssuesQueryOptions> options = null)
{
var queryOptions = new ProjectIssuesQueryOptions(projectId);
Expand All @@ -34,6 +51,12 @@ public async Task<IList<Issue>> GetAsync(string projectId, Action<ProjectIssuesQ
return await _httpFacade.GetPagedList<Issue>(url);
}

/// <summary>
/// Retrieves issues from all projects.
/// By default retrieves opened issues from all users.
/// </summary>
/// <param name="options">Issues retrieval options.</param>
/// <returns>Issues satisfying options.</returns>
public async Task<IList<Issue>> GetAsync(Action<IssuesQueryOptions> options = null)
{
var queryOptions = new IssuesQueryOptions();
Expand All @@ -43,10 +66,18 @@ public async Task<IList<Issue>> GetAsync(Action<IssuesQueryOptions> options = nu
return await _httpFacade.GetPagedList<Issue>(url);
}

/// <summary>
/// Creates new issue.
/// </summary>
/// <returns>The newly created issue.</returns>
public async Task<Issue> CreateAsync(CreateIssueRequest request) =>
await _httpFacade.Post<Issue>($"/projects/{request.ProjectId}/issues", request);

public async Task<Issue> EditAsync(UpdateIssueRequest request) =>
/// <summary>
/// Updated existing issue.
/// </summary>
/// <returns>The updated issue.</returns>
public async Task<Issue> UpdateAsync(UpdateIssueRequest request) =>
await _httpFacade.Put<Issue>($"/projects/{request.ProjectId}/issues/{request.IssueId}", request);
}
}
36 changes: 35 additions & 1 deletion src/GitLabApiClient/MergeRequestsClient.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Net.Http;
using System.Threading.Tasks;
using GitLabApiClient.Http;
using GitLabApiClient.Models.Merges;
Expand All @@ -9,6 +9,12 @@

namespace GitLabApiClient
{
/// <summary>
/// Used to query GitLab API to retrieve, modify, accept, create merge requests.
/// Every API call to merge requests must be authenticated.
/// <exception cref="GitLabException">Thrown if request to GitLab API does not indicate success</exception>
/// <exception cref="HttpRequestException">Thrown if request to GitLab API fails</exception>
/// </summary>
public sealed class MergeRequestsClient
{
private readonly GitLabHttpFacade _httpFacade;
Expand All @@ -25,6 +31,13 @@ internal MergeRequestsClient(
_projectMergeRequestsQueryBuilder = projectMergeRequestsQueryBuilder;
}

/// <summary>
/// Retrieves merge request from a project.
/// By default returns opened merged requests created by anyone.
/// </summary>
/// <param name="projectId">Id of the project.</param>
/// <param name="options">Merge requests retrieval options.</param>
/// <returns>Merge requests satisfying options.</returns>
public async Task<IList<MergeRequest>> GetAsync(int projectId, Action<ProjectMergeRequestsQueryOptions> options = null)
{
var projectMergeRequestOptions = new ProjectMergeRequestsQueryOptions(projectId);
Expand All @@ -36,6 +49,12 @@ public async Task<IList<MergeRequest>> GetAsync(int projectId, Action<ProjectMer
return await _httpFacade.GetPagedList<MergeRequest>(query);
}

/// <summary>
/// Retrieves merge request from all projects the authenticated user has access to.
/// By default returns opened merged requests created by anyone.
/// </summary>
/// <param name="options">Merge requests retrieval options.</param>
/// <returns>Merge requests satisfying options.</returns>
public async Task<IList<MergeRequest>> GetAsync(Action<MergeRequestsQueryOptions> options = null)
{
var projectMergeRequestOptions = new MergeRequestsQueryOptions();
Expand All @@ -47,12 +66,24 @@ public async Task<IList<MergeRequest>> GetAsync(Action<MergeRequestsQueryOptions
return await _httpFacade.GetPagedList<MergeRequest>(query);
}

/// <summary>
/// Creates merge request.
/// </summary>
/// <returns>The newly created merge request.</returns>
public async Task<MergeRequest> CreateAsync(CreateMergeRequest request) =>
await _httpFacade.Post<MergeRequest>($"/projects/{request.ProjectId}/merge_requests", request);

/// <summary>
/// Updated merge request
/// </summary>
/// <returns>The updated merge request</returns>
public async Task<MergeRequest> UpdateAsync(UpdateMergeRequest request) =>
await _httpFacade.Put<MergeRequest>($"/projects/{request.ProjectId}/merge_requests/{request.MergeRequestId}", request);

/// <summary>
/// Accepts merge request.
/// <returns>The accepted merge request.</returns>
/// </summary>
public async Task<MergeRequest> AcceptAsync(int projectId, int mergeRequestId, string mergeCommitMessage)
{
Guard.NotEmpty(mergeCommitMessage, nameof(mergeCommitMessage));
Expand All @@ -66,6 +97,9 @@ public async Task<MergeRequest> AcceptAsync(int projectId, int mergeRequestId, s
$"/projects/{projectId}/merge_requests/{mergeRequestId}/merge", commitMessage);
}

/// <summary>
/// Deletes merge request.
/// </summary>
public async Task DeleteAsync(int projectId, int mergeRequestId) =>
await _httpFacade.Delete($"/projects/{projectId}/merge_requests/{mergeRequestId}");

Expand Down
2 changes: 1 addition & 1 deletion src/GitLabApiClient/Models/Issues/CreateIssueRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace GitLabApiClient.Models.Issues
{
public class CreateIssueRequest
public sealed class CreateIssueRequest
{
public CreateIssueRequest(int projectId, string title)
{
Expand Down
2 changes: 1 addition & 1 deletion src/GitLabApiClient/Models/Issues/Issue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace GitLabApiClient.Models.Issues
{
public class Issue : ModifiableObject
public sealed class Issue : ModifiableObject
{
[JsonProperty("confidential")]
public bool Confidential { get; set; }
Expand Down
3 changes: 1 addition & 2 deletions src/GitLabApiClient/Models/Issues/IssuesQueryOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ internal IssuesQueryOptions() {}

/// <summary>
/// Return issues for the given scope.
/// Defaults to issues created by current user (Introduced in GitLab 9.5).
/// Combine with <see cref="Scope"/>
/// Defaults to issues created by anyone. (Introduced in GitLab 9.5).
/// </summary>
public Scope Scope { get; set; }

Expand Down
2 changes: 1 addition & 1 deletion src/GitLabApiClient/Models/Issues/UpdateIssueRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace GitLabApiClient.Models.Issues
{
public class UpdateIssueRequest
public sealed class UpdateIssueRequest
{
public UpdateIssueRequest(int projectId, int issueId)
{
Expand Down
2 changes: 1 addition & 1 deletion src/GitLabApiClient/Models/Merges/CreateMergeRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace GitLabApiClient.Models.Merges
{
public class CreateMergeRequest
public sealed class CreateMergeRequest
{
public CreateMergeRequest(int projectId, string sourceBranch, string targetBranch, string title)
{
Expand Down
2 changes: 1 addition & 1 deletion src/GitLabApiClient/Models/Merges/MergeRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace GitLabApiClient.Models.Merges
{
public class MergeRequest : ModifiableObject
public sealed class MergeRequest : ModifiableObject
{
[JsonProperty("labels")]
public List<string> Labels { get; } = new List<string>();
Expand Down
Loading

0 comments on commit 2d42dd7

Please sign in to comment.