Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.
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
17 changes: 8 additions & 9 deletions src/GitHub.Api/SimpleApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using GitHub.Extensions;
using GitHub.Primitives;
using GitHub.Services;
using Octokit;
Expand All @@ -11,8 +10,8 @@ namespace GitHub.Api
{
public class SimpleApiClient : ISimpleApiClient
{
public HostAddress HostAddress { get; private set; }
public Uri OriginalUrl { get; private set; }
public HostAddress HostAddress { get; }
public UriString OriginalUrl { get; }

readonly GitHubClient client;
readonly Lazy<IEnterpriseProbeTask> enterpriseProbe;
Expand All @@ -24,7 +23,7 @@ public class SimpleApiClient : ISimpleApiClient
bool? isEnterprise;
bool? hasWiki;

internal SimpleApiClient(HostAddress hostAddress, Uri repoUrl, GitHubClient githubClient,
internal SimpleApiClient(HostAddress hostAddress, UriString repoUrl, GitHubClient githubClient,
Lazy<IEnterpriseProbeTask> enterpriseProbe, Lazy<IWikiProbe> wikiProbe)
{
HostAddress = hostAddress;
Expand All @@ -51,19 +50,19 @@ async Task<Repository> GetRepositoryInternal()
{
if (owner == null && OriginalUrl != null)
{
var own = OriginalUrl.GetUser();
var name = OriginalUrl.GetRepo();
var ownerLogin = OriginalUrl.Owner;
var repositoryName = OriginalUrl.RepositoryName;

if (own != null && name != null)
if (ownerLogin != null && repositoryName != null)
{
var repo = await client.Repository.Get(own, name);
var repo = await client.Repository.Get(ownerLogin, repositoryName);
if (repo != null)
{
hasWiki = await HasWikiInternal(repo);
isEnterprise = await IsEnterpriseInternal();
repositoryCache = repo;
}
owner = own;
owner = ownerLogin;
}
}
}
Expand Down
16 changes: 8 additions & 8 deletions src/GitHub.Api/SimpleApiClientFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class SimpleApiClientFactory : ISimpleApiClientFactory
readonly Lazy<IEnterpriseProbeTask> lazyEnterpriseProbe;
readonly Lazy<IWikiProbe> lazyWikiProbe;

static readonly Dictionary<Uri, ISimpleApiClient> cache = new Dictionary<Uri, ISimpleApiClient>();
static readonly Dictionary<UriString, ISimpleApiClient> cache = new Dictionary<UriString, ISimpleApiClient>();

[ImportingConstructor]
public SimpleApiClientFactory(IProgram program, Lazy<IEnterpriseProbeTask> enterpriseProbe, Lazy<IWikiProbe> wikiProbe)
Expand All @@ -26,21 +26,21 @@ public SimpleApiClientFactory(IProgram program, Lazy<IEnterpriseProbeTask> enter
lazyWikiProbe = wikiProbe;
}

public ISimpleApiClient Create(Uri repoUrl)
public ISimpleApiClient Create(UriString repositoryUrl)
{
var contains = cache.ContainsKey(repoUrl);
var contains = cache.ContainsKey(repositoryUrl);
if (contains)
return cache[repoUrl];
return cache[repositoryUrl];

lock (cache)
{
if (!cache.ContainsKey(repoUrl))
if (!cache.ContainsKey(repositoryUrl))
{
var hostAddress = HostAddress.Create(repoUrl);
var hostAddress = HostAddress.Create(repositoryUrl);
var apiBaseUri = hostAddress.ApiUri;
cache.Add(repoUrl, new SimpleApiClient(hostAddress, repoUrl, new GitHubClient(productHeader, new SimpleCredentialStore(hostAddress), apiBaseUri), lazyEnterpriseProbe, lazyWikiProbe));
cache.Add(repositoryUrl, new SimpleApiClient(hostAddress, repositoryUrl, new GitHubClient(productHeader, new SimpleCredentialStore(hostAddress), apiBaseUri), lazyEnterpriseProbe, lazyWikiProbe));
}
return cache[repoUrl];
return cache[repositoryUrl];
}
}

Expand Down
6 changes: 2 additions & 4 deletions src/GitHub.Exports/Api/ISimpleApiClient.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
using System;
using System.Threading.Tasks;
using System.Threading.Tasks;
using GitHub.Primitives;
using GitHub.Services;
using Octokit;

namespace GitHub.Api
{
public interface ISimpleApiClient
{
HostAddress HostAddress { get; }
Uri OriginalUrl { get; }
UriString OriginalUrl { get; }
Task<Repository> GetRepository();
bool HasWiki();
bool IsEnterprise();
Expand Down
4 changes: 2 additions & 2 deletions src/GitHub.Exports/Api/ISimpleApiClientFactory.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using System;
using GitHub.Primitives;

namespace GitHub.Api
{
public interface ISimpleApiClientFactory
{
ISimpleApiClient Create(Uri repoUrl);
ISimpleApiClient Create(UriString repositoryUrl);
void ClearFromCache(ISimpleApiClient client);
}
}
85 changes: 27 additions & 58 deletions src/GitHub.Exports/Primitives/UriString.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
Expand Down Expand Up @@ -31,6 +29,7 @@ public class UriString : StringEquivalent<UriString>, IEquatable<UriString>

public UriString(string uriString) : base(NormalizePath(uriString))
{
if (uriString == null) throw new ArgumentNullException(nameof(uriString), "Cannot create a null UriString");
if (uriString.Length == 0) return;
if (Uri.TryCreate(uriString, UriKind.Absolute, out url))
{
Expand Down Expand Up @@ -71,43 +70,6 @@ void SetUri(Uri uri)
}

IsHypertextTransferProtocol = uri.IsHypertextTransferProtocol();

if (String.IsNullOrEmpty(uri.Query)) return;

try
{
var query = ParseQueryString(uri);

if (query.ContainsKey("branch") && !String.IsNullOrEmpty(query["branch"]))
{
Branch = query["branch"].Replace("%2F", "/");
}

if (query.ContainsKey("pr") && !String.IsNullOrEmpty(query["pr"]))
{
PullRequest = query["pr"].Replace("%2F", "/");
}

if (query.ContainsKey("filepath") && !String.IsNullOrEmpty(query["filepath"]))
{
RelativePathToOpen = query["filepath"].Replace("%2F", "/").Replace('/', '\\');
}
}
catch //(Exception ex)
{
//log.WarnException("Failed to read URI query", ex);
}
}

// This is not a complete query string parsing algorithm, but it's good enough for our needs.
static IDictionary<string, string> ParseQueryString(Uri uri)
{
Debug.Assert(uri.Query.StartsWith('?'),
String.Format(CultureInfo.InvariantCulture, "Uri.Query doesn't start with '?': '{0}'", uri.Query));
return uri.Query.Substring(1).Split(new[] {'&'}, StringSplitOptions.RemoveEmptyEntries)
.Select(pair => pair.Split('='))
.ToDictionary(pair => pair.First(), pair => pair.Length > 1 ? pair[1] : null,
StringComparer.OrdinalIgnoreCase);
}

void SetFilePath(Uri uri)
Expand Down Expand Up @@ -144,8 +106,6 @@ bool ParseScpSyntax(string scpString)
return false;
}

public string Branch { get; private set; }
public string PullRequest { get; set; }
public string Host { get; private set; }

public string Owner { get; private set; }
Expand All @@ -154,20 +114,31 @@ bool ParseScpSyntax(string scpString)

public string NameWithOwner { get; private set; }

public string RelativePathToOpen { get; private set; }

public bool IsFileUri { get; private set; }

public bool IsValidUri
{
get { return url != null; }
}
public bool IsValidUri => url != null;

public Uri ToUri()
/// <summary>
/// Attempts a best-effort to convert the remote origin to a GitHub Repository URL.
/// </summary>
/// <returns></returns>
public Uri ToRepositoryUrl()
{
if (url == null)
throw new InvalidOperationException("This Uri String is not a valid Uri");
return url;
if (url != null && IsFileUri) return url;

var scheme = url != null && IsHypertextTransferProtocol
? url.Scheme
: Uri.UriSchemeHttps;

return new UriBuilder
{
Scheme = scheme,
Host = Host,
Path = NameWithOwner,
Port = url?.Port == 80
? -1
: (url?.Port ?? -1)
}.Uri;
}

/// <summary>
Expand All @@ -186,9 +157,7 @@ public static implicit operator UriString(string value)
[SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates")]
public static implicit operator string(UriString uriString)
{
if (uriString == null) return null;

return uriString.Value;
return uriString?.Value;
}

[SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "No.")]
Expand All @@ -197,7 +166,7 @@ public override UriString Combine(string addition)
if (url != null)
{
var urlBuilder = new UriBuilder(url);
if (!String.IsNullOrEmpty(urlBuilder.Query))
if (!string.IsNullOrEmpty(urlBuilder.Query))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an advantage to doing this with the value type instead of the class type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. I think it's my overeager R# that does this. We should pick an approach and stick to it. Do you have a preference?

BTW, this has nothing to do with value type or class type. string is just the shorthand for System.String but it's the exact same class. 😄

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. I think it's my overeager R# that does this. We should pick an approach and stick to it. Do you have a preference?

Weeell, I always type String.Something out of habit, so I'm sure the code is way more sprinkled with the uppercase version than the lowercase one (and I doubt I can teach my fingers otherwise...) 😄

BTW, this has nothing to do with value type or class type. string is just the shorthand for System.String but it's the exact same class. 😄

Yeah, I keep forgetting we're in .NET land where there are no crazy peeps doing weird stuff with jit and string/String differences...

{
var query = urlBuilder.Query;
if (query.StartsWith("?", StringComparison.Ordinal))
Expand All @@ -221,7 +190,7 @@ public override UriString Combine(string addition)
}
return ToUriString(urlBuilder.Uri);
}
return String.Concat(Value, addition);
return string.Concat(Value, addition);
}

public override string ToString()
Expand Down Expand Up @@ -253,12 +222,12 @@ static string GetSerializedValue(SerializationInfo info)

static string NormalizePath(string path)
{
return path == null ? null : path.Replace('\\', '/');
return path?.Replace('\\', '/');
}

static string GetRepositoryName(string repositoryNameSegment)
{
if (String.IsNullOrEmpty(repositoryNameSegment)
if (string.IsNullOrEmpty(repositoryNameSegment)
|| repositoryNameSegment.Equals("/", StringComparison.Ordinal))
{
return null;
Expand Down
11 changes: 8 additions & 3 deletions src/GitHub.Exports/Services/ITeamExplorerServiceHolder.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.VisualStudio.TeamFoundation.Git.Extensibility;
using System;
using System;
using GitHub.Primitives;
using Microsoft.VisualStudio.TeamFoundation.Git.Extensibility;

namespace GitHub.Services
{
Expand Down Expand Up @@ -46,7 +47,11 @@ public interface ITeamExplorerServiceHolder
public interface IGitAwareItem
{
IGitRepositoryInfo ActiveRepo { get; }
Uri ActiveRepoUri { get; }

/// <summary>
/// Represents the web URL of the repository on GitHub.com, even if the origin is an SSH address.
/// </summary>
UriString ActiveRepoUri { get; }
string ActiveRepoName { get; }
}
}
23 changes: 8 additions & 15 deletions src/GitHub.Exports/Services/Services.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Microsoft.VisualStudio.TeamFoundation.Git.Extensibility;
using GitHub.Models;
using GitHub.Info;
using GitHub.Primitives;

namespace GitHub.VisualStudio
{
Expand Down Expand Up @@ -127,7 +128,7 @@ public static Uri GetRepoUrlFromSolution(IVsSolution solution)
return null;
using (var repo = new Repository(repoPath))
{
return GetUriFromRepository(repo);
return GetUriFromRepository(repo)?.ToRepositoryUrl();
}
}

Expand All @@ -144,21 +145,13 @@ public static Repository GetRepoFromSolution(this IVsSolution solution)
return new Repository(repoPath);
}

public static Uri GetUriFromRepository(Repository repo)
public static UriString GetUriFromRepository(Repository repo)
{
if (repo == null)
return null;
var remote = repo.Network.Remotes.FirstOrDefault(x => x.Name.Equals("origin", StringComparison.Ordinal));
if (remote == null)
return null;
Uri uri;
var url = remote.Url;
// fixup ssh urls
if (url.StartsWith("git@github.com:", StringComparison.Ordinal))
url = url.Replace("git@github.com:", "https://github.com/");
if (!Uri.TryCreate(url, UriKind.Absolute, out uri))
return null;
return uri;
return repo
?.Network
.Remotes
.FirstOrDefault(x => x.Name.Equals("origin", StringComparison.Ordinal))
?.Url;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotta love them null conditional operators! ❤️

}

public static Repository GetRepoFromIGit(this IGitRepositoryInfo repoInfo)
Expand Down
Loading