Example #1
0
        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();
            }
        }
Example #2
0
        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);
            }
        }
Example #3
0
        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);
            }
        }
Example #4
0
        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();
            }
        }
Example #5
0
        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);
            }
        }
Example #6
0
        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);
            }
        }
Example #7
0
        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);
        }