Skip to content

Instantly share code, notes, and snippets.

@davepcallan
Last active March 13, 2024 08:09
Show Gist options
  • Save davepcallan/0846a7300249ffd1ee110143a8f832ac to your computer and use it in GitHub Desktop.
Save davepcallan/0846a7300249ffd1ee110143a8f832ac to your computer and use it in GitHub Desktop.
Get Merged PRs from dotnet/runtime using GitHub GraphQL API
using Microsoft.AspNetCore.Mvc;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
namespace Samples;
public class GetMergedPRsController(IConfiguration configuration) : Controller
{
[HttpGet]
[Route("")]
public async Task<IActionResult> Index()
{
// hardcoded to dotnet/runtime repo, pass in what you want.
// play with graphQL queries at https://docs.github.com/en/graphql/overview/explorer
var query = @"
{
repository(owner: ""dotnet"", name: ""runtime"") {
pullRequests(states: MERGED, first: 100, orderBy: {field: CREATED_AT, direction: DESC}) {
edges {
node {
title
url
number
mergedAt
reviewDecision
author {
login
}
}
}
}
}
}";
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", configuration["GitHub:PersonalAccessToken"]);
httpClient.DefaultRequestHeaders.Add("User-Agent", "YourAppName");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var requestData = new { query };
var response = await httpClient.PostAsync("https://api.github.com/graphql",
new StringContent(JsonSerializer.Serialize(requestData), Encoding.UTF8, "application/json"));
if (!response.IsSuccessStatusCode) { // K&R for display only
return BadRequest("GitHub API request failed.");
}
var responseString = await response.Content.ReadAsStringAsync();
var gitHubResponse = JsonSerializer.Deserialize<GitHubGraphQLResponse>(responseString,
new JsonSerializerOptions {
PropertyNameCaseInsensitive = true
});
return View("~/Views/GetMergedPullRequests.cshtml", gitHubResponse);
}
}
public record Author(string Login);
public record PullRequestNode(
string Title, string Url, int Number, DateTime MergedAt, string ReviewDecision, Author Author);
public record PullRequestEdge(PullRequestNode Node);
public record PullRequestsData(List<PullRequestEdge> Edges);
public record Repository(PullRequestsData PullRequests);
public record GitHubGraphQLResponseData(Repository Repository);
public record GitHubGraphQLResponse(GitHubGraphQLResponseData Data);
@model Samples.GitHubGraphQLResponse
@{
ViewData["Title"] = "dotnet/runtime Merged Pull Requests";
Layout = null; // This removes the layout page, making the page standalone
var sortedEdges = Model?.Data?.Repository.PullRequests.Edges
.OrderByDescending(edge => edge.Node.MergedAt)
.ToList();
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewData["Title"]</title>
<!-- Bootstrap CSS v5 from the official CDN -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
<!-- Font Awesome CDN for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<!-- Custom Styles -->
<style>
:root {
--dotnet-purple: #512BD4;
}
.bg-dotnet {
background-color: var(--dotnet-purple) !important;
}
.btn-dotnet {
border-color: var(--dotnet-purple);
color: var(--dotnet-purple);
}
.btn-dotnet:hover {
background-color: var(--dotnet-purple);
color: white;
}
.text-dotnet {
color: var(--dotnet-purple) !important;
}
.table-hover tbody tr:hover {
background-color: #f3f0ff;
}
.nowrap {
white-space: nowrap;
}
</style>
</head>
<body>
<div class="container mt-5">
<h1 class="text-center mb-4 text-dotnet">@ViewData["Title"]</h1>
@if (sortedEdges?.Any() == true)
{
<table class="table table-bordered table-hover">
<thead class="bg-dotnet text-white">
<tr>
<th scope="col">Title</th>
<th scope="col">Number</th>
<th scope="col" class="nowrap">Merged At <i class="fas fa-sort-down"></i></th>
<th scope="col"><i class="fas fa-user"></i> Author</th>
<th scope="col">Decision</th>
</tr>
</thead>
<tbody>
@foreach (var edge in sortedEdges)
{
<tr>
<td>
<a href="@edge.Node.Url" target="_blank">@edge.Node.Title</a>
</td>
<td>@edge.Node.Number</td>
<td class="nowrap">@edge.Node.MergedAt.ToString("g")</td>
<td>
<a href="https://github.com/@edge.Node.Author.Login" target="_blank">
@edge.Node.Author.Login
</a>
</td>
<td>@edge.Node.ReviewDecision</td>
</tr>
}
</tbody>
</table>
}
else
{
<div class="alert alert-info" role="alert">
No merged pull requests found.
</div>
}
</div>
<!-- Bootstrap Bundle with Popper for Bootstrap 5 -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
@davepcallan
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment