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
Binary file added lib/Octokit.GraphQL.0.0.1.nupkg
Binary file not shown.
15 changes: 15 additions & 0 deletions src/GitHub.Api/GitHub.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@
</PropertyGroup>
<Import Project="$(SolutionDir)\src\common\signing.props" />
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Octokit.GraphQL, Version=0.0.1.0, Culture=neutral, PublicKeyToken=0be8860aee462442, processorArchitecture=MSIL">
<HintPath>..\..\packages\Octokit.GraphQL.0.0.1\lib\netstandard1.1\Octokit.GraphQL.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Octokit.GraphQL.Core, Version=0.0.1.0, Culture=neutral, PublicKeyToken=0be8860aee462442, processorArchitecture=MSIL">
<HintPath>..\..\packages\Octokit.GraphQL.0.0.1\lib\netstandard1.1\Octokit.GraphQL.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Serilog, Version=2.0.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10, processorArchitecture=MSIL">
<HintPath>..\..\packages\Serilog.2.5.0\lib\net46\Serilog.dll</HintPath>
<Private>True</Private>
Expand All @@ -66,6 +78,9 @@
<Link>ApiClientConfiguration_User.cs</Link>
</Compile>
<Compile Include="ApiClientConfiguration_User.cs" Condition="$(Buildtype) != 'Internal'" />
<Compile Include="GraphQLKeychainCredentialStore.cs" />
<Compile Include="IGraphQLClientFactory.cs" />
<Compile Include="GraphQLClientFactory.cs" />
<Compile Include="IKeychain.cs" />
<Compile Include="ILoginManager.cs" />
<Compile Include="IOAuthCallbackListener.cs" />
Expand Down
41 changes: 41 additions & 0 deletions src/GitHub.Api/GraphQLClientFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.ComponentModel.Composition;
using System.Threading.Tasks;
using GitHub.Models;
using GitHub.Primitives;
using Octokit.GraphQL;

