From a0c91f98a2d388a76d12139c101f79ca297367f3 Mon Sep 17 00:00:00 2001 From: "Wenzel, Toni" Date: Sat, 24 Sep 2022 10:29:20 +0200 Subject: [PATCH 1/6] Get work item ids Fixes #356 --- .../AzureDevOpsAliases.Pipelines.cs | 50 ++++++++++++++++++- .../Pipelines/AzureDevOpsBuild.cs | 34 +++++++++++-- 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs b/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs index 20769ac6..8a4e672f 100644 --- a/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs +++ b/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs @@ -1,9 +1,9 @@ namespace Cake.AzureDevOps { - using System.Collections.Generic; using Cake.AzureDevOps.Pipelines; using Cake.Core; using Cake.Core.Annotations; + using System.Collections.Generic; /// /// Contains functionality related to Azure Pipeline builds. @@ -331,6 +331,54 @@ public static IEnumerable AzureDevOpsBuildChanges( .GetChanges(); } + /// + /// Gets the work item ids associated with an Azure Pipelines build. + /// + /// The context. + /// Settings for getting the build. + /// + /// Get work item ids associated with an Azure Pipelines build: + /// + /// + /// + /// + /// The work item ids associated with the build. + /// Returns an empty list if build could not be found and + /// is set to false. + /// If build could not be found and + /// is set to true. + [CakeMethodAlias] + [CakeAliasCategory("Azure Pipelines")] + [CakeNamespaceImport("Cake.AzureDevOps.Pipelines")] + public static IEnumerable AzureDevOpsBuildWorkItemIds( + this ICakeContext context, + AzureDevOpsBuildSettings settings) + { + context.NotNull(nameof(context)); + settings.NotNull(nameof(settings)); + + return + new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory()) + .GetWorkItemIds(); + } + /// /// Gets the timeline entries for an Azure Pipelines build. /// diff --git a/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs b/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs index 83979974..2e381683 100644 --- a/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs +++ b/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs @@ -1,13 +1,13 @@ namespace Cake.AzureDevOps.Pipelines { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Threading.Tasks; using Cake.AzureDevOps.Authentication; using Cake.Core.Diagnostics; using Microsoft.TeamFoundation.Build.WebApi; using Microsoft.TeamFoundation.TestManagement.WebApi; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; /// /// Class for writing issues to Azure DevOps pull requests. @@ -369,6 +369,32 @@ public IEnumerable GetChanges() } } + /// + /// Gets the work item ids associated with a build. + /// + /// The work item ids associated with a build or an empty list if no build could be found and + /// is set to false. + /// If build could not be found and + /// is set to true. + public IEnumerable GetWorkItemIds() + { + if (!this.ValidateBuild()) + { + return new List(); + } + + using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.credentials)) + { + return + buildClient + .GetBuildWorkItemsRefsAsync(this.ProjectId, this.BuildId) + .ConfigureAwait(false) + .GetAwaiter() + .GetResult() + .Select(r => int.Parse(r.Id)); + } + } + /// /// Checks if the build is failing. /// From 6766d9d7248ff0eec1a7d073019b02efe93698a2 Mon Sep 17 00:00:00 2001 From: "Wenzel, Toni" Date: Sat, 24 Sep 2022 10:31:20 +0200 Subject: [PATCH 2/6] fix formatting --- src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs | 2 +- src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs b/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs index 8a4e672f..e4e47eba 100644 --- a/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs +++ b/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs @@ -1,9 +1,9 @@ namespace Cake.AzureDevOps { + using System.Collections.Generic; using Cake.AzureDevOps.Pipelines; using Cake.Core; using Cake.Core.Annotations; - using System.Collections.Generic; /// /// Contains functionality related to Azure Pipeline builds. diff --git a/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs b/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs index 2e381683..43bef9f3 100644 --- a/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs +++ b/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs @@ -1,13 +1,13 @@ namespace Cake.AzureDevOps.Pipelines { - using Cake.AzureDevOps.Authentication; - using Cake.Core.Diagnostics; - using Microsoft.TeamFoundation.Build.WebApi; - using Microsoft.TeamFoundation.TestManagement.WebApi; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; + using Cake.AzureDevOps.Authentication; + using Cake.Core.Diagnostics; + using Microsoft.TeamFoundation.Build.WebApi; + using Microsoft.TeamFoundation.TestManagement.WebApi; /// /// Class for writing issues to Azure DevOps pull requests. From 1903650526bbd664dfcd8dd162a1fa83bdc143f6 Mon Sep 17 00:00:00 2001 From: "Wenzel, Toni" Date: Tue, 27 Sep 2022 00:54:58 +0200 Subject: [PATCH 3/6] Return work items --- .../Fakes/FakeAllSetBuildClientFactory.cs | 8 ++ ...FakeAllSetWorkItemTrackingClientFactory.cs | 36 ++++++++ .../FakeWorkItemTrackingClientFactory.cs | 24 ++++++ .../Pipelines/AzureDevOpsBuildTests.cs | 39 +++++++-- .../Pipelines/BuildFixture.cs | 3 + .../AzureDevOpsAliases.Pipelines.cs | 63 ++++++++++++-- .../AzureDevOpsAliases.WorkItemTracking.cs | 2 +- .../WorkItemTracking/AzureDevOpsWorkItem.cs | 9 +- .../AzureDevOpsWorkItemSettings.cs | 11 +++ .../Pipelines/AzureDevOpsBuild.cs | 82 ++++++++++++++----- 10 files changed, 240 insertions(+), 37 deletions(-) create mode 100644 src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetWorkItemTrackingClientFactory.cs create mode 100644 src/Cake.AzureDevOps.Tests/Fakes/FakeWorkItemTrackingClientFactory.cs diff --git a/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetBuildClientFactory.cs b/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetBuildClientFactory.cs index 218c8afd..9c745eb6 100644 --- a/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetBuildClientFactory.cs +++ b/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetBuildClientFactory.cs @@ -1,10 +1,12 @@ namespace Cake.AzureDevOps.Tests.Fakes { using System; + using System.Collections.Generic; using System.Threading; using Cake.AzureDevOps.Authentication; using Microsoft.TeamFoundation.Build.WebApi; using Microsoft.TeamFoundation.Core.WebApi; + using Microsoft.VisualStudio.Services.WebApi; using Moq; public class FakeAllSetBuildClientFactory : FakeBuildClientFactory @@ -29,6 +31,12 @@ public override BuildHttpClient CreateBuildClient(Uri collectionUrl, IAzureDevOp Project = new TeamProjectReference { Name = projectName }, }); + mock.Setup(arg => arg.GetBuildWorkItemsRefsAsync(It.IsAny(), It.IsAny(), It.IsAny(), null, default)) + .ReturnsAsync((string projectName, int buildId, int? top, object userState, CancellationToken token) => new List + { + new ResourceRef { Id = "42" }, + }); + mock = this.Setup(mock); return mock.Object; diff --git a/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetWorkItemTrackingClientFactory.cs b/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetWorkItemTrackingClientFactory.cs new file mode 100644 index 00000000..59027717 --- /dev/null +++ b/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetWorkItemTrackingClientFactory.cs @@ -0,0 +1,36 @@ +namespace Cake.AzureDevOps.Tests.Fakes +{ + using System; + using System.Collections.Generic; + using System.Threading; + using Cake.AzureDevOps.Authentication; + using Microsoft.TeamFoundation.TestManagement.WebApi; + using Microsoft.TeamFoundation.WorkItemTracking.WebApi; + using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models; + using Moq; + + public class FakeAllSetWorkItemTrackingClientFactory : FakeWorkItemTrackingClientFactory + { + public override WorkItemTrackingHttpClient CreateWorkItemTrackingClient(Uri collectionUrl, IAzureDevOpsCredentials credentials) + { + var mock = new Mock(MockBehavior.Strict, collectionUrl, credentials.ToVssCredentials()); + + mock.Setup(arg => arg.GetWorkItemsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), null, default)) + .ReturnsAsync((IEnumerable workItemIds, IEnumerable fields, DateTime? asOf, WorkItemExpand? expand, WorkItemErrorPolicy? errorPolicy, object userState, CancellationToken token) => + { + var result = new List(); + + foreach (var workItemId in workItemIds) + { + result.Add(new WorkItem { Id = workItemId }); + } + + return result; + }); + + mock = this.Setup(mock); + + return mock.Object; + } + } +} \ No newline at end of file diff --git a/src/Cake.AzureDevOps.Tests/Fakes/FakeWorkItemTrackingClientFactory.cs b/src/Cake.AzureDevOps.Tests/Fakes/FakeWorkItemTrackingClientFactory.cs new file mode 100644 index 00000000..61c8ab0f --- /dev/null +++ b/src/Cake.AzureDevOps.Tests/Fakes/FakeWorkItemTrackingClientFactory.cs @@ -0,0 +1,24 @@ +namespace Cake.AzureDevOps.Tests.Fakes +{ + using System; + using Cake.AzureDevOps.Authentication; + using Microsoft.TeamFoundation.WorkItemTracking.WebApi; + using Microsoft.VisualStudio.Services.Identity; + using Moq; + + public abstract class FakeWorkItemTrackingClientFactory : IWorkItemTrackingClientFactory + { + public abstract WorkItemTrackingHttpClient CreateWorkItemTrackingClient(Uri collectionUrl, IAzureDevOpsCredentials credentials); + + public WorkItemTrackingHttpClient CreateWorkItemTrackingClient(Uri collectionUrl, IAzureDevOpsCredentials credentials, out Identity authorizedIdentity) + { + authorizedIdentity = new Identity { ProviderDisplayName = "FakeUser", Id = Guid.NewGuid(), IsActive = true }; + return this.CreateWorkItemTrackingClient(collectionUrl, credentials); + } + + protected virtual Mock Setup(Mock m) + { + return m; + } + } +} diff --git a/src/Cake.AzureDevOps.Tests/Pipelines/AzureDevOpsBuildTests.cs b/src/Cake.AzureDevOps.Tests/Pipelines/AzureDevOpsBuildTests.cs index 999776d5..293cf5ad 100644 --- a/src/Cake.AzureDevOps.Tests/Pipelines/AzureDevOpsBuildTests.cs +++ b/src/Cake.AzureDevOps.Tests/Pipelines/AzureDevOpsBuildTests.cs @@ -23,7 +23,8 @@ public void Should_Return_Empty_List_If_Build_Is_Invalid() fixture.Log, fixture.Settings, fixture.BuildClientFactory, - fixture.TestManagementClientFactory); + fixture.TestManagementClientFactory, + fixture.WorkItemTrackingClientFactory); // When var result = build.GetTestRuns(); @@ -42,7 +43,8 @@ public void Should_Return_Empty_List_If_Build_Does_Not_Contain_Test_Runs() fixture.Log, fixture.Settings, fixture.BuildClientFactory, - fixture.TestManagementClientFactory); + fixture.TestManagementClientFactory, + fixture.WorkItemTrackingClientFactory); // When var result = build.GetTestRuns(); @@ -64,7 +66,8 @@ public void Should_Return_List_Of_Test_Runs_With_X_Test_Results_If_X_Is_Less_The fixture.Log, fixture.Settings, fixture.BuildClientFactory, - fixture.TestManagementClientFactory); + fixture.TestManagementClientFactory, + fixture.WorkItemTrackingClientFactory); // When var result = build.GetTestRuns(testRunsCount); @@ -87,7 +90,8 @@ public void Should_Throw_If_Input_Test_Outcomes_Are_Invalid() fixture.Log, fixture.Settings, fixture.BuildClientFactory, - fixture.TestManagementClientFactory); + fixture.TestManagementClientFactory, + fixture.WorkItemTrackingClientFactory); // When var result = Record.Exception(() => build.GetTestRuns(null, new string[] { "FakeOutcome" })); @@ -105,7 +109,8 @@ public void Should_Return_List_Of_Test_Runs_With_Test_Results() fixture.Log, fixture.Settings, fixture.BuildClientFactory, - fixture.TestManagementClientFactory); + fixture.TestManagementClientFactory, + fixture.WorkItemTrackingClientFactory); // When var result = build.GetTestRuns(); @@ -126,5 +131,29 @@ public void Should_Return_List_Of_Test_Runs_With_Test_Results() new AzureDevOpsTestResult { AutomatedTestName = "t3", Outcome = "Passed", ErrorMessage = string.Empty }); } } + + public sealed class TheGetWorkItemsMethod + { + [Fact] + public void Should_Return_List_Of_WorkItems() + { + // Given + var fixture = new BuildFixture(BuildFixture.ValidAzureDevOpsCollectionUrl, "Foo", 42); + var build = new AzureDevOpsBuild( + fixture.Log, + fixture.Settings, + fixture.BuildClientFactory, + fixture.TestManagementClientFactory, + fixture.WorkItemTrackingClientFactory); + + // When + var result = build.GetWorkItems(); + + // Then + result.ShouldNotBeNull(); + result.ShouldHaveSingleItem(); + result.First().WorkItemId.ShouldBe(42); + } + } } } diff --git a/src/Cake.AzureDevOps.Tests/Pipelines/BuildFixture.cs b/src/Cake.AzureDevOps.Tests/Pipelines/BuildFixture.cs index 54f16707..7d5dd8cd 100644 --- a/src/Cake.AzureDevOps.Tests/Pipelines/BuildFixture.cs +++ b/src/Cake.AzureDevOps.Tests/Pipelines/BuildFixture.cs @@ -23,6 +23,8 @@ public BuildFixture(string collectionUrl, string projectName, int buildId) public ITestManagementClientFactory TestManagementClientFactory { get; set; } + public IWorkItemTrackingClientFactory WorkItemTrackingClientFactory { get; set; } + public AzureDevOpsBuildSettings Settings { get; set; } private void InitialzeFakes() @@ -30,6 +32,7 @@ private void InitialzeFakes() this.Log = new FakeLog(); this.BuildClientFactory = new FakeAllSetBuildClientFactory(); this.TestManagementClientFactory = new FakeAllSetTestManagementClientFactory(); + this.WorkItemTrackingClientFactory = new FakeAllSetWorkItemTrackingClientFactory(); } } } diff --git a/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs b/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs index e4e47eba..5c3db1c3 100644 --- a/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs +++ b/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs @@ -1,6 +1,7 @@ namespace Cake.AzureDevOps { using System.Collections.Generic; + using Cake.AzureDevOps.Boards.WorkItemTracking; using Cake.AzureDevOps.Pipelines; using Cake.Core; using Cake.Core.Annotations; @@ -47,7 +48,7 @@ public static AzureDevOpsBuild AzureDevOpsBuild( context.NotNull(nameof(context)); settings.NotNull(nameof(settings)); - var build = new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory()); + var build = new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory()); if (build.HasBuildLoaded) { @@ -279,7 +280,7 @@ public static bool AzureDevOpsBuildIsFailing( settings.NotNull(nameof(settings)); return - new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory()) + new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory()) .IsBuildFailing(); } @@ -327,7 +328,7 @@ public static IEnumerable AzureDevOpsBuildChanges( settings.NotNull(nameof(settings)); return - new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory()) + new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory()) .GetChanges(); } @@ -375,10 +376,58 @@ public static IEnumerable AzureDevOpsBuildWorkItemIds( settings.NotNull(nameof(settings)); return - new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory()) + new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory()) .GetWorkItemIds(); } + /// + /// Gets the work items associated with an Azure Pipelines build. + /// + /// The context. + /// Settings for getting the build. + /// + /// Get work items associated with an Azure Pipelines build: + /// + /// + /// + /// + /// The work item ids associated with the build. + /// Returns an empty list if build could not be found and + /// is set to false. + /// If build could not be found and + /// is set to true. + [CakeMethodAlias] + [CakeAliasCategory("Azure Pipelines")] + [CakeNamespaceImport("Cake.AzureDevOps.Pipelines")] + public static IEnumerable AzureDevOpsBuildWorkItems( + this ICakeContext context, + AzureDevOpsBuildSettings settings) + { + context.NotNull(nameof(context)); + settings.NotNull(nameof(settings)); + + return + new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory()) + .GetWorkItems(); + } + /// /// Gets the timeline entries for an Azure Pipelines build. /// @@ -423,7 +472,7 @@ public static IEnumerable AzureDevOpsBuildTimelineRec settings.NotNull(nameof(settings)); return - new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory()) + new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory()) .GetTimelineRecords(); } @@ -471,7 +520,7 @@ public static IEnumerable AzureDevOpsBuildArtifacts( settings.NotNull(nameof(settings)); return - new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory()) + new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory()) .GetArtifacts(); } @@ -519,7 +568,7 @@ public static IEnumerable AzureDevOpsBuildTestRuns( settings.NotNull(nameof(settings)); return - new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory()) + new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory()) .GetTestRuns(); } diff --git a/src/Cake.AzureDevOps/AzureDevOpsAliases.WorkItemTracking.cs b/src/Cake.AzureDevOps/AzureDevOpsAliases.WorkItemTracking.cs index a00367ea..f78528dd 100644 --- a/src/Cake.AzureDevOps/AzureDevOpsAliases.WorkItemTracking.cs +++ b/src/Cake.AzureDevOps/AzureDevOpsAliases.WorkItemTracking.cs @@ -61,7 +61,7 @@ public static AzureDevOpsWorkItem AzureDevOpsWorkItem( /// Make sure the build has the 'Allow Scripts to access OAuth token' option enabled. /// /// The context. - /// ID of the work witem. + /// ID of the work item. /// /// Get an Azure DevOps work item: /// diff --git a/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItem.cs b/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItem.cs index 219e061c..fadfff74 100644 --- a/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItem.cs +++ b/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItem.cs @@ -2,8 +2,6 @@ { using System; using System.Collections.Generic; - using System.Linq; - using Cake.AzureDevOps.Authentication; using Cake.Core.Diagnostics; using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models; using Microsoft.VisualStudio.Services.Common; @@ -36,7 +34,8 @@ public AzureDevOpsWorkItem(ICakeLog log, AzureDevOpsWorkItemSettings settings) /// The Cake log context. /// Settings for accessing AzureDevOps. /// The work item. - internal AzureDevOpsWorkItem(ICakeLog log, AzureDevOpsWorkItemSettings settings, WorkItem workItem) + /// A factory to communicate with work item tracking client. + internal AzureDevOpsWorkItem(ICakeLog log, AzureDevOpsWorkItemSettings settings, WorkItem workItem, IWorkItemTrackingClientFactory workItemTrackingClientFactory) { log.NotNull(nameof(log)); settings.NotNull(nameof(settings)); @@ -44,7 +43,7 @@ internal AzureDevOpsWorkItem(ICakeLog log, AzureDevOpsWorkItemSettings settings, this.log = log; this.workItem = workItem; - this.workItemTrackingClientFactory = new WorkItemTrackingClientFactory(); + this.workItemTrackingClientFactory = workItemTrackingClientFactory; this.settings = settings; } @@ -140,7 +139,7 @@ internal AzureDevOpsWorkItem( /// /// Gets the URL for accessing the web portal of the Azure DevOps collection. /// - public Uri CollectionUrl => this.settings.CollectionUrl; + public Uri CollectionUrl => this.settings.CollectionUrl; /// /// Gets the ID of the work item. diff --git a/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItemSettings.cs b/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItemSettings.cs index dd835a9e..c6860114 100644 --- a/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItemSettings.cs +++ b/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItemSettings.cs @@ -2,6 +2,7 @@ { using System; using Cake.AzureDevOps.Authentication; + using Cake.AzureDevOps.Pipelines; /// /// Settings for aliases handling work items. @@ -81,6 +82,16 @@ public AzureDevOpsWorkItemSettings(int workItemId, IAzureDevOpsCredentials crede this.WorkItemId = workItemId; } + /// + /// Initializes a new instance of the class + /// based on the instance of a class. + /// + /// Settings containing the parameters. + public AzureDevOpsWorkItemSettings(BaseAzureDevOpsProjectSettings settings) + : base(settings) + { + } + /// /// Gets the ID of the work item. /// diff --git a/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs b/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs index 43bef9f3..abe74b70 100644 --- a/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs +++ b/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; - using Cake.AzureDevOps.Authentication; + using Cake.AzureDevOps.Boards.WorkItemTracking; using Cake.Core.Diagnostics; using Microsoft.TeamFoundation.Build.WebApi; using Microsoft.TeamFoundation.TestManagement.WebApi; @@ -15,10 +15,11 @@ public sealed class AzureDevOpsBuild { private readonly ICakeLog log; - private readonly IAzureDevOpsCredentials credentials; + private readonly BaseAzureDevOpsProjectSettings settings; private readonly bool throwExceptionIfBuildCouldNotBeFound; private readonly IBuildClientFactory buildClientFactory; private readonly ITestManagementClientFactory testClientFactory; + private readonly IWorkItemTrackingClientFactory workItemTrackingClientFactory; private readonly Build build; /// @@ -29,7 +30,7 @@ public sealed class AzureDevOpsBuild /// If /// is set to true and no build could be found. public AzureDevOpsBuild(ICakeLog log, AzureDevOpsBuildSettings settings) - : this(log, settings, new BuildClientFactory(), new TestManagementClientFactory()) + : this(log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory()) { } @@ -49,8 +50,8 @@ internal AzureDevOpsBuild(ICakeLog log, AzureDevOpsBuildsSettings settings, Buil this.build = build; this.buildClientFactory = new BuildClientFactory(); this.testClientFactory = new TestManagementClientFactory(); - this.credentials = settings.Credentials; - this.CollectionUrl = settings.CollectionUrl; + this.workItemTrackingClientFactory = new WorkItemTrackingClientFactory(); + this.settings = settings; } /// @@ -60,24 +61,27 @@ internal AzureDevOpsBuild(ICakeLog log, AzureDevOpsBuildsSettings settings, Buil /// Settings for accessing AzureDevOps. /// A factory to communicate with build client. /// A factory to communicate with test management client. + /// A factory to communicate with work item tracking client. /// If /// is set to true and no build could be found. internal AzureDevOpsBuild( ICakeLog log, AzureDevOpsBuildSettings settings, IBuildClientFactory buildClientFactory, - ITestManagementClientFactory testManagementClientFactory) + ITestManagementClientFactory testManagementClientFactory, + IWorkItemTrackingClientFactory workItemTrackingClientFactory) { log.NotNull(nameof(log)); settings.NotNull(nameof(settings)); buildClientFactory.NotNull(nameof(buildClientFactory)); testManagementClientFactory.NotNull(nameof(testManagementClientFactory)); + workItemTrackingClientFactory.NotNull(nameof(workItemTrackingClientFactory)); this.log = log; this.buildClientFactory = buildClientFactory; this.testClientFactory = testManagementClientFactory; - this.credentials = settings.Credentials; - this.CollectionUrl = settings.CollectionUrl; + this.workItemTrackingClientFactory = workItemTrackingClientFactory; + this.settings = settings; this.throwExceptionIfBuildCouldNotBeFound = settings.ThrowExceptionIfBuildCouldNotBeFound; using (var buildClient = this.buildClientFactory.CreateBuildClient(settings.CollectionUrl, settings.Credentials, out var authorizedIdenity)) @@ -149,7 +153,7 @@ internal AzureDevOpsBuild( /// /// Gets the URL for accessing the web portal of the Azure DevOps collection. /// - public Uri CollectionUrl { get; } + public Uri CollectionUrl => this.settings.CollectionUrl; /// /// Gets the id of the Azure DevOps project. @@ -357,7 +361,7 @@ public IEnumerable GetChanges() return new List(); } - using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.credentials)) + using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.settings.Credentials)) { return buildClient @@ -380,14 +384,26 @@ public IEnumerable GetWorkItemIds() { if (!this.ValidateBuild()) { - return new List(); + return Array.Empty(); } - using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.credentials)) + using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.settings.Credentials)) { - return - buildClient - .GetBuildWorkItemsRefsAsync(this.ProjectId, this.BuildId) + Task> task; + if (this.ProjectId != Guid.Empty) + { + task = buildClient.GetBuildWorkItemsRefsAsync(this.ProjectId, this.BuildId); + } + else if (!string.IsNullOrWhiteSpace(this.ProjectName)) + { + task = buildClient.GetBuildWorkItemsRefsAsync(this.ProjectName, this.BuildId); + } + else + { + throw new InvalidOperationException("Either ProjectId or ProjectName needs to be set"); + } + + return task .ConfigureAwait(false) .GetAwaiter() .GetResult() @@ -395,6 +411,34 @@ public IEnumerable GetWorkItemIds() } } + /// + /// Gets the work item associated with a build. + /// + /// The work item associated with a build or an empty list if no build could be found and + /// is set to false. + /// If build could not be found and + /// is set to true. + public IEnumerable GetWorkItems() + { + if (!this.ValidateBuild()) + { + return Array.Empty(); + } + + var workItemIds = this.GetWorkItemIds(); + + using (var workItemTrackingClient = this.workItemTrackingClientFactory.CreateWorkItemTrackingClient(this.settings.CollectionUrl, this.settings.Credentials)) + { + return + workItemTrackingClient + .GetWorkItemsAsync(workItemIds, expand: Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.WorkItemExpand.Relations) + .ConfigureAwait(false) + .GetAwaiter() + .GetResult() + .Select(x => new AzureDevOpsWorkItem(this.log, new AzureDevOpsWorkItemSettings(this.settings), x, this.workItemTrackingClientFactory)); + } + } + /// /// Checks if the build is failing. /// @@ -422,7 +466,7 @@ public IEnumerable GetTimelineRecords() return new List(); } - using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.credentials)) + using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.settings.Credentials)) { return buildClient @@ -449,7 +493,7 @@ public IEnumerable GetArtifacts() return new List(); } - using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.credentials)) + using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.settings.Credentials)) { return buildClient @@ -502,7 +546,7 @@ public AzureDevOpsBuildArtifact LinkArtifact(string name, string type, string lo return null; } - using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.credentials)) + using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.settings.Credentials)) { var artifact = new BuildArtifact @@ -569,7 +613,7 @@ public IEnumerable GetTestRuns(int? maxResultsPerTestRun, IE return new List(); } - using (var testClient = this.testClientFactory.CreateTestManagementClient(this.CollectionUrl, this.credentials)) + using (var testClient = this.testClientFactory.CreateTestManagementClient(this.CollectionUrl, this.settings.Credentials)) { // Read test result details for current build. var testResultDetails = From 8f402f5fd764393f273f692eaa2a8b6d583cd2df Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Fri, 30 Sep 2022 20:13:06 +0200 Subject: [PATCH 4/6] Update src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs --- src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs b/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs index abe74b70..92955830 100644 --- a/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs +++ b/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs @@ -412,7 +412,7 @@ public IEnumerable GetWorkItemIds() } /// - /// Gets the work item associated with a build. + /// Gets the work items associated with a build. /// /// The work item associated with a build or an empty list if no build could be found and /// is set to false. From b0c0fd66781966bc99d73d7776d82e7e624dca1e Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Fri, 30 Sep 2022 20:13:21 +0200 Subject: [PATCH 5/6] Update src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs --- src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs b/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs index 92955830..a8745ce1 100644 --- a/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs +++ b/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs @@ -414,7 +414,7 @@ public IEnumerable GetWorkItemIds() /// /// Gets the work items associated with a build. /// - /// The work item associated with a build or an empty list if no build could be found and + /// The work items associated with a build or an empty list if no build could be found and /// is set to false. /// If build could not be found and /// is set to true. From 171a8888f0e835f052f2e1943e62cc2599c51d37 Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Fri, 30 Sep 2022 20:13:28 +0200 Subject: [PATCH 6/6] Update src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs --- src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs b/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs index 5c3db1c3..01744f53 100644 --- a/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs +++ b/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs @@ -408,7 +408,7 @@ public static IEnumerable AzureDevOpsBuildWorkItemIds( /// ]]> /// /// - /// The work item ids associated with the build. + /// The work items associated with the build. /// Returns an empty list if build could not be found and /// is set to false. /// If build could not be found and