Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/GitLabApiClient/CommitsClient.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GitLabApiClient.Internal.Http;
using GitLabApiClient.Internal.Paths;
using GitLabApiClient.Internal.Queries;
using GitLabApiClient.Models.Commits.Requests;
using GitLabApiClient.Models.Commits.Requests.CreateCommitRequest;
using GitLabApiClient.Models.Commits.Responses;
using GitLabApiClient.Models.Projects.Responses;

Expand Down Expand Up @@ -92,5 +95,24 @@ public async Task<IList<CommitStatuses>> GetStatusesAsync(ProjectId projectId, s
string url = _commitStatusesQueryBuilder.Build($"projects/{projectId}/repository/commits/{sha}/statuses", queryOptions);
return await _httpFacade.GetPagedList<CommitStatuses>(url);
}

/// <summary>
/// Creates a commit with multiple files and actions.
/// </summary>
/// <param name="projectId">The ID, path or <see cref="Project"/> of the project.</param>
/// <param name="request">Create commit request.</param>
/// <param name="autoEncodeToBase64">Automatically encode contents to base64 (default false).</param>
public async Task<Commit> CreateAsync(ProjectId projectId, CreateCommitRequest request, bool autoEncodeToBase64 = false)
{
if (autoEncodeToBase64)
{
foreach (var action in request.Actions.Where(action => !string.IsNullOrEmpty(action.Content)))
{
action.Encoding = CreateCommitRequestActionEncoding.Base64;
action.Content = Convert.ToBase64String(Encoding.UTF8.GetBytes(action.Content));
}
}
return await _httpFacade.Post<Commit>($"projects/{projectId}/repository/commits", request);
}
}
}
9 changes: 9 additions & 0 deletions src/GitLabApiClient/ICommitsClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Threading.Tasks;
using GitLabApiClient.Internal.Paths;
using GitLabApiClient.Models.Commits.Requests;
using GitLabApiClient.Models.Commits.Requests.CreateCommitRequest;
using GitLabApiClient.Models.Commits.Responses;
using GitLabApiClient.Models.Projects.Responses;

Expand Down Expand Up @@ -51,5 +52,13 @@ public interface ICommitsClient
/// <param name="sha">The commit hash</param>
/// <returns></returns>
Task<IList<CommitStatuses>> GetStatusesAsync(ProjectId projectId, string sha, Action<CommitStatusesQueryOptions> options = null);