namespace GitHub.Api
{
/// <summary>
/// Creates GraphQL <see cref="Octokit.GraphQL.IConnection"/>s for querying the
/// GitHub GraphQL API.
/// </summary>
[Export(typeof(IGraphQLClientFactory))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class GraphQLClientFactory : IGraphQLClientFactory
{
readonly IKeychain keychain;
readonly IProgram program;

/// <summary>
/// Initializes a new instance of the <see cref="GraphQLClientFactory"/> class.
/// </summary>
/// <param name="keychain">The <see cref="IKeychain"/> to use.</param>
/// <param name="program">The program details.</param>
[ImportingConstructor]
public GraphQLClientFactory(IKeychain keychain, IProgram program)
{
this.keychain = keychain;
this.program = program;
}

/// <inheirtdoc/>
public Task<Octokit.GraphQL.IConnection> CreateConnection(HostAddress address)
{
var credentials = new GraphQLKeychainCredentialStore(keychain, address);
var header = new ProductHeaderValue(program.ProductHeader.Name, program.ProductHeader.Version);
return Task.FromResult<Octokit.GraphQL.IConnection>(new Connection(header, credentials));
}
}
}
32 changes: 32 additions & 0 deletions src/GitHub.Api/GraphQLKeychainCredentialStore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Threading.Tasks;
using GitHub.Extensions;
using GitHub.Primitives;
using Octokit.GraphQL;

namespace GitHub.Api
{
/// <summary>
/// An Octokit.GraphQL credential store that reads from an <see cref="IKeychain"/>.
/// </summary>
public class GraphQLKeychainCredentialStore : ICredentialStore
{
readonly IKeychain keychain;
readonly HostAddress address;

public GraphQLKeychainCredentialStore(IKeychain keychain, HostAddress address)
{
Guard.ArgumentNotNull(keychain, nameof(keychain));
Guard.ArgumentNotNull(address, nameof(keychain));

this.keychain = keychain;
this.address = address;
}

public async Task<string> GetCredentials()
{
var userPass = await keychain.Load(address).ConfigureAwait(false);
return userPass?.Item2;
}
}
}
19 changes: 19 additions & 0 deletions src/GitHub.Api/IGraphQLClientFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Threading.Tasks;
using GitHub.Primitives;

namespace GitHub.Api
{
/// <summary>
/// Creates GraphQL <see cref="Octokit.GraphQL.IConnection"/>s for querying the
/// GitHub GraphQL API.
/// </summary>
public interface IGraphQLClientFactory
{
/// <summary>
/// Creates a new <see cref="Octokit.GraphQL.IConnection"/>.
/// </summary>
/// <param name="address">The address of the server.</param>
/// <returns>A task returning the created connection.</returns>
Task<Octokit.GraphQL.IConnection> CreateConnection(HostAddress address);
}
}
2 changes: 2 additions & 0 deletions src/GitHub.Api/packages.config
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" />
<package id="Octokit.GraphQL" version="0.0.1" targetFramework="net461" />
<package id="Serilog" version="2.5.0" targetFramework="net461" />
</packages>
5 changes: 5 additions & 0 deletions src/GitHub.App/Factories/ModelServiceFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.ComponentModel.Composition;
using System.Threading;
using System.Threading.Tasks;
using GitHub.Api;
using GitHub.Caches;
using GitHub.Models;
using GitHub.Services;
Expand All @@ -15,6 +16,7 @@ namespace GitHub.Factories
public sealed class ModelServiceFactory : IModelServiceFactory, IDisposable
{
readonly IApiClientFactory apiClientFactory;
readonly IGraphQLClientFactory graphQLClientFactory;
readonly IHostCacheFactory hostCacheFactory;
readonly IAvatarProvider avatarProvider;
readonly Dictionary<IConnection, ModelService> cache = new Dictionary<IConnection, ModelService>();
Expand All @@ -23,10 +25,12 @@ public sealed class ModelServiceFactory : IModelServiceFactory, IDisposable
[ImportingConstructor]
public ModelServiceFactory(
IApiClientFactory apiClientFactory,
IGraphQLClientFactory graphQLClientFactory,
IHostCacheFactory hostCacheFactory,
IAvatarProvider avatarProvider)
{
this.apiClientFactory = apiClientFactory;
this.graphQLClientFactory = graphQLClientFactory;
this.hostCacheFactory = hostCacheFactory;
this.avatarProvider = avatarProvider;
}
Expand All @@ -43,6 +47,7 @@ public async Task<IModelService> CreateAsync(IConnection connection)
{
result = new ModelService(
await apiClientFactory.Create(connection.HostAddress),
await graphQLClientFactory.CreateConnection(connection.HostAddress),
await hostCacheFactory.Create(connection.HostAddress),
avatarProvider);
result.InsertUser(AccountCacheItem.Create(connection.User));
Expand Down
18 changes: 13 additions & 5 deletions src/GitHub.App/GitHub.App.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,22 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\Microsoft.VisualStudio.Threading.14.1.131\lib\net45\Microsoft.VisualStudio.Threading.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Octokit.GraphQL, Version=0.0.1.0, Culture=neutral, PublicKeyToken=0be8860aee462442, processorArchitecture=MSIL">
<HintPath>..\..\packages\Octokit.GraphQL.0.0.1\lib\netstandard1.1\Octokit.GraphQL.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Octokit.GraphQL.Core, Version=0.0.1.0, Culture=neutral, PublicKeyToken=0be8860aee462442, processorArchitecture=MSIL">
<HintPath>..\..\packages\Octokit.GraphQL.0.0.1\lib\netstandard1.1\Octokit.GraphQL.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.Utilities, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.VisualStudio.Utilities.14.3.25407\lib\net45\Microsoft.VisualStudio.Utilities.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="rothko, Version=0.0.3.0, Culture=neutral, PublicKeyToken=9f664c41f503810a, processorArchitecture=MSIL">
Expand Down Expand Up @@ -199,6 +206,7 @@
<Compile Include="Models\IssueCommentModel.cs" />
<Compile Include="Models\PullRequestReviewCommentModel.cs" />
<Compile Include="Models\PullRequestDetailArgument.cs" />
<Compile Include="Models\PullRequestReviewModel.cs" />
<Compile Include="SampleData\PullRequestFilesViewModelDesigner.cs" />
<Compile Include="Services\EnterpriseCapabilitiesService.cs" />
<Compile Include="Services\GlobalConnection.cs" />
Expand Down
7 changes: 6 additions & 1 deletion src/GitHub.App/Models/Account.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public Account(
bool isEnterprise,
int ownedPrivateRepositoryCount,
long privateRepositoryInPlanCount,
string avatarUrl,
IObservable<BitmapSource> bitmapSource)
{
Guard.ArgumentNotEmptyString(login, nameof(login));
Expand All @@ -34,6 +35,7 @@ public Account(
PrivateReposInPlan = privateRepositoryInPlanCount;
IsOnFreePlan = privateRepositoryInPlanCount == 0;
HasMaximumPrivateRepositories = OwnedPrivateRepos >= PrivateReposInPlan;
AvatarUrl = avatarUrl;
this.bitmapSource = bitmapSource;

bitmapSourceSubscription = bitmapSource
Expand All @@ -54,6 +56,7 @@ public Account(Octokit.Account account)
OwnedPrivateRepos = account.OwnedPrivateRepos;
IsOnFreePlan = PrivateReposInPlan == 0;
HasMaximumPrivateRepositories = OwnedPrivateRepos >= PrivateReposInPlan;
AvatarUrl = account.AvatarUrl;
}

public Account(Octokit.Account account, IObservable<BitmapSource> bitmapSource)
Expand All @@ -77,6 +80,8 @@ public Account(Octokit.Account account, IObservable<BitmapSource> bitmapSource)

public long PrivateReposInPlan { get; private set; }

public string AvatarUrl { get; private set; }

public BitmapSource Avatar
{
get { return avatar; }
Expand Down Expand Up @@ -115,7 +120,7 @@ public override bool Equals(object obj)

public override int GetHashCode()
{
return (Login?.GetHashCode() ?? 0) ^ IsUser .GetHashCode() ^ IsEnterprise.GetHashCode();
return (Login?.GetHashCode() ?? 0) ^ IsUser.GetHashCode() ^ IsEnterprise.GetHashCode();
}

bool IEquatable<IAccount>.Equals(IAccount other)
Expand Down
3 changes: 2 additions & 1 deletion src/GitHub.App/Models/IssueCommentModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ namespace GitHub.Models
{
public class IssueCommentModel : ICommentModel
{
public string Body { get; set; }
public int Id { get; set; }
public string NodeId { get; set; }
public string Body { get; set; }
public DateTimeOffset CreatedAt { get; set; }
public IAccount User { get; set; }
}
Expand Down
20 changes: 16 additions & 4 deletions src/GitHub.App/Models/PullRequestModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,23 @@ public string Body
public DateTimeOffset CreatedAt { get; set; }
public DateTimeOffset UpdatedAt { get; set; }
public IAccount Author { get; set; }
public IReadOnlyCollection<IPullRequestFileModel> ChangedFiles { get; set; } = new IPullRequestFileModel[0];
public IReadOnlyCollection<ICommentModel> Comments { get; set; } = new ICommentModel[0];
public IReadOnlyList<IPullRequestFileModel> ChangedFiles { get; set; } = new IPullRequestFileModel[0];
public IReadOnlyList<ICommentModel> Comments { get; set; } = new ICommentModel[0];

IReadOnlyCollection<IPullRequestReviewCommentModel> reviewComments = new IPullRequestReviewCommentModel[0];
public IReadOnlyCollection<IPullRequestReviewCommentModel> ReviewComments
IReadOnlyList<IPullRequestReviewModel> reviews = new IPullRequestReviewModel[0];
public IReadOnlyList<IPullRequestReviewModel> Reviews
{
get { return reviews; }
set
{
Guard.ArgumentNotNull(value, nameof(value));
reviews = value;
this.RaisePropertyChange();
}
}

IReadOnlyList<IPullRequestReviewCommentModel> reviewComments = new IPullRequestReviewCommentModel[0];
public IReadOnlyList<IPullRequestReviewCommentModel> ReviewComments
{
get { return reviewComments; }
set
Expand Down
3 changes: 3 additions & 0 deletions src/GitHub.App/Models/PullRequestReviewCommentModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ namespace GitHub.Models
public class PullRequestReviewCommentModel : IPullRequestReviewCommentModel
{
public int Id { get; set; }
public string NodeId { get; set; }
public int PullRequestReviewId { get; set; }
public string Path { get; set; }
public int? Position { get; set; }
public int? OriginalPosition { get; set; }
Expand All @@ -14,5 +16,6 @@ public class PullRequestReviewCommentModel : IPullRequestReviewCommentModel
public IAccount User { get; set; }
public string Body { get; set; }
public DateTimeOffset CreatedAt { get; set; }
public bool IsPending { get; set; }
}
}
14 changes: 14 additions & 0 deletions src/GitHub.App/Models/PullRequestReviewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;

namespace GitHub.Models
{
public class PullRequestReviewModel : IPullRequestReviewModel
{
public long Id { get; set; }
public string NodeId { get; set; }
public IAccount User { get; set; }
public string Body { get; set; }
public PullRequestReviewState State { get; set; }
public string CommitId { get; set; }
}
}
1 change: 1 addition & 0 deletions src/GitHub.App/SampleData/AccountDesigner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public BitmapSource Avatar
public string Login { get; set; }
public int OwnedPrivateRepos { get; set; }
public long PrivateReposInPlan { get; set; }
public string AvatarUrl { get; set; }

public override string ToString()
{
Expand Down
15 changes: 15 additions & 0 deletions src/GitHub.App/Services/AvatarProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,21 @@ public IObservable<BitmapSource> GetAvatar(IAvatarContainer apiAccount)
.Catch<BitmapSource, Exception>(_ => Observable.Return(DefaultAvatar(apiAccount)));
}

public IObservable<BitmapSource> GetAvatar(string url)
{
if (url == null)
{
return Observable.Return(DefaultUserBitmapImage);
}

Uri avatarUrl;
Uri.TryCreate(url, UriKind.Absolute, out avatarUrl);
Log.Assert(avatarUrl != null, "Cannot have a null avatar url");

return imageCache.GetImage(avatarUrl)
.Catch<BitmapSource, Exception>(_ => Observable.Return(DefaultUserBitmapImage));
}

public IObservable<Unit> InvalidateAvatar(IAvatarContainer apiAccount)
{
return String.IsNullOrWhiteSpace(apiAccount?.Login)
Expand Down
Loading