Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit bda657a

Browse files
Merge pull request #2029 from github/protected-branches
Adding indications for protected branches
2 parents a489cbe + 018fdab commit bda657a

File tree

12 files changed

+117
-7
lines changed

12 files changed

+117
-7
lines changed

src/GitHub.App/SampleData/PullRequestCheckViewModelDesigner.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ namespace GitHub.SampleData
99
{
1010
public sealed class PullRequestCheckViewModelDesigner : ViewModelBase, IPullRequestCheckViewModel
1111
{
12+
public bool IsRequired { get; } = true;
13+
1214
public string Title { get; set; } = "continuous-integration/appveyor/pr";
1315

1416
public string Description { get; set; } = "AppVeyor build failed";

src/GitHub.App/Services/RepositoryService.cs

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using System;
22
using System.Collections.Generic;
33
using System.ComponentModel.Composition;
4+
using System.Linq;
45
using System.Threading.Tasks;
56
using GitHub.Api;
67
using GitHub.Extensions;
8+
using GitHub.Models;
79
using GitHub.Primitives;
810
using Octokit.GraphQL;
911
using static Octokit.GraphQL.Variable;
@@ -12,9 +14,10 @@ namespace GitHub.Services
1214
{
1315
[Export(typeof(IRepositoryService))]
1416
[PartCreationPolicy(CreationPolicy.Shared)]
15-
public class RepositoryService : IRepositoryService
17+
public class RepositoryService: IRepositoryService
1618
{
1719
static ICompiledQuery<Tuple<string, string>> readParentOwnerLogin;
20+
static ICompiledQuery<List<ProtectedBranch>> queryProtectedBranches;
1821
readonly IGraphQLClientFactory graphqlFactory;
1922

2023
[ImportingConstructor]
@@ -45,9 +48,47 @@ public RepositoryService(IGraphQLClientFactory graphqlFactory)
4548
{ nameof(name), name },
4649
};
4750

48-
var graphql = await graphqlFactory.CreateConnection(address);
49-
var result = await graphql.Run(readParentOwnerLogin, vars);
51+
var graphql = await graphqlFactory.CreateConnection(address).ConfigureAwait(false);
52+
var result = await graphql.Run(readParentOwnerLogin, vars).ConfigureAwait(false);
5053
return result != null ? (result.Item1, result.Item2) : ((string, string)?)null;
5154
}
55+
56+
public async Task<IList<ProtectedBranch>> GetProtectedBranches(HostAddress address, string owner, string name)
57+
{
58+
Guard.ArgumentNotNull(address, nameof(address));
59+
Guard.ArgumentNotEmptyString(owner, nameof(owner));
60+
Guard.ArgumentNotEmptyString(name, nameof(name));
61+
62+
if (queryProtectedBranches == null)
63+
{
64+
queryProtectedBranches = new Query()
65+
.Repository(Var(nameof(name)), Var(nameof(owner)))
66+
.Select(r =>
67+
r.ProtectedBranches(null, null, null, null)
68+
.AllPages()
69+
.Select(branch => new ProtectedBranch
70+
{
71+
Name = branch.Name,
72+
RequiredStatusCheckContexts = branch.RequiredStatusCheckContexts.ToArray()
73+
}).ToList()
74+
).Compile();
75+
}
76+
77+
var vars = new Dictionary<string, object>
78+
{
79+
{ nameof(owner), owner },
80+
{ nameof(name), name },
81+
};
82+
83+
var graphql = await graphqlFactory.CreateConnection(address).ConfigureAwait(false);
84+
return await graphql.Run(queryProtectedBranches, vars).ConfigureAwait(false);
85+
}
86+
87+
public async Task<ProtectedBranch> GetProtectedBranch(HostAddress address, string owner, string name, string branchName)
88+
{
89+
Guard.ArgumentNotNull(branchName, nameof(branchName));
90+
var protectedBranches = await GetProtectedBranches(address, owner, name).ConfigureAwait(false);
91+
return protectedBranches.FirstOrDefault(branch => branch.Name.Equals(branchName, StringComparison.InvariantCultureIgnoreCase));
92+
}
5293
}
5394
}

