2019-01-07 21:12:48 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using CompatApiClient;
|
2019-01-08 00:13:55 +00:00
|
|
|
|
using Microsoft.Extensions.Caching.Memory;
|
2023-04-20 16:22:50 +00:00
|
|
|
|
using Octokit;
|
2019-01-07 21:12:48 +00:00
|
|
|
|
|
2022-06-29 19:59:46 +00:00
|
|
|
|
namespace GithubClient;
|
|
|
|
|
|
|
|
|
|
public class Client
|
2019-01-07 21:12:48 +00:00
|
|
|
|
{
|
2023-04-20 16:22:50 +00:00
|
|
|
|
private readonly GitHubClient client;
|
2019-01-07 21:12:48 +00:00
|
|
|
|
|
2022-06-29 19:59:46 +00:00
|
|
|
|
private static readonly TimeSpan PrStatusCacheTime = TimeSpan.FromMinutes(3);
|
|
|
|
|
private static readonly TimeSpan IssueStatusCacheTime = TimeSpan.FromMinutes(30);
|
|
|
|
|
private static readonly MemoryCache StatusesCache = new(new MemoryCacheOptions { ExpirationScanFrequency = TimeSpan.FromMinutes(1) });
|
|
|
|
|
private static readonly MemoryCache IssuesCache = new(new MemoryCacheOptions { ExpirationScanFrequency = TimeSpan.FromMinutes(30) });
|
2019-01-07 21:12:48 +00:00
|
|
|
|
|
2022-06-29 19:59:46 +00:00
|
|
|
|
public static int RateLimit { get; private set; }
|
|
|
|
|
public static int RateLimitRemaining { get; private set; }
|
|
|
|
|
public static DateTime RateLimitResetTime { get; private set; }
|
2019-01-10 19:46:47 +00:00
|
|
|
|
|
2022-06-29 19:59:46 +00:00
|
|
|
|
public Client(string? githubToken)
|
|
|
|
|
{
|
2023-04-20 16:22:50 +00:00
|
|
|
|
client = new(new ProductHeaderValue(ApiConfig.ProductName, ApiConfig.ProductVersion));
|
|
|
|
|
if (githubToken is {Length: >0})
|
|
|
|
|
client.Credentials = new(githubToken);
|
2022-06-29 19:59:46 +00:00
|
|
|
|
}
|
2019-01-07 21:12:48 +00:00
|
|
|
|
|
2023-04-20 16:22:50 +00:00
|
|
|
|
public async Task<PullRequest?> GetPrInfoAsync(int pr, CancellationToken cancellationToken)
|
2022-06-29 19:59:46 +00:00
|
|
|
|
{
|
2023-04-20 16:22:50 +00:00
|
|
|
|
if (StatusesCache.TryGetValue(pr, out PullRequest? result))
|
2019-01-07 21:12:48 +00:00
|
|
|
|
{
|
2023-04-20 16:22:50 +00:00
|
|
|
|
ApiConfig.Log.Debug($"Returned {nameof(PullRequest)} for {pr} from cache");
|
2019-01-10 18:54:33 +00:00
|
|
|
|
return result;
|
2019-01-07 21:12:48 +00:00
|
|
|
|
}
|
2019-01-08 00:13:55 +00:00
|
|
|
|
|
2022-06-29 19:59:46 +00:00
|
|
|
|
try
|
2019-07-31 13:19:09 +00:00
|
|
|
|
{
|
2023-04-20 16:22:50 +00:00
|
|
|
|
result = await client.PullRequest.Get("RPCS3", "rpcs3", pr).WaitAsync(cancellationToken).ConfigureAwait(false);
|
2022-06-29 19:59:46 +00:00
|
|
|
|
UpdateRateLimitStats();
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
ApiConfig.Log.Error(e);
|
|
|
|
|
}
|
|
|
|
|
if (result == null)
|
|
|
|
|
{
|
2023-04-20 16:22:50 +00:00
|
|
|
|
ApiConfig.Log.Debug($"Failed to get {nameof(PullRequest)}, returning empty result");
|
2022-06-29 19:59:46 +00:00
|
|
|
|
return new(pr);
|
2019-07-31 13:19:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-29 19:59:46 +00:00
|
|
|
|
StatusesCache.Set(pr, result, PrStatusCacheTime);
|
2023-04-20 16:22:50 +00:00
|
|
|
|
ApiConfig.Log.Debug($"Cached {nameof(PullRequest)} for {pr} for {PrStatusCacheTime}");
|
2022-06-29 19:59:46 +00:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-20 16:22:50 +00:00
|
|
|
|
public async Task<Issue?> GetIssueInfoAsync(int issue, CancellationToken cancellationToken)
|
2022-06-29 19:59:46 +00:00
|
|
|
|
{
|
2023-04-20 16:22:50 +00:00
|
|
|
|
if (IssuesCache.TryGetValue(issue, out Issue? result))
|
2021-08-30 13:39:05 +00:00
|
|
|
|
{
|
2023-04-20 16:22:50 +00:00
|
|
|
|
ApiConfig.Log.Debug($"Returned {nameof(Issue)} for {issue} from cache");
|
2022-06-29 19:59:46 +00:00
|
|
|
|
return result;
|
|
|
|
|
}
|
2021-08-30 13:39:05 +00:00
|
|
|
|
|
2022-06-29 19:59:46 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
2023-04-20 16:22:50 +00:00
|
|
|
|
result = await client.Issue.Get("RPCS3", "rpcs3", issue).WaitAsync(cancellationToken).ConfigureAwait(false);
|
2022-06-29 19:59:46 +00:00
|
|
|
|
UpdateRateLimitStats();
|
2023-04-20 16:22:50 +00:00
|
|
|
|
IssuesCache.Set(issue, result, IssueStatusCacheTime);
|
|
|
|
|
ApiConfig.Log.Debug($"Cached {nameof(Issue)} for {issue} for {IssueStatusCacheTime}");
|
|
|
|
|
return result;
|
2022-06-29 19:59:46 +00:00
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
ApiConfig.Log.Error(e);
|
|
|
|
|
}
|
2023-04-20 16:22:50 +00:00
|
|
|
|
ApiConfig.Log.Debug($"Failed to get {nameof(Issue)}, returning empty result");
|
|
|
|
|
return new();
|
2022-06-29 19:59:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-20 16:22:50 +00:00
|
|
|
|
public Task<IReadOnlyList<PullRequest>?> GetOpenPrsAsync(CancellationToken cancellationToken)
|
|
|
|
|
=> GetPrsWithStatusAsync(new() { State = ItemStateFilter.Open }, cancellationToken);
|
2020-02-29 19:09:15 +00:00
|
|
|
|
|
2023-04-20 16:22:50 +00:00
|
|
|
|
public Task<IReadOnlyList<PullRequest>?> GetClosedPrsAsync(CancellationToken cancellationToken) => GetPrsWithStatusAsync(new()
|
2022-06-29 19:59:46 +00:00
|
|
|
|
{
|
2023-04-20 16:22:50 +00:00
|
|
|
|
State = ItemStateFilter.Closed,
|
|
|
|
|
SortProperty = PullRequestSort.Updated,
|
|
|
|
|
SortDirection = SortDirection.Descending
|
2022-06-29 19:59:46 +00:00
|
|
|
|
}, cancellationToken);
|
|
|
|
|
|
2023-04-20 16:22:50 +00:00
|
|
|
|
private async Task<IReadOnlyList<PullRequest>?> GetPrsWithStatusAsync(PullRequestRequest filter, CancellationToken cancellationToken)
|
2022-06-29 19:59:46 +00:00
|
|
|
|
{
|
2023-04-20 16:22:50 +00:00
|
|
|
|
var statusUri = "https://api.github.com/repos/RPCS3/rpcs3/pulls?state=" + filter;
|
|
|
|
|
if (StatusesCache.TryGetValue(statusUri, out IReadOnlyList<PullRequest>? result))
|
2019-01-08 02:04:23 +00:00
|
|
|
|
{
|
2022-06-29 19:59:46 +00:00
|
|
|
|
ApiConfig.Log.Debug("Returned list of opened PRs from cache");
|
2019-01-08 02:04:23 +00:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-29 19:59:46 +00:00
|
|
|
|
try
|
2019-01-08 00:13:55 +00:00
|
|
|
|
{
|
2023-04-20 16:22:50 +00:00
|
|
|
|
result = await client.PullRequest.GetAllForRepository("RPCS3", "rpcs3", filter).WaitAsync(cancellationToken).ConfigureAwait(false);
|
2022-06-29 19:59:46 +00:00
|
|
|
|
UpdateRateLimitStats();
|
2023-04-20 16:22:50 +00:00
|
|
|
|
StatusesCache.Set(statusUri, result, PrStatusCacheTime);
|
|
|
|
|
foreach (var prInfo in result)
|
|
|
|
|
StatusesCache.Set(prInfo.Number, prInfo, PrStatusCacheTime);
|
|
|
|
|
ApiConfig.Log.Debug($"Cached list of open PRs for {PrStatusCacheTime}");
|
2019-01-10 19:46:47 +00:00
|
|
|
|
}
|
2022-06-29 19:59:46 +00:00
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
ApiConfig.Log.Error(e);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateRateLimitStats()
|
|
|
|
|
{
|
|
|
|
|
var apiInfo = client.GetLastApiInfo();
|
|
|
|
|
if (apiInfo == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
RateLimit = apiInfo.RateLimit.Limit;
|
|
|
|
|
RateLimitRemaining = apiInfo.RateLimit.Remaining;
|
|
|
|
|
RateLimitResetTime = DateTimeOffset.FromUnixTimeSeconds(apiInfo.RateLimit.ResetAsUtcEpochSeconds).UtcDateTime;
|
|
|
|
|
if (RateLimitRemaining < 10)
|
|
|
|
|
ApiConfig.Log.Warn($"Github rate limit is low: {RateLimitRemaining} out of {RateLimit}, will be reset on {RateLimitResetTime:u}");
|
2019-01-07 21:12:48 +00:00
|
|
|
|
}
|
2022-06-29 19:59:46 +00:00
|
|
|
|
}
|