Skip to content

Commit

Permalink
Add pipeline cmdlets (#176)
Browse files Browse the repository at this point in the history
* Move to Definition namespace

* Update GetBuildDefinition

* Add SetTeamProject -Description

* Add StartBuild

* Refactor NewTeamProjectController to use Awaiter

* Refactor result type

* Fix get default team

* Fix Area/Iteration field names

* Add QueryContributionNodeAsync

* Add Get-TeamProjectMember

* Add release notes

* Update release notes

* Update docs

* Add AsIdentity mode

* Add formatting

* Update release notes

+semver: minor
  • Loading branch information
igoravl authored Aug 3, 2022
1 parent 86a036e commit f91c738
Show file tree
Hide file tree
Showing 36 changed files with 488 additions and 108 deletions.
2 changes: 1 addition & 1 deletion CSharp/TfsCmdlets.Common/Extensions/ObjectExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public static object CallHiddenMethod(this object self, string methodName, param
return method.Invoke(self, parameters);
}

public static object ToJsonString(this object self)
public static string ToJsonString(this object self)
{
return Newtonsoft.Json.JsonConvert.SerializeObject(self, Newtonsoft.Json.Formatting.None);
}
Expand Down
5 changes: 5 additions & 0 deletions CSharp/TfsCmdlets.Common/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,10 @@ public static int FindIndex(this string input, Predicate<char> predicate, int st

return -1;
}

public static T ToJsonObject<T>(this string self)
{
return (T) Newtonsoft.Json.JsonConvert.DeserializeObject(self);
}
}
}
13 changes: 13 additions & 0 deletions CSharp/TfsCmdlets.Common/Extensions/TeamProjectExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Microsoft.VisualStudio.Services.WebApi;

namespace TfsCmdlets.Extensions
{
public static class TeamProjectExtensions
Expand All @@ -6,5 +8,16 @@ public static string ProcessTemplate(this WebApiTeamProject project)
{
return project.Capabilities["processTemplate"]?["templateName"]?.ToString();
}

public static string GetLink(this WebApiTeamProject project, string linkName)
{
if (!project.Links.Links.ContainsKey(linkName))
{
IDataManager data = ServiceLocator.Instance.GetExport<IDataManager>();
project = data.GetItem<WebApiTeamProject>(new { Project = project.Id, IncludeDetails = true });
}

return ((ReferenceLink)project.Links.Links[linkName]).Href;
}
}
}
63 changes: 63 additions & 0 deletions CSharp/TfsCmdlets.Common/Models/ContributionNodeQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System.Runtime.Serialization;
using Newtonsoft.Json.Linq;

namespace TfsCmdlets.Models
{
[DataContract]
public class ContributionNodeQuery
{
[DataMember(EmitDefaultValue = false, Name = "dataProviderContext")]
public DataProviderContext DataProviderContext;

[DataMember(Name = "queryOptions", EmitDefaultValue = false)]
public ContributionQueryOptions QueryOptions { get; set; }

[DataMember(Name = "contributionIds")]
public IEnumerable<string> ContributionIds { get; set; }

[DataMember(Name = "includeProviderDetails", EmitDefaultValue = false)]
public bool IncludeProviderDetails { get; set; }
}

[DataContract]
public class ContributionNodeResponse
{
[DataMember(Name = "dataProviderSharedData")]
public DataProviderSharedData DataProviderSharedData { get; set; }

[DataMember(Name = "dataProviders")]
public DataProviders DataProviders { get; set; }
}


[DataContract]
public class DataProviderContext
{
[DataMember(Name = "properties")]
public Dictionary<string, object> Properties { get; set; }

[DataMember(Name = "sharedData", EmitDefaultValue = false)]
public DataProviderSharedData SharedData { get; set; }
}

[DataContract]
public class DataProviderSharedData : Dictionary<string, object>
{
}

[DataContract]
public class DataProviders : Dictionary<string, JObject>
{
}

[Flags]
public enum ContributionQueryOptions
{
[EnumMember(Value = "none")] None = 0,
[EnumMember(Value = "includeSelf")] IncludeSelf = 16,
[EnumMember(Value = "includeChildren")] IncludeChildren = 32,
[EnumMember(Value = "includeSubTree")] IncludeSubTree = 96,
[EnumMember(Value = "includeAll")] IncludeAll = IncludeSubTree | IncludeSelf,
[EnumMember(Value = "ignoreConstraints")] IgnoreConstraints = 256,
}
}
38 changes: 38 additions & 0 deletions CSharp/TfsCmdlets.Common/Models/TeamProjectMember.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Runtime.Serialization;