src/GitHub.App/ViewModels/GitHubPane/PullRequestCheckViewModel.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public static IEnumerable<IPullRequestCheckViewModel> Build(IViewViewModelFactor
5353

5454
var pullRequestCheckViewModel = (PullRequestCheckViewModel) viewViewModelFactory.CreateViewModel<IPullRequestCheckViewModel>();
5555
pullRequestCheckViewModel.CheckType = PullRequestCheckType.StatusApi;
56+
pullRequestCheckViewModel.IsRequired = statusModel.IsRequired;
5657
pullRequestCheckViewModel.Title = statusModel.Context;
5758
pullRequestCheckViewModel.Description = statusModel.Description;
5859
pullRequestCheckViewModel.Status = checkStatus;
@@ -102,6 +103,7 @@ public static IEnumerable<IPullRequestCheckViewModel> Build(IViewViewModelFactor
102103

103104
var pullRequestCheckViewModel = (PullRequestCheckViewModel)viewViewModelFactory.CreateViewModel<IPullRequestCheckViewModel>();
104105
pullRequestCheckViewModel.CheckType = PullRequestCheckType.ChecksApi;
106+
pullRequestCheckViewModel.IsRequired = arg.checkRun.IsRequired;
105107
pullRequestCheckViewModel.CheckRunId = arg.checkRun.Id;
106108
pullRequestCheckViewModel.HasAnnotations = arg.checkRun.Annotations?.Any() ?? false;
107109
pullRequestCheckViewModel.Title = arg.checkRun.Name;
@@ -147,6 +149,9 @@ private void DoOpenDetailsUrl()
147149
usageTracker.IncrementCounter(expression).Forget();
148150
}
149151

152+
/// <inheritdoc/>
153+
public bool IsRequired { get; private set; }
154+
150155
/// <inheritdoc/>
151156
public string Title { get; private set; }
152157

src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestCheckViewModel.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ namespace GitHub.ViewModels.GitHubPane
1010
/// </summary>
1111
public interface IPullRequestCheckViewModel: IViewModel
1212
{
13+
bool IsRequired { get; }
14+
1315
/// <summary>
1416
/// The title of the Status/Check.
1517
/// </summary>

src/GitHub.Exports/Models/CheckRunModel.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ public class CheckRunModel
5858
/// </summary>
5959
public string Summary { get; set; }
6060

61+
/// <summary>
62+
/// The flag that denotes if this check is required for the pull request.
63+
/// </summary>
64+
public bool IsRequired { get; set; }
65+
6166
/// <summary>
6267
/// The detail of a Check Run.
6368
/// </summary>

src/GitHub.Exports/Models/IPullRequestModel.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ public enum PullRequestChecksState
2121
Failure
2222
}
2323

24+
public enum PullRequestChecksSummaryState
25+
{
26+
None,
27+
Pending,
28+
Success,
29+
SuccessWithFailure,
30+
SuccessWithPending,
31+
Failure
32+
}
33+
2434
public interface IPullRequestModel : ICopyable<IPullRequestModel>,
2535
IEquatable<IPullRequestModel>, IComparable<IPullRequestModel>
2636
{
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace GitHub.Models
2+
{
3+
public class ProtectedBranch
4+
{
5+
public string Name { get; set; }
6+
public string[] RequiredStatusCheckContexts { get; set; }
7+
}
8+
}

src/GitHub.Exports/Models/StatusModel.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,7 @@ public class StatusModel
2424
/// The descritption for the Status
2525
/// </summary>
2626
public string Description { get; set; }
27+
28+
public bool IsRequired { get; set; }
2729
}
2830
}

src/GitHub.Exports/Services/IRepositoryService.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Threading.Tasks;
4+
using GitHub.Models;
35
using GitHub.Primitives;
46

57
namespace GitHub.Services
@@ -17,5 +19,9 @@ public interface IRepositoryService
1719
/// otherwise null.
1820
/// </returns>
1921
Task<(string owner, string name)?> FindParent(HostAddress address, string owner, string name);
22+
23+
Task<IList<ProtectedBranch>> GetProtectedBranches(HostAddress address, string owner, string name);
24+
25+
Task<ProtectedBranch> GetProtectedBranch(HostAddress address, string owner, string name, string branchName);
2026
}
2127
}

src/GitHub.InlineReviews/Services/PullRequestSessionService.cs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public class PullRequestSessionService : IPullRequestSessionService
4949
readonly IDiffService diffService;
5050
readonly IApiClientFactory apiClientFactory;
5151
readonly IGraphQLClientFactory graphqlFactory;
52+
readonly IRepositoryService repositoryService;
5253
readonly IUsageTracker usageTracker;
5354
readonly IDictionary<Tuple<string, string>, string> mergeBaseCache;
5455

