private async Task SyncTimerCallback(object state = null) { if (DateTimeOffset.UtcNow.Subtract(_lastSyncInterest) > SyncIdle) { DeactivateOnIdle(); return; } var metaDataMeaningfullyChanged = false; var updater = new DataUpdater(_contextFactory, _mapper); try { // NOTE: The following requests are (relatively) infrequent and important for access control (repos/orgs) // Give them high priority. // User if (_metadata.IsExpired()) { var user = await _github.User(_metadata, RequestPriority.Interactive); if (user.IsOk) { metaDataMeaningfullyChanged = true; await updater.UpdateAccounts(user.Date, new[] { user.Result }); // Unlike orgs, login renames are fine here. // Current user is implicit in all calls, not specified. } // Don't update until saved. _metadata = GitHubMetadata.FromResponse(user); } await _syncLimit.WaitAsync(); try { metaDataMeaningfullyChanged |= await InternalSync(updater); } finally { _syncLimit.Release(); } } catch (GitHubRateException) { // nothing to do } await updater.Changes.Submit(_queueClient, urgent : true); // Mentions _mentions.Sync().LogFailure(_userInfo); // Save changes if (metaDataMeaningfullyChanged) { await Save(); } }
private async Task UpdateProjects(DataUpdater updater, IGitHubPoolable github) { if (_projectMetadata.IsExpired()) { var projects = await github.OrganizationProjects(_login, _projectMetadata); if (projects.IsOk) { await updater.UpdateOrganizationProjects(_orgId, projects.Date, projects.Result); } _projectMetadata = GitHubMetadata.FromResponse(projects); } }
private async Task UpdateIssueReactions(IGitHubActor ghc, DataUpdater updater) { if (_reactionMetadata.IsExpired()) { var issueReactionsResponse = await ghc.IssueReactions(_repoFullName, _issueNumber, _reactionMetadata, RequestPriority.Interactive); if (issueReactionsResponse.IsOk) { await updater.UpdateIssueReactions(_repoId, issueReactionsResponse.Date, _issueId, issueReactionsResponse.Result); } _reactionMetadata = GitHubMetadata.FromResponse(issueReactionsResponse); } }
private async Task SyncTimerCallback(object state = null) { if (DateTimeOffset.UtcNow.Subtract(_lastSyncInterest) > SyncIdle) { DeactivateOnIdle(); return; } var metaDataMeaningfullyChanged = false; var updater = new DataUpdater(_contextFactory, _mapper); try { // Issue Mentions if (_mentionMetadata.IsExpired()) { var mentions = await _github.IssueMentions(_mentionSince, MentionNibblePages, _mentionMetadata, RequestPriority.Background); if (mentions.IsOk && mentions.Result.Any()) { metaDataMeaningfullyChanged = true; await updater.UpdateIssueMentions(_userId, mentions.Result); var maxSince = mentions.Result.Max(x => x.UpdatedAt).AddSeconds(-5); if (maxSince != _mentionSince) { await updater.UpdateAccountMentionSince(_userId, maxSince); _mentionSince = maxSince; } } // Don't update until saved. _mentionMetadata = GitHubMetadata.FromResponse(mentions); } } catch (GitHubRateException) { // nothing to do } await updater.Changes.Submit(_queueClient); // Save changes if (metaDataMeaningfullyChanged) { await Save(); } }
private async Task UpdateAdmins(DataUpdater updater, IGitHubPoolable github) { if (_adminMetadata.IsExpired()) { var admins = await github.OrganizationMembers(_login, role : "admin", cacheOptions : _adminMetadata); if (admins.IsOk) { await updater.SetOrganizationAdmins(_orgId, admins.Date, admins.Result); } else if (!admins.Succeeded) { throw new Exception($"Unexpected response: [{admins?.Request?.Uri}] {admins?.Status}"); } _adminMetadata = GitHubMetadata.FromResponse(admins); } }
private async Task UpdateDetails(DataUpdater updater, IGitHubPoolable github) { if (_metadata.IsExpired()) { var org = await github.Organization(_orgId, _metadata); if (org.IsOk) { await updater.UpdateAccounts(org.Date, new[] { org.Result }); // Update login For the rest of sync. _login = org.Result.Login; // Safest to just start over. DeactivateOnIdle(); } _metadata = GitHubMetadata.FromResponse(org); } }
public async Task <bool> InternalSync(DataUpdater updater) { if (_syncLimit.CurrentCount != 0) { throw new InvalidOperationException($"{nameof(InternalSync)} requires the sync semaphore be held."); } var metaDataMeaningfullyChanged = false; try { // NOTE: The following requests are (relatively) infrequent and important for access control (repos/orgs) // Give them high priority. // Update this user's org memberships if (_orgMetadata.IsExpired()) { var orgs = await _github.OrganizationMemberships(cacheOptions : _orgMetadata, priority : RequestPriority.Interactive); if (orgs.IsOk) { metaDataMeaningfullyChanged = true; await updater.SetUserOrganizations(_userId, orgs.Date, orgs.Result); _orgActors = orgs.Result .ToDictionary(x => x.Organization.Id, x => _grainFactory.GetGrain <IOrganizationActor>(x.Organization.Id)); // Also re-evaluate their linked repos Interlocked.Increment(ref _linkedReposDesired); } // Don't update until saved. _orgMetadata = GitHubMetadata.FromResponse(orgs); } // Update this user's repo memberships var savedLinkCurrent = _linkedReposCurrent; var savedLinkDesired = _linkedReposDesired; if ((savedLinkCurrent < savedLinkDesired) || _repoMetadata.IsExpired()) { var repos = await _github.Repositories(_repoMetadata, RequestPriority.Interactive); if (repos.IsOk) { metaDataMeaningfullyChanged = true; Interlocked.Increment(ref _syncReposDesired); await updater.SetUserRepositories(_userId, repos.Date, repos.Result); _linkedRepos = repos.Result.Select(x => x.Id).ToHashSet(); // don't update _repoActors yet } // Don't update until saved. _repoMetadata = GitHubMetadata.FromResponse(repos); Interlocked.CompareExchange(ref _linkedReposCurrent, savedLinkDesired, savedLinkCurrent); } // Update this user's sync repos var savedReposCurrent = _syncReposCurrent; var savedReposDesired = _syncReposDesired; if (savedReposCurrent < savedReposDesired) { IEnumerable <g.Repository> updateRepos = null; IDictionary <long, GitHubMetadata> combinedRepoMetadata = null; var date = DateTimeOffset.UtcNow; // Not *technically* correct, but probably ok if (_syncSettings?.Include.Any() == true) { // Request all "included" repos to verify access var repoReqs = _syncSettings.Include .Where(x => !_linkedRepos.Contains(x)) // Exclude linked repos (access already known) .Select(x => (RepoId: x, Request: _github.Repository(x, _includeRepoMetadata.Val(x), RequestPriority.Interactive))) .ToArray(); await Task.WhenAll(repoReqs.Select(x => x.Request)); // Collect the "successful" responses // Check explicitly for 404, since 502s are so common :/ var successful = repoReqs .Where(x => x.Request.Status == TaskStatus.RanToCompletion) .Where(x => x.Request.Result.Status != HttpStatusCode.NotFound) .ToArray(); _includeRepoMetadata = successful.ToDictionary(x => x.RepoId, x => GitHubMetadata.FromResponse(x.Request.Result)); updateRepos = successful .Where(x => x.Request.Result.IsOk) .Select(x => x.Request.Result.Result) .ToArray(); // now union/intersect all the things combinedRepoMetadata = new Dictionary <long, GitHubMetadata>(); foreach (var repoId in _syncSettings.Include) { if (_linkedRepos.Contains(repoId)) { combinedRepoMetadata.Add(repoId, null); } else if (_includeRepoMetadata.ContainsKey(repoId)) { combinedRepoMetadata.Add(repoId, _includeRepoMetadata[repoId]); } // else drop it } } var syncRepoMetadata = await updater.UpdateAccountSyncRepositories( _userId, _syncSettings?.AutoTrack ?? true, date, updateRepos, combinedRepoMetadata, _syncSettings?.Exclude); _repoActors = syncRepoMetadata.Keys .ToDictionary(x => x, x => _grainFactory.GetGrain <IRepositoryActor>(x)); _includeRepoMetadata = syncRepoMetadata .Where(x => !_linkedRepos.Contains(x.Key)) .ToDictionary(x => x.Key, x => x.Value); // TODO: Actually detect changes. var cs = new ChangeSummary(); cs.Add(userId: _userId); updater.UnionWithExternalChanges(cs); Interlocked.CompareExchange(ref _syncReposCurrent, savedReposDesired, savedReposCurrent); } } catch (GitHubRateException) { // nothing to do } // We do this here so newly added repos and orgs sync immediately // Sync repos foreach (var repo in _repoActors.Values) { repo.Sync().LogFailure(_userInfo); } // Sync orgs foreach (var org in _orgActors.Values) { org.Sync().LogFailure(_userInfo); } return(metaDataMeaningfullyChanged); }