Skip to content

Commit f9fbf71

Browse files
Merge pull request #17 from nullinside-development-group/feat/done-column
feat: Automatically move closed issues to done in backlog
2 parents 969fdf8 + d2eb88e commit f9fbf71

12 files changed

+305
-16
lines changed

src/Nullinside.Cicd.GitHub/.editorconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ root = true
44
[*]
55
indent_style = space
66

7+
# ReSharper properties
8+
resharper_csharp_wrap_lines = false
9+
10+
# We don't want to code comment auto-generated files
11+
[GraphQl/Model/*.cs]
12+
dotnet_diagnostic.CS1591.severity = none
13+
714
# Xml files
815
[*.xml]
916
indent_size = 2

src/Nullinside.Cicd.GitHub/Constants.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@ public static class Constants {
99
/// </summary>
1010
public const string GITHUB_ORG = "nullinside-development-group";
1111

12+
/// <summary>
13+
/// The url to the graphql endpoint to post against.
14+
/// </summary>
15+
public const string GITHUB_GRAPHQL_URL = "https://api.github.com/graphql";
16+
17+
/// <summary>
18+
/// The name of the project that be sent in user agent headers.
19+
/// </summary>
20+
public const string PROJECT_NAME = "nullinside-cicd-github";
21+
1222
/// <summary>
1323
/// The github project's unique identifier on github.
1424
/// </summary>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using System.Net.Http.Headers;
2+
using System.Text;
3+
4+
using Newtonsoft.Json;
5+
6+
using Nullinside.Cicd.GitHub.GraphQl.Model;
7+
8+
namespace Nullinside.Cicd.GitHub.GraphQl;
9+
10+
/// <summary>
11+
/// The contract for executing a simple graphql query.
12+
/// </summary>
13+
/// <typeparam name="T">The JSON response POCO to the query, <see cref="GraphQlGenericMutationResponse" /> for no response.</typeparam>
14+
public abstract class AbstractGitHubGraphQlQuery<T> {
15+
/// <summary>
16+
/// The query, typically coded into the object.
17+
/// </summary>
18+
public abstract string Query { get; }
19+
20+
/// <summary>
21+
/// The query variables, typically assembled in the constructor.
22+
/// </summary>
23+
public object? QueryVariables { get; set; }
24+
25+
/// <summary>
26+
/// Executes the request.
27+
/// </summary>
28+
/// <returns>The response object.</returns>
29+
public virtual async Task<T?> SendAsync() {
30+
using var httpClient = new HttpClient {
31+
BaseAddress = new Uri(Constants.GITHUB_GRAPHQL_URL),
32+
DefaultRequestHeaders = {
33+
UserAgent = { new ProductInfoHeaderValue(Constants.PROJECT_NAME, "0.0.0") },
34+
Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("GITHUB_PAT"))
35+
}
36+
};
37+
38+
var queryObject = new {
39+
query = Query,
40+
variables = QueryVariables
41+
};
42+
43+
var request = new HttpRequestMessage {
44+
Method = HttpMethod.Post,
45+
Content = new StringContent(JsonConvert.SerializeObject(queryObject), Encoding.UTF8, "application/json")
46+
};
47+
48+
using HttpResponseMessage response = await httpClient.SendAsync(request);
49+
response.EnsureSuccessStatusCode();
50+
string responseString = await response.Content.ReadAsStringAsync();
51+
52+
return JsonConvert.DeserializeObject<T>(responseString);
53+
}
54+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by a tool.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
using System.Globalization;
11+
12+
using Newtonsoft.Json;
13+
using Newtonsoft.Json.Converters;
14+
15+
namespace Nullinside.Cicd.GitHub.GraphQl.Model;
16+
17+
18+
public partial class GraphQlGenericMutationResponse
19+
{
20+
[JsonProperty("data")]
21+
public object Data { get; set; }
22+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by a tool.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
using Newtonsoft.Json;
11+
12+
namespace Nullinside.Cicd.GitHub.Model;
13+
14+
public class GraphQlProjectIssueResponse {
15+
[JsonProperty("data")] public Data Data { get; set; }
16+
}
17+
18+
public class Data {
19+
[JsonProperty("repository")] public Repository Repository { get; set; }
20+
}
21+
22+
public class Repository {
23+
[JsonProperty("id")] public string Id { get; set; }
24+
25+
[JsonProperty("issues")] public Issues Issues { get; set; }
26+
}
27+
28+
public class Issues {
29+
[JsonProperty("nodes")] public List<IssuesNode> Nodes { get; set; }
30+
31+
[JsonProperty("pageInfo")] public PageInfo PageInfo { get; set; }
32+
}
33+
34+
public class IssuesNode {
35+
[JsonProperty("number")] public long Number { get; set; }
36+
37+
[JsonProperty("closed")] public bool Closed { get; set; }
38+
39+
[JsonProperty("id")] public string Id { get; set; }
40+
41+
[JsonProperty("projectItems")] public ProjectItems ProjectItems { get; set; }
42+
}
43+
44+
public class ProjectItems {
45+
[JsonProperty("nodes")] public List<ProjectItemsNode> Nodes { get; set; }
46+
47+
[JsonProperty("pageInfo")] public PageInfo PageInfo { get; set; }
48+
}
49+
50+
public class ProjectItemsNode {
51+
[JsonProperty("fieldValueByName")] public FieldValueByName FieldValueByName { get; set; }
52+
53+
[JsonProperty("id")] public string Id { get; set; }
54+
}
55+
56+
public class FieldValueByName {
57+
[JsonProperty("field")] public Field Field { get; set; }
58+
59+
[JsonProperty("name")] public string Name { get; set; }
60+
}
61+
62+
public class Field {
63+
[JsonProperty("id")] public string Id { get; set; }
64+
}
65+
66+
public class PageInfo {
67+
[JsonProperty("hasNextPage")] public bool HasNextPage { get; set; }
68+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using Nullinside.Cicd.GitHub.GraphQl.Model;
2+
3+
namespace Nullinside.Cicd.GitHub.GraphQl;
4+
5+
/// <summary>
6+
/// Changes the status of the issue on the GitHub project board. (ex: Backlog, Ready, In progress, In review, Done)
7+
/// </summary>
8+
public class MutateIssueProjectStatuses : AbstractGitHubGraphQlQuery<GraphQlGenericMutationResponse> {
9+
/// <summary>
10+
/// Initializes a new instance of the <see cref="MutateIssueProjectStatuses" /> class.
11+
/// </summary>
12+
/// <param name="projectId">The project id</param>
13+
/// <param name="projectIssueId">The status field's id (ex: PVTI_lADOCZOBm84AdCw7zgQfohA NOT I_kwDOLcGb986MVe7p)</param>
14+
/// <param name="statusFieldId">The status field's id (ex: PVTSSF_lADOCZOBm84AdCw7zgSzu_A)</param>
15+
/// <param name="selectionId">The unique identifier of the single selection option to change the status to.</param>
16+
public MutateIssueProjectStatuses(string projectId, string projectIssueId, string statusFieldId, string selectionId) {
17+
QueryVariables = new { project = projectId, item = projectIssueId, field = statusFieldId, selectionId };
18+
}
19+
20+
/// <inheritdoc />
21+
public override string Query => @"mutation($project: ID!, $item: ID!, $field: ID!, $selectionId: String!) {
22+
updateProjectV2ItemFieldValue(
23+
input: {projectId: $project, itemId: $item, fieldId: $field, value: {singleSelectOptionId: $selectionId}}
24+
) {
25+
clientMutationId
26+
}
27+
}";
28+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using Nullinside.Cicd.GitHub.Model;
2+
3+
namespace Nullinside.Cicd.GitHub.GraphQl;
4+
5+
/// <summary>
6+
/// Queries the issues in a GitHub code base.
7+
/// </summary>
8+
public class QueryIssueProjectStatuses : AbstractGitHubGraphQlQuery<GraphQlProjectIssueResponse> {
9+
/// <summary>
10+
/// Initializes a new instance of the <see cref="QueryIssueProjectStatuses" /> class.
11+
/// </summary>
12+
/// <param name="repoOwner">The owner of the repo on github.</param>
13+
/// <param name="repoName">The name of the repo on github.</param>
14+
public QueryIssueProjectStatuses(string repoOwner, string repoName) {
15+
QueryVariables = new { owner = repoOwner, name = repoName };
16+
}
17+
18+
/// <inheritdoc />
19+
public override string Query => @"query ($name: String!, $owner: String!) {
20+
repository(name: $name, owner: $owner) {
21+
id
22+
issues(first: 100) {
23+
pageInfo {
24+
hasNextPage
25+
endCursor
26+
}
27+
nodes {
28+
id
29+
number
30+
closed
31+
projectItems(first: 100) {
32+
pageInfo {
33+
hasNextPage
34+
endCursor
35+
}
36+
nodes {
37+
id
38+
fieldValueByName(name: ""Status"") {
39+
... on ProjectV2ItemFieldSingleSelectValue {
40+
name
41+
field {
42+
... on ProjectV2SingleSelectField {
43+
id
44+
}
45+
}
46+
}
47+
}
48+
}
49+
}
50+
}
51+
}
52+
}
53+
}";
54+
}

src/Nullinside.Cicd.GitHub/Nullinside.Cicd.GitHub.csproj

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,20 @@
1919
</PropertyGroup>
2020

2121
<ItemGroup>
22-
<PackageReference Include="log4net" Version="3.0.4" />
23-
<PackageReference Include="log4net.Ext.Json" Version="3.0.3" />
24-
<PackageReference Include="Octokit" Version="14.0.0" />
22+
<PackageReference Include="log4net" Version="3.0.4"/>
23+
<PackageReference Include="log4net.Ext.Json" Version="3.0.3"/>
24+
<PackageReference Include="Octokit" Version="14.0.0"/>
2525
</ItemGroup>
2626

2727
<ItemGroup>
28-
<ProjectReference Include="..\octokit.graphql.net\src\Octokit.GraphQL\Octokit.GraphQL.csproj" />
28+
<ProjectReference Include="..\octokit.graphql.net\src\Octokit.GraphQL\Octokit.GraphQL.csproj"/>
2929
</ItemGroup>
3030

3131
<ItemGroup>
32-
<None Remove="log4net.config" />
33-
<Content Include="log4net.config">
34-
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
35-
</Content>
32+
<None Remove="log4net.config"/>
33+
<Content Include="log4net.config">
34+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
35+
</Content>
3636
</ItemGroup>
3737

3838
</Project>

src/Nullinside.Cicd.GitHub/Program.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
using log4net;
44
using log4net.Config;
5-
using log4net.Core;
65

76
using Nullinside.Cicd.GitHub;
87
using Nullinside.Cicd.GitHub.Rule;
@@ -16,7 +15,7 @@
1615
using Query = Octokit.GraphQL.Query;
1716

1817
XmlConfigurator.Configure(new FileInfo("log4net.config"));
19-
var log = LogManager.GetLogger(typeof(Program));
18+
ILog log = LogManager.GetLogger(typeof(Program));
2019

2120
IRepoRule?[] rules = AppDomain.CurrentDomain.GetAssemblies()
2221
.SelectMany(a => a.GetTypes())

src/Nullinside.Cicd.GitHub/Rule/AssociateIssuesWithProject.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ namespace Nullinside.Cicd.GitHub.Rule;
1515
/// </summary>
1616
public class AssociateIssuesWithProject : IRepoRule {
1717
/// <summary>
18-
/// The logger
18+
/// The logger
1919
/// </summary>
20-
private ILog _log = LogManager.GetLogger(typeof(AssociateIssuesWithProject));
21-
20+
private readonly ILog _log = LogManager.GetLogger(typeof(AssociateIssuesWithProject));
21+
2222
/// <inheritdoc />
2323
public async Task Handle(GitHubClient client, Connection graphQl, ID projectId, Repository repo) {
2424
if (!repo.HasIssues) {

0 commit comments

Comments
 (0)