Skip to content

Commit

Permalink
ARC-872: use GraphQL to count repos (#964)
Browse files Browse the repository at this point in the history
  • Loading branch information
thombergs committed Dec 9, 2021
1 parent 7999931 commit 6a7daa7
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 24 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion src/config/feature-flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ export enum BooleanFlags {
TRACE_LOGGING = "trace-logging",
USE_SQS_FOR_BACKFILL = "use-sqs-for-backfill",
SUPPORT_BRANCH_AND_MERGE_WORKFLOWS_FOR_BUILDS = "support-branch-and-merge-workflows-for-builds",
USE_NEW_GITHUB_CLIENT_FOR_PUSH = "use-new-github-client-for-push"
USE_NEW_GITHUB_CLIENT_FOR_PUSH = "use-new-github-client-for-push",
USE_NEW_GITHUB_CLIENT_TO_COUNT_REPOS = "use-new-github-client-to-count-repos"
}

export enum StringFlags {
Expand Down
52 changes: 37 additions & 15 deletions src/frontend/get-github-configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { Octokit } from "@octokit/rest";
import { booleanFlag, BooleanFlags } from "../config/feature-flags";
import { Errors } from "../config/errors";
import { Tracer } from "../config/tracer";
import GitHubClient from "../github/client/github-client";
import Logger from "bunyan";

const getConnectedStatus = (
installationsWithSubscriptions: any,
Expand Down Expand Up @@ -53,7 +55,11 @@ const installationConnectedStatus = async (
return mergeByLogin(installationsWithAdmin, connectedStatuses);
};

async function getInstallationsWithAdmin(installations: Octokit.AppsListInstallationsForAuthenticatedUserResponseInstallationsItem[], login: string, isAdmin: (args: { org: string, username: string, type: string }) => Promise<boolean>): Promise<InstallationWithAdmin[]> {
async function getInstallationsWithAdmin(jiraHost: string,
log: Logger,
installations: Octokit.AppsListInstallationsForAuthenticatedUserResponseInstallationsItem[],
login: string,
isAdmin: (args: { org: string, username: string, type: string }) => Promise<boolean>): Promise<InstallationWithAdmin[]> {
const installationsWithAdmin: InstallationWithAdmin[] = [];

for (const installation of installations) {
Expand All @@ -65,21 +71,37 @@ async function getInstallationsWithAdmin(installations: Octokit.AppsListInstalla
type: installation.target_type
});

const authedApp = await app.auth(installation.id);
enhanceOctokit(authedApp);
if(await booleanFlag(BooleanFlags.USE_NEW_GITHUB_CLIENT_TO_COUNT_REPOS, true, jiraHost)){
const githubClient = new GitHubClient(installation.id, log);
const numberOfReposPromise = githubClient.getNumberOfReposForInstallation();

const repositories = authedApp.paginate(
authedApp.apps.listRepos.endpoint.merge({ per_page: 100 }),
(res) => res.data
);
const [admin, numberOfRepos] = await Promise.all([checkAdmin, numberOfReposPromise]);

const [admin, numberOfRepos] = await Promise.all([checkAdmin, repositories]);
log.info("Number of repos in the org received via GraphQL: " + numberOfRepos);

installationsWithAdmin.push({
...installation,
numberOfRepos: numberOfRepos.length || 0,
admin
});
installationsWithAdmin.push({
...installation,
numberOfRepos: numberOfRepos || 0,
admin
});
}else {

const authedApp = await app.auth(installation.id);
enhanceOctokit(authedApp);

const repositories = authedApp.paginate(
authedApp.apps.listRepos.endpoint.merge({ per_page: 100 }),
(res) => res.data
);

const [admin, numberOfRepos] = await Promise.all([checkAdmin, repositories]);

installationsWithAdmin.push({
...installation,
numberOfRepos: numberOfRepos.length || 0,
admin
});
}
}
return installationsWithAdmin;
}
Expand Down Expand Up @@ -164,7 +186,7 @@ export default async (req: Request, res: Response, next: NextFunction): Promise<

tracer.trace(`got user's installations from GitHub`);

const installationsWithAdmin = await getInstallationsWithAdmin(installations, login, isAdmin);
const installationsWithAdmin = await getInstallationsWithAdmin(jiraHost, log, installations, login, isAdmin);

tracer.trace(`got user's installations with admin status from GitHub`);

Expand All @@ -176,7 +198,7 @@ export default async (req: Request, res: Response, next: NextFunction): Promise<
jiraHost,
client,
installationsWithAdmin,
req.log
log
);

tracer.trace(`got connected installations`);
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/get-jira-configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function mapSyncStatus(syncStatus: string): string {
}
}

export async function getInstallation(client, subscription, reqLog?) {
export async function getInstallation(client, subscription, reqLog?) {
const id = subscription.gitHubInstallationId;
try {
const response = await client.apps.getInstallation({ installation_id: id });
Expand Down
35 changes: 30 additions & 5 deletions src/github/client/github-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ 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 { urlParamsMiddleware } from "../../util/axios/common-middleware";

/**
* A GitHub client that supports authentication as a GitHub app.
Expand Down Expand Up @@ -95,14 +95,26 @@ export default class GitHubClient {
},
urlParams,
});
//TODO: error handling
return response;
}

private async graphql<T>(query: string): Promise<AxiosResponse> {
return await this.axios.post<T>("https://api.github.com/graphql",
{
query
},
{
...await this.installationAuthenticationHeaders(),
params: {
installationId: this.githubInstallationId,
}
});
}

/**
* Lists pull requests for the given repository.
*/
public async getPullRequests(owner:string, repo: string, pullRequestParams: GetPullRequestParams): Promise<AxiosResponse<Octokit.PullsListResponseItem[]>> {
public async getPullRequests(owner: string, repo: string, pullRequestParams: GetPullRequestParams): Promise<AxiosResponse<Octokit.PullsListResponseItem[]>> {
return await this.get<Octokit.PullsListResponseItem[]>(`/repos/:owner/:repo/pulls`, pullRequestParams, {
owner,
repo
Expand All @@ -114,7 +126,7 @@ export default class GitHubClient {
*/
// TODO: add a unit test
public async getPullRequest(owner: string, repo: string, pullNumber: string): Promise<AxiosResponse<Octokit.PullsGetResponse>> {
return await this.get<Octokit.PullsGetResponse>(`/repos/:owner/:repo/pulls/:pullNumber`, {}, {
return await this.get<Octokit.PullsGetResponse>(`/repos/:owner/:repo/pulls/:pullNumber`, {}, {
owner,
repo,
pullNumber
Expand All @@ -135,11 +147,24 @@ export default class GitHubClient {
* Get a single commit for the given repository.
*/
public getCommit = async (owner: string, repo: string, ref: string): Promise<AxiosResponse<Octokit.ReposGetCommitResponse>> => {
return await this.get<Octokit.ReposGetCommitResponse>(`/repos/:owner/:repo/commits/:ref`, {}, {
return await this.get<Octokit.ReposGetCommitResponse>(`/repos/:owner/:repo/commits/:ref`, {}, {
owner,
repo,
ref
});
}

public async getNumberOfReposForInstallation(): Promise<number> {
const response = await this.graphql(`
query {
viewer {
repositories {
totalCount
}
}
}`)

return response?.data?.data?.viewer?.repositories?.totalCount as number;
}

}

0 comments on commit 6a7daa7

Please sign in to comment.