Skip to content

Commit

Permalink
Added feature to show original commiter as build requester (#30)
Browse files Browse the repository at this point in the history
* Added feature to show original commiter as build requester when using TfsGit PullRequests

* Fixed issue with general settings not being updated properly
  • Loading branch information
Luka Grabarevic authored Jul 13, 2018
1 parent 9aed531 commit 11de294
Show file tree
Hide file tree
Showing 20 changed files with 235 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public static String GenerateTitle(this IBuild build, BuildViewStyle viewStyle)

public static String GenerateUsername(this IBuild build)
{
return build.Requester.DisplayName;
return build.DisplayUser.DisplayName;
}
}
}
3 changes: 3 additions & 0 deletions BuildsAppReborn.Access/BuildsAppReborn.Access.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,12 @@
<Compile Include="TFS2017\Models\Tfs2017User.cs" />
<Compile Include="TFS2017\Models\TfsTestRun.cs" />
<Compile Include="TFS2017\Tfs2017BuildProvider.cs" />
<Compile Include="TFS\Models\Internal\ITfsPush.cs" />
<Compile Include="TFS\Models\Internal\LinksContainer.cs" />
<Compile Include="TFS\Models\Internal\Repository.cs" />
<Compile Include="TFS\Models\Internal\RepositoryType.cs" />
<Compile Include="TFS\Models\Internal\Resource.cs" />
<Compile Include="TFS\Models\Internal\TfsPush.cs" />
<Compile Include="TFS\Models\TfsBuild.cs" />
<Compile Include="TFS\Models\TfsBuildDefinition.cs" />
<Compile Include="TFS\Models\TfsProject.cs" />
Expand All @@ -82,6 +84,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TFS\Models\TfsArtifact.cs" />
<Compile Include="TFS\TfsBuildProviderBase.cs" />
<Compile Include="TFS\TfsConsts.cs" />
<Compile Include="VSTS\Models\VstsPullRequest.cs" />
<Compile Include="VSTS\Models\VstsTestRun.cs" />
<Compile Include="VSTS\Models\VstsArtifact.cs" />
Expand Down
6 changes: 6 additions & 0 deletions BuildsAppReborn.Access/GitHub/GitHubSourceVersion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ internal class GitHubSourceVersion : ISourceVersion
[JsonIgnore]
public INamedObject Author => this.commit?.Author;

[JsonIgnore]
public INamedObject Committer { get; set; }

[JsonIgnore]
public IUser Pusher { get; set; }

[JsonIgnore]
public String Comment => this.commit?.Message;

Expand Down
9 changes: 9 additions & 0 deletions BuildsAppReborn.Access/TFS/Models/Internal/ITfsPush.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using BuildsAppReborn.Contracts.Models;

namespace BuildsAppReborn.Access.Models.Internal
{
internal interface ITfsPush
{
IUser PushUser { get; }
}
}
14 changes: 14 additions & 0 deletions BuildsAppReborn.Access/TFS/Models/Internal/TfsPush.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using BuildsAppReborn.Contracts.Models;
using Newtonsoft.Json;

namespace BuildsAppReborn.Access.Models.Internal
{
internal class TfsPush<TUser> : ITfsPush where TUser : IUser
{
[JsonIgnore]
public IUser PushUser => InternalUser;

[JsonProperty("pushedBy")]
private TUser InternalUser { get; set; }
}
}
61 changes: 61 additions & 0 deletions BuildsAppReborn.Access/TFS/Models/TfsBuild.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,23 @@ public TfsBuild()
[JsonProperty("definition")]
public abstract IBuildDefinition Definition { get; protected set; }

[JsonIgnore]
public IUser DisplayUser
{
get
{
if (SourceVersion != null)
{
if (SourceVersion.Pusher.Id != TfsConsts.MicrosoftTeamFoundationSystemId)
{
return SourceVersion.Pusher;
}
}

return Requester;
}
}

[JsonProperty("finishTime")]
public DateTime FinishDateTime { get; private set; }

Expand All @@ -40,6 +57,48 @@ public TfsBuild()
[JsonProperty("queueTime")]
public DateTime QueueDateTime { get; private set; }

[JsonIgnore]
public BuildReason Reason
{
get
{
if (this.reason == "batchedCI" ||
this.reason == "individualCI" ||
this.reason == "checkInShelveset")
{
return BuildReason.ContinuousIntegration;
}

if (this.reason == "manual")
{
return BuildReason.Manual;
}

if (this.reason == "pullRequest")
{
return BuildReason.PullRequest;
}

if (this.reason == "schedule")
{
return BuildReason.Schedule;
}

if (this.reason == "triggered")
{
return BuildReason.Triggered;
}

if (this.reason == "validateShelveset" ||
this.reason == "checkInShelveset")
{
return BuildReason.Validation;
}

return BuildReason.Unknown;
}
}

[JsonProperty("requestedFor")]
public abstract IUser Requester { get; protected set; }

Expand Down Expand Up @@ -123,6 +182,8 @@ internal String SourceVersionInternal
[JsonProperty("uri")]
internal String Uri { get; private set; }

