Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit 239ee9d

Browse files
committed
Implement GetRepositories with indexed cache
Implement the repository listing with an indexed cache and add support for putting it into ITrackingCollection, so we can stream results instead of having object blobs.
1 parent ed1f5c2 commit 239ee9d

File tree

8 files changed

+140
-66
lines changed

8 files changed

+140
-66
lines changed

src/GitHub.App/Api/ApiClient.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,5 +237,9 @@ public IObservable<PullRequest> GetPullRequestsForRepository(string owner, strin
237237
SortDirection = SortDirection.Descending
238238
});
239239
}
240+
public IObservable<Repository> GetRepositories()
241+
{
242+
return gitHubClient.Repository.GetAllForCurrent();
243+
}
240244
}
241245
}

src/GitHub.App/Models/RepositoryModel.cs

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,42 +5,96 @@
55

66
namespace GitHub.Models
77
{
8-
public class RepositoryModel : SimpleRepositoryModel, IRepositoryModel, IEquatable<RepositoryModel>
8+
public class RepositoryModel : SimpleRepositoryModel, IRepositoryModel,
9+
IEquatable<RepositoryModel>, IComparable<RepositoryModel>
910
{
10-
public RepositoryModel(string name, UriString cloneUrl, bool isPrivate, bool isFork, IAccount ownerAccount)
11+
public RepositoryModel(long id, string name, UriString cloneUrl, bool isPrivate, bool isFork, IAccount ownerAccount)
1112
: base(name, cloneUrl)
1213
{
14+
Id = id;
1315
Owner = ownerAccount;
1416
SetIcon(isPrivate, isFork);
1517
}
1618

17-
public IAccount Owner { get; private set; }
19+
public void CopyFrom(IRepositoryModel other)
20+
{
21+
if (!Equals(other))
22+
throw new ArgumentException("Instance to copy from doesn't match this instance. this:(" + this + ") other:(" + other + ")", nameof(other));
23+
Icon = other.Icon;
24+
}
25+
1826
public override int GetHashCode()
1927
{
20-
return (Owner?.GetHashCode() ?? 0) ^ base.GetHashCode();
28+
return Id.GetHashCode();
2129
}
2230

2331
public override bool Equals([AllowNull]object obj)
2432
{
2533
if (ReferenceEquals(this, obj))
2634
return true;
2735
var other = obj as RepositoryModel;
28-
return other != null && Equals(Owner, other.Owner) && base.Equals(obj);
36+
return other != null && Id == other.Id;
37+
}
38+
39+
bool IEquatable<IRepositoryModel>.Equals([AllowNull]IRepositoryModel other)
40+
{
41+
if (ReferenceEquals(this, other))
42+
return true;
43+
return other != null && Id == other.Id;
2944
}
3045

3146
bool IEquatable<RepositoryModel>.Equals([AllowNull]RepositoryModel other)
3247
{
3348
if (ReferenceEquals(this, other))
3449
return true;
35-
return other != null && Equals(Owner, other.Owner) && base.Equals(other as SimpleRepositoryModel);
50+
return other != null && Id == other.Id;
51+
}
52+
53+
public int CompareTo([AllowNull]IRepositoryModel other)
54+
{
55+
return other != null ? UpdatedAt.CompareTo(other.UpdatedAt) : 1;
56+
}
57+
58+
public int CompareTo([AllowNull]RepositoryModel other)
59+
{
60+
return other != null ? UpdatedAt.CompareTo(other.UpdatedAt) : 1;
3661
}
3762

63+
public static bool operator >([AllowNull]RepositoryModel lhs, [AllowNull]RepositoryModel rhs)
64+
{
65+
if (ReferenceEquals(lhs, rhs))
66+
return false;
67+
return lhs?.CompareTo(rhs) > 0;
68+
}
69+
70+
public static bool operator <([AllowNull]RepositoryModel lhs, [AllowNull]RepositoryModel rhs)
71+
{
72+
if (ReferenceEquals(lhs, rhs))
73+
return false;
74+
return (object)lhs == null || lhs.CompareTo(rhs) < 0;
75+
}
76+
77+
public static bool operator ==([AllowNull]RepositoryModel lhs, [AllowNull]RepositoryModel rhs)
78+
{
79+
return ReferenceEquals(lhs, rhs);
80+
}
81+
82+
public static bool operator !=([AllowNull]RepositoryModel lhs, [AllowNull]RepositoryModel rhs)
83+
{
84+
return !(lhs == rhs);
85+
}
86+
87+
public IAccount Owner { get; }
88+
public long Id { get; }
89+
public DateTimeOffset CreatedAt { get; set; }
90+
public DateTimeOffset UpdatedAt { get; set; }
91+
3892
internal string DebuggerDisplay
3993
{
4094
get
4195
{
4296
return String.Format(CultureInfo.InvariantCulture,
43-
"{4}\tName: {0} CloneUrl: {1} LocalPath: {2} Account: {3}", Name, CloneUrl, LocalPath, Owner, GetHashCode());
97+
"{5}\tId: {0} Name: {1} CloneUrl: {2} LocalPath: {3} Account: {4}", Id, Name, CloneUrl, LocalPath, Owner, GetHashCode());
4498
}
4599
}
46100
}

