Getting Linked issues and Projects associated with a pull request form github api v3
Asked Answered
K

5

5

How can I get the Projects and linked issues for a given pull request from github API V3? The pulls endpoint does not give information on either of them. In the github's pull requests section's sidebar there are Projects and Linked issues mentioned. But I could not find a way to get this information via API calls.

Screenshot of the sidebar for reference

I want to find out which issues a pull request closes on merging successfully.

Kylander answered 17/3, 2020 at 6:21 Comment(0)
C
5

To get the projects with cards linking to a specific pull request you can use Github GraphQL API using this payload :

{
  repository(owner: "twbs", name: "bootstrap") {
    pullRequest(number: 30342) {
      projectCards {
        nodes {
          project {
            name
          }
        }
      }
    }
  }
}

But for the linked issues I don't think the API is available yet. You can still scrape the list from github.com if the repo is public. The following script get the list of issues URL using :

import requests
from bs4 import BeautifulSoup
import re

repo = "twbs/bootstrap"
pr = "30342"

r = requests.get(f"https://github.com/{repo}/pull/{pr}")
soup = BeautifulSoup(r.text, 'html.parser')
issueForm = soup.find("form", { "aria-label": re.compile('Link issues')})

print([ i["href"] for i in issueForm.find_all("a")])
Cultured answered 20/3, 2020 at 19:29 Comment(0)
F
6

2022 Update:

There is now a closingIssuesReferences property on pull requests in GraphQL.

pullRequest(number: $number) {
    id
    closingIssuesReferences (first: 50) {
      edges {
        node {
          id
          body
          number
          title
        }
      }
    }
}
Fechter answered 13/7, 2022 at 13:42 Comment(0)
C
5

To get the projects with cards linking to a specific pull request you can use Github GraphQL API using this payload :

{
  repository(owner: "twbs", name: "bootstrap") {
    pullRequest(number: 30342) {
      projectCards {
        nodes {
          project {
            name
          }
        }
      }
    }
  }
}

But for the linked issues I don't think the API is available yet. You can still scrape the list from github.com if the repo is public. The following script get the list of issues URL using :

import requests
from bs4 import BeautifulSoup
import re

repo = "twbs/bootstrap"
pr = "30342"

r = requests.get(f"https://github.com/{repo}/pull/{pr}")
soup = BeautifulSoup(r.text, 'html.parser')
issueForm = soup.find("form", { "aria-label": re.compile('Link issues')})

print([ i["href"] for i in issueForm.find_all("a")])
Cultured answered 20/3, 2020 at 19:29 Comment(0)
C
1

