Skip to content

Commit

Permalink
add group labels and issues methods (#99)
Browse files Browse the repository at this point in the history
* #97 Add support for group labels

* #97 Add query options for group label list request

* #97 Add group issue list endpoint

- Add some missing issue query parameters
- Add `GetAllAsync` endpoint to query issue for project, group or all
- Mark `GetAsync` endpoint for issue list (for all and project) as deprecated
- Simplify use of issue query options (no need for specific project version) - exists only for backward compatibility

* Fix formatting

* #97 Small refactorings and documentation

* Fix unit test (#97)

* Fix deprecated calls in tests

Co-authored-by: Joseph Petersen <[email protected]>
  • Loading branch information
WebDucer and jetersen committed Jan 13, 2020
1 parent 9ce4696 commit fb4aae7
Show file tree
Hide file tree
Showing 17 changed files with 444 additions and 138 deletions.
6 changes: 3 additions & 3 deletions src/GitLabApiClient/GitLabClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ public GitLabClient(string hostUrl, string authenticationToken = "")
authenticationToken);

var projectQueryBuilder = new ProjectsQueryBuilder();
var projectIssuesQueryBuilder = new ProjectIssuesQueryBuilder();
var projectIssueNotesQueryBuilder = new ProjectIssueNotesQueryBuilder();
var issuesQueryBuilder = new IssuesQueryBuilder();
var mergeRequestsQueryBuilder = new MergeRequestsQueryBuilder();
var projectMilestonesQueryBuilder = new MilestonesQueryBuilder();
var projectMergeRequestsQueryBuilder = new ProjectMergeRequestsQueryBuilder();
var groupsQueryBuilder = new GroupsQueryBuilder();
var groupLabelsQueryBuilder = new GroupLabelsQueryBuilder();
var projectsGroupsQueryBuilder = new ProjectsGroupQueryBuilder();
var branchQueryBuilder = new BranchQueryBuilder();
var releaseQueryBuilder = new ReleaseQueryBuilder();
Expand All @@ -52,12 +52,12 @@ public GitLabClient(string hostUrl, string authenticationToken = "")
var pipelineQueryBuilder = new PipelineQueryBuilder();
var treeQueryBuilder = new TreeQueryBuilder();

Issues = new IssuesClient(_httpFacade, issuesQueryBuilder, projectIssuesQueryBuilder, projectIssueNotesQueryBuilder);
Issues = new IssuesClient(_httpFacade, issuesQueryBuilder, projectIssueNotesQueryBuilder);
Uploads = new UploadsClient(_httpFacade);
MergeRequests = new MergeRequestsClient(_httpFacade, mergeRequestsQueryBuilder, projectMergeRequestsQueryBuilder);
Projects = new ProjectsClient(_httpFacade, projectQueryBuilder, projectMilestonesQueryBuilder);
Users = new UsersClient(_httpFacade);
Groups = new GroupsClient(_httpFacade, groupsQueryBuilder, projectsGroupsQueryBuilder, projectMilestonesQueryBuilder);
Groups = new GroupsClient(_httpFacade, groupsQueryBuilder, projectsGroupsQueryBuilder, projectMilestonesQueryBuilder, groupLabelsQueryBuilder);
Branches = new BranchClient(_httpFacade, branchQueryBuilder);
Releases = new ReleaseClient(_httpFacade, releaseQueryBuilder);
Tags = new TagClient(_httpFacade, tagQueryBuilder);
Expand Down
93 changes: 72 additions & 21 deletions src/GitLabApiClient/GroupsClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,32 +26,35 @@ public sealed class GroupsClient
private readonly GroupsQueryBuilder _queryBuilder;
private readonly ProjectsGroupQueryBuilder _projectsQueryBuilder;
private readonly MilestonesQueryBuilder _queryMilestonesBuilder;
private readonly GroupLabelsQueryBuilder _queryGroupLabelBuilder;

internal GroupsClient(
GitLabHttpFacade httpFacade,
GroupsQueryBuilder queryBuilder,
ProjectsGroupQueryBuilder projectsQueryBuilder,
MilestonesQueryBuilder queryMilestonesBuilder)
MilestonesQueryBuilder queryMilestonesBuilder,
GroupLabelsQueryBuilder queryGroupLabelBuilder)
{
_httpFacade = httpFacade;
_queryBuilder = queryBuilder;
_projectsQueryBuilder = projectsQueryBuilder;
_queryMilestonesBuilder = queryMilestonesBuilder;
_queryGroupLabelBuilder = queryGroupLabelBuilder;
}

/// <summary>
/// Get all details of a group.
/// This endpoint can be accessed without authentication if the group is publicly accessible.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
public async Task<Group> GetAsync(GroupId groupId) =>
await _httpFacade.Get<Group>($"groups/{groupId}");

/// <summary>
/// Get all subgroups of a group.
/// This endpoint can be accessed without authentication if the group is publicly accessible.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
public async Task<IList<Group>> GetSubgroupsAsync(GroupId groupId) =>
await _httpFacade.GetPagedList<Group>($"groups/{groupId}/subgroups");

