feat(web): resolve walkthrough diffs for GitLab merge requests
This commit is contained in:
@@ -1,14 +1,20 @@
|
||||
import { getOctokitOrNull } from '../github/octokit.js';
|
||||
import { resolveGitHubRepoFromDirectory } from '../github/repo/index.js';
|
||||
import { getGitLabClientOrNull } from '../gitlab/client.js';
|
||||
import { resolveGitLabRepoFromDirectory } from '../gitlab/repo.js';
|
||||
|
||||
// GitLab diff pagination cap: never loop more than 10 pages of 100 files,
|
||||
// mirroring gitlab/routes.js.
|
||||
const GITLAB_DIFFS_MAX_PAGES = 10;
|
||||
|
||||
/**
|
||||
* Raw unified diff for a pull request.
|
||||
* Raw unified diff for a GitHub pull request.
|
||||
*
|
||||
* GitHub already returns the merge-base diff for a PR, so this matches the
|
||||
* three-dot semantics used for local branch reviews: work merged in from the
|
||||
* base branch is not part of it.
|
||||
*/
|
||||
export async function getPullRequestDiff(directory, number) {
|
||||
async function getGitHubPullRequestDiff(directory, number) {
|
||||
const octokit = getOctokitOrNull();
|
||||
if (!octokit) {
|
||||
throw Object.assign(new Error('Connect a GitHub account to review pull requests'), {
|
||||
@@ -44,3 +50,75 @@ export async function getPullRequestDiff(directory, number) {
|
||||
|
||||
return { patch, meta: { owner: repo.owner, repo: repo.repo, number } };
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw unified diff for a GitLab merge request.
|
||||
*
|
||||
* GitLab's merge request diffs endpoint returns one entry per file, so the
|
||||
* pages are concatenated into a single patch. `repo` comes from the
|
||||
* dispatcher's `resolveGitLabRepoFromDirectory` call and is never re-resolved
|
||||
* here.
|
||||
*/
|
||||
async function getGitLabMergeRequestDiff(repo, number) {
|
||||
const client = getGitLabClientOrNull();
|
||||
if (!client) {
|
||||
throw Object.assign(new Error('Connect a GitLab account to review merge requests'), {
|
||||
statusCode: 401,
|
||||
code: 'gitlab-not-connected',
|
||||
});
|
||||
}
|
||||
|
||||
// The parser always populates both fields; this guards a malformed repo so
|
||||
// the failure is explicit rather than a downstream TypeError.
|
||||
if (!repo?.namespace || !repo?.project) {
|
||||
throw Object.assign(new Error('This directory has no GitLab remote'), {
|
||||
statusCode: 400,
|
||||
code: 'no-gitlab-remote',
|
||||
});
|
||||
}
|
||||
|
||||
// The client URL-encodes the path internally; never pre-encode it.
|
||||
const projectPath = `${repo.namespace}/${repo.project}`;
|
||||
|
||||
const diffs = [];
|
||||
for (let page = 1; page <= GITLAB_DIFFS_MAX_PAGES; page += 1) {
|
||||
const response = await client.mergeRequestDiffs(projectPath, number, { per_page: 100, page });
|
||||
if (response.status !== 200 || !Array.isArray(response.data)) {
|
||||
break;
|
||||
}
|
||||
diffs.push(...response.data);
|
||||
// The page object is the authoritative signal; the 10-page cap above is
|
||||
// what stops a server that lies about hasMore from looping forever.
|
||||
if (!response.page?.hasMore) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const patch = diffs
|
||||
.map((item) => (typeof item?.diff === 'string' ? item.diff : ''))
|
||||
.filter(Boolean)
|
||||
.join('\n');
|
||||
if (!patch.trim()) {
|
||||
throw Object.assign(new Error(`Merge request #${number} has no diff`), {
|
||||
statusCode: 404,
|
||||
code: 'empty-diff',
|
||||
});
|
||||
}
|
||||
|
||||
return { patch, meta: { namespace: repo.namespace, project: repo.project, number } };
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw unified diff for a pull request or merge request.
|
||||
*
|
||||
* The provider is chosen by the repository's git remote: a GitLab remote uses
|
||||
* the GitLab merge request API, anything else falls back to the GitHub pull
|
||||
* request API.
|
||||
*/
|
||||
export async function getPullRequestDiff(directory, number) {
|
||||
const { repo } = await resolveGitLabRepoFromDirectory(directory);
|
||||
if (repo) {
|
||||
return getGitLabMergeRequestDiff(repo, number);
|
||||
}
|
||||
return getGitHubPullRequestDiff(directory, number);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user