namespace TfsCmdlets.Models
{
/// <summary>
/// Represents a member of a Team Foundation / Azure DevOps Team
/// </summary>
[DataContract]
public class TeamProjectMember
{
public string TeamProject { get; set; }

[DataMember(Name = "name")]
public string Name { get; set; }

[DataMember(Name = "id")]
public Guid Id { get; set; }

[DataMember(Name = "mail")]
public string Email { get; set; }
}

[DataContract]
public class TeamProjectMemberCollection
{
[DataMember(Name = "isCurrentUserAdmin")]
public bool IsCurrentUserAdmin { get; set; }

[DataMember(Name = "count")]
public int Count { get; set; }

[DataMember(Name = "hasMore")]
public bool HasMore { get; set; }

[DataMember(Name = "members")]
public TeamProjectMember[] Members { get; set; }
}
}
11 changes: 11 additions & 0 deletions CSharp/TfsCmdlets.Common/Services/IAsyncOperationAwaiter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Threading.Tasks;
using Microsoft.VisualStudio.Services.Operations;

namespace TfsCmdlets.Services
{
public interface IAsyncOperationAwaiter
{
Operation Wait(Task<OperationReference> operation, string errorMessage, int waitTimeInSecs = 2);
Operation Wait(OperationReference operation, int waitTimeInSecs = 2);
}
}
11 changes: 10 additions & 1 deletion CSharp/TfsCmdlets.Common/Services/IRestApiService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Services.Operations;
using TfsCmdlets.Models;

