diff --git a/src/Cake.AzureDevOps/AzureDevOpsAliases.Projects.cs b/src/Cake.AzureDevOps/AzureDevOpsAliases.Projects.cs new file mode 100644 index 0000000..95073c2 --- /dev/null +++ b/src/Cake.AzureDevOps/AzureDevOpsAliases.Projects.cs @@ -0,0 +1,49 @@ +namespace Cake.AzureDevOps +{ + using System.Collections.Generic; + using Cake.AzureDevOps.Collections; + using Cake.AzureDevOps.Projects; + using Cake.Core; + using Cake.Core.Annotations; + + /// + /// Contains functionality related to Azure DevOps projects. + /// + public static partial class AzureDevOpsAliases + { + /// + /// Gets all projects or a collection. + /// + /// The context. + /// Settings for getting the projects. + /// + /// Get the projects associated with an Azure DevOps collection: + /// + /// + /// + /// + /// The projects or an empty list of projects if no projects were found for the . + [CakeMethodAlias] + [CakeAliasCategory("Azure Pipelines")] + [CakeNamespaceImport("Cake.AzureDevOps.Projects")] + [CakeNamespaceImport("Cake.AzureDevOps.Collections")] + public static IEnumerable AzureDevOpsProjects( + this ICakeContext context, + AzureDevOpsCollectionSettings settings) + { + context.NotNull(nameof(context)); + settings.NotNull(nameof(settings)); + + return AzureDevOpsProjectsHelper.GetAzureDevOpsProjects(context.Log, settings); + } + } +} diff --git a/src/Cake.AzureDevOps/Collections/AzureDevOpsCollectionSettings.cs b/src/Cake.AzureDevOps/Collections/AzureDevOpsCollectionSettings.cs new file mode 100644 index 0000000..b817106 --- /dev/null +++ b/src/Cake.AzureDevOps/Collections/AzureDevOpsCollectionSettings.cs @@ -0,0 +1,40 @@ +namespace Cake.AzureDevOps.Collections +{ + using Cake.AzureDevOps.Authentication; + + /// + /// Settings for aliases handling collections. + /// + public class AzureDevOpsCollectionSettings : BaseAzureDevOpsCollectionSettings + { + /// + /// Initializes a new instance of the class + /// based on the instance of a class. + /// + /// Settings containing the parameters. + public AzureDevOpsCollectionSettings(AzureDevOpsCollectionSettings settings) + : base(settings) + { + } + + /// + /// Initializes a new instance of the class using environment variables + /// as set by an Azure Pipelines build. + /// + /// Credentials to use to authenticate against Azure DevOps. + public AzureDevOpsCollectionSettings(IAzureDevOpsCredentials credentials) + : base(credentials) + { + } + + /// + /// Constructs the settings object using the access token provided by Azure Pipelines. + /// + /// The instance of class. + public static AzureDevOpsCollectionSettings UsingAzurePipelinesOAuthToken() + { + var accessToken = EnvironmentVariableHelper.GetSystemAccessToken(); + return new AzureDevOpsCollectionSettings(new AzureDevOpsOAuthCredentials(accessToken)); + } + } +} diff --git a/src/Cake.AzureDevOps/IProjectClientFactory.cs b/src/Cake.AzureDevOps/IProjectClientFactory.cs new file mode 100644 index 0000000..5525b14 --- /dev/null +++ b/src/Cake.AzureDevOps/IProjectClientFactory.cs @@ -0,0 +1,30 @@ +namespace Cake.AzureDevOps +{ + using System; + using Cake.AzureDevOps.Authentication; + using Microsoft.TeamFoundation.Core.WebApi; + using Microsoft.VisualStudio.Services.Identity; + + /// + /// The interface for a Git client factory. + /// + internal interface IProjectClientFactory + { + /// + /// Creates the instance of the class. + /// + /// The URL of the Azure DevOps team project collection. + /// The credentials to connect to Azure DevOps. + /// The instance of class. + ProjectHttpClient CreateProjectClient(Uri collectionUrl, IAzureDevOpsCredentials credentials); + + /// + /// Creates the instance of the class. + /// + /// The URL of the Azure DevOps team project collection. + /// The credentials to connect to Azure DevOps. + /// Returns identity which is authorized. + /// The instance of class. + ProjectHttpClient CreateProjectClient(Uri collectionUrl, IAzureDevOpsCredentials credentials, out Identity authorizedIdentity); + } +} diff --git a/src/Cake.AzureDevOps/ProjectClientFactory.cs b/src/Cake.AzureDevOps/ProjectClientFactory.cs new file mode 100644 index 0000000..19e8c6d --- /dev/null +++ b/src/Cake.AzureDevOps/ProjectClientFactory.cs @@ -0,0 +1,35 @@ +namespace Cake.AzureDevOps +{ + using System; + using Cake.AzureDevOps.Authentication; + using Microsoft.TeamFoundation.Core.WebApi; + using Microsoft.VisualStudio.Services.Identity; + using Microsoft.VisualStudio.Services.WebApi; + + /// + internal class ProjectClientFactory : IProjectClientFactory + { + /// + public ProjectHttpClient CreateProjectClient(Uri collectionUrl, IAzureDevOpsCredentials credentials) + { + return this.CreateProjectClient(collectionUrl, credentials, out _); + } + + /// + public ProjectHttpClient CreateProjectClient(Uri collectionUrl, IAzureDevOpsCredentials credentials, out Identity authorizedIdentity) + { + var connection = + new VssConnection(collectionUrl, credentials.ToVssCredentials()); + + authorizedIdentity = connection.AuthorizedIdentity; + + var projectClient = connection.GetClient(); + if (projectClient == null) + { + throw new AzureDevOpsException("Could not retrieve the ProjectHttpClient object"); + } + + return projectClient; + } + } +} diff --git a/src/Cake.AzureDevOps/Projects/AzureDevOpsProject.cs b/src/Cake.AzureDevOps/Projects/AzureDevOpsProject.cs new file mode 100644 index 0000000..6f007f5 --- /dev/null +++ b/src/Cake.AzureDevOps/Projects/AzureDevOpsProject.cs @@ -0,0 +1,20 @@ +namespace Cake.AzureDevOps.Projects +{ + using System; + + /// + /// Class for writing issues to Azure DevOps pull requests. + /// + public sealed class AzureDevOpsProject + { + /// + /// Gets the project identifier. + /// + public Guid Id { get; internal set; } + + /// + /// Gets the project name. + /// + public string Name { get; internal set; } + } +} diff --git a/src/Cake.AzureDevOps/Projects/AzureDevOpsProjectsHelper.cs b/src/Cake.AzureDevOps/Projects/AzureDevOpsProjectsHelper.cs new file mode 100644 index 0000000..615099f --- /dev/null +++ b/src/Cake.AzureDevOps/Projects/AzureDevOpsProjectsHelper.cs @@ -0,0 +1,41 @@ +namespace Cake.AzureDevOps.Projects +{ + using System.Collections.Generic; + using System.Linq; + using Cake.AzureDevOps.Collections; + using Cake.Core.Diagnostics; + + /// + /// Provides functions for AzureDevOps projects. + /// + internal static class AzureDevOpsProjectsHelper + { + /// + /// Gets the projects for the parameter . + /// + /// The Cake log context. + /// Settings for getting the collection. + /// The projects or an empty list of projects if no projects were found for the . + internal static IEnumerable GetAzureDevOpsProjects( + ICakeLog log, + AzureDevOpsCollectionSettings settings) + { + log.NotNull(nameof(log)); + settings.NotNull(nameof(settings)); + + using (var projectHttpClient = new ProjectClientFactory().CreateProjectClient(settings.CollectionUrl, settings.Credentials)) + { + var projects = + projectHttpClient + .GetProjects() + .ConfigureAwait(false) + .GetAwaiter() + .GetResult() + .Select(x => x.ToAzureDevOpsProject()) + .ToList(); + + return projects; + } + } + } +} diff --git a/src/Cake.AzureDevOps/Projects/TeamProjectReferenceExtensions.cs b/src/Cake.AzureDevOps/Projects/TeamProjectReferenceExtensions.cs new file mode 100644 index 0000000..8fc9e96 --- /dev/null +++ b/src/Cake.AzureDevOps/Projects/TeamProjectReferenceExtensions.cs @@ -0,0 +1,27 @@ +namespace Cake.AzureDevOps.Projects +{ + using Microsoft.TeamFoundation.Core.WebApi; + + /// + /// Extensions for the class. + /// + internal static class TeamProjectReferenceExtensions + { + /// + /// Converts a to an . + /// + /// Team project to convert. + /// Converted team project. + public static AzureDevOpsProject ToAzureDevOpsProject(this TeamProjectReference teamProjectReference) + { + teamProjectReference.NotNull(nameof(teamProjectReference)); + + return + new AzureDevOpsProject + { + Id = teamProjectReference.Id, + Name = teamProjectReference.Name, + }; + } + } +}