Expand Down Expand Up @@ -80,7 +83,7 @@ public async Task<IList<Group>> GetAsync(Action<GroupsQueryOptions> options = nu
/// Get a list of projects in this group.
/// When accessed without authentication, only public projects are returned.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="options">Groups projects retrieval options.</param>
/// <returns>Issues satisfying options.</returns>
public async Task<IList<Project>> GetProjectsAsync(GroupId groupId, Action<ProjectsGroupQueryOptions> options = null)
Expand All @@ -95,7 +98,7 @@ public async Task<IList<Project>> GetProjectsAsync(GroupId groupId, Action<Proje
/// <summary>
/// Get a list of members in this group.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="search">A query string to search for members.</param>
/// <returns>Group members satisfying options.</returns>
public async Task<IList<Member>> GetMembersAsync(GroupId groupId, string search = null)
Expand All @@ -113,7 +116,7 @@ public async Task<IList<Member>> GetMembersAsync(GroupId groupId, string search
/// <summary>
/// Get a list of all members (including inherited) in this group.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="search">A query string to search for members.</param>
/// <returns>Group members satisfying options.</returns>
public async Task<IList<Member>> GetAllMembersAsync(GroupId groupId, string search = null)
Expand All @@ -131,7 +134,7 @@ public async Task<IList<Member>> GetAllMembersAsync(GroupId groupId, string sear
/// <summary>
/// Adds a member to the group.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="request">Create milestone request.</param>
/// <returns>Newly created milestone.</returns>
public async Task<Milestone> CreateMilestoneAsync(GroupId groupId, CreateGroupMilestoneRequest request)
Expand All @@ -143,7 +146,7 @@ public async Task<Milestone> CreateMilestoneAsync(GroupId groupId, CreateGroupMi
/// <summary>
/// Get a list of milestones in this group.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="options">Query options.</param>
public async Task<IList<Milestone>> GetMilestonesAsync(GroupId groupId, Action<MilestonesQueryOptions> options = null)
{
Expand All @@ -157,7 +160,7 @@ public async Task<IList<Milestone>> GetMilestonesAsync(GroupId groupId, Action<M
/// <summary>
/// Retrieves a group milestone by its id.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="milestoneId">Id of the milestone.</param>
public async Task<Milestone> GetMilestoneAsync(GroupId groupId, int milestoneId) =>
await _httpFacade.Get<Milestone>($"groups/{groupId}/milestones/{milestoneId}");
Expand All @@ -174,7 +177,7 @@ public async Task<Group> CreateAsync(CreateGroupRequest request) =>
/// <summary>
/// Adds a user to a group.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="request">Add group member request.</param>
/// <returns>Newly created membership.</returns>
public async Task<Member> AddMemberAsync(GroupId groupId, AddGroupMemberRequest request)
Expand All @@ -186,7 +189,7 @@ public async Task<Member> AddMemberAsync(GroupId groupId, AddGroupMemberRequest
/// <summary>
/// Updates a user's group membership.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="userId">The user ID of the member.</param>
/// <param name="request">Update group member request.</param>
/// <returns>Updated membership.</returns>
Expand All @@ -199,15 +202,15 @@ public async Task<Member> UpdateMemberAsync(GroupId groupId, int userId, AddGrou
/// <summary>
/// Removes a user as a member of the group.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="userId">The user ID of the member.</param>
public async Task RemoveMemberAsync(GroupId groupId, int userId) =>
await _httpFacade.Delete($"groups/{groupId}/members/{userId}");

/// <summary>
/// Transfer a project to the Group namespace. Available only for admin
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="projectId">The ID, path or <see cref="Project"/> of the project.</param>
/// <returns>The newly updated group.</returns>
public async Task<Group> TransferAsync(GroupId groupId, ProjectId projectId) =>
Expand All @@ -218,15 +221,15 @@ public async Task<Group> TransferAsync(GroupId groupId, ProjectId projectId) =>
/// Only available to group owners and administrators.
/// </summary>
/// <returns>The updated group.</returns>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="request">Update group request.</param>
public async Task<Group> UpdateAsync(GroupId groupId, UpdateGroupRequest request) =>
await _httpFacade.Put<Group>($"groups/{groupId}", request);

/// <summary>
/// Updates an existing group milestone.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="milestoneId">The ID of the group's milestone.</param>
/// <param name="request">Update milestone request.</param>
/// <returns>Newly modified milestone.</returns>
Expand All @@ -240,14 +243,14 @@ public async Task<Milestone> UpdateMilestoneAsync(GroupId groupId, int milestone
/// Removes group with all projects inside.
/// Only available to group owners and administrators.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
public async Task DeleteAsync(GroupId groupId) =>
await _httpFacade.Delete($"groups/{groupId}");

/// <summary>
/// Deletes a group milestone. Only for user with developer access to the group.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="milestoneId">The ID of the group's milestone.</param>
public async Task DeleteMilestoneAsync(GroupId groupId, int milestoneId) =>
await _httpFacade.Delete($"groups/{groupId}/milestones/{milestoneId}");
Expand All @@ -256,22 +259,22 @@ public async Task DeleteMilestoneAsync(GroupId groupId, int milestoneId) =>
/// Syncs the group with its linked LDAP group.
/// Only available to group owners and administrators.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
public async Task SyncLdapAsync(GroupId groupId) =>
await _httpFacade.Post($"groups/{groupId}/ldap_sync");

/// <summary>
/// Creates LDAP group link.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="request">Create LDAP group link request.</param>
public async Task CreateLdapLinkAsync(GroupId groupId, CreateLdapGroupLinkRequest request) =>
await _httpFacade.Post($"groups/{groupId}/ldap_group_links", request);

/// <summary>
/// Deletes a LDAP group link.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="cn">The CN of a LDAP group</param>
public async Task DeleteLdapLinkAsync(GroupId groupId, string cn) =>
await _httpFacade.Delete($"groups/{groupId}/ldap_group_links/{cn}");
Expand All @@ -280,10 +283,58 @@ public async Task DeleteLdapLinkAsync(GroupId groupId, string cn) =>
/// <summary>
/// Deletes a LDAP group link for a specific LDAP provider.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the project.</param>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="provider">Name of a LDAP provider</param>
/// <param name="cn">The CN of a LDAP group</param>
public async Task DeleteProviderLdapLinkAsync(GroupId groupId, string provider, string cn) =>
await _httpFacade.Delete($"groups/{groupId}/ldap_group_links/{provider}/{cn}");


/// <summary>
/// Get all labels for a given group.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="options">Query options</param>
public async Task<IList<GroupLabel>> GetLabelsAsync(GroupId groupId,
Action<GroupLabelsQueryOptions> options = null)
{
var labelOptions = new GroupLabelsQueryOptions();
options?.Invoke(labelOptions);

string url = _queryGroupLabelBuilder.Build($"groups/{groupId}/labels", labelOptions);
return await _httpFacade.GetPagedList<GroupLabel>(url);
}

/// <summary>
/// Creates new group label.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="request">Create label request.</param>
/// <returns>Newly created label.</returns>
public async Task<GroupLabel> CreateLabelAsync(GroupId groupId, CreateGroupLabelRequest request)
{
Guard.NotNull(request, nameof(request));
return await _httpFacade.Post<GroupLabel>($"groups/{groupId}/labels", request);
}

/// <summary>
/// Updates an existing label with new name or new color. At least one parameter is required, to update the label.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="request">Update label request.</param>
/// <returns>Newly modified label.</returns>
public async Task<GroupLabel> UpdateLabelAsync(GroupId groupId, UpdateGroupLabelRequest request)
{
Guard.NotNull(request, nameof(request));
return await _httpFacade.Put<GroupLabel>($"groups/{groupId}/labels", request);
}

/// <summary>
/// Deletes group labels.
/// </summary>
/// <param name="groupId">The ID, path or <see cref="Group"/> of the group.</param>
/// <param name="name">Name of the label.</param>
public async Task DeleteLabelAsync(GroupId groupId, string name) =>
await _httpFacade.Delete($"groups/{groupId}/labels?name={name}");
}
}
25 changes: 25 additions & 0 deletions src/GitLabApiClient/Internal/Queries/GroupLabelsQueryBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using GitLabApiClient.Models.Groups.Requests;

namespace GitLabApiClient.Internal.Queries
{
internal class GroupLabelsQueryBuilder : QueryBuilder<GroupLabelsQueryOptions>
{
#region Overrides of QueryBuilder<GroupLabelsQueryOptions>

/// <inheritdoc />
protected override void BuildCore(GroupLabelsQueryOptions options)
{
if (options.WithCounts)
{
Add("with_counts", true);
}

if (!options.IncludeAncestorGroups)
{
Add("include_ancestor_groups", false);
}
}

#endregion
}
}
15 changes: 15 additions & 0 deletions src/GitLabApiClient/Internal/Queries/IssuesQueryBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,21 @@ protected override void BuildCore(IssuesQueryOptions options)

if (!options.Filter.IsNullOrEmpty())
Add("search", options.Filter);

if (options.IsConfidential)
Add("confidential", true);

if (options.CreatedBefore.HasValue)
Add("created_before", options.CreatedBefore.Value);

if (options.CreatedAfter.HasValue)
Add("created_after", options.CreatedAfter.Value);

if (options.UpdatedBefore.HasValue)
Add("updated_before", options.UpdatedBefore.Value);

if (options.UpdatedAfter.HasValue)
Add("updated_after", options.UpdatedAfter.Value);
}

private static string GetStateQueryValue(IssueState state)
Expand Down
24 changes: 0 additions & 24 deletions src/GitLabApiClient/Internal/Queries/ProjectIssuesQueryBuilder.cs

This file was deleted.

Loading

0 comments on commit fb4aae7

Please sign in to comment.