@@ -59,13 +60,15 @@ public PullRequestSessionService(
5960
IDiffService diffService,
6061
IApiClientFactory apiClientFactory,
6162
IGraphQLClientFactory graphqlFactory,
63+
IRepositoryService repositoryService,
6264
IUsageTracker usageTracker)
6365
{
6466
this.gitService = gitService;
6567
this.gitClient = gitClient;
6668
this.diffService = diffService;
6769
this.apiClientFactory = apiClientFactory;
6870
this.graphqlFactory = graphqlFactory;
71+
this.repositoryService = repositoryService;
6972
this.usageTracker = usageTracker;
7073

7174
mergeBaseCache = new Dictionary<Tuple<string, string>, string>();
@@ -360,9 +363,12 @@ public virtual async Task<PullRequestDetailModel> ReadPullRequestDetail(HostAddr
360363
var connection = await graphqlFactory.CreateConnection(address);
361364
var result = await connection.Run(readPullRequest, vars);
362365

366+
var protectedBranches = await repositoryService.GetProtectedBranch(address, owner, name, result.BaseRefName);
367+
var protectedContexts = protectedBranches != null ? new HashSet<string>(protectedBranches.RequiredStatusCheckContexts) : null;
368+
363369
var apiClient = await apiClientFactory.Create(address);
364370
var files = await apiClient.GetPullRequestFiles(owner, name, number).ToList();
365-
var lastCommitModel = await GetPullRequestLastCommitAdapter(address, owner, name, number);
371+
var lastCommitModel = await GetPullRequestLastCommitAdapter(address, owner, name, number, protectedContexts);
366372

367373
result.Statuses = (IReadOnlyList<StatusModel>) lastCommitModel.Statuses ?? Array.Empty<StatusModel>();
368374

@@ -773,7 +779,8 @@ Task<IRepository> GetRepository(LocalRepositoryModel repository)
773779
return Task.Factory.StartNew(() => gitService.GetRepository(repository.LocalPath));
774780
}
775781

776-
async Task<LastCommitAdapter> GetPullRequestLastCommitAdapter(HostAddress address, string owner, string name, int number)
782+
async Task<LastCommitAdapter> GetPullRequestLastCommitAdapter(HostAddress address, string owner, string name,
783+
int number, HashSet<string> protectedContexts)
777784
{
778785
ICompiledQuery<IEnumerable<LastCommitAdapter>> query;
779786
if (address.IsGitHubDotCom())
@@ -867,8 +874,23 @@ async Task<LastCommitAdapter> GetPullRequestLastCommitAdapter(HostAddress addres
867874
};
868875

869876
var connection = await graphqlFactory.CreateConnection(address);
870-
var result = await connection.Run(query, vars);
871-
return result.First();
877+
var results = await connection.Run(query, vars);
878+
var result = results.First();
879+
880+
foreach (var resultCheckSuite in result.CheckSuites)
881+
{
882+
foreach (var checkRunModel in resultCheckSuite.CheckRuns)
883+
{
884+
checkRunModel.IsRequired = protectedContexts?.Contains(checkRunModel.Name) ?? false;
885+
}
886+
}
887+
888+
foreach (var resultStatus in result.Statuses)
889+
{
890+
resultStatus.IsRequired = protectedContexts?.Contains(resultStatus.Context) ?? false;
891+
}
892+
893+
return result;
872894
}
873895

874896
static void BuildPullRequestThreads(PullRequestDetailModel model)

src/GitHub.VisualStudio.UI/Views/GitHubPane/PullRequestCheckView.xaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@
4747
<TextBlock TextTrimming="CharacterEllipsis" Text="{Binding Description}" ToolTip="{Binding DurationStatus}"/>
4848
</Label>
4949

50+
<StackPanel Grid.Column="3" Grid.Row="0" HorizontalAlignment="Right" Orientation="Horizontal">
51+
<Label Visibility="{Binding IsRequired, Converter={ghfvs:BooleanToVisibilityConverter}}">
52+
Required
53+
</Label>
54+
</StackPanel>
55+
5056
<Label Grid.Column="2" Grid.Row="1" HorizontalContentAlignment="Stretch" Visibility="{Binding HasAnnotations, Converter={ghfvs:BooleanToVisibilityConverter}}">
5157
<Hyperlink ToolTip="View checks"
5258
Command="{Binding Path=DataContext.ShowAnnotations, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type local:PullRequestDetailView}}}"

test/GitHub.InlineReviews.UnitTests/Services/PullRequestSessionServiceTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ static PullRequestSessionService CreateTarget(IDiffService diffService)
296296
diffService,
297297
Substitute.For<IApiClientFactory>(),
298298
Substitute.For<IGraphQLClientFactory>(),
299+
Substitute.For<IRepositoryService>(),
299300
Substitute.For<IUsageTracker>());
300301
}
301302

0 commit comments

Comments
 (0)