diff --git a/src/GitLabApiClient/EpicsClient.cs b/src/GitLabApiClient/EpicsClient.cs new file mode 100644 index 00000000..82113d5a --- /dev/null +++ b/src/GitLabApiClient/EpicsClient.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using GitLabApiClient.Internal.Http; +using GitLabApiClient.Internal.Paths; +using GitLabApiClient.Internal.Queries; +using GitLabApiClient.Internal.Utilities; +using GitLabApiClient.Models.Epics.Requests; +using GitLabApiClient.Models.Epics.Responses; +using GitLabApiClient.Models.Groups.Requests; +using GitLabApiClient.Models.Issues.Requests; +using GitLabApiClient.Models.Issues.Responses; +using GitLabApiClient.Models.Notes.Requests; +using GitLabApiClient.Models.Notes.Responses; + +namespace GitLabApiClient +{ + public sealed class EpicsClient : IEpicsClient + { + private readonly GitLabHttpFacade _httpFacade; + private readonly EpicsGroupQueryBuilder _queryBuilder; + private readonly NotesQueryBuilder _notesQueryBuilder; + private readonly IssuesQueryBuilder _issuesQueryBuilder; + + internal EpicsClient(GitLabHttpFacade httpFacade, EpicsGroupQueryBuilder queryBuilder, NotesQueryBuilder notesQueryBuilder, IssuesQueryBuilder issuesQueryBuilder) + { + _httpFacade = httpFacade; + _queryBuilder = queryBuilder; + _notesQueryBuilder = notesQueryBuilder; + _issuesQueryBuilder = issuesQueryBuilder; + } + + public async Task CreateAsync(GroupId groupId, CreateEpicRequest request) + { + Guard.NotNull(request, nameof(request)); + return await _httpFacade.Post($"groups/{groupId}/epics", request); + } + + /// + /// Retrieves epic by its id from a group. + /// + /// The ID, path or of the group. + /// The ID of the epic. + /// + public async Task GetAsync(GroupId groupId, int epicId) => + await _httpFacade.Get($"groups/{groupId}/epics/{epicId}"); + + /// + /// Retrieves notes (comments) of an epic. + /// + /// The ID, path or of the group. + /// Id of the epic. + /// Notes retrieval options. + /// Epic satisfying options. + public async Task> GetNotesAsync(GroupId groupId, int epicId, Action options = null) + { + var queryOptions = new NotesQueryOptions(); + options?.Invoke(queryOptions); + + string url = _notesQueryBuilder.Build($"groups/{groupId}/epics/{epicId}/notes", queryOptions); + return await _httpFacade.GetPagedList(url); + } + /// + /// Retrieves issues linked to an epic. + /// + /// The ID, path or of the group. + /// Id of the epic. + /// Issues retrieval options. + /// Epic satisfying options. + public async Task> GetIssusAsync(GroupId groupId, int epicId, Action options = null) + { + var queryOptions = new IssuesQueryOptions(); + options?.Invoke(queryOptions); + + string url = _issuesQueryBuilder.Build($"groups/{groupId}/epics/{epicId}/issues", queryOptions); + return await _httpFacade.GetPagedList(url); + } + + /// + /// Creates a new note (comment) to a single epic. + /// + /// The newly created epic note. + /// The ID, path or of the group. + /// The IID of an epic. + /// Create epic note request. + public async Task CreateNoteAsync(GroupId groupId, int epicId, CreateNoteRequest request) => + await _httpFacade.Post($"groups/{groupId}/epics/{epicId}/notes", request); + + + /// + /// Get a list of visible epics for a group of the authenticated user. + /// + /// Query options. + public async Task> GetAsync(GroupId groupId, Action options = null) + { + var queryOptions = new EpicsGroupQueryOptions(); + options?.Invoke(queryOptions); + + string url = _queryBuilder.Build($"groups/{groupId}/epics", queryOptions); + return await _httpFacade.GetPagedList(url); + } + + /// + /// Assigns an issue to an epic + /// + /// Assigned issue + public async Task AssignIssueAsync(GroupId groupId, int epicId, int issueId) => + await _httpFacade.Post($"groups/{groupId}/epics/{epicId}/issues/{issueId}"); + + /// + /// Updates an epic. + /// + /// The ID, path or of the group. + /// The ID of an epic. + /// Update epic request. + /// The updated epic. + public async Task UpdateAsync(GroupId groupId, int epicId, UpdateEpicRequest request) => + await _httpFacade.Put($"groups/{groupId}/epics/{epicId}", request); + } +} diff --git a/src/GitLabApiClient/GitLabApiClient.csproj b/src/GitLabApiClient/GitLabApiClient.csproj index 374b1209..287c6377 100644 --- a/src/GitLabApiClient/GitLabApiClient.csproj +++ b/src/GitLabApiClient/GitLabApiClient.csproj @@ -5,7 +5,7 @@ false GitLabApiClient is a .NET rest client for GitLab API v4. - nmklotas + nmklotas (mhobl) https://github.com/nmklotas/GitLabApiClient https://github.com/nmklotas/GitLabApiClient GitLab REST API CI Client @@ -16,10 +16,12 @@ true true snupkg + 1.0.0.0 + 1.0.0.0 - true + true true diff --git a/src/GitLabApiClient/GitLabClient.cs b/src/GitLabApiClient/GitLabClient.cs index 63705304..859f3004 100644 --- a/src/GitLabApiClient/GitLabClient.cs +++ b/src/GitLabApiClient/GitLabClient.cs @@ -40,8 +40,7 @@ public GitLabClient(string hostUrl, string authenticationToken = "", HttpMessage clientTimeout); var projectQueryBuilder = new ProjectsQueryBuilder(); - var projectIssueNotesQueryBuilder = new ProjectIssueNotesQueryBuilder(); - var projectMergeRequestsNotesQueryBuilder = new ProjectMergeRequestsNotesQueryBuilder(); + var notesQueryBuilder = new NotesQueryBuilder(); var issuesQueryBuilder = new IssuesQueryBuilder(); var mergeRequestsQueryBuilder = new MergeRequestsQueryBuilder(); var projectMilestonesQueryBuilder = new MilestonesQueryBuilder(); @@ -49,6 +48,7 @@ public GitLabClient(string hostUrl, string authenticationToken = "", HttpMessage var groupsQueryBuilder = new GroupsQueryBuilder(); var groupLabelsQueryBuilder = new GroupLabelsQueryBuilder(); var projectsGroupsQueryBuilder = new ProjectsGroupQueryBuilder(); + var epicsGroupQueryBuilder = new EpicsGroupQueryBuilder(); var branchQueryBuilder = new BranchQueryBuilder(); var releaseQueryBuilder = new ReleaseQueryBuilder(); var tagQueryBuilder = new TagQueryBuilder(); @@ -60,12 +60,13 @@ public GitLabClient(string hostUrl, string authenticationToken = "", HttpMessage var jobQueryBuilder = new JobQueryBuilder(); var toDoListBuilder = new ToDoListQueryBuilder(); - Issues = new IssuesClient(_httpFacade, issuesQueryBuilder, projectIssueNotesQueryBuilder); + Issues = new IssuesClient(_httpFacade, issuesQueryBuilder, notesQueryBuilder); Uploads = new UploadsClient(_httpFacade); - MergeRequests = new MergeRequestsClient(_httpFacade, mergeRequestsQueryBuilder, projectMergeRequestsQueryBuilder, projectMergeRequestsNotesQueryBuilder); + MergeRequests = new MergeRequestsClient(_httpFacade, mergeRequestsQueryBuilder, projectMergeRequestsQueryBuilder, notesQueryBuilder); Projects = new ProjectsClient(_httpFacade, projectQueryBuilder, projectMilestonesQueryBuilder, jobQueryBuilder); Users = new UsersClient(_httpFacade); - Groups = new GroupsClient(_httpFacade, groupsQueryBuilder, projectsGroupsQueryBuilder, projectMilestonesQueryBuilder, groupLabelsQueryBuilder); + Groups = new GroupsClient(_httpFacade, groupsQueryBuilder, projectsGroupsQueryBuilder, projectMilestonesQueryBuilder, groupLabelsQueryBuilder, epicsGroupQueryBuilder); + Epics = new EpicsClient(_httpFacade, epicsGroupQueryBuilder, notesQueryBuilder, issuesQueryBuilder); Branches = new BranchClient(_httpFacade, branchQueryBuilder); Releases = new ReleaseClient(_httpFacade, releaseQueryBuilder); Tags = new TagClient(_httpFacade, tagQueryBuilder); @@ -110,6 +111,11 @@ public GitLabClient(string hostUrl, string authenticationToken = "", HttpMessage /// public IGroupsClient Groups { get; } + /// + /// Access GitLab's epics API. + /// + public IEpicsClient Epics { get; } + /// /// Access GitLab's branches API. /// diff --git a/src/GitLabApiClient/GroupsClient.cs b/src/GitLabApiClient/GroupsClient.cs index 11708bbf..c8a30d3d 100644 --- a/src/GitLabApiClient/GroupsClient.cs +++ b/src/GitLabApiClient/GroupsClient.cs @@ -7,6 +7,7 @@ using GitLabApiClient.Internal.Queries; using GitLabApiClient.Internal.Utilities; using GitLabApiClient.Models; +using GitLabApiClient.Models.Epics.Responses; using GitLabApiClient.Models.Groups.Requests; using GitLabApiClient.Models.Groups.Responses; using GitLabApiClient.Models.Milestones.Requests; @@ -28,19 +29,22 @@ public sealed class GroupsClient : IGroupsClient private readonly ProjectsGroupQueryBuilder _projectsQueryBuilder; private readonly MilestonesQueryBuilder _queryMilestonesBuilder; private readonly GroupLabelsQueryBuilder _queryGroupLabelBuilder; + private readonly EpicsGroupQueryBuilder _epicsGroupQueryBuilder; internal GroupsClient( GitLabHttpFacade httpFacade, GroupsQueryBuilder queryBuilder, ProjectsGroupQueryBuilder projectsQueryBuilder, MilestonesQueryBuilder queryMilestonesBuilder, - GroupLabelsQueryBuilder queryGroupLabelBuilder) + GroupLabelsQueryBuilder queryGroupLabelBuilder, + EpicsGroupQueryBuilder epicsGroupQueryBuilder) { _httpFacade = httpFacade; _queryBuilder = queryBuilder; _projectsQueryBuilder = projectsQueryBuilder; _queryMilestonesBuilder = queryMilestonesBuilder; _queryGroupLabelBuilder = queryGroupLabelBuilder; + _epicsGroupQueryBuilder = epicsGroupQueryBuilder; } /// @@ -96,6 +100,22 @@ public async Task> GetProjectsAsync(GroupId groupId, Action(url); } + /// + /// Get a list of epics in this group. + /// When accessed without authentication, only public epics are returned. + /// + /// The ID, path or of the group. + /// Epics projects retrieval options. + /// Epics satisfying options. + public async Task> GetEpicsAsync(GroupId groupId, Action options = null) + { + var queryOptions = new EpicsGroupQueryOptions(); + options?.Invoke(queryOptions); + + string url = _epicsGroupQueryBuilder.Build($"groups/{groupId}/epics", queryOptions); + return await _httpFacade.GetPagedList(url); + } + /// /// Get a list of members in this group. /// diff --git a/src/GitLabApiClient/IEpicsClient.cs b/src/GitLabApiClient/IEpicsClient.cs new file mode 100644 index 00000000..84bb710f --- /dev/null +++ b/src/GitLabApiClient/IEpicsClient.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using GitLabApiClient.Internal.Paths; +using GitLabApiClient.Models.Epics.Requests; +using GitLabApiClient.Models.Epics.Responses; +using GitLabApiClient.Models.Groups.Requests; +using GitLabApiClient.Models.Issues.Requests; +using GitLabApiClient.Models.Issues.Responses; +using GitLabApiClient.Models.Notes.Requests; +using GitLabApiClient.Models.Notes.Responses; + +namespace GitLabApiClient +{ + public interface IEpicsClient + { + /// + /// Retrieves epic by its id, path or . + /// + /// The ID, path or of the epic. + Task GetAsync(GroupId groupId, int epicId); + + /// + /// Get a list of visible epics for a group of the authenticated user. + /// + /// Query options. + Task> GetAsync(GroupId groupId, Action options = null); + + /// + /// Retrieves notes (comments) of an epic. + /// + /// The ID, path or of the group. + /// Id of the epic. + /// Notes retrieval options. + /// Epic satisfying options. + Task> GetNotesAsync(GroupId groupId, int epicId, Action options = null); + + /// + /// Retrieves issues linked to an epic. + /// + /// The ID, path or of the group. + /// Id of the epic. + /// Issues retrieval options. + /// Epic satisfying options. + Task> GetIssusAsync(GroupId groupId, int epicId, Action options = null); + + /// + /// Creates a new epic. + /// + /// The ID, path or of the group. + /// Create epic request. + /// Newly created epic. + Task CreateAsync(GroupId groupId, CreateEpicRequest request); + + /// + /// Creates a new note (comment) to a single epic. + /// + /// The ID, path or of the group. + /// The ID of an epic. + /// Create epic note request. + /// The newly created epic note. + Task CreateNoteAsync(GroupId groupId, int epicId, CreateNoteRequest request); + + /// + /// Assigns an issue to an epic + /// + /// Assigned issue + Task AssignIssueAsync(GroupId groupId, int epicId, int issueId); + + /// + /// Updates an epic. + /// + /// The ID, path or of the group. + /// The ID of an epic. + /// Update epic request. + /// The updated epic. + Task UpdateAsync(GroupId groupId, int epicId, UpdateEpicRequest request); + } +} diff --git a/src/GitLabApiClient/IGroupsClient.cs b/src/GitLabApiClient/IGroupsClient.cs index f25490fa..931a88ae 100644 --- a/src/GitLabApiClient/IGroupsClient.cs +++ b/src/GitLabApiClient/IGroupsClient.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using GitLabApiClient.Internal.Paths; using GitLabApiClient.Models; +using GitLabApiClient.Models.Epics.Responses; using GitLabApiClient.Models.Groups.Requests; using GitLabApiClient.Models.Groups.Responses; using GitLabApiClient.Models.Milestones.Requests; @@ -47,9 +48,18 @@ public interface IGroupsClient /// /// The ID, path or of the group. /// Groups projects retrieval options. - /// Issues satisfying options. + /// Projecs satisfying options. Task> GetProjectsAsync(GroupId groupId, Action options = null); + /// + /// Get a list of epics in this group. + /// When accessed without authentication, only public epics are returned. + /// + /// The ID, path or of the group. + /// Epics projects retrieval options. + /// Epics satisfying options. + Task> GetEpicsAsync(GroupId groupId, Action options = null); + /// /// Get a list of members in this group. /// @@ -254,5 +264,6 @@ Task> GetLabelsAsync(GroupId groupId, /// The ID, path or of the group. /// The Key ID of the variable. Task DeleteVariableAsync(GroupId groupId, string key); + } } diff --git a/src/GitLabApiClient/IIssuesClient.cs b/src/GitLabApiClient/IIssuesClient.cs index f6c39eb5..25a750f9 100644 --- a/src/GitLabApiClient/IIssuesClient.cs +++ b/src/GitLabApiClient/IIssuesClient.cs @@ -90,7 +90,7 @@ public interface IIssuesClient /// Iid of the issue. /// IssueNotes retrieval options. /// Issues satisfying options. - Task> GetNotesAsync(ProjectId projectId, int issueIid, Action options = null); + Task> GetNotesAsync(ProjectId projectId, int issueIid, Action options = null); /// /// Creates new issue. @@ -107,7 +107,16 @@ public interface IIssuesClient /// The ID, path or of the project. /// The IID of an issue. /// Create issue note request. - Task CreateNoteAsync(ProjectId projectId, int issueIid, CreateIssueNoteRequest request); + Task CreateNoteAsync(ProjectId projectId, int issueIid, CreateNoteRequest request); + + /// + /// Moves an issues to a new project + /// + /// The newly created issue note. + /// The ID, path or of the project. + /// The IID of an issue. + /// Create issue note request. + Task MoveIssueAsync(ProjectId projectId, int issueIid, MoveIssueRequest request); /// /// Updated existing issue. diff --git a/src/GitLabApiClient/IMergeRequestsClient.cs b/src/GitLabApiClient/IMergeRequestsClient.cs index 92bfce6b..f1f4f9ee 100644 --- a/src/GitLabApiClient/IMergeRequestsClient.cs +++ b/src/GitLabApiClient/IMergeRequestsClient.cs @@ -80,7 +80,7 @@ public interface IMergeRequestsClient /// The ID, path or of the project. /// The IID of an Merge Request. /// Create Merge Request note request. - Task CreateNoteAsync(ProjectId projectId, int mergeRequestId, CreateMergeRequestNoteRequest request); + Task CreateNoteAsync(ProjectId projectId, int mergeRequestId, CreateNoteRequest request); /// /// Retrieves notes (comments) of a merge request. @@ -89,7 +89,7 @@ public interface IMergeRequestsClient /// Iid of the merge request. /// MergeRequestNotes retrieval options. /// Merge requests satisfying options. - Task> GetNotesAsync(ProjectId projectId, int mergeRequestIid, Action options = null); + Task> GetNotesAsync(ProjectId projectId, int mergeRequestIid, Action options = null); /// /// List erge request pipelines diff --git a/src/GitLabApiClient/Internal/Http/GitLabHttpFacade.cs b/src/GitLabApiClient/Internal/Http/GitLabHttpFacade.cs index b9b1fc8b..6d10d443 100644 --- a/src/GitLabApiClient/Internal/Http/GitLabHttpFacade.cs +++ b/src/GitLabApiClient/Internal/Http/GitLabHttpFacade.cs @@ -74,6 +74,9 @@ public Task GetFile(string uri, string outputPath) => public Task Post(string uri, object data = null) where T : class => _requestor.Post(uri, data); + public Task PostFormData(string uri, object data = null) where T : class => + _requestor.PostFormData(uri, data); + public Task Post(string uri, object data = null) => _requestor.Post(uri, data); diff --git a/src/GitLabApiClient/Internal/Http/GitlabApiRequestor.cs b/src/GitLabApiClient/Internal/Http/GitlabApiRequestor.cs index c4d6947f..4d8c8eec 100644 --- a/src/GitLabApiClient/Internal/Http/GitlabApiRequestor.cs +++ b/src/GitLabApiClient/Internal/Http/GitlabApiRequestor.cs @@ -5,6 +5,7 @@ using System.Net.Http.Headers; using System.Threading.Tasks; using GitLabApiClient.Internal.Http.Serialization; +using GitLabApiClient.Models.Issues.Requests; using GitLabApiClient.Models.Uploads.Requests; using GitLabApiClient.Models.Uploads.Responses; @@ -46,6 +47,14 @@ public async Task Post(string url, object data = null) return await ReadResponse(responseMessage); } + public async Task PostFormData(string url, object data = null) + { + MultipartFormDataContent content = SerializeToFormData(data); + var responseMessage = await _client.PostAsync(url, content); + await EnsureSuccessStatusCode(responseMessage); + return await ReadResponse(responseMessage); + } + public async Task Post(string url, object data = null) { StringContent content = SerializeToString(data); @@ -145,5 +154,20 @@ private StringContent SerializeToString(object data) content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); return content; } + + private MultipartFormDataContent SerializeToFormData(object data) + { + var content = new MultipartFormDataContent(); + if(data is MoveIssueRequest req) + { + //Todo: Needs to be refactored! + content.Add(new StringContent(req.ToProjectId), "to_project_id"); + } + else + { + content.Add(new StringContent(string.Empty)); + } + return content; + } } } diff --git a/src/GitLabApiClient/Internal/Paths/GroupId.cs b/src/GitLabApiClient/Internal/Paths/GroupId.cs index 2f689dde..83334516 100644 --- a/src/GitLabApiClient/Internal/Paths/GroupId.cs +++ b/src/GitLabApiClient/Internal/Paths/GroupId.cs @@ -15,15 +15,9 @@ public class GroupId /// /// The project path ie. 'group/project' /// - public static implicit operator GroupId(string groupPath) - { - return new GroupId(groupPath.UrlEncode()); - } + public static implicit operator GroupId(string groupPath) => new GroupId(groupPath.UrlEncode()); - public static implicit operator GroupId(int groupId) - { - return new GroupId(groupId.ToString()); - } + public static implicit operator GroupId(int groupId) => new GroupId(groupId.ToString()); public static implicit operator GroupId(Group group) { diff --git a/src/GitLabApiClient/Internal/Paths/ProjectId.cs b/src/GitLabApiClient/Internal/Paths/ProjectId.cs index 16dc321a..b5bb0348 100644 --- a/src/GitLabApiClient/Internal/Paths/ProjectId.cs +++ b/src/GitLabApiClient/Internal/Paths/ProjectId.cs @@ -15,15 +15,9 @@ public class ProjectId /// /// The project path ie. 'group/project' /// - public static implicit operator ProjectId(string projectPath) - { - return new ProjectId(projectPath.UrlEncode()); - } + public static implicit operator ProjectId(string projectPath) => new ProjectId(projectPath.UrlEncode()); - public static implicit operator ProjectId(int projectId) - { - return new ProjectId(projectId.ToString()); - } + public static implicit operator ProjectId(int projectId) => new ProjectId(projectId.ToString()); public static implicit operator ProjectId(Project project) { diff --git a/src/GitLabApiClient/Internal/Queries/EpicsGroupQueryBuilder.cs b/src/GitLabApiClient/Internal/Queries/EpicsGroupQueryBuilder.cs new file mode 100644 index 00000000..beb223b2 --- /dev/null +++ b/src/GitLabApiClient/Internal/Queries/EpicsGroupQueryBuilder.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using GitLabApiClient.Internal.Utilities; +using GitLabApiClient.Models; +using GitLabApiClient.Models.Groups.Requests; +using GitLabApiClient.Models.Issues.Requests; + +namespace GitLabApiClient.Internal.Queries +{ + internal class EpicsGroupQueryBuilder : QueryBuilder + { + protected override void BuildCore(Query query, EpicsGroupQueryOptions options) + { + if (options.AuthorId.HasValue) + query.Add("author_id", options.AuthorId.Value); + + if (options.Labels.Any()) + query.Add("labels", options.Labels); + + if(options.WithLabelsDetails) + query.Add("with_labels_details", options.WithLabelsDetails); + + if (options.Order != EpicsIssuesOrder.CreatedAt) + query.Add("order_by", Order.GetIssuesOrderQueryValue(options.Order)); + + if (options.SortOrder != SortOrder.Descending) + query.Add("sort", GetSortOrderQueryValue(options.SortOrder)); + + if (!options.Filter.IsNullOrEmpty()) + query.Add("search", options.Filter); + + string stateQueryValue = State.GetStateQueryValue(options.State); + if (!stateQueryValue.IsNullOrEmpty()) + query.Add("state", stateQueryValue); + + if (options.CreatedBefore.HasValue) + query.Add("created_before", options.CreatedBefore.Value); + + if (options.CreatedAfter.HasValue) + query.Add("created_after", options.CreatedAfter.Value); + + if (options.UpdatedBefore.HasValue) + query.Add("updated_before", options.UpdatedBefore.Value); + + if (options.UpdatedAfter.HasValue) + query.Add("updated_after", options.UpdatedAfter.Value); + + if (options.IncludeAncestorGroups) + query.Add("include_ancestor_groups", options.IncludeAncestorGroups); + + if (options.IncludeDescendantGroups) + query.Add("include_descendant_groups", options.IncludeDescendantGroups); + } + } +} diff --git a/src/GitLabApiClient/Internal/Queries/IssuesQueryBuilder.cs b/src/GitLabApiClient/Internal/Queries/IssuesQueryBuilder.cs index 83415695..9ee184d9 100644 --- a/src/GitLabApiClient/Internal/Queries/IssuesQueryBuilder.cs +++ b/src/GitLabApiClient/Internal/Queries/IssuesQueryBuilder.cs @@ -11,7 +11,7 @@ internal class IssuesQueryBuilder : QueryBuilder { protected override void BuildCore(Query query, IssuesQueryOptions options) { - string stateQueryValue = GetStateQueryValue(options.State); + string stateQueryValue = State.GetStateQueryValue(options.State); if (!stateQueryValue.IsNullOrEmpty()) query.Add("state", stateQueryValue); @@ -35,8 +35,8 @@ protected override void BuildCore(Query query, IssuesQueryOptions options) query.Add(options.IssueIds); - if (options.Order != IssuesOrder.CreatedAt) - query.Add("order_by", GetIssuesOrderQueryValue(options.Order)); + if (options.Order != EpicsIssuesOrder.CreatedAt) + query.Add("order_by", Order.GetIssuesOrderQueryValue(options.Order)); if (options.SortOrder != SortOrder.Descending) query.Add("sort", GetSortOrderQueryValue(options.SortOrder)); @@ -59,33 +59,5 @@ protected override void BuildCore(Query query, IssuesQueryOptions options) if (options.UpdatedAfter.HasValue) query.Add("updated_after", options.UpdatedAfter.Value); } - - private static string GetStateQueryValue(IssueState state) - { - switch (state) - { - case IssueState.Opened: - return "opened"; - case IssueState.Closed: - return "closed"; - case IssueState.All: - return ""; - default: - throw new NotSupportedException($"State {state} is not supported"); - } - } - - private static string GetIssuesOrderQueryValue(IssuesOrder order) - { - switch (order) - { - case IssuesOrder.CreatedAt: - return "created_at"; - case IssuesOrder.UpdatedAt: - return "updated_at"; - default: - throw new NotSupportedException($"Order {order} is not supported"); - } - } } } diff --git a/src/GitLabApiClient/Internal/Queries/ProjectIssueNotesQueryBuilder.cs b/src/GitLabApiClient/Internal/Queries/NotesQueryBuilder.cs similarity index 83% rename from src/GitLabApiClient/Internal/Queries/ProjectIssueNotesQueryBuilder.cs rename to src/GitLabApiClient/Internal/Queries/NotesQueryBuilder.cs index bb1e7a6d..8b027d52 100644 --- a/src/GitLabApiClient/Internal/Queries/ProjectIssueNotesQueryBuilder.cs +++ b/src/GitLabApiClient/Internal/Queries/NotesQueryBuilder.cs @@ -4,9 +4,9 @@ namespace GitLabApiClient.Internal.Queries { - internal class ProjectIssueNotesQueryBuilder : QueryBuilder + internal class NotesQueryBuilder : QueryBuilder { - protected override void BuildCore(Query query, IssueNotesQueryOptions options) + protected override void BuildCore(Query query, NotesQueryOptions options) { if (options.SortOrder != SortOrder.Descending) query.Add("sort", GetSortOrderQueryValue(options.SortOrder)); diff --git a/src/GitLabApiClient/Internal/Queries/ProjectMergeRequestsNotesQueryBuilder.cs b/src/GitLabApiClient/Internal/Queries/ProjectMergeRequestsNotesQueryBuilder.cs deleted file mode 100644 index c372c0c5..00000000 --- a/src/GitLabApiClient/Internal/Queries/ProjectMergeRequestsNotesQueryBuilder.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using GitLabApiClient.Models; -using GitLabApiClient.Models.Notes.Requests; - -namespace GitLabApiClient.Internal.Queries -{ - internal class ProjectMergeRequestsNotesQueryBuilder : QueryBuilder - { - protected override void BuildCore(Query query, MergeRequestNotesQueryOptions options) - { - if (options.SortOrder != SortOrder.Descending) - query.Add("sort", GetSortOrderQueryValue(options.SortOrder)); - - if (options.Order != NoteOrder.CreatedAt) - query.Add("order_by", GetNoteOrderQueryValue(options.Order)); - } - - private static string GetNoteOrderQueryValue(NoteOrder order) - { - switch (order) - { - case NoteOrder.CreatedAt: - return "created_at"; - case NoteOrder.UpdatedAt: - return "updated_at"; - default: - throw new NotSupportedException($"Order {order} is not supported"); - } - } - } -} diff --git a/src/GitLabApiClient/IssuesClient.cs b/src/GitLabApiClient/IssuesClient.cs index 0c5bd2fb..9344d36f 100644 --- a/src/GitLabApiClient/IssuesClient.cs +++ b/src/GitLabApiClient/IssuesClient.cs @@ -24,16 +24,16 @@ public sealed class IssuesClient : IIssuesClient { private readonly GitLabHttpFacade _httpFacade; private readonly IssuesQueryBuilder _queryBuilder; - private readonly ProjectIssueNotesQueryBuilder _projectIssueNotesQueryBuilder; + private readonly NotesQueryBuilder _notesQueryBuilder; internal IssuesClient( GitLabHttpFacade httpFacade, IssuesQueryBuilder queryBuilder, - ProjectIssueNotesQueryBuilder projectIssueNotesQueryBuilder) + NotesQueryBuilder notesQueryBuilder) { _httpFacade = httpFacade; _queryBuilder = queryBuilder; - _projectIssueNotesQueryBuilder = projectIssueNotesQueryBuilder; + _notesQueryBuilder = notesQueryBuilder; } /// @@ -138,12 +138,12 @@ public async Task GetNoteAsync(ProjectId projectId, int issueIid, int note /// Iid of the issue. /// IssueNotes retrieval options. /// Notes satisfying options. - public async Task> GetNotesAsync(ProjectId projectId, int issueIid, Action options = null) + public async Task> GetNotesAsync(ProjectId projectId, int issueIid, Action options = null) { - var queryOptions = new IssueNotesQueryOptions(); + var queryOptions = new NotesQueryOptions(); options?.Invoke(queryOptions); - string url = _projectIssueNotesQueryBuilder.Build($"projects/{projectId}/issues/{issueIid}/notes", queryOptions); + string url = _notesQueryBuilder.Build($"projects/{projectId}/issues/{issueIid}/notes", queryOptions); return await _httpFacade.GetPagedList(url); } @@ -163,9 +163,19 @@ public async Task CreateAsync(ProjectId projectId, CreateIssueRequest req /// The ID, path or of the project. /// The IID of an issue. /// Create issue note request. - public async Task CreateNoteAsync(ProjectId projectId, int issueIid, CreateIssueNoteRequest request) => + public async Task CreateNoteAsync(ProjectId projectId, int issueIid, CreateNoteRequest request) => await _httpFacade.Post($"projects/{projectId}/issues/{issueIid}/notes", request); + /// + /// Moves an issues to a new project + /// + /// The newly created issue note. + /// The ID, path or of the project. + /// The IID of an issue. + /// Create issue note request. + public async Task MoveIssueAsync(ProjectId projectId, int issueIid, MoveIssueRequest request) => + await _httpFacade.PostFormData($"projects/{projectId}/issues/{issueIid}/move", request); + /// /// Updated existing issue. /// diff --git a/src/GitLabApiClient/MergeRequestsClient.cs b/src/GitLabApiClient/MergeRequestsClient.cs index 7926c86b..919610d7 100644 --- a/src/GitLabApiClient/MergeRequestsClient.cs +++ b/src/GitLabApiClient/MergeRequestsClient.cs @@ -27,18 +27,18 @@ public sealed class MergeRequestsClient : IMergeRequestsClient private readonly GitLabHttpFacade _httpFacade; private readonly MergeRequestsQueryBuilder _mergeRequestsQueryBuilder; private readonly ProjectMergeRequestsQueryBuilder _projectMergeRequestsQueryBuilder; - private readonly ProjectMergeRequestsNotesQueryBuilder _projectMergeRequestNotesQueryBuilder; + private readonly NotesQueryBuilder _notesQueryBuilder; internal MergeRequestsClient( GitLabHttpFacade httpFacade, MergeRequestsQueryBuilder mergeRequestsQueryBuilder, ProjectMergeRequestsQueryBuilder projectMergeRequestsQueryBuilder, - ProjectMergeRequestsNotesQueryBuilder projectMergeRequestNotesQueryBuilder) + NotesQueryBuilder notesQueryBuilder) { _httpFacade = httpFacade; _mergeRequestsQueryBuilder = mergeRequestsQueryBuilder; _projectMergeRequestsQueryBuilder = projectMergeRequestsQueryBuilder; - _projectMergeRequestNotesQueryBuilder = projectMergeRequestNotesQueryBuilder; + _notesQueryBuilder = notesQueryBuilder; } /// @@ -131,7 +131,7 @@ public async Task DeleteAsync(ProjectId projectId, int mergeRequestId) => /// The ID, path or of the project. /// The IID of an Merge Request. /// Create Merge Request note request. - public async Task CreateNoteAsync(ProjectId projectId, int mergeRequestId, CreateMergeRequestNoteRequest request) => + public async Task CreateNoteAsync(ProjectId projectId, int mergeRequestId, CreateNoteRequest request) => await _httpFacade.Post($"projects/{projectId}/merge_requests/{mergeRequestId}/notes", request); /// @@ -141,12 +141,12 @@ public async Task CreateNoteAsync(ProjectId projectId, int mergeRequestId, /// Iid of the merge request. /// MergeRequestNotes retrieval options. /// Merge requests satisfying options. - public async Task> GetNotesAsync(ProjectId projectId, int mergeRequestIid, Action options = null) + public async Task> GetNotesAsync(ProjectId projectId, int mergeRequestIid, Action options = null) { - var queryOptions = new MergeRequestNotesQueryOptions(); + var queryOptions = new NotesQueryOptions(); options?.Invoke(queryOptions); - string url = _projectMergeRequestNotesQueryBuilder.Build($"projects/{projectId}/merge_requests/{mergeRequestIid}/notes", queryOptions); + string url = _notesQueryBuilder.Build($"projects/{projectId}/merge_requests/{mergeRequestIid}/notes", queryOptions); return await _httpFacade.GetPagedList(url); } diff --git a/src/GitLabApiClient/Models/EpicIssueState.cs b/src/GitLabApiClient/Models/EpicIssueState.cs new file mode 100644 index 00000000..ef15ce28 --- /dev/null +++ b/src/GitLabApiClient/Models/EpicIssueState.cs @@ -0,0 +1,28 @@ +using System; + +namespace GitLabApiClient.Models +{ + public enum EpicIssueState + { + Opened, + Closed, + All + } + public static class State + { + public static string GetStateQueryValue(EpicIssueState state) + { + switch (state) + { + case EpicIssueState.Opened: + return "opened"; + case EpicIssueState.Closed: + return "closed"; + case EpicIssueState.All: + return ""; + default: + throw new NotSupportedException($"State {state} is not supported"); + } + } + } +} diff --git a/src/GitLabApiClient/Models/Epics/Requests/CreateEpicRequest.cs b/src/GitLabApiClient/Models/Epics/Requests/CreateEpicRequest.cs new file mode 100644 index 00000000..a359cb76 --- /dev/null +++ b/src/GitLabApiClient/Models/Epics/Requests/CreateEpicRequest.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Text; +using GitLabApiClient.Internal.Http.Serialization; +using GitLabApiClient.Internal.Paths; +using GitLabApiClient.Internal.Utilities; +using Newtonsoft.Json; + +namespace GitLabApiClient.Models.Epics.Requests +{ + /// + /// Used to create epics in a group. + /// + public sealed class CreateEpicRequest + { + /// + /// Initializes a new instance of the class. + /// + /// Title of the epic. + public CreateEpicRequest(string title) + { + Guard.NotEmpty(title, nameof(title)); + Title = title; + } + + /// + /// The title of an epic. + /// + [JsonProperty("title")] + public string Title { get; } + + /// + /// The comma separated list of labels + /// + [JsonProperty("labels")] + [JsonConverter(typeof(CollectionToCommaSeparatedValuesConverter))] + public IList Labels { get; set; } = new List(); + + /// + /// The description of the epic. Limited to 1,048,576 characters. + /// + [JsonProperty("description")] + public string Description { get; set; } + + /// + /// Whether the epic should be confidential + /// + [JsonProperty("confidential")] + public bool? Confidential { get; set; } + + /// + /// When the epic was created. Date time string, ISO 8601 formatted, + /// for example 2016-03-11T03:45:40Z (requires admin or project owner rights). + /// + [JsonProperty("created_at")] + public DateTime? CreatedAt { get; set; } + + /// + /// Whether start date should be sourced from start_date_fixed or from milestones. + /// + [JsonProperty("start_date_is_fixed")] + public bool StartDateIsFixed { get; set; } + + /// + /// The fixed start date of an epic. + /// + [JsonProperty("start_date_fixed")] + public string StartDateFixed { get; set; } + + /// + /// Whether due date should be sourced from due_date_fixed or from milestones. + /// + [JsonProperty("due_date_is_fixed")] + public bool DueDateIsFixed { get; set; } + + /// + /// The fixed due date of an epic. + /// + [JsonProperty("due_date_fixed")] + public string DueDateFixed { get; set; } + + /// + /// The ID of a parent epic. + /// + [JsonProperty("parent_id")] + public int? ParentId { get; set; } + } +} diff --git a/src/GitLabApiClient/Models/Epics/Requests/UpdateEpicRequest.cs b/src/GitLabApiClient/Models/Epics/Requests/UpdateEpicRequest.cs new file mode 100644 index 00000000..e7585dfa --- /dev/null +++ b/src/GitLabApiClient/Models/Epics/Requests/UpdateEpicRequest.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using GitLabApiClient.Internal.Http.Serialization; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace GitLabApiClient.Models.Epics.Requests +{ + /// + /// Used to update epics in a group. + /// + public sealed class UpdateEpicRequest + { + /// + /// The title of an epic. + /// + [JsonProperty("title")] + public string Title { get; set; } + + /// + /// The description of an epic. + /// + [JsonProperty("description")] + public string Description { get; set; } + + /// + /// Updates an epic to be confidential. + /// + [JsonProperty("confidential")] + public bool? Confidential { get; set; } + + /// + /// Label names for an epic. + /// + [JsonProperty("labels")] + [JsonConverter(typeof(CollectionToCommaSeparatedValuesConverter))] + public IList Labels { get; set; } = new List(); + + /// + /// The state event of an epic. Set close to close the epic and reopen to reopen it. + /// + [JsonProperty("state_event")] + [JsonConverter(typeof(StringEnumConverter))] + public UpdatedEpicIssueState? State { get; set; } + + /// + /// Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project owner rights). + /// + [JsonProperty("updated_at")] + public DateTime? UpdatedAt { get; set; } + + /// + /// Whether start date should be sourced from start_date_fixed or from milestones + /// + [JsonProperty("start_date_is_fixed")] + public bool? StartDateIsFixed { get; set; } + + /// + /// Date time string in the format YEAR-MONTH-DAY, e.g. 2016-03-11. + /// + [JsonProperty("start_date_fixed")] + public string StartDateFixed { get; set; } + + /// + /// Whether due date should be sourced from due_date_fixed or from milestones + /// + [JsonProperty("due_date_is_fixed")] + public bool? DueDateIsFixed { get; set; } + + /// + /// Date time string in the format YEAR-MONTH-DAY, e.g. 2016-03-11. + /// + [JsonProperty("due_date_fixed")] + public string DueDateFixed { get; set; } + + } +} diff --git a/src/GitLabApiClient/Models/Epics/Responses/Epic.cs b/src/GitLabApiClient/Models/Epics/Responses/Epic.cs new file mode 100644 index 00000000..ccbc816f --- /dev/null +++ b/src/GitLabApiClient/Models/Epics/Responses/Epic.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace GitLabApiClient.Models.Epics.Responses +{ + public class Epic : ModifiableObject + { + [JsonProperty("group_id")] + public int GroupId { get; set; } + + [JsonProperty("parent_id")] + public int? ParentId { get; set; } + + [JsonProperty("title")] + public string Title { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("state")] + public EpicIssueState State { get; set; } + + [JsonProperty("confidential")] + public bool Confidential { get; set; } + + [JsonProperty("web_url")] + public string WebUrl { get; set; } + + [JsonProperty("author")] + public Assignee Author { get; set; } + + [JsonProperty("start_date")] + public DateTime? StartDate { get; set; } + + [JsonProperty("start_date_is_fixed")] + public bool StartDateIsFixed { get; set; } + + [JsonProperty("start_date_fixed")] + public DateTime? StartDateFixed { get; set; } + + [JsonProperty("start_date_from_inherited_source")] + public DateTime? StartDateFromInheritedSource { get; set; } + + [JsonProperty("due_date")] + public string DueDate { get; set; } + + [JsonProperty("due_date_is_fixed")] + public bool DueDateIsFixed { get; set; } + + [JsonProperty("Due_date_fixed")] + public DateTime? DueDateFixed { get; set; } + + [JsonProperty("due_date_from_inherited_source")] + public DateTime? DueDateFromInheritedSource { get; set; } + + [JsonProperty("closed_at")] + public DateTime? ClosedAt { get; set; } + + [JsonProperty("labels")] + public List Labels { get; } = new List(); + + [JsonProperty("Upvotes")] + public int Upvotes { get; set; } + + [JsonProperty("Downvotes")] + public int Downvotes { get; set; } + } +} diff --git a/src/GitLabApiClient/Models/Groups/Requests/EpicsGroupQueryOptions.cs b/src/GitLabApiClient/Models/Groups/Requests/EpicsGroupQueryOptions.cs new file mode 100644 index 00000000..3abb421e --- /dev/null +++ b/src/GitLabApiClient/Models/Groups/Requests/EpicsGroupQueryOptions.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Text; +using GitLabApiClient.Models.Issues.Requests; + +namespace GitLabApiClient.Models.Groups.Requests +{ + public class EpicsGroupQueryOptions + { + internal EpicsGroupQueryOptions() + { + } + + /// + /// Return epics created by the given user id + /// + public int? AuthorId { get; set; } + + /// + /// Return epics matching a comma separated list of labels names. Label + /// names from the epic group or a parent group can be used + /// + public IList Labels { get; set; } = new List(); + + /// + /// If true, response returns more details for each label in labels field: :name, + /// :color, :description, :description_html, :text_color. Default is false. + /// + public bool WithLabelsDetails { get; set; } + + /// + /// Return epics ordered by created_at or updated_at fields. Default is created_at + /// + public EpicsIssuesOrder Order { get; set; } + + /// + /// Return epics sorted in asc or desc order. Default is desc + /// + public SortOrder SortOrder { get; set; } + + /// + /// Search epics against their title and description + /// + public string Filter { get; set; } + + /// + /// Search epics against their state, possible filters: opened, closed and all, default: all + /// + public EpicIssueState State { get; set; } + + /// + /// Return epics created on or after the given time (inclusive) + /// + public DateTime? CreatedAfter { get; set; } + + /// + /// Return epics created before the given time (inclusive) + /// + public DateTime? CreatedBefore { get; set; } + + /// + /// Return epics updated on or after the given time + /// + public DateTime? UpdatedAfter { get; set; } + + /// + /// Return epics updated on or before the given time + /// + public DateTime? UpdatedBefore { get; set; } + + /// + /// Include epics from the requested group’s ancestors. + /// Default is false + /// + public bool IncludeAncestorGroups { get; set; } + + /// + /// Include epics from the requested group’s descendants. + /// Default is true + /// + public bool IncludeDescendantGroups { get; set; } + } +} diff --git a/src/GitLabApiClient/Models/Issues/Requests/EpicsIssuesOrder.cs b/src/GitLabApiClient/Models/Issues/Requests/EpicsIssuesOrder.cs new file mode 100644 index 00000000..fcc83a11 --- /dev/null +++ b/src/GitLabApiClient/Models/Issues/Requests/EpicsIssuesOrder.cs @@ -0,0 +1,27 @@ +using System; + +namespace GitLabApiClient.Models.Issues.Requests +{ + public enum EpicsIssuesOrder + + { + CreatedAt, + UpdatedAt + } + + public static class Order + { + public static string GetIssuesOrderQueryValue(EpicsIssuesOrder order) + { + switch (order) + { + case EpicsIssuesOrder.CreatedAt: + return "created_at"; + case EpicsIssuesOrder.UpdatedAt: + return "updated_at"; + default: + throw new NotSupportedException($"Order {order} is not supported"); + } + } + } +} diff --git a/src/GitLabApiClient/Models/Issues/Requests/IssuesOrder.cs b/src/GitLabApiClient/Models/Issues/Requests/IssuesOrder.cs deleted file mode 100644 index a64c672d..00000000 --- a/src/GitLabApiClient/Models/Issues/Requests/IssuesOrder.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace GitLabApiClient.Models.Issues.Requests -{ - public enum IssuesOrder - { - CreatedAt, - UpdatedAt - } -} diff --git a/src/GitLabApiClient/Models/Issues/Requests/IssuesQueryOptions.cs b/src/GitLabApiClient/Models/Issues/Requests/IssuesQueryOptions.cs index bc91cc68..780b4d13 100644 --- a/src/GitLabApiClient/Models/Issues/Requests/IssuesQueryOptions.cs +++ b/src/GitLabApiClient/Models/Issues/Requests/IssuesQueryOptions.cs @@ -15,7 +15,7 @@ internal IssuesQueryOptions() { } /// Return all issues or just those that are opened or closed. /// Default is Opened. /// - public IssueState State { get; set; } + public EpicIssueState State { get; set; } /// /// List of label names, issues must have all labels to be returned. @@ -63,7 +63,7 @@ internal IssuesQueryOptions() { } /// /// Specifies issues order. Default is Creation time. /// - public IssuesOrder Order { get; set; } + public EpicsIssuesOrder Order { get; set; } /// /// Specifies project sort order. Default is descending. diff --git a/src/GitLabApiClient/Models/Issues/Requests/MoveIssueRequest.cs b/src/GitLabApiClient/Models/Issues/Requests/MoveIssueRequest.cs new file mode 100644 index 00000000..cce365ca --- /dev/null +++ b/src/GitLabApiClient/Models/Issues/Requests/MoveIssueRequest.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Text; +using GitLabApiClient.Internal.Paths; +using GitLabApiClient.Internal.Utilities; +using Newtonsoft.Json; + +namespace GitLabApiClient.Models.Issues.Requests +{ + /// + /// Used to move issues to another project. + /// + public sealed class MoveIssueRequest + { + /// + /// Initializes a new instance of the class. + /// + /// Title of the issue. + public MoveIssueRequest(string toProjectId) + { + Guard.NotEmpty(toProjectId, nameof(toProjectId)); + ToProjectId = toProjectId; + } + + /// + /// The projectId of the project where the issue should be moved to + /// + [JsonProperty("to_project_id")] + public string ToProjectId { get; } + } +} diff --git a/src/GitLabApiClient/Models/Issues/Requests/UpdateIssueRequest.cs b/src/GitLabApiClient/Models/Issues/Requests/UpdateIssueRequest.cs index 74998188..dbc77d56 100644 --- a/src/GitLabApiClient/Models/Issues/Requests/UpdateIssueRequest.cs +++ b/src/GitLabApiClient/Models/Issues/Requests/UpdateIssueRequest.cs @@ -53,7 +53,7 @@ public sealed class UpdateIssueRequest /// [JsonProperty("state_event")] [JsonConverter(typeof(StringEnumConverter))] - public UpdatedIssueState? State { get; set; } + public UpdatedEpicIssueState? State { get; set; } /// /// Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project owner rights). diff --git a/src/GitLabApiClient/Models/Issues/Responses/Issue.cs b/src/GitLabApiClient/Models/Issues/Responses/Issue.cs index a355cc34..c4a9d691 100644 --- a/src/GitLabApiClient/Models/Issues/Responses/Issue.cs +++ b/src/GitLabApiClient/Models/Issues/Responses/Issue.cs @@ -47,7 +47,7 @@ public sealed class Issue : ModifiableObject public int UserNotesCount { get; set; } [JsonProperty("state")] - public IssueState State { get; set; } + public EpicIssueState State { get; set; } [JsonProperty("web_url")] public string WebUrl { get; set; } diff --git a/src/GitLabApiClient/Models/Issues/Responses/IssueState.cs b/src/GitLabApiClient/Models/Issues/Responses/IssueState.cs deleted file mode 100644 index 7e71b39d..00000000 --- a/src/GitLabApiClient/Models/Issues/Responses/IssueState.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace GitLabApiClient.Models.Issues.Responses -{ - public enum IssueState - { - Opened, - Closed, - All - } -} diff --git a/src/GitLabApiClient/Models/Notes/Requests/CreateMergeRequestNoteRequest.cs b/src/GitLabApiClient/Models/Notes/Requests/CreateMergeRequestNoteRequest.cs deleted file mode 100644 index 620f91ed..00000000 --- a/src/GitLabApiClient/Models/Notes/Requests/CreateMergeRequestNoteRequest.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using Newtonsoft.Json; - -namespace GitLabApiClient.Models.Notes.Requests -{ - /// - /// Used to create issue notes in a project. - /// - public sealed class CreateMergeRequestNoteRequest - { - /// - /// Initializes a new instance of the class. - /// - /// The content of a note. - public CreateMergeRequestNoteRequest(string body) => Body = body; - - public CreateMergeRequestNoteRequest() - { - } - - /// - /// The content of a note. - /// - [JsonProperty("body")] - public string Body { get; set; } - - /// - /// Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights) - /// - [JsonProperty("created_at")] - public DateTime? CreatedAt { get; set; } - } -} diff --git a/src/GitLabApiClient/Models/Notes/Requests/CreateIssueNoteRequest.cs b/src/GitLabApiClient/Models/Notes/Requests/CreateNoteRequest.cs similarity index 74% rename from src/GitLabApiClient/Models/Notes/Requests/CreateIssueNoteRequest.cs rename to src/GitLabApiClient/Models/Notes/Requests/CreateNoteRequest.cs index c05c0476..52e82b1d 100644 --- a/src/GitLabApiClient/Models/Notes/Requests/CreateIssueNoteRequest.cs +++ b/src/GitLabApiClient/Models/Notes/Requests/CreateNoteRequest.cs @@ -6,15 +6,15 @@ namespace GitLabApiClient.Models.Notes.Requests /// /// Used to create issue notes in a project. /// - public sealed class CreateIssueNoteRequest + public sealed class CreateNoteRequest { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The content of a note. - public CreateIssueNoteRequest(string body) => Body = body; + public CreateNoteRequest(string body) => Body = body; - public CreateIssueNoteRequest() + public CreateNoteRequest() { } diff --git a/src/GitLabApiClient/Models/Notes/Requests/MergeRequestNotesQueryOptions.cs b/src/GitLabApiClient/Models/Notes/Requests/MergeRequestNotesQueryOptions.cs deleted file mode 100644 index cb777daf..00000000 --- a/src/GitLabApiClient/Models/Notes/Requests/MergeRequestNotesQueryOptions.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace GitLabApiClient.Models.Notes.Requests -{ - /// - /// Options for note (comment) listing - /// - public sealed class MergeRequestNotesQueryOptions - { - internal MergeRequestNotesQueryOptions() { } - - /// - /// Return merge request notes sorted in asc or desc order. Default is desc - /// - public SortOrder SortOrder { get; set; } = SortOrder.Descending; - - /// - /// Return merge request notes ordered by created_at or updated_at fields. Default is created_at - /// - public NoteOrder Order { get; set; } = NoteOrder.CreatedAt; - } -} diff --git a/src/GitLabApiClient/Models/Notes/Requests/IssueNotesQueryOptions.cs b/src/GitLabApiClient/Models/Notes/Requests/NotesQueryOptions.cs similarity index 85% rename from src/GitLabApiClient/Models/Notes/Requests/IssueNotesQueryOptions.cs rename to src/GitLabApiClient/Models/Notes/Requests/NotesQueryOptions.cs index 4848aa28..f03f7528 100644 --- a/src/GitLabApiClient/Models/Notes/Requests/IssueNotesQueryOptions.cs +++ b/src/GitLabApiClient/Models/Notes/Requests/NotesQueryOptions.cs @@ -3,9 +3,9 @@ namespace GitLabApiClient.Models.Notes.Requests /// /// Options for note (comment) listing /// - public sealed class IssueNotesQueryOptions + public sealed class NotesQueryOptions { - internal IssueNotesQueryOptions() { } + internal NotesQueryOptions() { } /// /// Return issue notes sorted in asc or desc order. Default is desc diff --git a/src/GitLabApiClient/Models/Issues/Requests/UpdatedIssueState.cs b/src/GitLabApiClient/Models/UpdatedEpicIssueState.cs similarity index 65% rename from src/GitLabApiClient/Models/Issues/Requests/UpdatedIssueState.cs rename to src/GitLabApiClient/Models/UpdatedEpicIssueState.cs index a5e050dc..4a453344 100644 --- a/src/GitLabApiClient/Models/Issues/Requests/UpdatedIssueState.cs +++ b/src/GitLabApiClient/Models/UpdatedEpicIssueState.cs @@ -1,8 +1,8 @@ using System.Runtime.Serialization; -namespace GitLabApiClient.Models.Issues.Requests +namespace GitLabApiClient.Models { - public enum UpdatedIssueState + public enum UpdatedEpicIssueState { [EnumMember(Value = "close")] Close, diff --git a/src/GitLabApiClient/ProjectsClient.cs b/src/GitLabApiClient/ProjectsClient.cs index ffd52c0f..2710eaca 100644 --- a/src/GitLabApiClient/ProjectsClient.cs +++ b/src/GitLabApiClient/ProjectsClient.cs @@ -106,7 +106,7 @@ public async Task> GetJobsAsync(ProjectId projectId, Action(url); } diff --git a/test/GitLabApiClient.Test/GroupsClientTest.cs b/test/GitLabApiClient.Test/GroupsClientTest.cs index 47a0ffdb..e6d985d1 100644 --- a/test/GitLabApiClient.Test/GroupsClientTest.cs +++ b/test/GitLabApiClient.Test/GroupsClientTest.cs @@ -29,7 +29,8 @@ public class GroupsClientTest new GroupsQueryBuilder(), new ProjectsGroupQueryBuilder(), new MilestonesQueryBuilder(), - new GroupLabelsQueryBuilder()); + new GroupLabelsQueryBuilder(), + new EpicsGroupQueryBuilder()); [Fact] public async Task GroupCanBeRetrievedByGroupId() diff --git a/test/GitLabApiClient.Test/Internal/Queries/IssuesQueryBuilderTest.cs b/test/GitLabApiClient.Test/Internal/Queries/IssuesQueryBuilderTest.cs index 8c739e1e..6c3c5c6a 100644 --- a/test/GitLabApiClient.Test/Internal/Queries/IssuesQueryBuilderTest.cs +++ b/test/GitLabApiClient.Test/Internal/Queries/IssuesQueryBuilderTest.cs @@ -20,14 +20,14 @@ public void NonDefaultQueryBuilt() "https://gitlab.com/api/v4/issues", new IssuesQueryOptions { - State = IssueState.Opened, + State = EpicIssueState.Opened, Labels = { "label1", "label2" }, MilestoneTitle = "milestone1", Scope = Scope.All, AuthorId = 1, AssigneeId = 2, IssueIds = { 3, 4 }, - Order = IssuesOrder.UpdatedAt, + Order = EpicsIssuesOrder.UpdatedAt, SortOrder = SortOrder.Ascending, Filter = "filter", CreatedAfter = new DateTime(1991, 11, 11, 1, 1, 1), @@ -66,14 +66,14 @@ public void NonDefaultQueryBuiltWithUserNames() "https://gitlab.com/api/v4/issues", new IssuesQueryOptions { - State = IssueState.Opened, + State = EpicIssueState.Opened, Labels = { "label1", "label2" }, MilestoneTitle = "milestone1", Scope = Scope.All, AuthorId = null, AssigneeId = null, IssueIds = { 3, 4 }, - Order = IssuesOrder.UpdatedAt, + Order = EpicsIssuesOrder.UpdatedAt, SortOrder = SortOrder.Ascending, Filter = "filter", CreatedAfter = new DateTime(1991, 11, 11, 1, 1, 1), diff --git a/test/GitLabApiClient.Test/Internal/Queries/ProjectIssueNotesQueryBuilderTest.cs b/test/GitLabApiClient.Test/Internal/Queries/ProjectIssueNotesQueryBuilderTest.cs index 7ef10970..6ff46273 100644 --- a/test/GitLabApiClient.Test/Internal/Queries/ProjectIssueNotesQueryBuilderTest.cs +++ b/test/GitLabApiClient.Test/Internal/Queries/ProjectIssueNotesQueryBuilderTest.cs @@ -11,11 +11,11 @@ public class ProjectIssueNotesQueryBuilderTest [Fact] public void NonDefaultQueryBuilt() { - var sut = new ProjectIssueNotesQueryBuilder(); + var sut = new NotesQueryBuilder(); string query = sut.Build( "https://gitlab.com/api/v4/project/1/issues/1/notes", - new IssueNotesQueryOptions() + new NotesQueryOptions() { SortOrder = SortOrder.Ascending, Order = NoteOrder.UpdatedAt diff --git a/test/GitLabApiClient.Test/IssuesClientTest.cs b/test/GitLabApiClient.Test/IssuesClientTest.cs index f333e13d..1d2eda0a 100644 --- a/test/GitLabApiClient.Test/IssuesClientTest.cs +++ b/test/GitLabApiClient.Test/IssuesClientTest.cs @@ -19,7 +19,7 @@ namespace GitLabApiClient.Test public class IssuesClientTest { private readonly IssuesClient _sut = new IssuesClient( - GetFacade(), new IssuesQueryBuilder(), new ProjectIssueNotesQueryBuilder()); + GetFacade(), new IssuesQueryBuilder(), new NotesQueryBuilder()); [Fact] public async Task CreatedIssueCanBeUpdated() @@ -65,11 +65,11 @@ public async Task CreatedIssueCanBeClosed() //act var updatedIssue = await _sut.UpdateAsync(TestProjectTextId, createdIssue.Iid, new UpdateIssueRequest() { - State = UpdatedIssueState.Close + State = UpdatedEpicIssueState.Close }); //assert - updatedIssue.Should().Match(i => i.State == IssueState.Closed); + updatedIssue.Should().Match(i => i.State == EpicIssueState.Closed); } [Fact] @@ -130,7 +130,7 @@ public async Task CreatedIssueNoteCanBeRetrieved() }); //act - var note = await _sut.CreateNoteAsync(TestProjectTextId, issue.Iid, new CreateIssueNoteRequest + var note = await _sut.CreateNoteAsync(TestProjectTextId, issue.Iid, new CreateNoteRequest { Body = body, CreatedAt = DateTime.Now @@ -154,7 +154,7 @@ public async Task CreatedIssueNoteCanBeUpdated() { Description = "Description1" }); - var createdIssueNote = await _sut.CreateNoteAsync(TestProjectTextId, createdIssue.Iid, new CreateIssueNoteRequest("comment2")); + var createdIssueNote = await _sut.CreateNoteAsync(TestProjectTextId, createdIssue.Iid, new CreateNoteRequest("comment2")); //act var updatedIssueNote = await _sut.UpdateNoteAsync(TestProjectTextId, createdIssue.Iid, createdIssueNote.Id, new UpdateIssueNoteRequest("comment22")); diff --git a/test/GitLabApiClient.Test/MergeRequestClientTest.cs b/test/GitLabApiClient.Test/MergeRequestClientTest.cs index a031eb3b..95ca25d8 100644 --- a/test/GitLabApiClient.Test/MergeRequestClientTest.cs +++ b/test/GitLabApiClient.Test/MergeRequestClientTest.cs @@ -18,7 +18,7 @@ public class MergeRequestClientTest : IAsyncLifetime { private readonly MergeRequestsClient _sut = new MergeRequestsClient( GitLabApiHelper.GetFacade(), new MergeRequestsQueryBuilder(), new ProjectMergeRequestsQueryBuilder(), - new ProjectMergeRequestsNotesQueryBuilder()); + new NotesQueryBuilder()); [Fact] public async Task CreatedMergeRequestCanBeRetrieved() diff --git a/test/GitLabApiClient.Test/ToDoListClientTest.cs b/test/GitLabApiClient.Test/ToDoListClientTest.cs index 1a69e2ef..3e07f6ec 100644 --- a/test/GitLabApiClient.Test/ToDoListClientTest.cs +++ b/test/GitLabApiClient.Test/ToDoListClientTest.cs @@ -17,8 +17,8 @@ namespace GitLabApiClient.Test [Collection("GitLabContainerFixture")] public class ToDoListClientTest : IAsyncLifetime { - private readonly MergeRequestsClient mergeRequestsClient = new MergeRequestsClient(GitLabApiHelper.GetFacade(), new MergeRequestsQueryBuilder(), new ProjectMergeRequestsQueryBuilder(), new ProjectMergeRequestsNotesQueryBuilder()); - private readonly IssuesClient issuesClient = new IssuesClient(GitLabApiHelper.GetFacade(), new IssuesQueryBuilder(), new ProjectIssueNotesQueryBuilder()); + private readonly MergeRequestsClient mergeRequestsClient = new MergeRequestsClient(GitLabApiHelper.GetFacade(), new MergeRequestsQueryBuilder(), new ProjectMergeRequestsQueryBuilder(), new NotesQueryBuilder()); + private readonly IssuesClient issuesClient = new IssuesClient(GitLabApiHelper.GetFacade(), new IssuesQueryBuilder(), new NotesQueryBuilder()); private readonly ToDoListClient _sut = new ToDoListClient(