refactor records on disk, record most flows, add issue load test

This commit is contained in:
Ryan Nystrom
2018-01-28 15:34:03 -05:00
parent 6a69e40594
commit 657e5786e3
38 changed files with 162 additions and 61 deletions

View File

@@ -127,6 +127,7 @@ IssueManagingNavSectionControllerDelegate {
feed.viewDidLoad()
feed.adapter.dataSource = self
feed.collectionView.accessibilityIdentifier = "issues-collectionView"
// setup after feed is lazy loaded
setup(scrollView: feed.collectionView)

View File

@@ -62,6 +62,8 @@ FlatCacheListener {
override func viewDidLoad() {
super.viewDidLoad()
feed.collectionView.accessibilityIdentifier = "inbox-collectionView"
makeBackBarItemEmpty()
resetRightBarItem()

View File

@@ -15,6 +15,51 @@
#define RECORDING_ENABLED 0
#endif
@interface RecordedResponse: NSObject<NSCoding>
@property (nonatomic, strong) NSDictionary *headerFields;
@property (nonatomic, strong) NSURL *url;
@property (nonatomic, strong) NSData *data;
@property (nonatomic, assign) NSInteger statusCode;
@end
@implementation RecordedResponse
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
_data = [aDecoder decodeObjectForKey:@"data"];
_statusCode = [aDecoder decodeIntegerForKey:@"statusCode"];
_url = [aDecoder decodeObjectForKey:@"url"];
_headerFields = [aDecoder decodeObjectForKey:@"headerFields"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.data forKey:@"data"];
[aCoder encodeInteger:self.statusCode forKey:@"statusCode"];
[aCoder encodeObject:self.headerFields forKey:@"headerFields"];
[aCoder encodeObject:self.url forKey:@"url"];
}
@end
@interface NSString (EscapedFileName)
- (NSString *)gh_escapedFileName;
@end
@implementation NSString (EscapedFileName)
- (NSString *)gh_escapedFileName {
NSCharacterSet* illegalFileNameCharacters = [NSCharacterSet characterSetWithCharactersInString:@"/\\?%*|\"<>"];
return [[self componentsSeparatedByCharactersInSet:illegalFileNameCharacters] componentsJoinedByString:@""];
}
@end
static NSString *projectPath(void) {
// set to $(PROJECT_DIR) in env vars
NSString *path = [[NSProcessInfo processInfo] environment][@"NETWORK_RECORD_PATH"];
@@ -32,22 +77,8 @@ static NSString *recordsPath(void) {
}
static NSString *requestKey(NSURLRequest *request) {
// dont key using the access token so that it can be faked in tests/playback
NSURLComponents *components = [NSURLComponents componentsWithURL:request.URL resolvingAgainstBaseURL:NO];
NSMutableArray *queryItems = [[components queryItems] mutableCopy];
[[components queryItems] enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSURLQueryItem *obj, NSUInteger idx, BOOL *stop) {
if ([obj.name isEqualToString:@"access_token"]) {
[queryItems removeObjectAtIndex:idx];
}
}];
[queryItems sortUsingComparator:^NSComparisonResult(NSURLQueryItem *obj1, NSURLQueryItem *obj2) {
return [obj1.name compare:obj2.name];
}];
components.queryItems = queryItems;
NSMutableString *raw = [NSMutableString new];
[raw appendString:request.HTTPMethod ?: @""];
[raw appendString:components.URL.absoluteString ?: @""];
[raw appendString:request.HTTPMethod ?: @"GET"];
[raw appendString:[[NSString alloc] initWithData:request.HTTPBody encoding:NSUTF8StringEncoding] ?: @""];
const char *cStr = [raw UTF8String];
@@ -62,32 +93,65 @@ static NSString *requestKey(NSURLRequest *request) {
}
static NSString *requestPath(NSURLRequest *request) {
NSString *key = requestKey(request);
return [recordsPath() stringByAppendingPathComponent:key];
NSString *path = recordsPath();
for (NSString *component in [request.URL.host componentsSeparatedByString:@"."]) {
path = [path stringByAppendingPathComponent:component];
}
path = [path stringByAppendingPathComponent:request.URL.path];
// dont key using the access token so that it can be faked in tests/playback
NSURLComponents *components = [NSURLComponents componentsWithURL:request.URL resolvingAgainstBaseURL:NO];
NSMutableArray *queryItems = [[components queryItems] mutableCopy];
[[components queryItems] enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSURLQueryItem *obj, NSUInteger idx, BOOL *stop) {
if ([obj.name isEqualToString:@"access_token"]) {
[queryItems removeObjectAtIndex:idx];
}
}];
[queryItems sortUsingComparator:^NSComparisonResult(NSURLQueryItem *obj1, NSURLQueryItem *obj2) {
return [obj1.name compare:obj2.name];
}];
components.queryItems = queryItems;
path = [path stringByAppendingPathComponent:[components.query gh_escapedFileName]];
if (![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:nil]) {
[[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
}
return [path stringByAppendingPathComponent:requestKey(request)];
}
static void store(NSCachedURLResponse *cachedResponse, NSURLRequest *request) {
static void store(NSURLResponse *response, NSData *data, NSURLRequest *request) {
if (!RECORDING_ENABLED) {
return;
}
if (![cachedResponse.response isKindOfClass:[NSHTTPURLResponse class]]
|| ![cachedResponse.response.MIMEType containsString:@"application/json"]) {
if (![response isKindOfClass:[NSHTTPURLResponse class]]
|| ![response.MIMEType containsString:@"application/json"]) {
return;
}
NSString *path = requestPath(request);
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
NSLog(@"WARNING: Overwriting file for request %@", request.URL.absoluteString);
}
[cachedResponse.data writeToFile:requestPath(request) atomically:YES];
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
RecordedResponse *record = [RecordedResponse new];
record.data = data;
record.statusCode = [httpResponse statusCode];
record.url = response.URL;
record.headerFields = [httpResponse allHeaderFields];
[NSKeyedArchiver archiveRootObject:record toFile:requestPath(request)];
}
@interface PlaybackURLProtocol: NSURLProtocol
@end
@interface RecordingURLCache : NSURLCache
@interface RecordingURLProtocol: NSURLProtocol
@end
static NSURLSession *recordingSession = nil;
BOOL SetupMockNetworkEnvironment(NSURLSessionConfiguration *config) {
const BOOL playbackEnabled = [[[NSProcessInfo processInfo] arguments] containsObject:@"--network-playback"];
@@ -96,11 +160,8 @@ BOOL SetupMockNetworkEnvironment(NSURLSessionConfiguration *config) {
}
if (RECORDING_ENABLED) {
NSURLCache *cache = [[RecordingURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
diskCapacity:20 * 1024 * 1024
diskPath:nil];
[NSURLCache setSharedURLCache:cache];
config.URLCache = cache;
recordingSession = [NSURLSession sessionWithConfiguration:config];
config.protocolClasses = @[[RecordingURLProtocol class]];
}
if (playbackEnabled) {
@@ -137,14 +198,14 @@ BOOL SetupMockNetworkEnvironment(NSURLSessionConfiguration *config) {
NSURLRequest *request = _task.originalRequest ?: self.request;
NSString *path = requestPath(request);
NSLog(@"Loading request from disk: %@\n path: %@", request.URL.absoluteString, path);
NSData *data = [[NSData alloc] initWithContentsOfFile:path];
NSCAssert(data.length > 0, @"Loaded empty data for request %@ at path %@", request.URL.absoluteString, path);
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:request.URL
statusCode:200
RecordedResponse *record = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:record.url
statusCode:record.statusCode
HTTPVersion:(NSString *)kCFHTTPVersion1_1
headerFields:nil];
[self.client URLProtocol:self didLoadData:data];
headerFields:record.headerFields];
[self.client URLProtocol:self didLoadData:record.data];
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
[self.client URLProtocolDidFinishLoading:self];
}
@@ -153,26 +214,44 @@ BOOL SetupMockNetworkEnvironment(NSURLSessionConfiguration *config) {
@end
@implementation RecordingURLCache {
BOOL _isStoringRequest;
@implementation RecordingURLProtocol {
NSURLSessionTask *_task;
}
- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request {
if (!_isStoringRequest) {
store(cachedResponse, request);
}
_isStoringRequest = YES;
[super storeCachedResponse:cachedResponse forRequest:request];
_isStoringRequest = NO;
+ (BOOL)canInitWithTask:(NSURLSessionTask *)task {
return YES;
}
- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forDataTask:(NSURLSessionDataTask *)dataTask {
if (!_isStoringRequest) {
store(cachedResponse, dataTask.originalRequest);
}
_isStoringRequest = YES;
[super storeCachedResponse:cachedResponse forDataTask:dataTask];
_isStoringRequest = NO;
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
return YES;
}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
return request;
}
- (instancetype)initWithTask:(NSURLSessionTask *)task cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id<NSURLProtocolClient>)client {
if (self = [super initWithTask:task cachedResponse:cachedResponse client:client]) {
_task = task;
}
return self;
}
- (void)startLoading {
__weak typeof(self) weakSelf = self;
id<NSURLProtocolClient> client = self.client;
NSURLRequest *request = _task.originalRequest ?: self.request;
NSURLSessionDataTask *task = [recordingSession
dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
store(response, data, request);
[client URLProtocol:weakSelf didLoadData:data];
[client URLProtocol:weakSelf didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
[client URLProtocolDidFinishLoading:weakSelf];
}];
[task resume];
}
- (void)stopLoading {}
@end

View File

@@ -289,6 +289,7 @@
29B981FE201CE7E40002DA39 /* LoginUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B981FD201CE7E40002DA39 /* LoginUITests.swift */; };
29B98207201CEA1D0002DA39 /* Launch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B98206201CEA1D0002DA39 /* Launch.swift */; };
29B9820A201CEE730002DA39 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B98208201CEE510002DA39 /* main.swift */; };
29B9820C201E540B0002DA39 /* InboxUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B9820B201E540B0002DA39 /* InboxUITests.swift */; };
29C0E7071ECBC6C50051D756 /* GithubClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29C0E7061ECBC6C50051D756 /* GithubClient.swift */; };
29C167671ECA005500439D62 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29C167661ECA005500439D62 /* Constants.swift */; };
29C167691ECA016500439D62 /* EmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29C167681ECA016500439D62 /* EmptyView.swift */; };
@@ -730,6 +731,7 @@
29B981FF201CE7E40002DA39 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
29B98206201CEA1D0002DA39 /* Launch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Launch.swift; sourceTree = "<group>"; };
29B98208201CEE510002DA39 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
29B9820B201E540B0002DA39 /* InboxUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InboxUITests.swift; sourceTree = "<group>"; };
29C0E7061ECBC6C50051D756 /* GithubClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GithubClient.swift; sourceTree = "<group>"; };
29C167661ECA005500439D62 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
29C167681ECA016500439D62 /* EmptyView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyView.swift; sourceTree = "<group>"; };
@@ -1613,6 +1615,7 @@
isa = PBXGroup;
children = (
29B981FD201CE7E40002DA39 /* LoginUITests.swift */,
29B9820B201E540B0002DA39 /* InboxUITests.swift */,
29B98206201CEA1D0002DA39 /* Launch.swift */,
29B981FF201CE7E40002DA39 /* Info.plist */,
);
@@ -2727,6 +2730,7 @@
buildActionMask = 2147483647;
files = (
29B98207201CEA1D0002DA39 /* Launch.swift in Sources */,
29B9820C201E540B0002DA39 /* InboxUITests.swift in Sources */,
29B981FE201CE7E40002DA39 /* LoginUITests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
disableMainThreadChecker = "YES"
language = ""
systemAttachmentLifetime = "keepNever"
shouldUseLaunchSchemeArgsEnv = "YES">

View File

@@ -0,0 +1,26 @@
//
// InboxUITests.swift
// FreetimeUITests
//
// Created by Ryan Nystrom on 1/28/18.
// Copyright © 2018 Ryan Nystrom. All rights reserved.
//
import XCTest
class InboxUITests: XCTestCase {
var app: XCUIApplication!
override func setUp() {
super.setUp()
continueAfterFailure = false
app = launch(options: [.mockUser])
}
func test_tapNotification() {
app.collectionViews["inbox-collectionView"].cells.firstMatch.tap()
XCTAssertTrue(app.collectionViews["issues-collectionView"].exists)
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
{"status":"good","last_updated":"2018-01-22T18:36:22Z"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
{"data":{"repository":{"__typename":"Repository","name":"iina","hasIssuesEnabled":true,"viewerCanAdminister":false,"mentionableUsers":{"__typename":"UserConnection","nodes":[{"__typename":"User","avatarUrl":"https://avatars3.githubusercontent.com/u/75213?v=4","login":"sevenfourk"},{"__typename":"User","avatarUrl":"https://avatars0.githubusercontent.com/u/120057?v=4","login":"metacosm"},{"__typename":"User","avatarUrl":"https://avatars2.githubusercontent.com/u/708277?v=4","login":"droid-Q"},{"__typename":"User","avatarUrl":"https://avatars3.githubusercontent.com/u/861659?v=4","login":"HarukaMa"},{"__typename":"User","avatarUrl":"https://avatars2.githubusercontent.com/u/1141722?v=4","login":"superzazu"},{"__typename":"User","avatarUrl":"https://avatars0.githubusercontent.com/u/1356659?v=4","login":"Andeling"},{"__typename":"User","avatarUrl":"https://avatars3.githubusercontent.com/u/2270433?v=4","login":"LeoNatan"},{"__typename":"User","avatarUrl":"https://avatars2.githubusercontent.com/u/2375962?v=4","login":"inflation"},{"__typename":"User","avatarUrl":"https://avatars1.githubusercontent.com/u/3105373?v=4","login":"xu-cheng"},{"__typename":"User","avatarUrl":"https://avatars0.githubusercontent.com/u/3475083?v=4","login":"pNre"},{"__typename":"User","avatarUrl":"https://avatars1.githubusercontent.com/u/3591338?v=4","login":"braineo"},{"__typename":"User","avatarUrl":"https://avatars2.githubusercontent.com/u/4042863?v=4","login":"J-rg"},{"__typename":"User","avatarUrl":"https://avatars0.githubusercontent.com/u/4348897?v=4","login":"vit9696"},{"__typename":"User","avatarUrl":"https://avatars2.githubusercontent.com/u/4469383?v=4","login":"LEOYoon-Tsaw"},{"__typename":"User","avatarUrl":"https://avatars1.githubusercontent.com/u/5009493?v=4","login":"jwells89"},{"__typename":"User","avatarUrl":"https://avatars0.githubusercontent.com/u/5242016?v=4","login":"dreness"},{"__typename":"User","avatarUrl":"https://avatars1.githubusercontent.com/u/5794213?v=4","login":"cgand"},{"__typename":"User","avatarUrl":"https://avatars2.githubusercontent.com/u/6215387?v=4","login":"DrabWeb"},{"__typename":"User","avatarUrl":"https://avatars2.githubusercontent.com/u/7279067?v=4","login":"neesonqk"},{"__typename":"User","avatarUrl":"https://avatars0.githubusercontent.com/u/7720052?v=4","login":"urmyfaith"}]},"defaultBranchRef":{"__typename":"Ref","name":"develop"},"issueOrPullRequest":{"__typename":"Issue","timeline":{"__typename":"IssueTimelineConnection","pageInfo":{"__typename":"PageInfo","hasPreviousPage":false,"startCursor":null},"nodes":[]},"milestone":null,"number":1429,"title":"(snow) crash","assignees":{"__typename":"UserConnection","nodes":[]},"labels":{"__typename":"LabelConnection","nodes":[]},"closed":false,"locked":false,"viewerCanUpdate":false,"author":{"__typename":"User","login":"jerkstore369","avatarUrl":"https://avatars1.githubusercontent.com/u/33273805?v=4"},"editor":null,"lastEditedAt":null,"body":"https://pastebin.com/r1N6Lyc8\r\n\r\nI had a video paused for a while, minimized. Closed the macbook lid, next day, open it and I see iina has quit unexpectedly. Thanks.","createdAt":"2018-01-27T15:38:45Z","viewerDidAuthor":false,"viewerCanReact":true,"reactionGroups":[{"__typename":"ReactionGroup","viewerHasReacted":false,"users":{"__typename":"ReactingUserConnection","nodes":[],"totalCount":0},"content":"THUMBS_UP"},{"__typename":"ReactionGroup","viewerHasReacted":false,"users":{"__typename":"ReactingUserConnection","nodes":[],"totalCount":0},"content":"THUMBS_DOWN"},{"__typename":"ReactionGroup","viewerHasReacted":false,"users":{"__typename":"ReactingUserConnection","nodes":[],"totalCount":0},"content":"LAUGH"},{"__typename":"ReactionGroup","viewerHasReacted":false,"users":{"__typename":"ReactingUserConnection","nodes":[],"totalCount":0},"content":"HOORAY"},{"__typename":"ReactionGroup","viewerHasReacted":false,"users":{"__typename":"ReactingUserConnection","nodes":[],"totalCount":0},"content":"CONFUSED"},{"__typename":"ReactionGroup","viewerHasReacted":false,"users":{"__typename":"ReactingUserConnection","nodes":[],"totalCount":0},"content":"HEART"}],"id":"MDU6SXNzdWUyOTIxMjk1NzU="}}}}

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
{"login":"rnystromtest","id":28745486,"avatar_url":"https://avatars1.githubusercontent.com/u/28745486?v=4","gravatar_id":"","url":"https://api.github.com/users/rnystromtest","html_url":"https://github.com/rnystromtest","followers_url":"https://api.github.com/users/rnystromtest/followers","following_url":"https://api.github.com/users/rnystromtest/following{/other_user}","gists_url":"https://api.github.com/users/rnystromtest/gists{/gist_id}","starred_url":"https://api.github.com/users/rnystromtest/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rnystromtest/subscriptions","organizations_url":"https://api.github.com/users/rnystromtest/orgs","repos_url":"https://api.github.com/users/rnystromtest/repos","events_url":"https://api.github.com/users/rnystromtest/events{/privacy}","received_events_url":"https://api.github.com/users/rnystromtest/received_events","type":"User","site_admin":false,"name":null,"company":null,"blog":"","location":null,"email":null,"hireable":null,"bio":null,"public_repos":2,"public_gists":0,"followers":0,"following":0,"created_at":"2017-05-17T02:12:19Z","updated_at":"2018-01-23T12:42:42Z","private_gists":0,"total_private_repos":0,"owned_private_repos":0,"disk_usage":3,"collaborators":0,"two_factor_authentication":false,"plan":{"name":"free","space":976562499,"collaborators":0,"private_repos":0}}

View File

@@ -1 +0,0 @@
{"data":{"k297631233":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":0}}},"k282739475":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":10}}},"k263365731":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":28}}},"k297620512":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":0}}},"k297554276":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":2}}},"k297604012":{"issueOrPullRequest":{"state":"CLOSED","comments":{"totalCount":10}}},"k288244157":{"issueOrPullRequest":{"state":"CLOSED","comments":{"totalCount":10}}},"k297609381":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":0}}},"k297609151":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":0}}},"k297606952":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":0}}},"k293060619":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":3}}},"k297547782":{"issueOrPullRequest":{"state":"MERGED","comments":{"totalCount":1}}},"k276967961":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":2}}},"k297022703":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":2}}},"k297597005":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":2}}},"k297595651":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":0}}},"k297595378":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":0}}},"k292010271":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":5}}},"k296422598":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":8}}},"k297446074":{"issueOrPullRequest":{"state":"MERGED","comments":{"totalCount":1}}},"k243460686":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":4}}},"k245207176":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":7}}},"k272174411":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":7}}},"k295021613":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":3}}},"k283392597":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":8}}},"k297560743":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":2}}},"k297554669":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":0}}},"k285054729":{"issueOrPullRequest":{"state":"CLOSED","comments":{"totalCount":4}}},"k292388225":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":4}}},"k297449595":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":2}}},"k297512889":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":2}}},"k261337046":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":12}}},"k295101301":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":3}}},"k297492985":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":1}}},"k294749174":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":6}}},"k294865401":{"issueOrPullRequest":{"state":"CLOSED","comments":{"totalCount":9}}},"k297170830":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":2}}},"k297206073":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":2}}},"k273015244":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":75}}},"k297462005":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":0}}},"k296229795":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":5}}},"k297385315":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":2}}},"k297323053":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":2}}},"k297271712":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":2}}},"k296904051":{"issueOrPullRequest":{"state":"CLOSED","comments":{"totalCount":7}}},"k297390111":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":2}}},"k297178889":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":1}}},"k297410572":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":2}}},"k296013239":{"issueOrPullRequest":{"state":"OPEN","comments":{"totalCount":2}}},"k279174326":{"issueOrPullRequest":{"state":"CLOSED","comments":{"totalCount":11}}}}}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long