tgstation-server 6.1.4
The /tg/station 13 server suite
Loading...
Searching...
No Matches
GitHubClientFactory.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Linq;
4
5using Microsoft.Extensions.Logging;
6using Microsoft.Extensions.Options;
7using Octokit;
8
11
13{
16 {
21 const uint ClientCacheDays = 7;
22
26 const string DefaultCacheKey = "~!@TGS_DEFAULT_GITHUB_CLIENT_CACHE_KEY@!~";
27
32
36 readonly ILogger<GitHubClientFactory> logger;
37
42
46 readonly Dictionary<string, (GitHubClient Client, DateTimeOffset LastUsed)> clientCache;
47
56 ILogger<GitHubClientFactory> logger,
57 IOptions<GeneralConfiguration> generalConfigurationOptions)
58 {
59 this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
60 this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
61 generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
62
63 clientCache = new Dictionary<string, (GitHubClient, DateTimeOffset)>();
64 }
65
68
70 public IGitHubClient CreateClient(string accessToken)
72 accessToken ?? throw new ArgumentNullException(nameof(accessToken)));
73
79 GitHubClient GetOrCreateClient(string? accessToken)
80 {
81 GitHubClient client;
82 bool cacheHit;
83 DateTimeOffset? lastUsed;
84 lock (clientCache)
85 {
86 string cacheKey;
87 if (String.IsNullOrWhiteSpace(accessToken))
88 {
89 accessToken = null;
90 cacheKey = DefaultCacheKey;
91 }
92 else
93 cacheKey = accessToken;
94
95 cacheHit = clientCache.TryGetValue(cacheKey, out var tuple);
96
97 var now = DateTimeOffset.UtcNow;
98 if (!cacheHit)
99 {
101 client = new GitHubClient(
102 new ProductHeaderValue(
103 product.Name,
104 product.Version));
105
106 if (accessToken != null)
107 client.Credentials = new Credentials(accessToken);
108
109 clientCache.Add(cacheKey, (Client: client, LastUsed: now));
110 lastUsed = null;
111 }
112 else
113 {
114 logger.LogTrace("Cache hit for GitHubClient");
115 client = tuple.Client;
116 lastUsed = tuple.LastUsed;
117 tuple.LastUsed = now;
118 }
119
120 // Prune the cache
121 var purgeCount = 0U;
122 var purgeAfter = now.AddDays(-ClientCacheDays);
123 foreach (var key in clientCache.Keys.ToList())
124 {
125 if (key == cacheKey)
126 continue; // save the hash lookup
127
128 tuple = clientCache[key];
129 if (tuple.LastUsed <= purgeAfter)
130 {
131 clientCache.Remove(key);
132 ++purgeCount;
133 }
134 }
135
136 if (purgeCount > 0)
137 logger.LogDebug(
138 "Pruned {count} expired GitHub client(s) from cache that haven't been used in {purgeAfterHours} days.",
139 purgeCount,
141 }
142
143 var rateLimitInfo = client.GetLastApiInfo()?.RateLimit;
144 if (rateLimitInfo != null)
145 if (rateLimitInfo.Remaining == 0)
146 logger.LogWarning(
147 "Requested GitHub client has no requests remaining! Limit resets at {resetTime}",
148 rateLimitInfo.Reset.ToString("o"));
149 else if (rateLimitInfo.Remaining < 25) // good luck hitting these lines on codecov
150 logger.LogWarning(
151 "Requested GitHub client has only {remainingRequests} requests remaining after the usage at {lastUse}! Limit resets at {resetTime}",
152 rateLimitInfo.Remaining,
153 lastUsed,
154 rateLimitInfo.Reset.ToString("o"));
155 else
156 logger.LogDebug(
157 "Requested GitHub client has {remainingRequests} requests remaining after the usage at {lastUse}. Limit resets at {resetTime}",
158 rateLimitInfo.Remaining,
159 lastUsed,
160 rateLimitInfo.Reset.ToString("o"));
161
162 return client;
163 }
164 }
165}
string? GitHubAccessToken
A GitHub personal access token to use for bypassing rate limits on requests. Requires no scopes.
IGitHubClient CreateClient()
Create a IGitHubClient client. Low rate limit unless the server's GitHubAccessToken is set to bypass ...
const string DefaultCacheKey
The clientCache KeyValuePair<TKey, TValue>.Key used in place of null when accessing a configuration-b...
readonly Dictionary< string,(GitHubClient Client, DateTimeOffset LastUsed)> clientCache
Cache of created GitHubClients and last used times, keyed by access token.
GitHubClientFactory(IAssemblyInformationProvider assemblyInformationProvider, ILogger< GitHubClientFactory > logger, IOptions< GeneralConfiguration > generalConfigurationOptions)
Initializes a new instance of the GitHubClientFactory class.
readonly IAssemblyInformationProvider assemblyInformationProvider
The IAssemblyInformationProvider for the GitHubClientFactory.
IGitHubClient CreateClient(string accessToken)
Create a client with authentication using a personal access token. A new IGitHubClient.
readonly ILogger< GitHubClientFactory > logger
The ILogger for the GitHubClientFactory.
readonly GeneralConfiguration generalConfiguration
The GeneralConfiguration for the GitHubClientFactory.
GitHubClient GetOrCreateClient(string? accessToken)
Retrieve a GitHubClient from the clientCache or add a new one based on a given accessToken .
const uint ClientCacheDays
Limit to the amount of days a GitHubClient can live in the clientCache.
ProductInfoHeaderValue ProductInfoHeaderValue
The ProductInfoHeaderValue for the assembly.