src/GitHub.App/SampleData/SampleViewModels.cs

Lines changed: 13 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -318,41 +318,14 @@ public IObservable<Unit> LogOut()
318318
}
319319

320320
[ExcludeFromCodeCoverage]
321-
public class RepositoryModelDesigner : NotificationAwareObject, IRepositoryModel
321+
public static class RepositoryModelDesigner
322322
{
323-
public RepositoryModelDesigner() : this("repo")
323+
public static IRepositoryModel Create(string name = null, string owner = null)
324324
{
325+
name = name ?? "octocat";
326+
owner = owner ?? "github";
327+
return new RepositoryModel(0, name, new UriString("http://github.com/" + name + "/" + owner), false, false, new AccountDesigner() { Login = owner });
325328
}
326-
327-
public RepositoryModelDesigner(string name) : this("repo", "github")
328-
{
329-
Name = name;
330-
}
331-
332-
public RepositoryModelDesigner(string name, string owner)
333-
{
334-
Name = name;
335-
Owner = new AccountDesigner { Login = owner };
336-
}
337-
338-
public void SetIcon(bool isPrivate, bool isFork)
339-
{
340-
}
341-
342-
public UriString GenerateUrl(string path = null, int startLine = -1, int endLine = -1)
343-
{
344-
return null;
345-
}
346-
347-
public string Name { get; set; }
348-
public UriString CloneUrl { get; set; }
349-
public string LocalPath { get; set; }
350-
351-
public Octicon Icon { get; set; }
352-
353-
public IAccount Owner { get; set; }
354-
355-
public void Refresh() { }
356329
}
357330

