Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding search functionality to repos #117

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/AzureExtension/AzureExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public AzureExtension(ManualResetEvent extensionDisposedEvent)
case ProviderType.DeveloperId:
return DeveloperIdProvider.GetInstance();
case ProviderType.Repository:
return new RepositoryProvider();
return RepositoryProvider.GetInstance();
case ProviderType.FeaturedApplications:
return new object();
default:
Expand Down
2 changes: 1 addition & 1 deletion src/AzureExtension/AzureExtension.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.206-beta" />
<PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.0.2" />
<PackageReference Include="Microsoft.Windows.DevHome.SDK" Version="0.100.369" />
<PackageReference Include="Microsoft.Windows.DevHome.SDK" Version="0.1099.397.2259" />
dhoehna marked this conversation as resolved.
Show resolved Hide resolved
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.4.231115000" />
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="16.205.1" />
<PackageReference Include="Microsoft.VisualStudio.Services.Client" Version="16.205.1" />
Expand Down
156 changes: 156 additions & 0 deletions src/AzureExtension/Helpers/AzureRepositoryHierarchy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using DevHomeAzureExtension.Client;
using DevHomeAzureExtension.DeveloperId;
using Microsoft.TeamFoundation.Core.WebApi;
using Microsoft.VisualStudio.Services.Account.Client;
using Microsoft.VisualStudio.Services.WebApi;

// In the past, an organization was known as an account. Typedef to organization to make the code easier to read.
using Organization = Microsoft.VisualStudio.Services.Account.Account;

namespace AzureExtension.Helpers;

/// <summary>
/// Handles the hierarchy between organizations and projects. Handles querying for both as well.
/// </summary>
public class AzureRepositoryHierarchy
{
private readonly object _getOrganizationsLock = new();

private readonly object _getProjectsLock = new();

// 1:N Organization to project.
private readonly Dictionary<Organization, List<TeamProjectReference>> _organizationsAndProjects = new();

private readonly DeveloperId _developerId;

/// <summary>
/// Initializes a new instance of the <see cref="AzureRepositoryHierarchy"/> class.
/// Class to handle searching organizations and projects within a server.
/// </summary>
/// <param name="developerId">A developerId to an ADO instance.</param>
/// <remarks>
/// A server can have multiple organizations and each organization can have multiple projects.
/// Additionally, organizations and projects need to be fetched from the network.
/// This calss handles fetching the data, caching it, and searching it.
/// </remarks>
public AzureRepositoryHierarchy(DeveloperId developerId)
{
_developerId = developerId;
}

/// <summary>
/// Get all organizations the user is apart of.
/// </summary>
/// <returns>A list of all organizations.</returns>
public List<Organization> GetOrganizations()
{
lock (_getOrganizationsLock)
{
if (_organizationsAndProjects.Keys.Count == 0)
{
var organizations = QueryForOrganizations();
foreach (var organization in organizations)
{
_organizationsAndProjects.TryAdd(organization, new List<TeamProjectReference>());
}
}

return _organizationsAndProjects.Keys.ToList();
}
}

/// <summary>
/// Get all projects the user is apart of.
/// </summary>
/// <param name="organization">Filters down the returned list to those under organization</param>
/// <returns>A list of projects.</returns>
public async Task<List<TeamProjectReference>> GetProjectsAsync(Organization organization)
{
// Makes sure _organizationsAndProjects has all organizations.
await Task.Run(() => GetOrganizations());

lock (_getProjectsLock)
{
_organizationsAndProjects.TryGetValue(organization, out var projects);

if (projects == null || projects.Count == 0)
{
_organizationsAndProjects[organization] = QueryForProjects(organization);
}

return _organizationsAndProjects[organization];
}
}

/// <summary>
/// Contacts the server to get all organizations the user is apart of.
/// </summary>
/// <returns>A list of organizations.</returns>
/// <remarks>
/// The returned list does include disabled organizations.
/// _queryOrganizationsTask is set here.
/// </remarks>
private List<Organization> QueryForOrganizations()
{
// Establish a connection to get all organizations.
// The library calls these "accounts".
VssConnection accountConnection;

try
{
accountConnection = AzureClientProvider.GetConnectionForLoggedInDeveloper(new Uri(@"https://app.vssps.visualstudio.com/"), _developerId);
}
catch
{
return new List<Organization>();
}

var accountClient = accountConnection.GetClient<AccountHttpClient>();

try
{
return accountClient.GetAccountsByMemberAsync(
memberId: accountConnection.AuthorizedIdentity.Id).Result;
}
catch
{
return new List<Organization>();
}
}

/// <summary>
/// Contacts the server to get all projects in an organization.
/// </summary>
/// <param name="organization">PRojects are returned only for this organization.</param>
/// <returns>A list of projects.</returns>
/// <remarks>
/// the Task to get the projects is added to _organizationsAndProjectTask.
/// </remarks>
private List<TeamProjectReference> QueryForProjects(Organization organization)
{
try
{
var connection = AzureClientProvider.GetConnectionForLoggedInDeveloper(organization.AccountUri, _developerId);

// connection can be null if the organization is disabled.
if (connection != null)
{
var projectClient = connection.GetClient<ProjectHttpClient>();
var projects = projectClient.GetProjects().Result.ToList();
_organizationsAndProjects[organization] = projects;

// in both cases, wait for the task to finish.
return projects;
}
}
catch (Exception e)
{
DevHomeAzureExtension.Client.Log.Logger()?.ReportError("DevHomeRepository", e);
}

return new List<TeamProjectReference>();
}
}
2 changes: 1 addition & 1 deletion src/AzureExtension/Providers/DevHomeRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public DevHomeRepository(GitRepository gitRepository)
}

var repoInformation = new AzureUri(localUrl);
_owningAccountName = Path.Join(repoInformation.Organization, repoInformation.Repository);
_owningAccountName = Path.Join(repoInformation.Connection.Host, repoInformation.Organization, repoInformation.Project);

cloneUrl = localUrl;

Expand Down
Loading
Loading