Skip to content

Commit c19e86f

Browse files
add to do list client (#122)
Co-authored-by: Joseph Petersen <josephp90@gmail.com>
1 parent c90c235 commit c19e86f

File tree

13 files changed

+508
-0
lines changed

13 files changed

+508
-0
lines changed

src/GitLabApiClient/GitLabClient.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public GitLabClient(string hostUrl, string authenticationToken = "")
5454
var pipelineQueryBuilder = new PipelineQueryBuilder();
5555
var treeQueryBuilder = new TreeQueryBuilder();
5656
var jobQueryBuilder = new JobQueryBuilder();
57+
var toDoListBuilder = new ToDoListQueryBuilder();
5758

5859
Issues = new IssuesClient(_httpFacade, issuesQueryBuilder, projectIssueNotesQueryBuilder);
5960
Uploads = new UploadsClient(_httpFacade);
@@ -71,6 +72,7 @@ public GitLabClient(string hostUrl, string authenticationToken = "")
7172
Trees = new TreesClient(_httpFacade, treeQueryBuilder);
7273
Files = new FilesClient(_httpFacade);
7374
Runners = new RunnersClient(_httpFacade);
75+
ToDoList = new ToDoListClient(_httpFacade, toDoListBuilder);
7476
}
7577

7678
/// <summary>
@@ -153,6 +155,11 @@ public GitLabClient(string hostUrl, string authenticationToken = "")
153155
/// </summary>
154156
public RunnersClient Runners { get; }
155157

158+
/// <summary>
159+
/// Access GitLab's ToDo-List API.
160+
/// </summary>
161+
public ToDoListClient ToDoList { get; }
162+
156163
/// <summary>
157164
/// Host address of GitLab instance. For example https://gitlab.example.com or https://gitlab.example.com/api/v4/.
158165
/// </summary>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System.Runtime.Serialization;
2+
using GitLabApiClient.Internal.Utilities;
3+
using GitLabApiClient.Models.ToDoList.Requests;
4+
5+
namespace GitLabApiClient.Internal.Queries
6+
{
7+
internal sealed class ToDoListQueryBuilder : QueryBuilder<ToDoListQueryOptions>
8+
{
9+
protected override void BuildCore(ToDoListQueryOptions options)
10+
{
11+
if (options.ActionType != null)
12+
Add("action", options.ActionType.GetAttribute<EnumMemberAttribute>().Value);
13+
14+
if (options.AuthorId.HasValue)
15+
Add("author_id", options.AuthorId.Value);
16+
17+
if (options.ProjectId.HasValue)
18+
Add("project_id", options.ProjectId.Value);
19+
20+
if (options.GroupId.HasValue)
21+
Add("group_id", options.GroupId.Value);
22+
23+
if (options.State != null)
24+
Add("state", options.State.GetAttribute<EnumMemberAttribute>().Value);
25+
26+
if (options.Type != null)
27+
Add("type", options.Type.GetAttribute<EnumMemberAttribute>().Value);
28+
}
29+
}
30+
}

