Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve issue pagination #24

Merged
merged 12 commits into from
Aug 30, 2023
Binary file modified requirements.txt
Binary file not shown.
10 changes: 5 additions & 5 deletions server/api/repos/[repo_id]/clone.post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ export default defineEventHandler(async (event) => {
// TODO: skip issues that are already up to date
console.log('fetching issues ...');
let page = 1;
const perPage = 50;
while (true) {
const { items: issues, total } = await userForgeApi.getIssues(repo.remoteId.toString(), { page, perPage: 50 });
const { items: issues, total } = await userForgeApi.getIssues(repo.remoteId.toString(), { page, perPage });
for await (const issue of issues) {
let issueString = `# issue "${issue.title}" (${issue.number})`;
if (issue.labels.length !== 0) {
Expand All @@ -76,15 +77,14 @@ export default defineEventHandler(async (event) => {
await fs.writeFile(path.join(folder, 'issues', `${issue.number}.md`), issueString);
}

console.log('wrote', issues.length, 'issues');

// TODO: improve stop condition
if (issues.length < 50) {
if (issues.length < perPage || page * perPage >= total) {
break;
}
page += 1;
}

console.log(`wrote ${page * perPage} issues`);

console.log('start indexing ...');
const indexingResponse = await $fetch<{ error?: string }>(`${config.api.url}/index`, {
method: 'POST',
Expand Down
50 changes: 43 additions & 7 deletions server/forges/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { H3Event } from 'h3';
import { Forge, Credentials, Tokens, ForgeUser, Repo, PaginatedList, Pagination, Issue } from './types';
import { Forge as DBForge } from '../schemas';
import { Octokit } from 'octokit';
import type { ResponseHeaders } from '@octokit/types';

export class Github implements Forge {
private clientId: string;
Expand All @@ -27,6 +28,33 @@ export class Github implements Forge {
};
}

private getTotalPagesFromHeaders(headers: ResponseHeaders) {
/*
Sample link header:
link:
<https://api.github.com/repositories/1300192/issues?page=2>; rel="prev",
<https://api.github.com/repositories/1300192/issues?page=4>; rel="next",
<https://api.github.com/repositories/1300192/issues?page=515>; rel="last",
<https://api.github.com/repositories/1300192/issues?page=1>; rel="first"
*/

if (!headers.link) {
return 0;
}

const linkToLastPage = headers.link.split(',').find((link) => link.includes('rel="last"'));
if (!linkToLastPage) {
return 0;
}

const match = /\?page=(.*?)\>/.exec(linkToLastPage);
if (!match || match.length != 2) {
return 0;
}

return parseInt(match[1]);
}

public getOauthRedirectUrl({ state }: { state: string }): string {
const scopes = ['read:user', 'user:email', 'repo'];
return `https://github.com/login/oauth/authorize?client_id=${
Expand Down Expand Up @@ -98,10 +126,13 @@ export class Github implements Forge {

public async getRepos(token: string, search?: string, pagination?: Pagination): Promise<PaginatedList<Repo>> {
const client = this.getClient(token);

const perPage = pagination?.perPage || 10;
const repos = await client.request('GET /search/repositories', {
q: `is:public fork:false archived:false ${search}`.trim(), // TODO: filter by owned repos
per_page: 10,
per_page: perPage,
sort: 'updated',
page: pagination?.page,
});

return {
Expand All @@ -115,7 +146,7 @@ export class Github implements Forge {
url: repo.url,
}) satisfies Repo,
),
total: 0, // TODO
total: this.getTotalPagesFromHeaders(repos.headers) * perPage,
};
}

Expand All @@ -139,11 +170,16 @@ export class Github implements Forge {
async getIssues(token: string, repoId: string, pagination?: Pagination): Promise<PaginatedList<Issue>> {
const client = this.getClient(token);

const repo = await client.request(`GET /repositories/{repoId}`, {
repoId,
});

const perPage = pagination?.perPage || 10;
const issues = await client.request(`GET /repos/{owner}/{repo}/issues`, {
owner: repoId.split('/')[0],
repo: repoId.split('/')[1],
per_page: pagination?.perPage || 10,
page: pagination?.page || 1,
owner: repo.data.owner.login,
repo: repo.data.name,
per_page: perPage,
page: pagination?.page,
});

return {
Expand All @@ -154,7 +190,7 @@ export class Github implements Forge {
labels: issue.labels.map((label) => (typeof label === 'string' ? label : label.name || '')),
comments: [], // TODO: get comments
})),
total: 0, // TODO: get total
total: this.getTotalPagesFromHeaders(issues.headers) * perPage,
};
}
}
5 changes: 3 additions & 2 deletions server/forges/gitlab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,11 @@ export class Gitlab implements Forge {
async getIssues(token: string, repoId: string, pagination?: Pagination): Promise<PaginatedList<Issue>> {
const client = this.getClient(token);

const issues = await client.Issues.all({
const { data: issues, paginationInfo } = await client.Issues.all({
projectId: repoId,
perPage: pagination?.perPage || 10,
page: pagination?.page || 1,
showExpanded: true,
});

return {
Expand All @@ -148,7 +149,7 @@ export class Gitlab implements Forge {
labels: issue.labels || [],
comments: [], // TODO: get comments
})),
total: 0, // TODO: get total
total: paginationInfo.total,
};
}
}