(Post this here in case it's helpful for anyone else looking to get Issues linked to a Pull Request)

There's no direct way of getting a list of Issues linked to a Pull Request.

But each Pull Request includes a timeline of events and one of those events is when an Issue gets linked. Using the timeline of events I was able to write a GitHub APIv4 API request and some javascript to get the Issues linked to a PR:

First, here's the GraphQL query:

{
  resource(url: "https://github.com/Org/Project/pull/1234") {
    ... on PullRequest {
      timelineItems(itemTypes: [CONNECTED_EVENT, DISCONNECTED_EVENT], first: 100) {
        nodes {
          ... on ConnectedEvent {
            id
            subject {
              ... on Issue {
                number
              }
            }
          }
          ... on DisconnectedEvent {
            id
            subject {
              ... on Issue {
                number
              }
            }
          }
        }
      }
    }
  }
}

If you run this in the GitHub GraphQL Explorer (https://developer.github.com/v4/explorer/), you'll see all the events that an Issue was connected and disconnected to a Pull Request.

Using that query, I pass the response to this code that I wrote for a nodejs app that then determines which Issue is still linked to the Pull Request

const issues = {};
resource.timelineItems.nodes.map(node => {
    if (issues.hasOwnProperty(node.subject.number)) {
        issues[node.subject.number]++;
    } else {
        issues[node.subject.number] = 1;
    }
});
const linkedIssues = [];
for (const [issue, count] of Object.entries(issues)) {
    if (count % 2 != 0) {
        linkedIssues.push(issue);
    }
}
console.log(issues);

console.log(linkedIssues);

The logic here is as follows:

  1. Get a list of all events on a Pull Request of the type CONNECTED_EVENT and DISCONNECTED_EVENT

  2. Create a map, keyed by Issue number and keep a count of how may times the issue is CONNECTED and DISCONNECTED

  3. From that map, look for keys that have an odd-numbered count, as these are the events that have been CONNECTED that don't have a corresponding DISCONNECTED event.

It's not a super elegant solution, but it solves what I need, which was to find those Linked issues.

Hopefully this helps someone else out 

Chiller answered 21/5, 2020 at 17:37 Comment(1)
I've tried your solution replacing the pull request url with github.com/twbs/bootstrap/pull/30342 which has 4 linked issues but it returned nothing, maybe I'm missing something ?Cultured
Z
0

In theory, the following query should return the linked pull request numbers for an issue. However it returns an error message hinting about internal error right now. I opened a ticket at github about it.

(project kode-konveyor/TaskMarket, issue #121)

{
  repository(owner: "kode-konveyor", name: "TaskMarket") {
    issue(number: 121) {
      id
      number
      title
      timelineItems {
        __typename
        ... on IssueTimelineItemsConnection {
          nodes {
            __typename
            ... on ConnectedEvent {
              source {
                __typename
                ... on PullRequest {
                  number
                }
              }
              subject {
                __typename
                ... on PullRequest {
                  number
                }
              }
            }
          }
        }
      }
    }
  }
}
Zebulen answered 10/5, 2020 at 17:7 Comment(0)
D
0

Find linked PRs from Issues and vice-versa. Not exactly what you're asking, but this was the most relevant SO post I could find.

Fully working Python implementation (PyGithub):

def get_linked_pr_from_issue(issue: Issue) -> PullRequest | None:
  """Check if the given issue has a linked pull request.

  This function iterates over the timeline of the issue and checks if there is a 'cross-referenced' event.
  If such an event is found, it checks if the source of the event is an issue and if so, it returns the issue as a pull request.

  Usage: 
  issue: Issue = repo.get_issue(number=8)
  pr_or_none = check_if_issue_has_linked_pr(issue)

  Args:
      issue (Issue): The issue to check for a linked pull request.

  Returns:
      PullRequest: The linked pull request if it exists, None otherwise.
  """
  events_pages: PaginatedList[TimelineEvent] = issue.get_timeline()
  pg_num = 0
  while events_pages.get_page(pg_num):
    page = events_pages.get_page(pg_num)
    pg_num += 1
    for e in page:
      if str(e.event) == 'cross-referenced':
        if e.source and e.source.issue:
            return e.source.issue.as_pull_request()

def get_linked_issue_from_pr(pr: PullRequest) -> Issue | None:
  """Check if the given pull request has a linked issue.

  This function iterates over the timeline of the pull request and checks if there is a 'cross-referenced' event.
  If such an event is found, it checks if the source of the event is a pull request and if so, it returns the pull request as an issue.

  Usage: 
  pr: PullRequest = repo.get_pull(number=8)
  issue_or_none = check_if_pr_has_linked_issue(pr)

  Args:
      pr (PullRequest): The pull request to check for a linked issue.

  Returns:
      Issue: The linked issue if it exists, None otherwise.
  """
  events_pages: PaginatedList[TimelineEvent] = pr.as_issue().get_timeline()
  pg_num = 0
  while events_pages.get_page(pg_num):
    page = events_pages.get_page(pg_num)
    pg_num += 1
    for e in page:
      if str(e.event) == 'cross-referenced':
        if e.source and e.source.issue:
            return e.source.issue

Dauphine answered 3/11, 2023 at 17:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.