namespace TfsCmdlets.Services
{
Expand Down Expand Up @@ -42,6 +43,14 @@ Task<OperationReference> QueueOperationAsync(
string apiVersion = "4.1",
string serviceHostName = null);

Uri Url {get;}
Task<ContributionNodeResponse> QueryContributionNodeAsync(
Models.Connection connection,
ContributionNodeQuery query,
Dictionary<string, string> additionalHeaders = null,
Dictionary<string, string> queryParameters = null,
string apiVersion = "6.1",
string serviceHostName = null);

Uri Url { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Services.Operations;

namespace TfsCmdlets.Services.Impl
{
[Export(typeof(IAsyncOperationAwaiter))]
internal class AsyncOperationAwaiterImpl: IAsyncOperationAwaiter
{
private IDataManager Data { get; }

public Operation Wait(Task<OperationReference> operation, string errorMessage, int waitTimeInSecs = 2)
{
return Wait(operation.GetResult(errorMessage), waitTimeInSecs);
}

public Operation Wait(OperationReference operation, int waitTimeInSecs = 2)
{
var client = Data.GetClient<OperationsHttpClient>();
var token = client.GetOperation(operation.Id)
.GetResult("Error getting operation status");
while (
(token.Status != OperationStatus.Succeeded) &&
(token.Status != OperationStatus.Failed) &&
(token.Status != OperationStatus.Cancelled))
{
Thread.Sleep(waitTimeInSecs * 1000);
token = client.GetOperation(operation.Id)
.GetResult("Error getting operation status");
}
return token;
}

[ImportingConstructor]
public AsyncOperationAwaiterImpl(IDataManager dataManager)
{
Data = dataManager;
}
}
}
29 changes: 25 additions & 4 deletions CSharp/TfsCmdlets.Common/Services/Impl/RestApiServiceImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
using Microsoft.VisualStudio.Services.Operations;
using Microsoft.VisualStudio.Services.WebApi;
using TfsCmdlets.HttpClient;
using TfsCmdlets.Models;

namespace TfsCmdlets.Services.Impl
{
[Export(typeof(IRestApiService))]
public class RestApiServiceImpl : IRestApiService
{
private GenericHttpClient _client;
private ILogger Logger {get;set;}
private ILogger Logger { get; set; }
public Uri Url => _client.Uri;

Task<HttpResponseMessage> IRestApiService.InvokeAsync(
Expand Down Expand Up @@ -63,7 +64,7 @@ Task<OperationReference> IRestApiService.QueueOperationAsync(
{
return GetClient(connection, serviceHostName)
.InvokeAsync<OperationReference>(new HttpMethod(method), path.TrimStart('/'), body,
requestContentType, responseContentType, additionalHeaders,
requestContentType, responseContentType, additionalHeaders,
queryParameters, apiVersion);
}

Expand Down Expand Up @@ -98,11 +99,31 @@ private GenericHttpClient GetClient(Models.Connection connection, string service

return _client = client;
}


public Task<ContributionNodeResponse> QueryContributionNodeAsync(
Models.Connection connection,
ContributionNodeQuery query,
Dictionary<string, string> additionalHeaders,
Dictionary<string, string> queryParameters,
string apiVersion,
string serviceHostName)
{
return GetClient(connection, serviceHostName)
.InvokeAsync<ContributionNodeResponse>(
HttpMethod.Post,
"_apis/Contribution/HierarchyQuery",
query.ToJsonString(),
"application/json",
"application/json",
additionalHeaders,
queryParameters,
apiVersion);
}

[ImportingConstructor]
public RestApiServiceImpl(IDataManager data, ILogger logger)
{
Logger = logger;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Management.Automation;
using Microsoft.TeamFoundation.Build.WebApi;

namespace TfsCmdlets.Cmdlets.Pipeline.Build
namespace TfsCmdlets.Cmdlets.Pipeline.Build.Definition
{
/// <summary>
/// Disables a build/pipeline definition.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Management.Automation;
using Microsoft.TeamFoundation.Build.WebApi;

namespace TfsCmdlets.Cmdlets.Pipeline.Build
namespace TfsCmdlets.Cmdlets.Pipeline.Build.Definition
{
/// <summary>
/// Enables a previously disabled build/pipeline definition.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Management.Automation;
using Microsoft.TeamFoundation.Build.WebApi;

namespace TfsCmdlets.Cmdlets.Pipeline.Build
namespace TfsCmdlets.Cmdlets.Pipeline.Build.Definition
{
/// <summary>
/// Gets one or more build/pipeline definitions in a team project.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Management.Automation;
using Microsoft.TeamFoundation.Build.WebApi;

namespace TfsCmdlets.Cmdlets.Pipeline.Build
namespace TfsCmdlets.Cmdlets.Pipeline.Build.Definition
{
/// <summary>
/// Resumes (unpauses) a previously suspended build/pipeline definition.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Management.Automation;
using Microsoft.TeamFoundation.Build.WebApi;

namespace TfsCmdlets.Cmdlets.Pipeline.Build
namespace TfsCmdlets.Cmdlets.Pipeline.Build.Definition
{
/// <summary>
/// Suspends (pauses) a build/pipeline definition.
Expand Down
5 changes: 5 additions & 0 deletions CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/Definition/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
layout: module
title: Pipeline Definitions
---

18 changes: 18 additions & 0 deletions CSharp/TfsCmdlets/Cmdlets/Pipeline/Build/StartBuild.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Microsoft.TeamFoundation.Build.WebApi;

namespace TfsCmdlets.Cmdlets.Pipeline.Build
{
/// <summary>
/// Gets one or more build/pipeline definitions in a team project.
/// </summary>
[TfsCmdlet(CmdletScope.Project, OutputType = typeof(BuildDefinitionReference))]
partial class StartBuild
{
/// <summary>
/// Specifies the pipeline to start.
/// </summary>
[Parameter(Position = 0, ValueFromPipeline = true)]
[Alias("Path", "Pipeline")]
public object Definition { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Management.Automation;
using TfsCmdlets.Models;

namespace TfsCmdlets.Cmdlets.TeamProject.Member
{
/// <summary>
/// Gets the members of a team project.
/// </summary>
[TfsCmdlet(CmdletScope.Project, OutputType = typeof(TeamProjectMember))]
[OutputType(typeof(WebApiIdentity), ParameterSetName = new[] { "As Identity" })]
partial class GetTeamProjectMember
{
/// <summary>
/// Specifies the name of a team project member. Wildcards are supported.
/// When omitted, all team project members are returned.
/// </summary>
[Parameter()]
public object Member { get; set; } = "*";

/// <summary>
/// Returns the members as fully resolved <see cref="WebApiIdentity"/> objects.
/// When omitted, it returns only the name, ID and email of the users.
/// </summary>
[Parameter()]
public SwitchParameter AsIdentity { get; set; }
}
}
5 changes: 5 additions & 0 deletions CSharp/TfsCmdlets/Cmdlets/TeamProject/Member/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
layout: module
title: Team Project Members
---

Loading

0 comments on commit f91c738

Please sign in to comment.