diff --git a/NGitLab.Mock/Clients/GitLabClient.cs b/NGitLab.Mock/Clients/GitLabClient.cs index a7a91dc0..3721fc64 100644 --- a/NGitLab.Mock/Clients/GitLabClient.cs +++ b/NGitLab.Mock/Clients/GitLabClient.cs @@ -83,5 +83,10 @@ public IEventClient GetProjectEvents(int projectId) public IWikiClient GetWikiClient(int projectId) => new WikiClient(Context, projectId); public IProjectLevelApprovalRulesClient GetProjectLevelApprovalRulesClient(int projectId) => new ProjectLevelApprovalRulesClient(Context, projectId); + + public IProtectedBranchClient GetProtectedBranchClient(int projectId) + { + throw new System.NotImplementedException(); + } } } diff --git a/NGitLab.Tests/ProtectedBranchTests.cs b/NGitLab.Tests/ProtectedBranchTests.cs new file mode 100644 index 00000000..0fc0ee88 --- /dev/null +++ b/NGitLab.Tests/ProtectedBranchTests.cs @@ -0,0 +1,66 @@ +using System.Threading.Tasks; +using NGitLab.Models; +using NGitLab.Tests.Docker; +using NUnit.Framework; + +namespace NGitLab.Tests +{ + public class ProtectedBranchTests + { + [Test] + public async Task ProtectBranch_Test() + { + using var context = await GitLabTestContext.CreateAsync(); + var project = context.CreateProject(initializeWithCommits: true); + var branchClient = context.Client.GetRepository(project.Id).Branches; + var branch = branchClient.Create(new BranchCreate() { Name = "protectedBranch", Ref = project.DefaultBranch }); + var protectedBranchClient = context.Client.GetProtectedBranchClient(project.Id); + var branchProtect = new BranchProtect(branch.Name) + { + PushAccessLevel = AccessLevel.Maintainer, + MergeAccessLevel = AccessLevel.NoAccess, + AllowForcePush = true, + AllowedToPush = new AccessLevelInfo[] + { + new AccessLevelInfo() + { + AccessLevel = AccessLevel.Admin, + Description = "Admin", + }, + }, + AllowedToUnprotect = new AccessLevelInfo[] + { + new AccessLevelInfo() + { + AccessLevel = AccessLevel.NoAccess, + Description = "Example", + }, + }, + }; + + // Protect branch + ProtectedBranchAndBranchProtectAreEquals(branchProtect, protectedBranchClient.ProtectBranch(branchProtect)); + + // Get branch + ProtectedBranchAndBranchProtectAreEquals(branchProtect, protectedBranchClient.GetProtectedBranch(branch.Name)); + + // Get branches + Assert.IsNotEmpty(protectedBranchClient.GetProtectedBranches()); + var protectedBranches = protectedBranchClient.GetProtectedBranches(branch.Name); + Assert.IsNotEmpty(protectedBranches); + ProtectedBranchAndBranchProtectAreEquals(branchProtect, protectedBranches[0]); + + // Unprotect branch + protectedBranchClient.UnprotectBranch(branch.Name); + Assert.IsEmpty(protectedBranchClient.GetProtectedBranches(branch.Name)); + } + + private void ProtectedBranchAndBranchProtectAreEquals(BranchProtect branchProtect, ProtectedBranch protectedBranch) + { + Assert.AreEqual(branchProtect.BranchName, protectedBranch.Name); + Assert.AreEqual(branchProtect.PushAccessLevel, protectedBranch.PushAccessLevels[0].AccessLevel); + Assert.AreEqual(branchProtect.MergeAccessLevel, protectedBranch.MergeAccessLevels[0].AccessLevel); + Assert.AreEqual(branchProtect.AllowForcePush, protectedBranch.AllowForcePush); + } + } +} diff --git a/NGitLab/GitLabClient.cs b/NGitLab/GitLabClient.cs index 822eecc1..4e4d31ba 100644 --- a/NGitLab/GitLabClient.cs +++ b/NGitLab/GitLabClient.cs @@ -191,5 +191,8 @@ public IProjectLevelApprovalRulesClient GetProjectLevelApprovalRulesClient(int p { return new ProjectLevelApprovalRulesClient(_api, projectId); } + + public IProtectedBranchClient GetProtectedBranchClient(int projectId) + => new ProtectedBranchClient(_api, projectId); } } diff --git a/NGitLab/IBranchClient.cs b/NGitLab/IBranchClient.cs index 24b7b859..9a8acb2e 100644 --- a/NGitLab/IBranchClient.cs +++ b/NGitLab/IBranchClient.cs @@ -17,4 +17,4 @@ public interface IBranchClient void Delete(string name); } -} \ No newline at end of file +} diff --git a/NGitLab/IGitLabClient.cs b/NGitLab/IGitLabClient.cs index 495bbe3b..934f2628 100644 --- a/NGitLab/IGitLabClient.cs +++ b/NGitLab/IGitLabClient.cs @@ -79,5 +79,7 @@ public interface IGitLabClient IGroupVariableClient GetGroupVariableClient(int groupId); IProjectLevelApprovalRulesClient GetProjectLevelApprovalRulesClient(int projectId); + + IProtectedBranchClient GetProtectedBranchClient(int projectId); } } diff --git a/NGitLab/IProtectedBranchClient.cs b/NGitLab/IProtectedBranchClient.cs new file mode 100644 index 00000000..deb1d4d2 --- /dev/null +++ b/NGitLab/IProtectedBranchClient.cs @@ -0,0 +1,15 @@ +using NGitLab.Models; + +namespace NGitLab +{ + public interface IProtectedBranchClient + { + ProtectedBranch ProtectBranch(BranchProtect branchProtect); + + void UnprotectBranch(string branchName); + + ProtectedBranch GetProtectedBranch(string branchName); + + ProtectedBranch[] GetProtectedBranches(string search = null); + } +} diff --git a/NGitLab/Impl/BranchClient.cs b/NGitLab/Impl/BranchClient.cs index c908cfc6..889d0a0d 100644 --- a/NGitLab/Impl/BranchClient.cs +++ b/NGitLab/Impl/BranchClient.cs @@ -27,4 +27,4 @@ public BranchClient(API api, string repoPath) public void Delete(string name) => _api.Delete().Execute(_repoPath + "/branches/" + Uri.EscapeDataString(name)); } -} \ No newline at end of file +} diff --git a/NGitLab/Impl/ProtectedBranchClient.cs b/NGitLab/Impl/ProtectedBranchClient.cs new file mode 100644 index 00000000..47e93b15 --- /dev/null +++ b/NGitLab/Impl/ProtectedBranchClient.cs @@ -0,0 +1,31 @@ +using System; +using NGitLab.Models; + +namespace NGitLab.Impl +{ + internal sealed class ProtectedBranchClient : IProtectedBranchClient + { + private readonly API _api; + private readonly int _projectId; + private readonly string _protectedBranchesUrl; + + public ProtectedBranchClient(API api, int projectId) + { + _api = api; + _projectId = projectId; + _protectedBranchesUrl = $"{Project.Url}/{_projectId}/protected_branches"; + } + + public ProtectedBranch ProtectBranch(BranchProtect branchProtect) + => _api.Post().With(branchProtect).To(_protectedBranchesUrl); + + public void UnprotectBranch(string branchName) + => _api.Delete().Execute($"{_protectedBranchesUrl}/{Uri.EscapeDataString(branchName)}"); + + public ProtectedBranch GetProtectedBranch(string branchName) + => _api.Get().To($"{_protectedBranchesUrl}/{Uri.EscapeDataString(branchName)}"); + + public ProtectedBranch[] GetProtectedBranches(string search = null) + => _api.Get().To(Utils.AddParameter(_protectedBranchesUrl, "search", search)); + } +} diff --git a/NGitLab/Models/AccessLevel.cs b/NGitLab/Models/AccessLevel.cs index 14254cad..f9e2737d 100644 --- a/NGitLab/Models/AccessLevel.cs +++ b/NGitLab/Models/AccessLevel.cs @@ -8,6 +8,7 @@ namespace NGitLab.Models /// https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/members.md public enum AccessLevel { + NoAccess = 0, Guest = 10, Reporter = 20, Developer = 30, @@ -19,5 +20,6 @@ public enum AccessLevel /// Only valid for groups. /// Owner = 50, + Admin = 60, } -} \ No newline at end of file +} diff --git a/NGitLab/Models/AccessLevelInfo.cs b/NGitLab/Models/AccessLevelInfo.cs new file mode 100644 index 00000000..29e1f090 --- /dev/null +++ b/NGitLab/Models/AccessLevelInfo.cs @@ -0,0 +1,14 @@ +using System.Runtime.Serialization; + +namespace NGitLab.Models +{ + [DataContract] + public class AccessLevelInfo + { + [DataMember(Name = "access_level")] + public AccessLevel AccessLevel { get; set; } + + [DataMember(Name = "access_level_description")] + public string Description { get; set; } + } +} diff --git a/NGitLab/Models/BranchProtect.cs b/NGitLab/Models/BranchProtect.cs new file mode 100644 index 00000000..de2a4f91 --- /dev/null +++ b/NGitLab/Models/BranchProtect.cs @@ -0,0 +1,40 @@ +using System.Runtime.Serialization; + +namespace NGitLab.Models +{ + [DataContract] + public class BranchProtect + { + public BranchProtect(string branchName) + { + BranchName = branchName; + } + + [DataMember(Name = "name")] + public string BranchName { get; set; } + + [DataMember(Name = "push_access_level")] + public AccessLevel? PushAccessLevel { get; set; } = null; + + [DataMember(Name = "merge_access_level")] + public AccessLevel? MergeAccessLevel { get; set; } = null; + + [DataMember(Name = "unprotect_access_level")] + public AccessLevel? UnprotectAccessLevel { get; set; } = null; + + [DataMember(Name = "allow_force_push")] + public bool AllowForcePush { get; set; } = false; + + [DataMember(Name = "allowed_to_merge")] + public AccessLevelInfo[] AllowedToMerge { get; set; } + + [DataMember(Name = "allowed_to_push")] + public AccessLevelInfo[] AllowedToPush { get; set; } + + [DataMember(Name = "allowed_to_unprotect")] + public AccessLevelInfo[] AllowedToUnprotect { get; set; } + + [DataMember(Name = "code_owner_approval_required")] + public bool CodeOwnerApprovalRequired { get; set; } = false; + } +} diff --git a/NGitLab/Models/ProtectedBranch.cs b/NGitLab/Models/ProtectedBranch.cs new file mode 100644 index 00000000..f54cc8c1 --- /dev/null +++ b/NGitLab/Models/ProtectedBranch.cs @@ -0,0 +1,26 @@ +using System.Runtime.Serialization; + +namespace NGitLab.Models +{ + [DataContract] + public class ProtectedBranch + { + [DataMember(Name = "id")] + public long Id { get; set; } + + [DataMember(Name = "name")] + public string Name { get; set; } + + [DataMember(Name = "push_access_levels")] + public AccessLevelInfo[] PushAccessLevels { get; set; } + + [DataMember(Name = "merge_access_levels")] + public AccessLevelInfo[] MergeAccessLevels { get; set; } + + [DataMember(Name = "allow_force_push")] + public bool AllowForcePush { get; set; } + + [DataMember(Name = "code_owner_approval_required")] + public bool CodeOwnerApprovalRequired { get; set; } + } +}