358331
public class RepositoryCloneViewModelDesigner : BaseViewModel, IRepositoryCloneViewModel
@@ -361,14 +334,14 @@ public RepositoryCloneViewModelDesigner()
361334
{
362335
var repositories = new ReactiveList<IRepositoryModel>
363336
{
364-
new RepositoryModelDesigner("encourage", "haacked"),
365-
new RepositoryModelDesigner("haacked.com", "haacked"),
366-
new RepositoryModelDesigner("octokit.net", "octokit"),
367-
new RepositoryModelDesigner("octokit.rb", "octokit"),
368-
new RepositoryModelDesigner("octokit.objc", "octokit"),
369-
new RepositoryModelDesigner("windows", "github"),
370-
new RepositoryModelDesigner("mac", "github"),
371-
new RepositoryModelDesigner("github", "github")
337+
RepositoryModelDesigner.Create("encourage", "haacked"),
338+
RepositoryModelDesigner.Create("haacked.com", "haacked"),
339+
RepositoryModelDesigner.Create("octokit.net", "octokit"),
340+
RepositoryModelDesigner.Create("octokit.rb", "octokit"),
341+
RepositoryModelDesigner.Create("octokit.objc", "octokit"),
342+
RepositoryModelDesigner.Create("windows", "github"),
343+
RepositoryModelDesigner.Create("mac", "github"),
344+
RepositoryModelDesigner.Create("github", "github")
372345
};
373346

374347
BrowseForDirectory = ReactiveCommand.Create();

src/GitHub.App/Services/ModelService.cs

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,32 @@ public ITrackingCollection<IPullRequestModel> GetPullRequests(ISimpleRepositoryM
167167
return collection;
168168
}
169169

170+
public ITrackingCollection<IRepositoryModel> GetRepositories(ITrackingCollection<IRepositoryModel> collection)
171+
{
172+
var keyobs = GetUserFromCache()
173+
.Select(user => string.Format(CultureInfo.InvariantCulture, "{0}|{1}", CacheIndex.RepoPrefix, user.Login));
174+
175+
var source = Observable.Defer(() => keyobs
176+
.SelectMany(key =>
177+
hostCache.GetAndFetchLatestFromIndex(key, () =>
178+
apiClient.GetRepositories()
179+
.Select(RepositoryCacheItem.Create),
180+
item =>
181+
{
182+
// this could blow up due to the collection being disposed somewhere else
183+
try { collection.RemoveItem(Create(item)); }
184+
catch (ObjectDisposedException) { }
185+
},
186+
TimeSpan.FromMinutes(5),
187+
TimeSpan.FromDays(1))
188+
)
189+
.Select(Create)
190+
);
191+
192+
collection.Listen(source);
193+
return collection;
194+
}
195+
170196
public IObservable<Unit> InvalidateAll()
171197
{
172198
return hostCache.InvalidateAll().ContinueAfter(() => hostCache.Vacuum());
@@ -251,14 +277,19 @@ Models.Account Create(AccountCacheItem accountCacheItem)
251277
avatarProvider.GetAvatar(accountCacheItem));
252278
}
253279

254-
IRepositoryModel Create(RepositoryCacheItem repositoryCacheItem)
280+
IRepositoryModel Create(RepositoryCacheItem item)
255281
{
256282
return new RepositoryModel(
257-
repositoryCacheItem.Name,
258-
new UriString(repositoryCacheItem.CloneUrl),
259-
repositoryCacheItem.Private,
260-
repositoryCacheItem.Fork,
261-
Create(repositoryCacheItem.Owner));
283+
item.Id,
284+
item.Name,
285+
new UriString(item.CloneUrl),
286+
item.Private,
287+
item.Fork,
288+
Create(item.Owner))
289+
{
290+
CreatedAt = item.CreatedAt,
291+
UpdatedAt = item.UpdatedAt
292+
};
262293
}
263294

264295
IPullRequestModel Create(PullRequestCacheItem prCacheItem)
@@ -323,15 +354,20 @@ public RepositoryCacheItem() {}
323354

324355
public RepositoryCacheItem(Repository apiRepository)
325356
{
357+
Id = apiRepository.Id;
326358
Name = apiRepository.Name;
327359
Owner = AccountCacheItem.Create(apiRepository.Owner);
328360
CloneUrl = apiRepository.CloneUrl;
329361
Private = apiRepository.Private;
330362
Fork = apiRepository.Fork;
331-
Key = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", Name, Owner);
363+
Key = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", Owner, Name);
364+
CreatedAt = apiRepository.CreatedAt;
365+
UpdatedAt = apiRepository.UpdatedAt;
332366
Timestamp = apiRepository.UpdatedAt;
333367
}
334368

