import Logger from "bunyan" ; import { Octokit } from "@octokit/rest" ; import axios , { AxiosInstance , AxiosRequestConfig , AxiosResponse } from "axios" ; import AppTokenHolder from "./app-token-holder" ; import InstallationTokenCache from "./installation-token-cache" ; import AuthToken from "./auth-token" ; import { GetPullRequestParams } from "./types" ; import { handleFailedRequest , instrumentFailedRequest , instrumentRequest , setRequestStartTime } from "./interceptors" ; import { metricHttpRequest } from "../../config/metric-names" ; import { getLogger } from "../../config/logger" ; import { urlParamsMiddleware } from "../../util/axios/common-middleware" ; import { InstallationId } from "./installation-id" ; import { GetBranchesQuery , GetBranchesResponse , ViewerRepositoryCountQuery } from "./github-queries" ; /**
 * The response type for GitHub GraphQL calls.
 * Was copied from @octokit/graphql to avoid adding a dependency on Octokit Graphql client
 */ declare type GraphQlQueryResponse < ResponseData > = { data : ResponseData ; errors ? : [ { message : string ; path : [ string ] ; extensions : { [ key ] : any ; } ; locations : [ { line : number ; column : number ; } ] ; } ] ; } ; see https export default class GitHubClient { private readonly axios : AxiosInstance ; private readonly appTokenHolder : AppTokenHolder ; private readonly installationTokenCache : InstallationTokenCache ; private readonly githubInstallationId : InstallationId ; private readonly logger : Logger ; constructor ( githubInstallationId , logger , appTokenHolder = AppTokenHolder . getInstance ( ) ) { this . logger = logger || getLogger ( "github.client.axios" ) ; this . axios = axios . create ( { baseURL : githubInstallationId . githubBaseUrl } ) ; this . axios . interceptors . request . use ( urlParamsMiddleware ) ; this . axios . interceptors . request . use ( setRequestStartTime ) ; this . axios . interceptors . response . use ( undefined , handleFailedRequest ( this . logger ) ) ; this . axios . interceptors . response . use ( instrumentRequest ( metricHttpRequest . github ) , instrumentFailedRequest ( metricHttpRequest . github ) ) ; this . appTokenHolder = appTokenHolder ; this . installationTokenCache = InstallationTokenCache . getInstance ( ) ; this . githubInstallationId = githubInstallationId ; } /**
     * Use this config in a request to authenticate with the app token.
     */ private appAuthenticationHeaders ( ) { const appToken = this . appTokenHolder . getAppToken ( this . githubInstallationId ) ; return { headers : { Accept : "application/vnd.github.v3+json" , Authorization : `Bearer ${ appToken . token }` } } ; } /**
     * Use this config in a request to authenticate with an installation token for the githubInstallationId.
     */ private async installationAuthenticationHeaders ( ) { const installationToken = await this . installationTokenCache . getInstallationToken ( this . githubInstallationId . installationId , ( ) => this . createInstallationToken ( this . githubInstallationId . installationId ) ) ; return { headers : { Accept : "application/vnd.github.v3+json" , Authorization : `Bearer ${ installationToken . token }` } } ; } /**
     * Calls the GitHub API in the name of the GitHub app to generate a token that in turn can be used to call the GitHub
     * API in the name of an installation of that app (to access the users' data).
     */ private async createInstallationToken ( githubInstallationId ) { const response = await this . axios . post < Octokit . AppsCreateInstallationTokenResponse > ( `/app/installations/${ githubInstallationId }/access_tokens` , { } , { ... this . appAuthenticationHeaders ( ) } ) ; const tokenResponse = response . data ; return new AuthToken ( tokenResponse . token , new Date ( tokenResponse . expires_at ) ) ; } private async get < T > ( url , params = { } , urlParams = { } ) { const response = await this . axios . get < T > ( url , { ... await this . installationAuthenticationHeaders ( ) , params : { ... params , } , urlParams , } ) ; return response ; } private async graphql < T > ( query , variables ? ) { return await this . axios . post < GraphQlQueryResponse < T > > ( "https://api.github.com/graphql" , { query , variables } , { ... await this . installationAuthenticationHeaders ( ) , } ) ; } /**
     * Lists pull requests for the given repository.
     */ public async getPullRequests ( owner , repo , pullRequestParams ) { return await this . get < Octokit . PullsListResponseItem [ ] > ( `/repos/:owner/:repo/pulls` , pullRequestParams , { owner , repo } ) ; } /**
     * Get a single pull request for the given repository.
     */ public async getPullRequest ( owner , repo , pullNumber ) { return await this . get < Octokit . PullsGetResponse > ( `/repos/:owner/:repo/pulls/:pullNumber` , { } , { owner , repo , pullNumber } ) ; } /**
     * Get publicly available information for user with given username.
     */ public getUserByUsername = async ( username ) : Promise < AxiosResponse < Octokit . UsersGetByUsernameResponse > > => { return await this . get < Octokit . UsersGetByUsernameResponse > ( `/users/:username` , { } , { username } ) ; } ; /**
     * Get a single commit for the given repository.
     */ public getCommit = async ( owner , repo , ref ) : Promise < AxiosResponse < Octokit . ReposGetCommitResponse > > => { return await this . get < Octokit . ReposGetCommitResponse > ( `/repos/:owner/:repo/commits/:ref` , { } , { owner , repo , ref } ) ; } ; public async getNumberOfReposForInstallation ( ) { const response = await this . graphql < { viewer : { repositories : { totalCount : number ; } ; } ; } > ( ViewerRepositoryCountQuery ) ; return response ?. data ?. data ?. viewer ?. repositories ?. totalCount ; } public async getBranchesPage ( owner , repoName , perPage ? , cursor ? ) { const response = await this . graphql < GetBranchesResponse > ( GetBranchesQuery , { owner : owner , repo : repoName , per_page : perPage , cursor } ) ; return response ?. data ?. data ; } }