From 6c1cee298b156f8a74fbebd551192732046cc468 Mon Sep 17 00:00:00 2001 From: OJ Kwon Date: Wed, 5 Jul 2017 13:04:41 -0700 Subject: [PATCH] feat(getPullRequestCommits): support retrive paginated pull request commit list --- changelog.md | 1 + package.json | 1 + source/ambient.d.ts | 1 + source/platforms/github/GitHubAPI.ts | 54 ++++++++++++++++++++++++++-- yarn.lock | 16 ++++----- 5 files changed, 63 insertions(+), 10 deletions(-) diff --git a/changelog.md b/changelog.md index 22316891b..8d91ccb36 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,7 @@ // Please add your own contribution below inside the Master section, ideally with a consumer's perspective in mind. ### Master +- Support retrive paginated pull request commit list - kwonoj ### 1.0.0 diff --git a/package.json b/package.json index 252e600b7..fa4f5829e 100644 --- a/package.json +++ b/package.json @@ -124,6 +124,7 @@ "lodash.keys": "^4.0.8", "node-fetch": "^1.6.3", "parse-diff": "^0.4.0", + "parse-link-header": "^1.0.1", "rfc6902": "^1.3.0", "voca": "^1.2.0" }, diff --git a/source/ambient.d.ts b/source/ambient.d.ts index d6f6bbef5..cbe569e39 100644 --- a/source/ambient.d.ts +++ b/source/ambient.d.ts @@ -10,5 +10,6 @@ declare module "jest-config" declare module "voca" declare module "jsome" declare module "jsonpointer" +declare module "parse-link-header" declare module "*/package.json" diff --git a/source/platforms/github/GitHubAPI.ts b/source/platforms/github/GitHubAPI.ts index 0826b1a88..a207067ab 100644 --- a/source/platforms/github/GitHubAPI.ts +++ b/source/platforms/github/GitHubAPI.ts @@ -3,9 +3,11 @@ import { RepoMetaData } from "../../ci_source/ci_source" import { GitHubPRDSL, GitHubUser } from "../../dsl/GitHubDSL" import * as find from "lodash.find" import * as v from "voca" +import * as parse from "parse-link-header" import * as node_fetch from "node-fetch" import * as GitHubNodeAPI from "github" +import * as debug from "debug" import { dangerSignaturePostfix } from "../../runner/templates/githubIssueTemplate" // The Handle the API specific parts of the github @@ -20,6 +22,7 @@ export type APIToken = string export class GitHubAPI { fetch: typeof fetch additionalHeaders: any + private readonly d = debug("danger:GitHubAPI") constructor(public readonly repoMetadata: RepoMetaData, public readonly token?: APIToken) { // This allows Peril to DI in a new Fetch function @@ -132,12 +135,59 @@ export class GitHubAPI { return res.ok ? res.json() as Promise : {} as GitHubPRDSL } + /** + * Get list of commits in pull requests. This'll try to iterate all available pages + * Until it reaches hard limit of api itself (250 commits). + * https://developer.github.com/v3/pulls/#list-commits-on-a-pull-request + * + */ async getPullRequestCommits(): Promise { const repo = this.repoMetadata.repoSlug const prID = this.repoMetadata.pullRequestID - const res = await this.get(`repos/${repo}/pulls/${prID}/commits`) - return res.ok ? res.json() : [] + const ret: Array = [] + + /** + * Read response header and locate next page for pagination via link header. + * If not found, will return -1. + * + * @param response Github API response sent via node-fetch + */ + const getNextPageFromLinkHeader = (response: node_fetch.Response): number => { + const linkHeader = response.headers.get("link") + if (!linkHeader) { + this.d(`getNextPageFromLinkHeader:: Given response does not contain link header for pagination`) + return -1 + } + + const parsedHeader = parse(linkHeader) + this.d(`getNextPageFromLinkHeader:: Link header found`, parsedHeader) + if (!!parsedHeader.next && !!parsedHeader.next.page) { + return parsedHeader.next.page + } + return -1 + } + + //iterates commit request pages until next page's not available, or response failed for some reason. + let page = 0 + while (page >= 0) { + const requestUrl = `repos/${repo}/pulls/${prID}/commits${page > 0 ? `?page=${page}` : ""}` + this.d(`getPullRequestCommits:: Sending pull request commit request for ${page === 0 ? "first" : `${page}`} page`) + this.d(`getPullRequestCommits:: Request url generated "${requestUrl}"`) + + const response = await this.get(requestUrl) + if (response.ok) { + ret.push(...(await response.json())) + page = getNextPageFromLinkHeader(response) + } else { + this.d( + `getPullRequestCommits:: Failed to get response while traverse page ${page} with ${response.status}, bailing rest of pages if exists` + ) + page = -1 + } + } + + return ret } async getUserInfo(): Promise { diff --git a/yarn.lock b/yarn.lock index ead6e3941..18e4c7d65 100644 --- a/yarn.lock +++ b/yarn.lock @@ -96,13 +96,7 @@ ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" -ansi-styles@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.0.0.tgz#5404e93a544c4fec7f048262977bebfe3155e0c1" - dependencies: - color-convert "^1.0.0" - -ansi-styles@^3.1.0: +ansi-styles@^3.0.0, ansi-styles@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.1.0.tgz#09c202d5c917ec23188caa5c9cb9179cd9547750" dependencies: @@ -3188,6 +3182,12 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" +parse-link-header@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-link-header/-/parse-link-header-1.0.1.tgz#bedfe0d2118aeb84be75e7b025419ec8a61140a7" + dependencies: + xtend "~4.0.1" + parse-ms@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-1.0.1.tgz#56346d4749d78f23430ca0c713850aef91aa361d" @@ -4210,7 +4210,7 @@ wrappy@1: version "2.0.1" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" -"xtend@>=4.0.0 <4.1.0-0": +"xtend@>=4.0.0 <4.1.0-0", xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"