aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docker/crupest-api/CrupestApi/.gitignore1
-rw-r--r--docker/crupest-api/CrupestApi/Program.cs136
-rw-r--r--docker/crupest-api/CrupestApi/Properties/launchSettings.json4
-rw-r--r--docker/crupest-api/CrupestApi/Services/FileService.cs10
-rw-r--r--docker/crupest-api/CrupestApi/Services/TodoService.cs146
5 files changed, 179 insertions, 118 deletions
diff --git a/docker/crupest-api/CrupestApi/.gitignore b/docker/crupest-api/CrupestApi/.gitignore
index 7de5508..f3d2489 100644
--- a/docker/crupest-api/CrupestApi/.gitignore
+++ b/docker/crupest-api/CrupestApi/.gitignore
@@ -1,2 +1,3 @@
obj
bin
+dev-config.json
diff --git a/docker/crupest-api/CrupestApi/Program.cs b/docker/crupest-api/CrupestApi/Program.cs
index 7fd6675..9f7e7df 100644
--- a/docker/crupest-api/CrupestApi/Program.cs
+++ b/docker/crupest-api/CrupestApi/Program.cs
@@ -1,152 +1,54 @@
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.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.DependencyInjection;
using CrupestApi.Config;
+using CrupestApi.Services;
namespace CrupestApi
{
- 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!;
- }
-
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";
-
+ string configFilePath = Environment.GetEnvironmentVariable("CRUPEST_API_CONFIG_FILE") ?? "/crupest-api-config.json";
builder.Configuration.AddJsonFile(configFilePath, optional: false, reloadOnChange: true);
- var app = builder.Build();
-
- app.MapGet("/api/todos", async ([FromServices] IConfiguration configuration, [FromServices] ILoggerFactory loggerFactory) =>
+ builder.Services.AddOptions<TodoConfiguration>();
+ builder.Services.Configure<TodoConfiguration>(builder.Configuration.GetSection("Todos"));
+ builder.Services.PostConfigure<TodoConfiguration>(config =>
{
- 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<TodoConfiguration>();
- if (todoConfiguration is null)
+ if (config.Count is null)
{
- throw new Exception("Fail to get todos configuration.");
+ config.Count = 20;
}
+ });
+ builder.Services.TryAddScoped<TodoService>();
- 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);
- request.Headers.TryAddWithoutValidation("User-Agent", "crupest");
-
- using var response = await httpClient.SendAsync(request);
- var responseBody = await response.Content.ReadAsStringAsync();
- logger.LogInformation(response.StatusCode.ToString());
- logger.LogInformation(responseBody);
-
+ var app = builder.Build();
- if (response.IsSuccessStatusCode)
+ app.MapGet("/api/todos", async ([FromServices] TodoService todoService) =>
+ {
+ try
{
- 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 Results.Json(result, new JsonSerializerOptions
+ var todos = await todoService.GetTodosAsync();
+ return Results.Json(todos, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
}, statusCode: 200);
}
- else
+ catch (Exception e)
{
- const string message = "Fail to get todos from GitHub.";
- logger.LogError(message);
-
return Results.Json(new
{
- message
+ e.Message
}, statusCode: StatusCodes.Status503ServiceUnavailable);
}
});
diff --git a/docker/crupest-api/CrupestApi/Properties/launchSettings.json b/docker/crupest-api/CrupestApi/Properties/launchSettings.json
index 01f2a12..a4a5cbf 100644
--- a/docker/crupest-api/CrupestApi/Properties/launchSettings.json
+++ b/docker/crupest-api/CrupestApi/Properties/launchSettings.json
@@ -5,8 +5,10 @@
"commandName": "Project",
"dotnetRunMessages": true,
"applicationUrl": "http://localhost:5188",
+ "workingDirectory": ".",
"environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "CRUPEST_API_CONFIG_FILE": "dev-config.json"
}
}
}
diff --git a/docker/crupest-api/CrupestApi/Services/FileService.cs b/docker/crupest-api/CrupestApi/Services/FileService.cs
new file mode 100644
index 0000000..d862702
--- /dev/null
+++ b/docker/crupest-api/CrupestApi/Services/FileService.cs
@@ -0,0 +1,10 @@
+namespace CrupestApi.Services
+{
+ public class FileService
+ {
+ public FileService()
+ {
+
+ }
+ }
+}
diff --git a/docker/crupest-api/CrupestApi/Services/TodoService.cs b/docker/crupest-api/CrupestApi/Services/TodoService.cs
new file mode 100644
index 0000000..eb4fef6
--- /dev/null
+++ b/docker/crupest-api/CrupestApi/Services/TodoService.cs
@@ -0,0 +1,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);
+ }
+ }
+ }
+} \ No newline at end of file