[JsonProperty("reason")] private String reason;

[JsonProperty("result")] private String result;

private String sourceVersionInternal;
Expand Down
12 changes: 12 additions & 0 deletions BuildsAppReborn.Access/TFS/Models/TfsSourceVersion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,19 @@ internal abstract class TfsSourceVersion : LinksContainer, ISourceVersion
[JsonProperty("comment")]
public String Comment { get; private set; }

[JsonProperty("committer")]
public abstract INamedObject Committer { get; protected set; }

[JsonIgnore]
public String PortalUrl => WebLink;

[JsonIgnore]
public IUser Pusher => Push?.PushUser;

[JsonProperty("parents")]
internal String[] Parents { get; set; }

[JsonProperty("push")]
internal abstract ITfsPush Push { get; set; }
}
}
81 changes: 52 additions & 29 deletions BuildsAppReborn.Access/TFS/TfsBuildProviderBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,54 @@ private static async Task<HttpResponseMessage> GetRequestResponseAsync(String re
return await HttpRequestHelper.GetRequestResponseAsync(requestUrl, credentials).ConfigureAwait(false);
}

private static async Task<TInnerSourceVersion> GetSourceVersionAsync<TInnerSourceVersion>(BuildMonitorSettings settings, String requestUrl)
where TInnerSourceVersion : ISourceVersion, new()
{
var requestResponse = await GetRequestResponseAsync(requestUrl, settings).ConfigureAwait(false);
if (requestResponse.IsSuccessStatusCode)
{
var result = await requestResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
return JsonConvert.DeserializeObject<TInnerSourceVersion>(result);
}

return default(TInnerSourceVersion);
}

private static async Task<TSourceVersion> GetTfsGitSourceVersionAsync(BuildMonitorSettings settings, String projectUrl, TBuild build)
{
var requestUrl = $"{projectUrl}/_apis/git/repositories/{build.Repository.Id}/commits/{build.SourceVersionInternal}?api-version=1.0";
var sourceVersion = await GetSourceVersionAsync<TSourceVersion>(settings, requestUrl).ConfigureAwait(false);

// special case detection if it as auto merge commit made by TFS pull requests
if (build.Reason == BuildReason.PullRequest ||
build.Reason == BuildReason.Validation)
{
if (sourceVersion != null && sourceVersion.Parents.Length > 1 && sourceVersion.Pusher.Id == TfsConsts.MicrosoftTeamFoundationSystemId)
{
var innerRequestUrl = $"{projectUrl}/_apis/git/repositories/{build.Repository.Id}/commits/{sourceVersion.Parents.Last()}?api-version=1.0";

return await GetSourceVersionAsync<TSourceVersion>(settings, innerRequestUrl).ConfigureAwait(false);
}
}

return sourceVersion;
}

private static async Task ResolveTestRunsAsync(IEnumerable<TBuild> builds, String projectUrl, BuildMonitorSettings settings)
{
foreach (var build in builds)
{
var requestUrl = $"{projectUrl}/_apis/test/runs?api-version=1.0&buildUri={build.Uri}";

var requestResponse = await GetRequestResponseAsync(requestUrl, settings).ConfigureAwait(false);
if (requestResponse.IsSuccessStatusCode)
{
var result = await requestResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
build.TestRuns = JsonConvert.DeserializeObject<IEnumerable<TTestRun>>(JObject.Parse(result)["value"].ToString());
}
}
}

private async Task<DataResponse<IEnumerable<TBuild>>> GetBuildsOfPullRequestAsync(IPullRequest pullRequest, BuildMonitorSettings settings)
{
var projectUrl = settings.GetValueStrict<String>(ProjectUrlSettingKey).TrimEnd('/');
Expand Down Expand Up @@ -220,11 +268,12 @@ private Tuple<String, String> GetGitOwnerAndRepo(String gitHubRepoUrl)
private async Task ResolveAdditionalBuildDataAsync(BuildMonitorSettings settings, List<TBuild> data, String projectUrl)
{
data.Select(d => d.Definition).OfType<TBuildDefinition>().ToList().ForEach(d => d.BuildSettingsId = settings.UniqueId);
data.Select(d => d.Requester).OfType<TUser>().ToList().ForEach(a => a.ImageDataLoader = GetImageDataAsync(settings, a));

await ResolveSourceVersionAsync(data, projectUrl, settings).ConfigureAwait(false);
await ResolveArtifactsAsync(data, projectUrl, settings).ConfigureAwait(false);
await ResolveTestRunsAsync(data, projectUrl, settings).ConfigureAwait(false);

data.Select(d => d.DisplayUser).OfType<TUser>().ToList().ForEach(a => a.ImageDataLoader = GetImageDataAsync(settings, a));
}

private async Task ResolveArtifactsAsync(IEnumerable<TBuild> builds, String projectUrl, BuildMonitorSettings settings)
Expand Down Expand Up @@ -252,15 +301,14 @@ private async Task ResolveSourceVersionAsync(IEnumerable<TBuild> builds, String
foreach (var build in group)
{
var requestUrl = $"{projectUrl}/_apis/tfvc/changesets/{build.SourceVersionInternal}?api-version=1.0&includeDetails=true";
await SetSourceVersionAsync<TSourceVersion>(settings, requestUrl, build).ConfigureAwait(false);
build.SourceVersion = await GetSourceVersionAsync<TSourceVersion>(settings, requestUrl).ConfigureAwait(false);
}
}
else if (group.Key == RepositoryType.TfsGit)
{
foreach (var build in group)
{
var requestUrl = $"{projectUrl}/_apis/git/repositories/{build.Repository.Id}/commits/{build.SourceVersionInternal}?api-version=1.0";
await SetSourceVersionAsync<TSourceVersion>(settings, requestUrl, build).ConfigureAwait(false);
build.SourceVersion = await GetTfsGitSourceVersionAsync(settings, projectUrl, build).ConfigureAwait(false);
}
}
else if (group.Key == RepositoryType.GitHub)
Expand All @@ -286,31 +334,6 @@ private async Task ResolveSourceVersionAsync(IEnumerable<TBuild> builds, String
}
}
}