/// <summary>
/// Creates a commit with multiple files and actions.
/// </summary>
/// <param name="projectId">The ID, path or <see cref="Project"/> of the project.</param>
/// <param name="request">Create commit request.</param>
/// <param name="autoEncodeToBase64">Automatically encode contents to base64 (default false).</param>
Task<Commit> CreateAsync(ProjectId projectId, CreateCommitRequest request, bool autoEncodeToBase64 = false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System.Collections.Generic;
using GitLabApiClient.Internal.Paths;
using GitLabApiClient.Internal.Utilities;
using Newtonsoft.Json;

namespace GitLabApiClient.Models.Commits.Requests.CreateCommitRequest
{
public class CreateCommitRequest
{
/// <summary>
/// Initializes a new instance of the <see cref="CreateCommitRequest"/> class.
/// </summary>
/// <param name="branch">Name of the branch</param>
/// <param name="commitMessage">Commit message</param>
/// <param name="actions">File actions, see <see cref="CreateCommitRequestAction"/></param>
public CreateCommitRequest(string branch, string commitMessage, List<CreateCommitRequestAction> actions)
{
Guard.NotEmpty(branch, nameof(branch));
Guard.NotEmpty(commitMessage, nameof(commitMessage));
Guard.NotEmpty(actions, nameof(actions));

Branch = branch;
CommitMessage = commitMessage;
Actions = actions;
//Default of GitLab
Stats = true;
}

/// <summary>
/// Name of the branch to commit into. To create a new branch, also provide either StartBranch or StartSha, and optionally StartProject.
/// </summary>
[JsonProperty("branch")]
public string Branch { get; set; }

/// <summary>
/// Commit message
/// </summary>
[JsonProperty("commit_message")]
public string CommitMessage { get; set; }

/// <summary>
/// Name of the branch to start the new branch from.
/// </summary>
[JsonProperty("start_branch")]
public string StartBranch { get; set; }

/// <summary>
/// SHA of the commit to start the new branch from.
/// </summary>
[JsonProperty("start_sha")]
public string StartSha { get; set; }

/// <summary>
/// The project ID or URL-encoded path of the project to start the new branch from. Defaults to the value of id.
/// See <see cref="ProjectId"/>.
/// </summary>
[JsonProperty("start_project")]
public ProjectId StartProjectId { get; set; }

/// <summary>
/// A list of action hashes to commit as a batch. See <see cref="CreateCommitRequestAction"/>.
/// </summary>
[JsonProperty("actions")]
public List<CreateCommitRequestAction> Actions { get; set; }

/// <summary>
/// Specify the commit author’s email address.
/// </summary>
[JsonProperty("author_email")]
public string AuthorEmail { get; set; }

/// <summary>
/// Specify the commit author’s name.
/// </summary>
[JsonProperty("author_name")]
public string AuthorName { get; set; }

/// <summary>
/// Include commit stats. Default is true.
/// </summary>
[JsonProperty("stats")]
public bool? Stats { get; set; }

/// <summary>
/// When true overwrites the target branch with a new commit based on the StartBranch or StartSha.
/// </summary>
[JsonProperty("force")]
public bool? Force { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using GitLabApiClient.Internal.Utilities;
using Newtonsoft.Json;

namespace GitLabApiClient.Models.Commits.Requests.CreateCommitRequest
{
public class CreateCommitRequestAction
{
/// <summary>
/// Initializes a new instance of the <see cref="CreateCommitRequestAction"/> class.
/// </summary>
/// <param name="action">The action to perform, see <see cref="CreateCommitRequestActionType"/>.</param>
/// <param name="filePath">Full path to the file.</param>
public CreateCommitRequestAction(CreateCommitRequestActionType action, string filePath)
{
Guard.NotEmpty(filePath, nameof(filePath));

Action = action;
FilePath = filePath;
//Default of GitLab
Encoding = CreateCommitRequestActionEncoding.Text;
}

/// <summary>
/// The action to perform, see <see cref="CreateCommitRequestActionType"/>.
/// </summary>
[JsonProperty("action")]
public CreateCommitRequestActionType Action { get; set; }

/// <summary>
/// Full path to the file. Ex. lib/class.rb
/// </summary>
[JsonProperty("file_path")]
public string FilePath { get; set; }

/// <summary>
/// Original full path to the file being moved. Ex. lib/class1.rb. Only considered for move action.
/// </summary>
[JsonProperty("previous_path")]
public string PreviousPath { get; set; }

/// <summary>
/// File content, required for all except delete, chmod, and move. Move actions that do not specify content
/// preserve the existing file content, and any other value of content overwrites the file content.
/// </summary>
[JsonProperty("content")]
public string Content { get; set; }

/// <summary>
/// text or base64. text is default. See <see cref="CreateCommitRequestActionEncoding"/>.
/// </summary>
[JsonProperty("encoding")]
public CreateCommitRequestActionEncoding Encoding { get; set; }

/// <summary>
/// Last known file commit ID. Only considered in update, move, and delete actions.
/// </summary>
[JsonProperty("last_commit_id")]
public string LastCommitId { get; set; }

/// <summary>
/// When true/false enables/disables the execute flag on the file. Only considered for chmod action.
/// </summary>
// ReSharper disable once StringLiteralTypo
[JsonProperty("execute_filemode")]
public bool? ExecuteFileMode { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Runtime.Serialization;

namespace GitLabApiClient.Models.Commits.Requests.CreateCommitRequest
{
public enum CreateCommitRequestActionEncoding
{
[EnumMember(Value = "text")]
Text,
[EnumMember(Value = "base64")]
Base64
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Runtime.Serialization;

namespace GitLabApiClient.Models.Commits.Requests.CreateCommitRequest
{
public enum CreateCommitRequestActionType
{
[EnumMember(Value = "create")]
Create,
[EnumMember(Value = "delete")]
Delete,
[EnumMember(Value = "move")]
Move,
[EnumMember(Value = "update")]
Update,
[EnumMember(Value = "chmod")]
Chmod
}
}
134 changes: 134 additions & 0 deletions test/GitLabApiClient.Test/CommitsClientMockedTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http;
using FakeItEasy;
using FluentAssertions;
using GitLabApiClient.Internal.Http;
using GitLabApiClient.Internal.Http.Serialization;
using GitLabApiClient.Internal.Queries;
using GitLabApiClient.Test.TestUtilities;
using Xunit;

namespace GitLabApiClient.Test
{
[ExcludeFromCodeCoverage]
public class CommitsClientMockedTest
{
[Fact]
public async void GetCommitBySha()
{
string gitlabServer = "http://fake-gitlab.com/";
string projectId = "id";
string sha = "6104942438c14ec7bd21c6cd5bd995272b3faff6";
string url = $"/projects/{projectId}/repository/commits/{sha}";

var handler = A.Fake<MockHandler>(opt => opt.CallsBaseMethods());
A.CallTo(() => handler.SendAsync(HttpMethod.Get, url))
.ReturnsLazily(() => HttpResponseMessageProducer.Success(
$"{{\"id\": \"{sha}\", }}"));
using (var client = new HttpClient(handler) { BaseAddress = new Uri(gitlabServer) })
{
var gitlabHttpFacade = new GitLabHttpFacade(new RequestsJsonSerializer(), client);
var commitsClient = new CommitsClient(gitlabHttpFacade, new CommitQueryBuilder(), new CommitRefsQueryBuilder(), new CommitStatusesQueryBuilder());

var commitFromClient = await commitsClient.GetAsync(projectId, sha);
commitFromClient.Id.Should().BeEquivalentTo(sha);
}
}

[Fact]
public async void GetCommitsByRefName()
{
string gitlabServer = "http://fake-gitlab.com/";
string projectId = "id";
string refName = "6104942438c14ec7bd21c6cd5bd995272b3faff6";
string url = $"/projects/id/repository/commits?ref_name={refName}&per_page=100&page=1";

var handler = A.Fake<MockHandler>(opt => opt.CallsBaseMethods());
A.CallTo(() => handler.SendAsync(HttpMethod.Get, url))
.ReturnsLazily(() => HttpResponseMessageProducer.Success(
$"[ {{ \"id\": \"id1\",}},\n {{\"id\": \"id2\",}}]"));
using (var client = new HttpClient(handler) { BaseAddress = new Uri(gitlabServer) })
{
var gitlabHttpFacade = new GitLabHttpFacade(new RequestsJsonSerializer(), client);
var commitsClient = new CommitsClient(gitlabHttpFacade, new CommitQueryBuilder(), new CommitRefsQueryBuilder(), new CommitStatusesQueryBuilder());

var commitsFromClient = await commitsClient.GetAsync(projectId, o => o.RefName = refName);
commitsFromClient[0].Id.Should().BeEquivalentTo("id1");
commitsFromClient[1].Id.Should().BeEquivalentTo("id2");
}

}

[Fact]
public async void GetDiffsForCommit()
{
string gitlabServer = "http://fake-gitlab.com/";
string projectId = "id";
string sha = "6104942438c14ec7bd21c6cd5bd995272b3faff6";
string url = $"/projects/id/repository/commits/{sha}/diff?per_page=100&page=1";

var handler = A.Fake<MockHandler>(opt => opt.CallsBaseMethods());
A.CallTo(() => handler.SendAsync(HttpMethod.Get, url))
.ReturnsLazily(() => HttpResponseMessageProducer.Success(
$"[ {{ \"diff\": \"diff1\", \"new_path\": \"new_path1\", \"old_path\": \"old_path1\", \"a_mode\": \"a_mode1\", \"b_mode\": \"b_mode1\", \"new_file\": \"true\", \"renamed_file\": \"false\", \"deleted_file\": \"false\" }},\n {{\"diff\": \"diff2\", \"new_path\": \"new_path2\", \"old_path\": \"old_path2\", \"a_mode\": \"a_mode2\", \"b_mode\": \"b_mode2\", \"new_file\": \"false\", \"renamed_file\": \"true\", \"deleted_file\": \"true\"}}]"));
using (var client = new HttpClient(handler) { BaseAddress = new Uri(gitlabServer) })
{
var gitlabHttpFacade = new GitLabHttpFacade(new RequestsJsonSerializer(), client);
var commitsClient = new CommitsClient(gitlabHttpFacade, new CommitQueryBuilder(), new CommitRefsQueryBuilder(), new CommitStatusesQueryBuilder());

var diffsFromClient = await commitsClient.GetDiffsAsync(projectId, sha);
diffsFromClient[0].DiffText.Should().BeEquivalentTo("diff1");
diffsFromClient[0].NewPath.Should().BeEquivalentTo("new_path1");
diffsFromClient[0].OldPath.Should().BeEquivalentTo("old_path1");
diffsFromClient[0].AMode.Should().BeEquivalentTo("a_mode1");
diffsFromClient[0].BMode.Should().BeEquivalentTo("b_mode1");
diffsFromClient[0].IsNewFile.Should().BeTrue();
diffsFromClient[0].IsRenamedFile.Should().BeFalse();
diffsFromClient[0].IsDeletedFile.Should().BeFalse();

diffsFromClient[1].DiffText.Should().BeEquivalentTo("diff2");
diffsFromClient[1].NewPath.Should().BeEquivalentTo("new_path2");
diffsFromClient[1].OldPath.Should().BeEquivalentTo("old_path2");
diffsFromClient[1].AMode.Should().BeEquivalentTo("a_mode2");
diffsFromClient[1].BMode.Should().BeEquivalentTo("b_mode2");
diffsFromClient[1].IsNewFile.Should().BeFalse();
diffsFromClient[1].IsRenamedFile.Should().BeTrue();
diffsFromClient[1].IsDeletedFile.Should().BeTrue();

}
}

[Fact]
public async void GetStatusesForCommit()
{
string gitlabServer = "http://fake-gitlab.com/";
string projectId = "id";
string sha = "6104942438c14ec7bd21c6cd5bd995272b3faff6";
string Name = "name1";
string url = $"/projects/id/repository/commits/{sha}/statuses?name={Name}&per_page=100&page=1";

var handler = A.Fake<MockHandler>(opt => opt.CallsBaseMethods());
A.CallTo(() => handler.SendAsync(HttpMethod.Get, url))
.ReturnsLazily(() => HttpResponseMessageProducer.Success(
$"[ {{\"id\":1,\"sha\":\"{sha}\",\"ref \":\"\",\"status\":\"success\",\"name\":\"name1\",\"target_url\":\"target_url1\",\"description\":\"success\",\"created_at\":\"2020-04-08T11:57:49.136+05:30\",\"started_at\":\"2020-04-08T11:58:00.362+05:30\",\"finished_at\":\"2020-04-08T11:58:06.121+05:30\",\"allow_failure\":false,\"coverage\":null,\"author\":{{\"id\":1,\"name\":\"name\",\"username\":\"username\",\"state\":\"active\",\"avatar_url\":\"avatar_url1\",\"web_url\":\"web_url1\"}} }},{{\"id\":2,\"sha\":\"{sha}\",\"ref \":\"\",\"status\":\"success\",\"name\":\"name2\",\"target_url\":\"target_url2\",\"description\":\"success\",\"created_at\":\"2020-04-08T11:57:49.136+05:30\",\"started_at\":\"2020-04-08T11:58:00.362+05:30\",\"finished_at\":\"2020-04-08T11:58:06.121+05:30\",\"allow_failure\":false,\"coverage\":null,\"author\":{{\"id\":2,\"name\":\"name2\",\"username\":\"username2\",\"state\":\"activ2\",\"avatar_url2\":\"avatar_url2\",\"web_url\":\"web_url2\"}} }}]"));
using (var client = new HttpClient(handler) { BaseAddress = new Uri(gitlabServer) })
{
var gitlabHttpFacade = new GitLabHttpFacade(new RequestsJsonSerializer(), client);
var commitsClient = new CommitsClient(gitlabHttpFacade, new CommitQueryBuilder(), new CommitRefsQueryBuilder(), new CommitStatusesQueryBuilder());

var statusesFromClient = await commitsClient.GetStatusesAsync(projectId, sha, o => o.Name = Name);
statusesFromClient[0].Status.Should().BeEquivalentTo("success");
statusesFromClient[0].Name.Should().BeEquivalentTo("name1");
statusesFromClient[0].TargetUrl.Should().BeEquivalentTo("target_url1");
statusesFromClient[0].Id.Should().BeEquivalentTo("1");

statusesFromClient[1].Status.Should().BeEquivalentTo("success");
statusesFromClient[1].Name.Should().BeEquivalentTo("name2");
statusesFromClient[1].TargetUrl.Should().BeEquivalentTo("target_url2");
statusesFromClient[1].Id.Should().BeEquivalentTo("2");

}
}
}
}
Loading