Skip to content

Commit 4af5eab

Browse files
committed
Cleanups, reduce ReductionPerPercentOverdue to 1
1 parent 68733ad commit 4af5eab

5 files changed

Lines changed: 147 additions & 132 deletions

File tree

Digdir.Bod.RoadmapReport/GithubIssueCache.cs

Lines changed: 114 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -11,40 +11,25 @@ namespace Digdir.Bod.RoadmapReport;
1111
using System.Threading.Tasks;
1212
using Microsoft.Extensions.Configuration;
1313

14-
public class GitHubIssue
15-
{
16-
public int Number { get; init; }
17-
public string Title { get; init; } = null!;
18-
public DateTimeOffset? ClosedAt { get; init; }
19-
public List<GitHubLabel> Labels { get; init; } = new();
20-
public List<GitHubCustomProperty> CustomProperties { get; init; } = new();
21-
}
22-
23-
public class GitHubLabel
24-
{
25-
public string Name { get; init; } = null!;
26-
}
27-
28-
public class GitHubCustomProperty
29-
{
30-
public string Name { get; init; } = null!;
31-
public string Value { get; init; } = null!;
32-
}
33-
3414
public class GitHubIssueCache
3515
{
36-
private const string GraphQlEndpoint = "https://api.github.com/graphql";
37-
private const string ProjectId = "PVT_kwDOAwyZKM4ANYNj"; // Replace with the actual project ID from GitHub
16+
private const string GqlEndpoint = "https://api.github.com/graphql";
3817
private static readonly string CacheDirectory = Path.Combine(Environment.GetEnvironmentVariable("HOME") ?? AppContext.BaseDirectory, "cache");
3918
private static readonly string CacheFilePath = Path.Combine(CacheDirectory, "GitHubIssuesCache.json");
40-
private static readonly TimeSpan CacheDuration = TimeSpan.FromHours(1);
4119

4220
private readonly IConfiguration _configuration;
21+
private readonly IHttpClientFactory _clientFactory;
4322
private static readonly SemaphoreSlim CacheLock = new(1, 1);
23+
private static readonly JsonSerializerOptions JsonSerializerOptions = new()
24+
{
25+
PropertyNameCaseInsensitive = true,
26+
WriteIndented = true
27+
};
4428

45-
public GitHubIssueCache(IConfiguration configuration)
29+
public GitHubIssueCache(IConfiguration configuration, IHttpClientFactory clientFactory)
4630
{
4731
_configuration = configuration;
32+
_clientFactory = clientFactory;
4833
}
4934

5035
public async Task<List<GitHubIssue>> GetIssuesWithCustomPropertiesAsync()
@@ -57,10 +42,10 @@ public async Task<List<GitHubIssue>> GetIssuesWithCustomPropertiesAsync()
5742
if (File.Exists(CacheFilePath))
5843
{
5944
var fileInfo = new FileInfo(CacheFilePath);
60-
if (DateTime.UtcNow - fileInfo.LastWriteTimeUtc < CacheDuration)
45+
if (DateTime.UtcNow - fileInfo.LastWriteTimeUtc < TimeSpan.Parse(_configuration["GitHubCacheDuration"] ?? "01:00:00"))
6146
{
6247
var cachedData = await File.ReadAllTextAsync(CacheFilePath);
63-
return JsonSerializer.Deserialize<List<GitHubIssue>>(cachedData) ?? new List<GitHubIssue>();
48+
return JsonSerializer.Deserialize<List<GitHubIssue>>(cachedData) ?? [];
6449
}
6550
}
6651

@@ -70,72 +55,75 @@ public async Task<List<GitHubIssue>> GetIssuesWithCustomPropertiesAsync()
7055
throw new InvalidOperationException("GitHub token is not configured.");
7156
}
7257

73-
using var httpClient = new HttpClient();
58+
var httpClient = _clientFactory.CreateClient();
7459
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
7560
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("github.com_digdir_roadmap-report");
7661

7762
var issues = new List<GitHubIssue>();
7863
string? cursor = null;
79-
bool hasMore = true;
64+
var hasMore = true;
8065