private static async Task ResolveTestRunsAsync(IEnumerable<TBuild> builds, String projectUrl, BuildMonitorSettings settings)
{
foreach (var build in builds)
{
var requestUrl = $"{projectUrl}/_apis/test/runs?api-version=1.0&buildUri={build.Uri}";

var requestResponse = await GetRequestResponseAsync(requestUrl, settings).ConfigureAwait(false);
if (requestResponse.IsSuccessStatusCode)
{
var result = await requestResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
build.TestRuns = JsonConvert.DeserializeObject<IEnumerable<TTestRun>>(JObject.Parse(result)["value"].ToString());
}
}
}

private static async Task SetSourceVersionAsync<TInnerSourceVersion>(BuildMonitorSettings settings, String requestUrl, TBuild build) where TInnerSourceVersion : ISourceVersion, new()
{
var requestResponse = await GetRequestResponseAsync(requestUrl, settings).ConfigureAwait(false);
if (requestResponse.IsSuccessStatusCode)
{
var result = await requestResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
build.SourceVersion = JsonConvert.DeserializeObject<TInnerSourceVersion>(result);
}
}
}

internal abstract class TfsBuildProviderBase
Expand Down
9 changes: 9 additions & 0 deletions BuildsAppReborn.Access/TFS/TfsConsts.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System;

namespace BuildsAppReborn.Access
{
public static class TfsConsts
{
public const String MicrosoftTeamFoundationSystemId = "000007f5-0000-8888-8000-000000000000";
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using BuildsAppReborn.Contracts.Models;
using BuildsAppReborn.Access.Models.Internal;
using BuildsAppReborn.Contracts.Models;
using BuildsAppReborn.Infrastructure;
using Newtonsoft.Json;

Expand All @@ -8,5 +9,11 @@ internal class Tfs2017SourceVersion : TfsSourceVersion
{
[JsonConverter(typeof(InterfaceTypeConverter<Tfs2017User, INamedObject>))]
public override INamedObject Author { get; protected set; }

[JsonConverter(typeof(InterfaceTypeConverter<Tfs2017User, INamedObject>))]
public override INamedObject Committer { get; protected set; }

[JsonConverter(typeof(InterfaceTypeConverter<TfsPush<Tfs2017User>, ITfsPush>))]
internal override ITfsPush Push { get; set; }
}
}
9 changes: 8 additions & 1 deletion BuildsAppReborn.Access/VSTS/Models/VstsSourceVersion.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using BuildsAppReborn.Contracts.Models;
using BuildsAppReborn.Access.Models.Internal;
using BuildsAppReborn.Contracts.Models;
using BuildsAppReborn.Infrastructure;
using Newtonsoft.Json;

Expand All @@ -8,5 +9,11 @@ internal class VstsSourceVersion : TfsSourceVersion
{
[JsonConverter(typeof(InterfaceTypeConverter<VstsUser, INamedObject>))]
public override INamedObject Author { get; protected set; }

[JsonConverter(typeof(InterfaceTypeConverter<VstsUser, INamedObject>))]
public override INamedObject Committer { get; protected set; }

[JsonConverter(typeof(InterfaceTypeConverter<TfsPush<VstsUser>, ITfsPush>))]
internal override ITfsPush Push { get; set; }
}
}
2 changes: 1 addition & 1 deletion BuildsAppReborn.Client/Cache/BuildCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ private void UpdateCurrentIcon()
var relevantBuilds = new List<IBuild>();
foreach (var buildStatus in BuildsStatus)
{
var lastFinishedBuild = buildStatus.AllBuildItems.Last(a => a.Build.Status != BuildStatus.Running && a.Build.Status != BuildStatus.Queued);
var lastFinishedBuild = buildStatus.AllBuildItems.LastOrDefault(a => a.Build.Status != BuildStatus.Running && a.Build.Status != BuildStatus.Queued);
if (lastFinishedBuild != null)
{
relevantBuilds.Add(lastFinishedBuild.Build);
Expand Down
Loading

0 comments on commit 11de294

Please sign in to comment.