diff options
| author | crupest <crupest@outlook.com> | 2022-11-30 18:34:49 +0800 | 
|---|---|---|
| committer | crupest <crupest@outlook.com> | 2022-11-30 18:36:05 +0800 | 
| commit | 3c918a6096e77190db83c998e777074493515326 (patch) | |
| tree | f5e3b9c2d66e0485fe36fb9812c2985edeaeec3b /docker | |
| parent | c82166324124c7941ac58455912d92184c7cb370 (diff) | |
| download | crupest-3c918a6096e77190db83c998e777074493515326.tar.gz crupest-3c918a6096e77190db83c998e777074493515326.tar.bz2 crupest-3c918a6096e77190db83c998e777074493515326.zip | |
Prepare for file api.
Diffstat (limited to 'docker')
| -rw-r--r-- | docker/crupest-api/CrupestApi/.gitignore | 1 | ||||
| -rw-r--r-- | docker/crupest-api/CrupestApi/Program.cs | 136 | ||||
| -rw-r--r-- | docker/crupest-api/CrupestApi/Properties/launchSettings.json | 4 | ||||
| -rw-r--r-- | docker/crupest-api/CrupestApi/Services/FileService.cs | 10 | ||||
| -rw-r--r-- | docker/crupest-api/CrupestApi/Services/TodoService.cs | 146 | 
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 | 
