feat(ui): forge write operations — comments, replies, close/reopen, edit, reviews, draft, metadata
- server: write routes for all three providers (issue/PR comments, inline review-comment replies, issue/MR updates w/ labels-assignees-milestone, review submit, draft toggle) - ui: ForgeProvider gains six write ops; shared action components (composer, thread reply, state/review/draft/metadata/edit) wired into ForgeEntityDetailView and GitHub PR Overview
This commit is contained in:
@@ -2,9 +2,13 @@ import type {
|
||||
GiteaAPI,
|
||||
GiteaAuthStatus,
|
||||
GiteaBranchesResult,
|
||||
GiteaIssueCommentInput,
|
||||
GiteaIssueCommentResult,
|
||||
GiteaIssueCommentsResult,
|
||||
GiteaIssueGetResult,
|
||||
GiteaIssuesListResult,
|
||||
GiteaIssueUpdateInput,
|
||||
GiteaIssueUpdateResult,
|
||||
GiteaPullRequest,
|
||||
GiteaPullRequestCommitsResult,
|
||||
GiteaPullRequestContextResult,
|
||||
@@ -15,6 +19,9 @@ import type {
|
||||
GiteaPullRequestsListResult,
|
||||
GiteaPullRequestStatusesResult,
|
||||
GiteaPullRequestUpdateInput,
|
||||
GiteaPullReviewInput,
|
||||
GiteaPullReviewResult,
|
||||
GiteaRepoLabelsResult,
|
||||
GiteaUserSummary,
|
||||
} from '@openchamber/ui/lib/api/types';
|
||||
import { runtimeFetch } from '@openchamber/ui/lib/runtime-fetch';
|
||||
@@ -287,6 +294,77 @@ export const createWebGiteaAPI = ({ urls }: WebGiteaAPIOptions): GiteaAPI => ({
|
||||
};
|
||||
},
|
||||
|
||||
async issueComment(input: GiteaIssueCommentInput): Promise<GiteaIssueCommentResult> {
|
||||
const response = await runtimeFetch('/api/gitea/issues/comment', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
const body = await jsonOrNull<GiteaIssueCommentResult & { error?: string }>(response);
|
||||
if (!response.ok || !body) {
|
||||
throw new Error(body?.error || response.statusText || 'Failed to post Gitea issue comment');
|
||||
}
|
||||
return body;
|
||||
},
|
||||
|
||||
async issueUpdate(input: GiteaIssueUpdateInput): Promise<GiteaIssueUpdateResult> {
|
||||
const response = await runtimeFetch('/api/gitea/issues/update', {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
const body = await jsonOrNull<GiteaIssueUpdateResult & { error?: string }>(response);
|
||||
if (!response.ok || !body) {
|
||||
throw new Error(body?.error || response.statusText || 'Failed to update Gitea issue');
|
||||
}
|
||||
return body;
|
||||
},
|
||||
|
||||
async prComment(input: GiteaIssueCommentInput): Promise<GiteaIssueCommentResult> {
|
||||
const response = await runtimeFetch('/api/gitea/prs/comment', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
const body = await jsonOrNull<GiteaIssueCommentResult & { error?: string }>(response);
|
||||
if (!response.ok || !body) {
|
||||
throw new Error(body?.error || response.statusText || 'Failed to post Gitea pull request comment');
|
||||
}
|
||||
return body;
|
||||
},
|
||||
|
||||
async prSubmitReview(input: GiteaPullReviewInput): Promise<GiteaPullReviewResult> {
|
||||
const response = await runtimeFetch('/api/gitea/prs/review', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
const body = await jsonOrNull<GiteaPullReviewResult & { error?: string }>(response);
|
||||
if (!response.ok || !body) {
|
||||
throw new Error(body?.error || response.statusText || 'Failed to submit Gitea pull request review');
|
||||
}
|
||||
return body;
|
||||
},
|
||||
|
||||
async repoLabels(directory: string, options?: { owner?: string; repo?: string }): Promise<GiteaRepoLabelsResult> {
|
||||
const params = new URLSearchParams({ directory });
|
||||
if (options?.owner) {
|
||||
params.set('owner', options.owner);
|
||||
}
|
||||
if (options?.repo) {
|
||||
params.set('repo', options.repo);
|
||||
}
|
||||
const response = await runtimeFetch(
|
||||
`/api/gitea/repo/labels?${params.toString()}`,
|
||||
{ method: 'GET', headers: { Accept: 'application/json' } }
|
||||
);
|
||||
const body = await jsonOrNull<GiteaRepoLabelsResult & { error?: string }>(response);
|
||||
if (!response.ok || !body) {
|
||||
throw new Error(body?.error || response.statusText || 'Failed to fetch Gitea repo labels');
|
||||
}
|
||||
return body;
|
||||
},
|
||||
|
||||
async repoBranches(owner: string, repo: string): Promise<GiteaBranchesResult> {
|
||||
const response = await runtimeFetch(
|
||||
`/api/gitea/repo/branches?owner=${encodeURIComponent(owner)}&repo=${encodeURIComponent(repo)}`,
|
||||
|
||||
@@ -2,7 +2,11 @@ import type {
|
||||
GitHubAPI,
|
||||
GitHubAuthStatus,
|
||||
GitHubIssueCommentsResult,
|
||||
GitHubIssueCommentInput,
|
||||
GitHubIssueCommentResult,
|
||||
GitHubIssueGetResult,
|
||||
GitHubIssueUpdateInput,
|
||||
GitHubIssueUpdateResult,
|
||||
GitHubIssuesListResult,
|
||||
GitHubPullRequestContextResult,
|
||||
GitHubPullRequestCommitsResult,
|
||||
@@ -16,7 +20,11 @@ import type {
|
||||
GitHubPullRequestReadyResult,
|
||||
GitHubPullRequestUpdateInput,
|
||||
GitHubPullRequestStatus,
|
||||
GitHubPullRequestReviewInput,
|
||||
GitHubPullRequestReviewResult,
|
||||
GitHubRepoUpstreamResult,
|
||||
GitHubReviewCommentInput,
|
||||
GitHubReviewCommentResult,
|
||||
GitHubDeviceFlowComplete,
|
||||
GitHubDeviceFlowStart,
|
||||
GitHubUserSummary,
|
||||
@@ -325,4 +333,69 @@ export const createWebGitHubAPI = ({ urls }: WebGitHubAPIOptions): GitHubAPI =>
|
||||
}
|
||||
return payload;
|
||||
},
|
||||
|
||||
async issueComment(input: GitHubIssueCommentInput): Promise<GitHubIssueCommentResult> {
|
||||
const response = await runtimeFetch('/api/github/issues/comment', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
const body = await jsonOrNull<GitHubIssueCommentResult & { error?: string }>(response);
|
||||
if (!response.ok || !body) {
|
||||
throw new Error(body?.error || response.statusText || 'Failed to post GitHub comment');
|
||||
}
|
||||
return body;
|
||||
},
|
||||
|
||||
async issueUpdate(input: GitHubIssueUpdateInput): Promise<GitHubIssueUpdateResult> {
|
||||
const response = await runtimeFetch('/api/github/issues/update', {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
const body = await jsonOrNull<GitHubIssueUpdateResult & { error?: string }>(response);
|
||||
if (!response.ok || !body) {
|
||||
throw new Error(body?.error || response.statusText || 'Failed to update GitHub issue');
|
||||
}
|
||||
return body;
|
||||
},
|
||||
|
||||
async prComment(input: GitHubIssueCommentInput): Promise<GitHubIssueCommentResult> {
|
||||
const response = await runtimeFetch('/api/github/pulls/comment', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
const body = await jsonOrNull<GitHubIssueCommentResult & { error?: string }>(response);
|
||||
if (!response.ok || !body) {
|
||||
throw new Error(body?.error || response.statusText || 'Failed to post GitHub PR comment');
|
||||
}
|
||||
return body;
|
||||
},
|
||||
|
||||
async prReviewComment(input: GitHubReviewCommentInput): Promise<GitHubReviewCommentResult> {
|
||||
const response = await runtimeFetch('/api/github/pulls/review-comment', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
const body = await jsonOrNull<GitHubReviewCommentResult & { error?: string }>(response);
|
||||
if (!response.ok || !body) {
|
||||
throw new Error(body?.error || response.statusText || 'Failed to post GitHub review comment');
|
||||
}
|
||||
return body;
|
||||
},
|
||||
|
||||
async prSubmitReview(input: GitHubPullRequestReviewInput): Promise<GitHubPullRequestReviewResult> {
|
||||
const response = await runtimeFetch('/api/github/pulls/review', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
const body = await jsonOrNull<GitHubPullRequestReviewResult & { error?: string }>(response);
|
||||
if (!response.ok || !body) {
|
||||
throw new Error(body?.error || response.statusText || 'Failed to submit GitHub review');
|
||||
}
|
||||
return body;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,9 +2,13 @@ import type {
|
||||
GitLabAPI,
|
||||
GitLabAuthStatus,
|
||||
GitLabBranchesResult,
|
||||
GitLabIssueCommentResult,
|
||||
GitLabIssueCommentsResult,
|
||||
GitLabIssueCommentInput,
|
||||
GitLabIssueGetResult,
|
||||
GitLabIssuesListResult,
|
||||
GitLabIssueUpdateInput,
|
||||
GitLabIssueUpdateResult,
|
||||
GitLabMergeRequest,
|
||||
GitLabMergeRequestCommitsResult,
|
||||
GitLabMergeRequestContextResult,
|
||||
@@ -16,6 +20,10 @@ import type {
|
||||
GitLabMergeRequestTimelineResult,
|
||||
GitLabMergeRequestUpdateInput,
|
||||
GitLabMergeRequestUpdateResult,
|
||||
GitLabMrApproveInput,
|
||||
GitLabMrApproveResult,
|
||||
GitLabMrNoteInput,
|
||||
GitLabMrNoteResult,
|
||||
GitLabUserSummary,
|
||||
} from '@openchamber/ui/lib/api/types';
|
||||
import { runtimeFetch } from '@openchamber/ui/lib/runtime-fetch';
|
||||
@@ -266,6 +274,58 @@ export const createWebGitLabAPI = ({ urls }: WebGitLabAPIOptions): GitLabAPI =>
|
||||
};
|
||||
},
|
||||
|
||||
async issueComment(input: GitLabIssueCommentInput): Promise<GitLabIssueCommentResult> {
|
||||
const response = await runtimeFetch('/api/gitlab/issues/comment', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
const body = await jsonOrNull<GitLabIssueCommentResult & { error?: string }>(response);
|
||||
if (!response.ok || !body) {
|
||||
throw new Error(body?.error || response.statusText || 'Failed to post GitLab issue comment');
|
||||
}
|
||||
return body;
|
||||
},
|
||||
|
||||
async issueUpdate(input: GitLabIssueUpdateInput): Promise<GitLabIssueUpdateResult> {
|
||||
const response = await runtimeFetch('/api/gitlab/issues/update', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
const body = await jsonOrNull<GitLabIssueUpdateResult & { error?: string }>(response);
|
||||
if (!response.ok || !body) {
|
||||
throw new Error(body?.error || response.statusText || 'Failed to update GitLab issue');
|
||||
}
|
||||
return body;
|
||||
},
|
||||
|
||||
async mrComment(input: GitLabMrNoteInput): Promise<GitLabMrNoteResult> {
|
||||
const response = await runtimeFetch('/api/gitlab/mrs/comment', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
const body = await jsonOrNull<GitLabMrNoteResult & { error?: string }>(response);
|
||||
if (!response.ok || !body) {
|
||||
throw new Error(body?.error || response.statusText || 'Failed to post GitLab merge request comment');
|
||||
}
|
||||
return body;
|
||||
},
|
||||
|
||||
async mrApprove(input: GitLabMrApproveInput): Promise<GitLabMrApproveResult> {
|
||||
const response = await runtimeFetch('/api/gitlab/mrs/approve', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
const body = await jsonOrNull<GitLabMrApproveResult & { error?: string }>(response);
|
||||
if (!response.ok || !body) {
|
||||
throw new Error(body?.error || response.statusText || 'Failed to approve GitLab merge request');
|
||||
}
|
||||
return body;
|
||||
},
|
||||
|
||||
async repoBranches(namespace: string, project: string): Promise<GitLabBranchesResult> {
|
||||
const response = await runtimeFetch(
|
||||
`/api/gitlab/repo/branches?namespace=${encodeURIComponent(namespace)}&project=${encodeURIComponent(project)}`,
|
||||
|
||||
Reference in New Issue
Block a user