From 80bb178ab3234fb7b883c6489ff3c5e2631e208e Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 26 Nov 2022 17:21:33 +0800 Subject: Add crupest-api. --- docker/crupest-api/CrupestApi/Program.cs | 160 +++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 docker/crupest-api/CrupestApi/Program.cs (limited to 'docker/crupest-api/CrupestApi/Program.cs') diff --git a/docker/crupest-api/CrupestApi/Program.cs b/docker/crupest-api/CrupestApi/Program.cs new file mode 100644 index 0000000..c62bf4d --- /dev/null +++ b/docker/crupest-api/CrupestApi/Program.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Net.Mime; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Console; +using Microsoft.AspNetCore.Http; +using CrupestApi.Config; + +public class TodoItem +{ + public string Status { get; set; } = default!; + public string Title { get; set; } = default!; +} + +internal class Program +{ + private static void Main(string[] args) + { + using var httpClient = new HttpClient(); + + var builder = WebApplication.CreateBuilder(args); + + string configFilePath = Environment.GetEnvironmentVariable("CRUPEST_API_CONFIG_FILE") ?? "/config.json"; + + builder.Configuration.AddJsonFile(configFilePath, optional: false, reloadOnChange: true); + + string? logFilePath = Environment.GetEnvironmentVariable("CRUPEST_API_LOG_FILE"); + if (logFilePath is not null) + { + // TODO: Log to file. + builder.Logging.AddSimpleConsole(logger => + { + logger.ColorBehavior = LoggerColorBehavior.Disabled; + }); + } + + var app = builder.Build(); + + app.MapGet("/api/todos", async ([FromServices] IConfiguration configuration, [FromServices] ILoggerFactory loggerFactory) => + { + var logger = loggerFactory.CreateLogger("CrupestApi.Todos"); + + static string CreateGraphQLQuery(TodoConfiguration todoConfiguration) + { + return $$""" +{ + user(login: "{{todoConfiguration.Username}}") { + projectV2(number: {{todoConfiguration.ProjectNumber}}) { + items(last: {{todoConfiguration.Count ?? 20}}) { + nodes { + __typename + content { + __typename + ... on Issue { + title + closed + } + ... on PullRequest { + title + closed + } + ... on DraftIssue { + title + } + } + } + } + } + } + } +"""; + } + + var todoConfiguration = configuration.GetSection("Todos").Get(); + if (todoConfiguration is null) + { + throw new Exception("Fail to get todos configuration."); + } + + using var requestContent = new StringContent(JsonSerializer.Serialize(new + { + query = CreateGraphQLQuery(todoConfiguration) + })); + 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", todoConfiguration.Token); + + using var response = await httpClient.SendAsync(request); + var responseBody = await response.Content.ReadAsStringAsync(); + logger.LogInformation(response.StatusCode.ToString()); + logger.LogInformation(responseBody); + + + if (response.IsSuccessStatusCode) + { + using var responseJson = JsonSerializer.Deserialize(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(); + + 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" + }); + } + + return Results.Json(result, new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }, statusCode: 200); + } + else + { + const string message = "Fail to get todos from GitHub."; + logger.LogError(message); + + return Results.Json(new + { + message + }, statusCode: StatusCodes.Status503ServiceUnavailable); + } + }); + + app.Run(); + } +} \ No newline at end of file -- cgit v1.2.3