import { fetchJson, type ApiRequestOptions } from "../client"; import type { AssociateAzureDevOpsPullRequestRequest, AzureDevOpsConfig, AzureDevOpsProject, AzureDevOpsPullRequestFeedback, AzureDevOpsPullRequestPage, AzureDevOpsRepository, AzureDevOpsSavedView, AzureDevOpsTaskPullRequest, AzureDevOpsWorkItem, AzureDevOpsWorkItemSearchResult, SetAzureDevOpsConfigRequest, TestAzureDevOpsConnectionResult, } from "@/lib/types/azure-devops"; import { invalidateIntegrationAvailabilityAfter } from "@/lib/integrations/integration-availability-events"; const BASE = "/api/v1/azure-devops"; function withWorkspace(path: string, workspaceId: string): string { const search = new URLSearchParams(); search.set("workspace_id", workspaceId); return `${path}${path.includes("?") ? "&" : "?"}${search}`; } function appendWorkspace(search: URLSearchParams, workspaceId: string): void { search.set("workspace_id", workspaceId); } export async function getAzureDevOpsConfig( workspaceId: string, options?: ApiRequestOptions, ): Promise { const result = await fetchJson( withWorkspace(`${BASE}/config`, workspaceId), options, ); return result ?? null; } export function setAzureDevOpsConfig( workspaceId: string, payload: SetAzureDevOpsConfigRequest, options?: ApiRequestOptions, ) { return invalidateIntegrationAvailabilityAfter( fetchJson(withWorkspace(`${BASE}/config`, workspaceId), { ...options, init: { ...options?.init, method: "POST", body: JSON.stringify(payload) }, }), ); } export function deleteAzureDevOpsConfig(workspaceId: string, options?: ApiRequestOptions) { return invalidateIntegrationAvailabilityAfter( fetchJson<{ deleted: boolean }>(withWorkspace(`${BASE}/config`, workspaceId), { ...options, init: { ...options?.init, method: "DELETE" }, }), ); } export function testAzureDevOpsConnection( workspaceId: string, payload: SetAzureDevOpsConfigRequest, options?: ApiRequestOptions, ) { return fetchJson( withWorkspace(`${BASE}/config/test`, workspaceId), { ...options, init: { ...options?.init, method: "POST", body: JSON.stringify(payload) }, }, ); } export function copyAzureDevOpsConfig( sourceWorkspaceId: string, targetWorkspaceId: string, options?: ApiRequestOptions, ) { return invalidateIntegrationAvailabilityAfter( fetchJson(withWorkspace(`${BASE}/config/copy`, sourceWorkspaceId), { ...options, init: { ...options?.init, method: "POST", body: JSON.stringify({ targetWorkspaceId }), }, }), ); } export function listAzureDevOpsProjects(workspaceId: string, options?: ApiRequestOptions) { return fetchJson<{ projects: AzureDevOpsProject[] }>( withWorkspace(`${BASE}/projects`, workspaceId), options, ); } export function listAzureDevOpsRepositories( workspaceId: string, project: string, options?: ApiRequestOptions, ) { const search = new URLSearchParams({ project }); appendWorkspace(search, workspaceId); return fetchJson<{ repositories: AzureDevOpsRepository[] }>( `${BASE}/repositories?${search}`, options, ); } export function listAzureDevOpsBranches( workspaceId: string, organization: string, project: string, repository: string, options?: ApiRequestOptions, ) { const search = new URLSearchParams({ organization, project, repository }); appendWorkspace(search, workspaceId); return fetchJson<{ branches: Array<{ name: string }> }>(`${BASE}/branches?${search}`, options); } export function getAzureDevOpsSavedViews(workspaceId: string, options?: ApiRequestOptions) { return fetchJson<{ views: AzureDevOpsSavedView[] }>( withWorkspace(`${BASE}/views`, workspaceId), options, ); } export function setAzureDevOpsSavedViews( workspaceId: string, views: AzureDevOpsSavedView[], options?: ApiRequestOptions, ) { return fetchJson<{ views: AzureDevOpsSavedView[] }>(withWorkspace(`${BASE}/views`, workspaceId), { ...options, init: { ...options?.init, method: "PUT", body: JSON.stringify({ views }) }, }); } export function searchAzureDevOpsWorkItems( workspaceId: string, payload: { project: string; wiql: string; top?: number }, options?: ApiRequestOptions, ) { return fetchJson( withWorkspace(`${BASE}/work-items/search`, workspaceId), { ...options, init: { ...options?.init, method: "POST", body: JSON.stringify(payload) }, }, ); } export function getAzureDevOpsWorkItem( workspaceId: string, project: string, id: number, options?: ApiRequestOptions, ) { const search = new URLSearchParams({ project }); appendWorkspace(search, workspaceId); return fetchJson(`${BASE}/work-items/${id}?${search}`, options); } export type AzureDevOpsPullRequestFilters = { project: string; repository: string; status?: string; creator?: string; reviewer?: string; sourceBranch?: string; targetBranch?: string; skip?: number; top?: number; }; export function listAzureDevOpsPullRequests( workspaceId: string, filters: AzureDevOpsPullRequestFilters, options?: ApiRequestOptions, ) { const search = new URLSearchParams({ project: filters.project, repository: filters.repository, }); if (filters.status) search.set("status", filters.status); if (filters.creator) search.set("creator", filters.creator); if (filters.reviewer) search.set("reviewer", filters.reviewer); if (filters.sourceBranch) search.set("source_branch", filters.sourceBranch); if (filters.targetBranch) search.set("target_branch", filters.targetBranch); if (filters.skip !== undefined) search.set("skip", String(filters.skip)); if (filters.top !== undefined) search.set("top", String(filters.top)); appendWorkspace(search, workspaceId); return fetchJson(`${BASE}/pull-requests?${search}`, options); } function pullRequestPath(projectId: string, repositoryId: string, pullRequestId: number): string { return `${BASE}/pull-requests/${encodeURIComponent(projectId)}/${encodeURIComponent(repositoryId)}/${pullRequestId}`; } export function getAzureDevOpsPullRequest( workspaceId: string, projectId: string, repositoryId: string, pullRequestId: number, options?: ApiRequestOptions, ) { return fetchJson( withWorkspace(pullRequestPath(projectId, repositoryId, pullRequestId), workspaceId), options, ); } export function getAzureDevOpsPullRequestFeedback( workspaceId: string, projectId: string, repositoryId: string, pullRequestId: number, options?: ApiRequestOptions, ) { return fetchJson( withWorkspace( `${pullRequestPath(projectId, repositoryId, pullRequestId)}/feedback`, workspaceId, ), options, ); } export function listWorkspaceAzureDevOpsTaskPullRequests( workspaceId: string, options?: ApiRequestOptions, ) { return fetchJson<{ taskPrs: Record }>( `${BASE}/workspaces/${encodeURIComponent(workspaceId)}/task-prs`, options, ); } function taskPullRequestMutation( action: "associate" | "sync", workspaceId: string, taskId: string, payload: AssociateAzureDevOpsPullRequestRequest, options?: ApiRequestOptions, ) { const suffix = action === "sync" ? "/sync" : ""; return fetchJson( withWorkspace( `${BASE}/tasks/${encodeURIComponent(taskId)}/pull-requests${suffix}`, workspaceId, ), { ...options, init: { ...options?.init, method: "POST", body: JSON.stringify(payload) }, }, ); } export function associateAzureDevOpsPullRequest( workspaceId: string, taskId: string, payload: AssociateAzureDevOpsPullRequestRequest, options?: ApiRequestOptions, ) { return taskPullRequestMutation("associate", workspaceId, taskId, payload, options); } export function syncAzureDevOpsTaskPullRequest( workspaceId: string, taskId: string, payload: AssociateAzureDevOpsPullRequestRequest, options?: ApiRequestOptions, ) { return taskPullRequestMutation("sync", workspaceId, taskId, payload, options); }