Skip to content

Commit 5e26d18

Browse files
committed
Add ProjectsV2Item payload
Frankly, this thing is underdocumented and IMHO not very usable but I suppose it will improve over time. Someone asked for it in my Quarkus GitHub App project so I thought I might as well implement it.
1 parent 08785e5 commit 5e26d18

File tree

9 files changed

+640
-0
lines changed

9 files changed

+640
-0
lines changed

src/main/java/org/kohsuke/github/GHEventPayload.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1630,4 +1630,26 @@ public Date getStarredAt() {
16301630
return GitHubClient.parseDate(starredAt);
16311631
}
16321632
}
1633+
1634+
/**
1635+
* A project v2 item was archived, converted, created, edited, restored, deleted, or reordered.
1636+
*
1637+
* @see <a href=
1638+
* "https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#projects_v2_item">star
1639+
* event</a>
1640+
*/
1641+
public static class ProjectsV2Item extends GHEventPayload {
1642+
1643+
private GHProjectsV2Item projectsV2Item;
1644+
private GHProjectsV2ItemChanges changes;
1645+
1646+
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Expected")
1647+
public GHProjectsV2Item getProjectsV2Item() {
1648+
return projectsV2Item;
1649+
}
1650+
1651+
public GHProjectsV2ItemChanges getChanges() {
1652+
return changes;
1653+
}
1654+
}
16331655
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package org.kohsuke.github;
2+
3+
import org.kohsuke.github.internal.EnumUtils;
4+
5+
import java.io.IOException;
6+
import java.net.URL;
7+
import java.util.Date;
8+
9+
/**
10+
* A Projects V2 item in the organization.
11+
* <p>
12+
* Projects V2 are not attached to a repository but to an organization, even if it is possible to create shortcuts at
13+
* the repository level.
14+
* <p>
15+
* This event exposes the GraphQL object (more or less - the ids are handled differently for instance) directly. The new
16+
* Projects V2 API is only available through GraphQL so for now you cannot execute any actions on this object.
17+
*
18+
* @author Guillaume Smet
19+
* @see <a href=
20+
* "https://docs.github.com/en/issues/planning-and-tracking-with-projects/automating-your-project/using-the-api-to-manage-projects">The
21+
* GraphQL API for Projects V2</a>
22+
*/
23+
public class GHProjectsV2Item extends GHObject {
24+
25+
private String projectNodeId;
26+
private String contentNodeId;
27+
private String contentType;
28+
29+
private GHUser creator;
30+
private String archivedAt;
31+
32+
public String getProjectNodeId() {
33+
return projectNodeId;
34+
}
35+
36+
public String getContentNodeId() {
37+
return contentNodeId;
38+
}
39+
40+
public ContentType getContentType() {
41+
return EnumUtils.getEnumOrDefault(ContentType.class, contentType, ContentType.UNKNOWN);
42+
}
43+
44+
public GHUser getCreator() throws IOException {
45+
return root().intern(creator);
46+
}
47+
48+
public Date getArchivedAt() {
49+
return GitHubClient.parseDate(archivedAt);
50+
}
51+
52+
public URL getHtmlUrl() {
53+
throw new IllegalStateException(getClass().getName() + " does not offer a HTML URL.");
54+
}
55+
56+
public enum ContentType {
57+
ISSUE, DRAFTISSUE, PULLREQUEST, UNKNOWN;
58+
}
59+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package org.kohsuke.github;
2+
3+
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
4+
import org.kohsuke.github.internal.EnumUtils;
5+
6+
import java.util.Date;
7+
8+
/**
9+
* An object to track changes in projects_v2_item payloads.
10+
* <p>
11+
* Note that this is best effort only as nothing is documented in the GitHub documentation.
12+
*/
13+
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_FIELD" }, justification = "JSON API")
14+
public class GHProjectsV2ItemChanges {
15+
16+
private FieldValue fieldValue;
17+
18+
private FromToDate archivedAt;
19+
20+
private FromTo previousProjectsV2ItemNodeId;
21+
22+
public FieldValue getFieldValue() {
23+
return fieldValue;
24+
}
25+
26+
public FromToDate getArchivedAt() {
27+
return archivedAt;
28+
}
29+
30+
public FromTo getPreviousProjectsV2ItemNodeId() {
31+
return previousProjectsV2ItemNodeId;
32+
}
33+
34+
public static class FieldValue {
35+
36+
private String fieldNodeId;
37+
private String fieldType;
38+
39+
public String getFieldNodeId() {
40+
return fieldNodeId;
41+
}
42+
43+
public FieldType getFieldType() {
44+
return EnumUtils.getEnumOrDefault(FieldType.class, fieldType, FieldType.UNKNOWN);
45+
}
46+
}
47+
48+
public static class FromTo {
49+
50+
private String from;
51+
private String to;
52+
53+
public String getFrom() {
54+
return from;
55+
}
56+
57+
public String getTo() {
58+
return to;
59+
}
60+
}
61+
62+
public static class FromToDate {
63+
64+
private String from;
65+
private String to;
66+
67+
public Date getFrom() {
68+
return GitHubClient.parseDate(from);
69+
}
70+
71+
public Date getTo() {
72+
return GitHubClient.parseDate(to);
73+
}
74+
}
75+
76+
public enum FieldType {
77+
78+
TEXT, NUMBER, DATE, SINGLE_SELECT, ITERATION, UNKNOWN;
79+
}
80+
}

src/test/java/org/kohsuke/github/GHEventPayloadTest.java

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import org.junit.Test;
55
import org.kohsuke.github.GHCheckRun.Conclusion;
66
import org.kohsuke.github.GHCheckRun.Status;
7+
import org.kohsuke.github.GHProjectsV2Item.ContentType;
8+
import org.kohsuke.github.GHProjectsV2ItemChanges.FieldType;
79

810
import java.io.IOException;
911
import java.text.SimpleDateFormat;
@@ -1152,4 +1154,102 @@ public void starred() throws Exception {
11521154
assertThat(starPayload.getSender().getLogin(), is("gsmet"));
11531155
assertThat(starPayload.getStarredAt().getTime(), is(1654017876000L));
11541156
}
1157+
1158+
@Test
1159+
public void projectsv2item_created() throws Exception {
1160+
final GHEventPayload.ProjectsV2Item projectsV2ItemPayload = GitHub.offline()
1161+
.parseEventPayload(payload.asReader(), GHEventPayload.ProjectsV2Item.class);
1162+
1163+
assertThat(projectsV2ItemPayload.getAction(), is("created"));
1164+
1165+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getId(), is(8083254L));
1166+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getNodeId(), is("PVTI_lADOBNft-M4AEjBWzgB7VzY"));
1167+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getProjectNodeId(), is("PVT_kwDOBNft-M4AEjBW"));
1168+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getContentNodeId(), is("I_kwDOFOkjw85Ozz26"));
1169+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getContentType(), is(ContentType.ISSUE));
1170+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getCreator().getLogin(), is("gsmet"));
1171+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getCreator().getNodeId(), is("MDQ6VXNlcjEyNzk3NDk="));
1172+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getCreatedAt().getTime(), is(1659532028000L));
1173+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getUpdatedAt().getTime(), is(1659532028000L));
1174+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getArchivedAt(), is(nullValue()));
1175+
1176+
assertThat(projectsV2ItemPayload.getOrganization().getLogin(), is("gsmet-bot-playground"));
1177+
assertThat(projectsV2ItemPayload.getOrganization().getId(), is(81260024L));
1178+
assertThat(projectsV2ItemPayload.getOrganization().getNodeId(), is("MDEyOk9yZ2FuaXphdGlvbjgxMjYwMDI0"));
1179+
1180+
assertThat(projectsV2ItemPayload.getSender().getLogin(), is("gsmet"));
1181+
assertThat(projectsV2ItemPayload.getSender().getId(), is(1279749L));
1182+
assertThat(projectsV2ItemPayload.getSender().getNodeId(), is("MDQ6VXNlcjEyNzk3NDk="));
1183+
1184+
assertThat(projectsV2ItemPayload.getInstallation().getId(), is(16779846L));
1185+
assertThat(projectsV2ItemPayload.getInstallation().getNodeId(),
1186+
is("MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMTY3Nzk4NDY="));
1187+
}
1188+
1189+
@Test
1190+
public void projectsv2item_edited() throws Exception {
1191+
final GHEventPayload.ProjectsV2Item projectsV2ItemPayload = GitHub.offline()
1192+
.parseEventPayload(payload.asReader(), GHEventPayload.ProjectsV2Item.class);
1193+
1194+
assertThat(projectsV2ItemPayload.getAction(), is("edited"));
1195+
1196+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getId(), is(8083254L));
1197+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getCreatedAt().getTime(), is(1659532028000L));
1198+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getUpdatedAt().getTime(), is(1659532033000L));
1199+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getArchivedAt(), is(nullValue()));
1200+
1201+
assertThat(projectsV2ItemPayload.getChanges().getFieldValue().getFieldNodeId(),
1202+
is("PVTF_lADOBNft-M4AEjBWzgCnp5Q"));
1203+
assertThat(projectsV2ItemPayload.getChanges().getFieldValue().getFieldType(), is(FieldType.SINGLE_SELECT));
1204+
}
1205+
1206+
@Test
1207+
public void projectsv2item_archived() throws Exception {
1208+
final GHEventPayload.ProjectsV2Item projectsV2ItemPayload = GitHub.offline()
1209+
.parseEventPayload(payload.asReader(), GHEventPayload.ProjectsV2Item.class);
1210+
1211+
assertThat(projectsV2ItemPayload.getAction(), is("archived"));
1212+
1213+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getId(), is(8083794L));
1214+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getCreatedAt().getTime(), is(1659532431000L));
1215+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getUpdatedAt().getTime(), is(1660086629000L));
1216+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getArchivedAt().getTime(), is(1660086629000L));
1217+
1218+
assertThat(projectsV2ItemPayload.getChanges().getArchivedAt().getFrom(), is(nullValue()));
1219+
assertThat(projectsV2ItemPayload.getChanges().getArchivedAt().getTo().getTime(), is(1660086629000L));
1220+
}
1221+
1222+
@Test
1223+
public void projectsv2item_restored() throws Exception {
1224+
final GHEventPayload.ProjectsV2Item projectsV2ItemPayload = GitHub.offline()
1225+
.parseEventPayload(payload.asReader(), GHEventPayload.ProjectsV2Item.class);
1226+
1227+
assertThat(projectsV2ItemPayload.getAction(), is("restored"));
1228+
1229+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getId(), is(8083254L));
1230+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getCreatedAt().getTime(), is(1659532028000L));
1231+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getUpdatedAt().getTime(), is(1659532419000L));
1232+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getArchivedAt(), is(nullValue()));
1233+
1234+
assertThat(projectsV2ItemPayload.getChanges().getArchivedAt().getFrom().getTime(), is(1659532142000L));
1235+
assertThat(projectsV2ItemPayload.getChanges().getArchivedAt().getTo(), is(nullValue()));
1236+
}
1237+
1238+
@Test
1239+
public void projectsv2item_reordered() throws Exception {
1240+
final GHEventPayload.ProjectsV2Item projectsV2ItemPayload = GitHub.offline()
1241+
.parseEventPayload(payload.asReader(), GHEventPayload.ProjectsV2Item.class);
1242+
1243+
assertThat(projectsV2ItemPayload.getAction(), is("reordered"));
1244+
1245+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getId(), is(8083794L));
1246+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getCreatedAt().getTime(), is(1659532431000L));
1247+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getUpdatedAt().getTime(), is(1659532439000L));
1248+
assertThat(projectsV2ItemPayload.getProjectsV2Item().getArchivedAt(), is(nullValue()));
1249+
1250+
assertThat(projectsV2ItemPayload.getChanges().getPreviousProjectsV2ItemNodeId().getFrom(),
1251+
is("PVTI_lADOBNft-M4AEjBWzgB7VzY"));
1252+
assertThat(projectsV2ItemPayload.getChanges().getPreviousProjectsV2ItemNodeId().getTo(),
1253+
is("PVTI_lADOBNft-M4AEjBWzgB7VzY"));
1254+
}
11551255
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
{
2+
"action": "archived",
3+
"projects_v2_item": {
4+
"id": 8083794,
5+
"node_id": "PVTI_lADOBNft-M4AEjBWzgB7WVI",
6+
"project_node_id": "PVT_kwDOBNft-M4AEjBW",
7+
"content_node_id": "I_kwDOFOkjw85OzN4w",
8+
"content_type": "Issue",
9+
"creator": {
10+
"login": "gsmet",
11+
"id": 1279749,
12+
"node_id": "MDQ6VXNlcjEyNzk3NDk=",
13+
"avatar_url": "https://avatars.githubusercontent.com/u/1279749?v=4",
14+
"gravatar_id": "",
15+
"url": "https://api.github.com/users/gsmet",
16+
"html_url": "https://github.com/gsmet",
17+
"followers_url": "https://api.github.com/users/gsmet/followers",
18+
"following_url": "https://api.github.com/users/gsmet/following{/other_user}",
19+
"gists_url": "https://api.github.com/users/gsmet/gists{/gist_id}",
20+
"starred_url": "https://api.github.com/users/gsmet/starred{/owner}{/repo}",
21+
"subscriptions_url": "https://api.github.com/users/gsmet/subscriptions",
22+
"organizations_url": "https://api.github.com/users/gsmet/orgs",
23+
"repos_url": "https://api.github.com/users/gsmet/repos",
24+
"events_url": "https://api.github.com/users/gsmet/events{/privacy}",
25+
"received_events_url": "https://api.github.com/users/gsmet/received_events",
26+
"type": "User",
27+
"site_admin": false
28+
},
29+
"created_at": "2022-08-03T13:13:51Z",
30+
"updated_at": "2022-08-09T23:10:29Z",
31+
"archived_at": "2022-08-09T23:10:29Z"
32+
},
33+
"changes": {
34+
"archived_at": {
35+
"from": null,
36+
"to": "2022-08-10T01:10:29+02:00"
37+
}
38+
},
39+
"organization": {
40+
"login": "gsmet-bot-playground",
41+
"id": 81260024,
42+
"node_id": "MDEyOk9yZ2FuaXphdGlvbjgxMjYwMDI0",
43+
"url": "https://api.github.com/orgs/gsmet-bot-playground",
44+
"repos_url": "https://api.github.com/orgs/gsmet-bot-playground/repos",
45+
"events_url": "https://api.github.com/orgs/gsmet-bot-playground/events",
46+
"hooks_url": "https://api.github.com/orgs/gsmet-bot-playground/hooks",
47+
"issues_url": "https://api.github.com/orgs/gsmet-bot-playground/issues",
48+
"members_url": "https://api.github.com/orgs/gsmet-bot-playground/members{/member}",
49+
"public_members_url": "https://api.github.com/orgs/gsmet-bot-playground/public_members{/member}",
50+
"avatar_url": "https://avatars.githubusercontent.com/u/81260024?v=4",
51+
"description": null
52+
},
53+
"sender": {
54+
"login": "gsmet",
55+
"id": 1279749,
56+
"node_id": "MDQ6VXNlcjEyNzk3NDk=",
57+
"avatar_url": "https://avatars.githubusercontent.com/u/1279749?v=4",
58+
"gravatar_id": "",
59+
"url": "https://api.github.com/users/gsmet",
60+
"html_url": "https://github.com/gsmet",
61+
"followers_url": "https://api.github.com/users/gsmet/followers",
62+
"following_url": "https://api.github.com/users/gsmet/following{/other_user}",
63+
"gists_url": "https://api.github.com/users/gsmet/gists{/gist_id}",
64+
"starred_url": "https://api.github.com/users/gsmet/starred{/owner}{/repo}",
65+
"subscriptions_url": "https://api.github.com/users/gsmet/subscriptions",
66+
"organizations_url": "https://api.github.com/users/gsmet/orgs",
67+
"repos_url": "https://api.github.com/users/gsmet/repos",
68+
"events_url": "https://api.github.com/users/gsmet/events{/privacy}",
69+
"received_events_url": "https://api.github.com/users/gsmet/received_events",
70+
"type": "User",
71+
"site_admin": false
72+
},
73+
"installation": {
74+
"id": 16779846,
75+
"node_id": "MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMTY3Nzk4NDY="
76+
}
77+
}

0 commit comments

Comments
 (0)