1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using CrupestApi.Config;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace CrupestApi.Services
{
public class TodoItem
{
public string Status { get; set; } = default!;
public string Title { get; set; } = default!;
public bool Closed { get; set; }
public string color { get; set; } = default!;
}
public class TodoService
{
private readonly IOptionsSnapshot<TodoConfiguration> _options;
private readonly ILogger<TodoService> _logger;
public TodoService(IOptionsSnapshot<TodoConfiguration> options, ILogger<TodoService> logger)
{
_options = options;
_logger = logger;
}
private static string CreateGraphQLQuery(TodoConfiguration todoConfiguration)
{
return $$"""
{
user(login: "{{todoConfiguration.Username}}") {
projectV2(number: {{todoConfiguration.ProjectNumber}}) {
items(last: {{todoConfiguration.Count}}) {
nodes {
__typename
content {
__typename
... on Issue {
title
closed
}
... on PullRequest {
title
closed
}
... on DraftIssue {
title
}
}
}
}
}
}
}
""";
}
public async Task<List<TodoItem>> GetTodosAsync()
{
var todoOptions = _options.Value;
if (todoOptions is null)
{
throw new Exception("Fail to get todos configuration.");
}
_logger.LogInformation("Username: {}; ProjectNumber: {}; Count: {}", todoOptions.Username, todoOptions.ProjectNumber, todoOptions.Count);
_logger.LogInformation("Getting todos from GitHub GraphQL API...");
using var httpClient = new HttpClient();
using var requestContent = new StringContent(JsonSerializer.Serialize(new
{
query = CreateGraphQLQuery(todoOptions)
}));
requestContent.Headers.ContentType = new MediaTypeHeaderValue(MediaTypeNames.Application.Json, Encoding.UTF8.WebName);
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.github.com/graphql");
request.Content = requestContent;
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", todoOptions.Token);
request.Headers.TryAddWithoutValidation("User-Agent", todoOptions.Username);
using var response = await httpClient.SendAsync(request);
var responseBody = await response.Content.ReadAsStringAsync();
_logger.LogInformation("GitHub server returned status code: {}", response.StatusCode);
_logger.LogInformation("GitHub server returned body: {}", responseBody);
if (response.IsSuccessStatusCode)
{
using var responseJson = JsonSerializer.Deserialize<JsonDocument>(responseBody);
if (responseJson is null)
{
throw new Exception("Fail to deserialize response body.");
}
var nodes = responseJson.RootElement.GetProperty("data").GetProperty("user").GetProperty("projectV2").GetProperty("items").GetProperty("nodes").EnumerateArray();
var result = new List<TodoItem>();
foreach (var node in nodes)
{
var content = node.GetProperty("content");
var title = content.GetProperty("title").GetString();
if (title is null)
{
throw new Exception("Fail to get title.");
}
JsonElement closedElement;
bool closed;
if (content.TryGetProperty("closed", out closedElement))
{
closed = closedElement.GetBoolean();
}
else
{
closed = false;
}
result.Add(new TodoItem
{
Title = title,
Status = closed ? "Done" : "Todo",
Closed = closed,
color = closed ? "green" : "blue"
});
}
return result;
}
else
{
const string message = "Fail to get todos from GitHub.";
_logger.LogError(message);
throw new Exception(message);
}
}
}
}
|