From c021e94fcec24bff89d93cbdeff62d82472f1452 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Fri, 29 Sep 2017 01:05:46 +0200 Subject: [PATCH] chore(dashboard): http function to list pull requests with conflicts (#6675) Introduces a new http trigger in the Dashboard functions that returns all Pull Requests with merge conflicts. This allows us to show a list of PRs with merge conflicts in the dashboard. To ensure that the API calls are as fast as possible, the pull requests with conflicts are queried using Github's new GraphQL API. --- tools/dashboard/functions/functions.ts | 1 + .../dashboard/functions/github/github-api.ts | 22 ++++++++++ .../github/github-graphql-queries.ts | 43 +++++++++++++++++++ .../functions/github/github-status.ts | 30 +++---------- tools/dashboard/functions/package.json | 2 + .../functions/pull-requests-with-conflicts.ts | 13 ++++++ 6 files changed, 87 insertions(+), 24 deletions(-) create mode 100644 tools/dashboard/functions/github/github-api.ts create mode 100644 tools/dashboard/functions/github/github-graphql-queries.ts create mode 100644 tools/dashboard/functions/pull-requests-with-conflicts.ts diff --git a/tools/dashboard/functions/functions.ts b/tools/dashboard/functions/functions.ts index ff59155dee7..82583391d20 100644 --- a/tools/dashboard/functions/functions.ts +++ b/tools/dashboard/functions/functions.ts @@ -1 +1,2 @@ export {payloadGithubStatus} from './payload-github-status'; +export {pullRequestsWithConflicts} from './pull-requests-with-conflicts'; diff --git a/tools/dashboard/functions/github/github-api.ts b/tools/dashboard/functions/github/github-api.ts new file mode 100644 index 00000000000..0790045103c --- /dev/null +++ b/tools/dashboard/functions/github/github-api.ts @@ -0,0 +1,22 @@ +import {GraphQLClient} from 'graphql-request'; +import {config} from 'firebase-functions'; +import * as GithubApi from 'github'; + +/** API token for the Github repository. Required to set the github status on commits and PRs. */ +const githubAccessToken = config().secret.github; + +/** API Endpoint for the Github API v4 using GraphQL. */ +const githubApiV4Endpoint = 'https://api.github.com/graphql'; + +/** Export the GitHub V3 API instance that is authenticated. */ +export const githubApiV3 = new GithubApi(); + +/** Export the GraphQL client that can be used to query the Github API v4. */ +export const githubApiV4 = new GraphQLClient(githubApiV4Endpoint, { + headers: { + Authorization: `Bearer ${githubAccessToken}`, + } +}); + +// Authenticate the Github API package with the user token. +githubApiV3.authenticate({type: 'token', token: githubAccessToken}); diff --git a/tools/dashboard/functions/github/github-graphql-queries.ts b/tools/dashboard/functions/github/github-graphql-queries.ts new file mode 100644 index 00000000000..a0aa582ce19 --- /dev/null +++ b/tools/dashboard/functions/github/github-graphql-queries.ts @@ -0,0 +1,43 @@ +import {githubApiV4} from './github-api'; + +/** GraphQL query that finds all Pull Requests and their mergeable state. */ +const getOpenPullRequestsWithMergeableStateQuery = ` + query getOpenPullRequestsWithMergeableState($lastCursor: String) { + repository(owner: "angular", name: "material2") { + pullRequests(states: OPEN, first: 100, after: $lastCursor) { + pageInfo { + hasNextPage, + endCursor + } + nodes { + number, + mergeable + } + } + } + }`; + +/** Pull Request node that will be returned by the Github V4 API. */ +export interface PullRequestWithMergeableState { + number: number; + mergeable: string; +} + +/** Queries the GitHub API to find all open pull requests and their mergeable state. */ +export async function getOpenPullRequestsWithMergeableState() + : Promise { + const nodes: PullRequestWithMergeableState[] = []; + let lastData: any|null = null; + + while (!lastData || lastData.repository.pullRequests.pageInfo.hasNextPage) { + lastData = await githubApiV4.request(getOpenPullRequestsWithMergeableStateQuery, { + lastCursor: lastData && lastData.repository.pullRequests.pageInfo.endCursor + }); + + nodes.push(...lastData.repository.pullRequests.nodes); + } + + return nodes; +} + + diff --git a/tools/dashboard/functions/github/github-status.ts b/tools/dashboard/functions/github/github-status.ts index ac02ee39787..b0fd674ad3a 100644 --- a/tools/dashboard/functions/github/github-status.ts +++ b/tools/dashboard/functions/github/github-status.ts @@ -1,10 +1,4 @@ -import {config} from 'firebase-functions'; - -const request = require('request'); -const {version, name} = require('../package.json'); - -/** API token for the Github repository. Required to set the github status on commits and PRs. */ -const repoToken = config().secret.github; +import {githubApiV3} from './github-api'; /** Data that must be specified to set a Github PR status. */ export type GithubStatusData = { @@ -18,25 +12,13 @@ export type GithubStatusData = { export function setGithubStatus(commitSHA: string, data: GithubStatusData) { const state = data.result ? 'success' : 'failure'; - const requestData = { + return githubApiV3.repos.createStatus({ + owner: 'angular', + repo: 'material2', + sha: commitSHA, state: state, target_url: data.url, + description: data.description, context: data.name, - description: data.description - }; - - const headers = { - 'Authorization': `token ${repoToken}`, - 'User-Agent': `${name}/${version}` - }; - - return new Promise((resolve, reject) => { - request({ - url: `https://api.github.com/repos/angular/material2/statuses/${commitSHA}`, - method: 'POST', - body: requestData, - headers: headers, - json: true - }, (error: any, response: any) => error ? reject(error) : resolve(response.statusCode)); }); } diff --git a/tools/dashboard/functions/package.json b/tools/dashboard/functions/package.json index 5ea08a76d2c..b51fc4c4b05 100644 --- a/tools/dashboard/functions/package.json +++ b/tools/dashboard/functions/package.json @@ -6,6 +6,8 @@ "@types/jsonwebtoken": "^7.2.1", "firebase-admin": "~4.2.1", "firebase-functions": "^0.5.7", + "github": "^10.0.0", + "graphql-request": "^1.3.4", "jsonwebtoken": "^7.4.1", "request": "^2.81.0", "ts-node": "^3.0.6", diff --git a/tools/dashboard/functions/pull-requests-with-conflicts.ts b/tools/dashboard/functions/pull-requests-with-conflicts.ts new file mode 100644 index 00000000000..c0ddc7cb11a --- /dev/null +++ b/tools/dashboard/functions/pull-requests-with-conflicts.ts @@ -0,0 +1,13 @@ +import {https} from 'firebase-functions'; +import {getOpenPullRequestsWithMergeableState} from './github/github-graphql-queries'; + +/** + * Firebase HTTP trigger that responds with a list of Pull Requests that have merge conflicts. + */ +export const pullRequestsWithConflicts = https.onRequest(async (_request, response) => { + const pullRequests = (await getOpenPullRequestsWithMergeableState()) + .filter(pullRequest => pullRequest.mergeable === 'CONFLICTING'); + + response.status(200).json(pullRequests); +}); +