369+
public long Id { get; set; }
370+
335371
public string Name { get; set; }
336372
[AllowNull]
337373
public AccountCacheItem Owner
@@ -342,6 +378,8 @@ public AccountCacheItem Owner
342378
public string CloneUrl { get; set; }
343379
public bool Private { get; set; }
344380
public bool Fork { get; set; }
381+
public DateTimeOffset CreatedAt { get; set; }
382+
public DateTimeOffset UpdatedAt { get; set; }
345383
}
346384

347385
public class PullRequestCacheItem : CacheItem

src/GitHub.Exports.Reactive/Api/IApiClient.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,6 @@ IObservable<ApplicationAuthorization> GetOrCreateApplicationAuthenticationCode(
3333
IObservable<LicenseMetadata> GetLicenses();
3434
IObservable<Unit> DeleteApplicationAuthorization(int id, string twoFactorAuthorizationCode);
3535
IObservable<PullRequest> GetPullRequestsForRepository(string owner, string name);
36+
IObservable<Repository> GetRepositories();
3637
}
3738
}
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
using GitHub.Primitives;
2-
using GitHub.UI;
1+
using GitHub.Collections;
2+
using System;
33

44
namespace GitHub.Models
55
{
6-
public interface IRepositoryModel : ISimpleRepositoryModel
6+
public interface IRepositoryModel : ISimpleRepositoryModel, ICopyable<IRepositoryModel>,
7+
IEquatable<IRepositoryModel>, IComparable<IRepositoryModel>
78
{
9+
long Id { get; }
810
IAccount Owner { get; }
11+
DateTimeOffset CreatedAt { get; }
12+
DateTimeOffset UpdatedAt { get; }
913
}
1014
}

src/UnitTests/GitHub.App/Models/RepositoryModelTests.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,24 @@ public void SameContentEqualsTrue(string name1, string url1, string path1, strin
2929
}
3030

3131
[Theory]
32-
[InlineData("a name", "https://github.com/github/VisualStudio", "a name", "https://github.com/github/VisualStudio")]
33-
public void SameContentEqualsTrue2(string name1, string url1, string name2, string url2)
32+
[InlineData(1, "a name", "https://github.com/github/VisualStudio", 1, "a name", "https://github.com/github/VisualStudio")]
33+
public void SameContentEqualsTrue2(long id1, string name1, string url1, long id2, string name2, string url2)
3434
{
3535
var account = Substitute.For<IAccount>();
36-
var a = new RepositoryModel(name1, new UriString(url1), false, false, account);
37-
var b = new RepositoryModel(name2, new UriString(url2), false, false, account);
36+
var a = new RepositoryModel(id1, name1, new UriString(url1), false, false, account);
37+
var b = new RepositoryModel(id2, name2, new UriString(url2), false, false, account);
3838
Assert.Equal(a, b);
3939
Assert.False(a == b);
4040
Assert.Equal(a.GetHashCode(), b.GetHashCode());
4141
}
4242

4343
[Theory]
44-
[InlineData("a name1", "https://github.com/github/VisualStudio", "a name", "https://github.com/github/VisualStudio")]
45-
public void DifferentContentEqualsFalse(string name1, string url1, string name2, string url2)
44+
[InlineData(1, "a name1", "https://github.com/github/VisualStudio", 2, "a name", "https://github.com/github/VisualStudio")]
45+
public void DifferentContentEqualsFalse(long id1, string name1, string url1, long id2, string name2, string url2)
4646
{
4747
var account = Substitute.For<IAccount>();
48-
var a = new RepositoryModel(name1, new UriString(url1), false, false, account);
49-
var b = new RepositoryModel(name2, new UriString(url2), false, false, account);
48+
var a = new RepositoryModel(id1, name1, new UriString(url1), false, false, account);
49+
var b = new RepositoryModel(id2, name2, new UriString(url2), false, false, account);
5050
Assert.NotEqual(a, b);
5151
Assert.False(a == b);
5252
Assert.NotEqual(a.GetHashCode(), b.GetHashCode());

0 commit comments

Comments
 (0)