src/GitLabApiClient/Internal/Utilities/Extensions.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
using System;
2+
using System.Linq;
3+
14
namespace GitLabApiClient.Internal.Utilities
25
{
36
internal static class Extensions
@@ -18,5 +21,12 @@ public static string ToLowerCaseString(this object obj) =>
1821
/// <returns>Encoded URL path</returns>
1922
public static string UrlEncode(this string value) =>
2023
value.Contains("%") ? value : System.Uri.EscapeDataString(value);
24+
25+
public static T GetAttribute<T>(this Enum value) where T : Attribute
26+
{
27+
var enumType = value.GetType();
28+
string name = Enum.GetName(enumType, value);
29+
return enumType.GetField(name).GetCustomAttributes(false).OfType<T>().SingleOrDefault();
30+
}
2131
}
2232
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using GitLabApiClient.Models.ToDoList.Responses;
2+
3+
namespace GitLabApiClient.Models.ToDoList.Requests
4+
{
5+
/// <summary>
6+
/// Options for ToDoList listing
7+
/// </summary>
8+
public sealed class ToDoListQueryOptions
9+
{
10+
internal ToDoListQueryOptions() { }
11+
12+
/// <summary>
13+
/// The action to be filtered. Can be assigned, mentioned, build_failed,
14+
/// marked, approval_required, unmergeable or directly_addressed.
15+
/// </summary>
16+
public ToDoActionType? ActionType { get; set; } = null;
17+
18+
/// <summary>
19+
/// The ID of an author
20+
/// </summary>
21+
public int? AuthorId { get; set; }
22+
23+
/// <summary>
24+
/// The ID of a project
25+
/// </summary>
26+
public int? ProjectId { get; set; }
27+
28+
/// <summary>
29+
/// The ID of a group
30+
/// </summary>
31+
public int? GroupId { get; set; }
32+
33+
/// <summary>
34+
/// The state of the todo. Can be either pending or done
35+
/// </summary>
36+
public ToDoState? State { get; set; }
37+
38+
/// <summary>
39+
/// The type of a todo. Can be either Issue or MergeRequest
40+
/// </summary>
41+
public ToDoTargetType? Type { get; set; }
42+
}
43+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
using System;
2+
using GitLabApiClient.Models.Issues.Responses;
3+
using GitLabApiClient.Models.Projects.Responses;
4+
using GitLabApiClient.Models.MergeRequests.Responses;
5+
using Newtonsoft.Json;
6+
using Newtonsoft.Json.Converters;
7+
using Newtonsoft.Json.Linq;
8+
9+
namespace GitLabApiClient.Models.ToDoList.Responses
10+
{
11+
12+
[JsonConverter(typeof(ToDoItemConverter))]
13+
public interface IToDo
14+
{
15+
int? Id { get; set; }
16+
Project Project { get; set; }
17+
Assignee Author { get; set; }
18+
ToDoActionType? ActionType { get; set; }
19+
ToDoTargetType? TargetType { get; set; }
20+
string TargetUrl { get; set; }
21+
string Body { get; set; }
22+
ToDoState? State { get; set; }
23+
string CreatedAt { get; set; }
24+
}
25+
26+
public abstract class ToDo : IToDo
27+
{
28+
[JsonProperty("id")]
29+
public int? Id { get; set; }
30+
31+
[JsonProperty("project")]
32+
public Project Project { get; set; }
33+
34+
[JsonProperty("author")]
35+
public Assignee Author { get; set; }
36+
37+
[JsonProperty("action_name")]
38+
public ToDoActionType? ActionType { get; set; }
39+
40+
[JsonProperty("target_type")]
41+
public ToDoTargetType? TargetType { get; set; }
42+
43+
[JsonProperty("target_url")]
44+
public string TargetUrl { get; set; }
45+
46+
[JsonProperty("body")]
47+
public string Body { get; set; }
48+
49+
[JsonProperty("state")]
50+
public ToDoState? State { get; set; }
51+
52+
[JsonProperty("created_at")]
53+
public string CreatedAt { get; set; }
54+
}
55+
56+
public sealed class ToDoIssue : ToDo
57+
{
58+
[JsonProperty("target")]
59+
public Issue Target { get; set; }
60+
}
61+
62+
public sealed class ToDoMergeRequest : ToDo
63+
{
64+
[JsonProperty("target")]
65+
public MergeRequest Target { get; set; }
66+
}
67+
68+
public class ToDoItemConverter : CustomCreationConverter<IToDo>
69+
{
70+
public override IToDo Create(Type objectType)
71+
{
72+
throw new NotImplementedException();
73+
}
74+
75+
public IToDo Create(JObject jObject)
76+
{
77+
string type = (string)jObject.Property("target_type");
78+
switch (type)
79+
{
80+
case "Issue":
81+
return new ToDoIssue();
82+
case "MergeRequest":
83+
return new ToDoMergeRequest();
84+
default:
85+
throw new ApplicationException($"ToDo target not supported.");
86+
}
87+
}
88+
89+
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
90+
{
91+
var jObject = JObject.Load(reader);
92+
var target = Create(jObject);
93+
serializer.Populate(jObject.CreateReader(), target);
94+
return target;
95+
}
96+
}
97+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System.Runtime.Serialization;
2+
3+
namespace GitLabApiClient.Models.ToDoList.Responses
4+
{
5+
public enum ToDoActionType
6+
{
7+
[EnumMember(Value = "assigned")]
8+
Assigned,
9+
10+
[EnumMember(Value = "mentioned")]
11+
Mentioned,
12+
13+
[EnumMember(Value = "build_failed")]
14+
BuildFailed,
15+
16+
[EnumMember(Value = "marked")]
17+
Marked,
18+
19+
[EnumMember(Value = "approval_required")]
20+
ApprovalRequired,
21+
22+
[EnumMember(Value = "unmergeable")]
23+
Unmergeable,
24+
25+
[EnumMember(Value = "directly_addressed")]
26+
DirectlyAddressed,
27+
}
28+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System.Runtime.Serialization;
2+
3+
namespace GitLabApiClient.Models.ToDoList.Responses
4+
{
5+
public enum ToDoState
6+
{
7+
[EnumMember(Value = "pending")]
8+
Pending,
9+
10+
[EnumMember(Value = "done")]
11+
Done,
12+
}
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System.Runtime.Serialization;
2+
3+
namespace GitLabApiClient.Models.ToDoList.Responses
4+
{
5+
public enum ToDoTargetType
6+
{
7+
[EnumMember(Value = "Issue")]
8+
Issue,
9+
10+
[EnumMember(Value = "MergeRequest")]
11+
MergeRequest,
12+
}
13+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Net.Http;
4+
using System.Threading.Tasks;
5+
using GitLabApiClient.Internal.Http;
6+
using GitLabApiClient.Internal.Queries;
7+
using GitLabApiClient.Models.ToDoList.Requests;
8+
using GitLabApiClient.Models.ToDoList.Responses;
9+
10+
namespace GitLabApiClient
11+
{
12+
/// <summary>
13+
/// Used to query GitLab API to retrieve and mark ToDos.
14+
/// <exception cref="GitLabException">Thrown if request to GitLab API fails</exception>
15+
/// <exception cref="HttpRequestException">Thrown if request to GitLab API fails</exception>
16+
/// </summary>
17+
public sealed class ToDoListClient
18+
{
19+
private readonly GitLabHttpFacade _httpFacade;
20+
private readonly ToDoListQueryBuilder _queryBuilder;
21+
22+
internal ToDoListClient(GitLabHttpFacade httpFacade, ToDoListQueryBuilder queryBuilder)
23+
{
24+
_httpFacade = httpFacade;
25+
_queryBuilder = queryBuilder;
26+
}
27+
28+
/// <summary>
29+
/// Get a list of ToDos for current user.
30+
/// </summary>
31+
/// <param name="options">Query options.</param>
32+
public async Task<IList<IToDo>> GetAsync(Action<ToDoListQueryOptions> options = null)
33+
{
34+
var queryOptions = new ToDoListQueryOptions();
35+
options?.Invoke(queryOptions);
36+
37+
string url = _queryBuilder.Build("todos", queryOptions);
38+
return await _httpFacade.GetPagedList<IToDo>(url);
39+
}
40+
41+
/// <summary>
42+
/// Marks a specific ToDo, for the current user, as done.
43+
/// </summary>
44+
/// <param name="toDoId">The Id of the <see cref="IToDo"/> to mark as done.</param>
45+
public async Task MarkAsDoneAsync(int toDoId) =>
46+
await _httpFacade.Post($"todos/{toDoId}/mark_as_done");
47+
48+
/// <summary>
49+
/// Marks all ToDos, for the current user, as done.
50+
/// </summary>
51+
public async Task MarkAllAsDoneAsync() =>
52+
await _httpFacade.Post($"todos/mark_as_done");
53+
54+
}
55+
}

test/GitLabApiClient.Test/GitLabClientTest.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public void CheckClients()
5050
sut.Users.Should().BeAssignableTo(typeof(UsersClient));
5151
sut.Webhooks.Should().BeAssignableTo(typeof(WebhookClient));
5252
sut.MergeRequests.Should().BeAssignableTo(typeof(MergeRequestsClient));
53+
sut.ToDoList.Should().BeAssignableTo(typeof(ToDoListClient));
5354
}
5455

5556
[Trait("Category", "LinuxIntegration")]

0 commit comments

Comments
 (0)