8166
while (hasMore)
8267
{
8368
var query = new
8469
{
85-
query = @"query ListConnectedIssuesWithLabel($projectId: ID!, $cursor: String) {
86-
node(id: $projectId) {
87-
... on ProjectV2 {
88-
items(first: 100, after: $cursor) {
89-
pageInfo {
90-
hasNextPage
91-
endCursor
92-
}
93-
edges {
94-
node {
95-
content {
96-
... on Issue {
97-
number
98-
title
99-
closedAt
100-
labels(first: 10) {
101-
nodes {
102-
name
103-
}
104-
}
105-
}
70+
query = """
71+
query ListConnectedIssuesWithLabel($projectId: ID!, $cursor: String) {
72+
node(id: $projectId) {
73+
... on ProjectV2 {
74+
items(first: 100, after: $cursor) {
75+
pageInfo {
76+
hasNextPage
77+
endCursor
10678
}
107-
fieldValues(first: 100) {
108-
nodes {
109-
... on ProjectV2ItemFieldTextValue {
110-
text
111-
field {
112-
... on ProjectV2FieldCommon {
113-
name
79+
edges {
80+
node {
81+
content {
82+
... on Issue {
83+
number
84+
title
85+
closedAt
86+
labels(first: 10) {
87+
nodes {
88+
name
89+
}
11490
}
11591
}
11692
}
117-
... on ProjectV2ItemFieldSingleSelectValue {
118-
name
119-
optionId
120-
field {
121-
... on ProjectV2FieldCommon {
122-
name
93+
fieldValues(first: 100) {
94+
nodes {
95+
... on ProjectV2ItemFieldTextValue {
96+
text
97+
field {
98+
... on ProjectV2FieldCommon {
99+
name
100+
}
101+
}
123102
}
124-
}
125-
}
126-
... on ProjectV2ItemFieldNumberValue {
127-
number
128-
field {
129-
... on ProjectV2FieldCommon {
103+
... on ProjectV2ItemFieldSingleSelectValue {
130104
name
105+
optionId
106+
field {
107+
... on ProjectV2FieldCommon {
108+
name
109+
}
110+
}
131111
}
132-
}
133-
}
134-
... on ProjectV2ItemFieldDateValue {
135-
date
136-
field {
137-
... on ProjectV2FieldCommon {
138-
name
112+
... on ProjectV2ItemFieldNumberValue {
113+
number
114+
field {
115+
... on ProjectV2FieldCommon {
116+
name
117+
}
118+
}
119+
}
120+
... on ProjectV2ItemFieldDateValue {
121+
date
122+
field {
123+
... on ProjectV2FieldCommon {
124+
name
125+
}
126+
}
139127
}
140128
}
141129
}
@@ -145,27 +133,23 @@ ... on ProjectV2FieldCommon {
145133
}
146134
}
147135
}
148-
}
149-
}",
150-
variables = new { projectId = ProjectId, cursor }
136+
""",
137+
variables = new { projectId = _configuration["GitHubProjectId"], cursor }
151138
};
152139

153140
var jsonRequest = JsonSerializer.Serialize(query);
154141
var httpContent = new StringContent(jsonRequest);
155142
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
156143

157-
var response = await httpClient.PostAsync(GraphQlEndpoint, httpContent);
144+
var response = await httpClient.PostAsync(GqlEndpoint, httpContent);
158145

159146
if (!response.IsSuccessStatusCode)
160147
{
161148
throw new InvalidOperationException($"Failed to fetch data: {response.StatusCode}");
162149
}
163150

164151
var responseContent = await response.Content.ReadAsStringAsync();
165-
var data = JsonSerializer.Deserialize<GraphQLResponse>(responseContent, new JsonSerializerOptions
166-
{
167-
PropertyNameCaseInsensitive = true
168-
});
152+
var data = JsonSerializer.Deserialize<GqlResponse>(responseContent, JsonSerializerOptions);
169153

170154
var items = data?.Data.Node.Items;
171155
if (items == null) break;
@@ -174,22 +158,19 @@ ... on ProjectV2FieldCommon {
174158
{
175159
var content = edge.Node.Content;
176160
var fieldValues = edge.Node.FieldValues.Nodes;
177-
178-
if (!content.Labels.Nodes.Exists(x => x.Name == "program/nye-altinn")) continue;
179161

180162
var issue = new GitHubIssue
181163
{
182164
Number = content.Number,
183165
Title = content.Title,
184166
ClosedAt = content.ClosedAt,
185167
Labels = content.Labels.Nodes,
186-
CustomProperties = new List<GitHubCustomProperty>()
168+
CustomProperties = []
187169
};
188170

189171
foreach (var fieldValue in fieldValues)
190172
{
191173
if (fieldValue.Field == null) continue;
192-
//if (fieldValue.Date == null) continue;
193174
issue.CustomProperties.Add(new GitHubCustomProperty
194175
{
195176
Name = fieldValue.Field.Name,
@@ -204,7 +185,7 @@ ... on ProjectV2FieldCommon {
204185
cursor = items.PageInfo.EndCursor;
205186
}
206187

207-
var serializedData = JsonSerializer.Serialize(issues, new JsonSerializerOptions { WriteIndented = true });
188+
var serializedData = JsonSerializer.Serialize(issues, JsonSerializerOptions);
208189
await File.WriteAllTextAsync(CacheFilePath, serializedData);
209190

210191
return issues;
@@ -216,49 +197,70 @@ ... on ProjectV2FieldCommon {
216197
}
217198
}
218199

219-
public class GraphQLResponse
200+
public record GitHubIssue
201+
{
202+
public int Number { get; init; }
203+
public string Title { get; init; } = null!;
204+
public DateTimeOffset? ClosedAt { get; init; }
205+
public List<GitHubLabel> Labels { get; init; } = [];
206+
public List<GitHubCustomProperty> CustomProperties { get; init; } = [];
207+
}
208+
209+
public record GitHubLabel
210+
{
211+
public string Name { get; init; } = null!;
212+
}
213+
214+
public record GitHubCustomProperty
215+
{
216+
public string Name { get; init; } = null!;
217+
public string Value { get; init; } = null!;
218+
}
219+
220+
221+
public record GqlResponse
220222
{
221223
[JsonPropertyName("data")]
222-
public GraphQLData Data { get; init; } = null!;
224+
public GqlData Data { get; init; } = null!;
223225
}
224226

225-
public class GraphQLData
227+
public record GqlData
226228
{
227229
[JsonPropertyName("node")]
228-
public GraphQLNode Node { get; init; } = null!;
230+
public GqlNode Node { get; init; } = null!;
229231
}
230232

231-
public class GraphQLNode
233+
public record GqlNode
232234
{
233235
[JsonPropertyName("items")]
234-
public GraphQLItems Items { get; init; } = null!;
236+
public GqlItems Items { get; init; } = null!;
235237
}
236238

237-
public class GraphQLItems
239+
public record GqlItems
238240
{
239241
[JsonPropertyName("pageInfo")]
240-
public GraphQLPageInfo PageInfo { get; init; } = null!;
242+
public GqlPageInfo PageInfo { get; init; } = null!;
241243

242244
[JsonPropertyName("edges")]
243-
public List<GraphQLEdge> Edges { get; init; } = new();
245+
public List<GqlEdge> Edges { get; init; } = [];
244246
}
245247

246-
public class GraphQLEdge
248+
public record GqlEdge
247249
{
248250
[JsonPropertyName("node")]
249-
public GraphQLItemNode Node { get; init; } = null!;
251+
public GqlItemNode Node { get; init; } = null!;
250252
}
251253

252-
public class GraphQLItemNode
254+
public record GqlItemNode
253255
{
254256
[JsonPropertyName("content")]
255-
public GraphQLContent Content { get; init; } = null!;
257+
public GqlContent Content { get; init; } = null!;
256258

257259
[JsonPropertyName("fieldValues")]
258-
public GraphQLFieldValues FieldValues { get; init; } = null!;
260+
public GqlFieldValues FieldValues { get; init; } = null!;
259261
}
260262

261-
public class GraphQLContent
263+
public record GqlContent
262264
{
263265
[JsonPropertyName("number")]
264266
public int Number { get; init; }
@@ -270,25 +272,25 @@ public class GraphQLContent
270272
public DateTimeOffset? ClosedAt { get; init; } = null;
271273

272274
[JsonPropertyName("labels")]
273-
public GraphQLLabels Labels { get; init; } = new();
275+
public GqlLabels Labels { get; init; } = new();
274276
}
275277

276-
public class GraphQLLabels
278+
public record GqlLabels
277279
{
278280
[JsonPropertyName("nodes")]
279-
public List<GitHubLabel> Nodes { get; init; } = new();
281+
public List<GitHubLabel> Nodes { get; init; } = [];
280282
}
281283

282-
public class GraphQLFieldValues
284+
public record GqlFieldValues
283285
{
284286
[JsonPropertyName("nodes")]
285-
public List<GraphQLFieldValue> Nodes { get; init; } = new();
287+
public List<GqlFieldValue> Nodes { get; init; } = [];
286288
}
287289

288-
public class GraphQLFieldValue
290+
public record GqlFieldValue
289291
{
290292
[JsonPropertyName("field")]
291-
public GraphQLField Field { get; init; } = null!;
293+
public GqlField? Field { get; init; } = null!;
292294

293295
[JsonPropertyName("text")]
294296
public string? Text { get; init; }
@@ -303,13 +305,13 @@ public class GraphQLFieldValue
303305
public string? Date { get; init; }
304306
}
305307

306-
public class GraphQLField
308+
public record GqlField
307309
{
308310
[JsonPropertyName("name")]
309311
public string Name { get; init; } = null!;
310312
}
311313

312-
public class GraphQLPageInfo
314+
public record GqlPageInfo
313315
{
314316
[JsonPropertyName("hasNextPage")]
315317
public bool HasNextPage { get; init; }

0 commit comments

Comments
 (0)