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); +}); +