Gitee

by normal-coder
Verified
  • operations
import { z } from "zod"; import { giteeRequest, validateOwnerName, validateRepositoryName, getGiteeApiBaseUrl } from "../common/utils.js"; import { GiteeIssueCommentSchema, GiteeIssueSchema } from "../common/types.js"; // Schema definitions export const CreateIssueSchema = z.object({ // 仓库所属空间地址 (企业、组织或个人的地址 path) owner: z.string().describe("Repository owner path (enterprise, organization, or personal path)"), // 仓库路径 (path) repo: z.string().describe("Repository path"), // Issue 标题 title: z.string().describe("Issue title"), // Issue 内容 body: z.string().optional().describe("Issue content"), // Issue 分配的用户 assignees: z.array(z.string()).optional().describe("Users assigned to the issue"), // 里程碑 ID milestone: z.number().optional().describe("Milestone ID"), // 标签 labels: z.array(z.string()).optional().describe("Labels"), // 是否是私有 Issue,默认为 false security_hole: z.boolean().optional().describe("Whether the issue is private, default is false"), }); export const ListIssuesOptionsSchema = z.object({ // 仓库所属空间地址 (企业、组织或个人的地址 path) owner: z.string().describe("Repository owner path (enterprise, organization, or personal path)"), // 仓库路径 (path) repo: z.string().describe("Repository path"), // Issue 状态 state: z.enum(["open", "closed", "all"]).default("open").optional().describe("Issue state"), // 排序字段 sort: z.enum(["created", "updated", "comments"]).default("created").optional().describe("Sort field"), // 排序方向 direction: z.enum(["asc", "desc"]).default("desc").optional().describe("Sort direction"), // 里程碑 ID milestone: z.number().optional().describe("Milestone ID"), // 标签,多个标签以逗号分隔 labels: z.string().optional().describe("Labels, multiple labels separated by commas"), // 当前的页码 page: z.number().int().default(1).optional().describe("Page number"), // 每页的数量,最大为 100 per_page: z.number().int().min(1).max(100).optional().describe("Number of items per page, maximum 100"), // 筛选指定用户负责的 Issue assignee: z.string().optional().describe("Filter issues assigned to a specific user"), // 筛选指定用户创建的 Issue creator: z.string().optional().describe("Filter issues created by a specific user"), // 筛选指定项目的 Issue program: z.string().optional().describe("Filter issues for a specific program"), }); export const GetIssueSchema = z.object({ // 仓库所属空间地址 (企业、组织或个人的地址 path) owner: z.string().describe("Repository owner path (enterprise, organization, or personal path)"), // 仓库路径 (path) repo: z.string().describe("Repository path"), // Issue 编号 issue_number: z.union([z.number(), z.string()]).describe("Issue number"), }); export const UpdateIssueOptionsSchema = z.object({ // 仓库所属空间地址 (企业、组织或个人的地址 path) owner: z.string().describe("Repository owner path (enterprise, organization, or personal path)"), // 仓库路径 (path) repo: z.string().describe("Repository path"), // Issue 编号 issue_number: z.union([z.number(), z.string()]).describe("Issue number"), // Issue 标题 title: z.string().optional().describe("Issue title"), // Issue 内容 body: z.string().optional().describe("Issue content"), // Issue 分配的用户 assignees: z.array(z.string()).optional().describe("Users assigned to the issue"), // 里程碑 ID milestone: z.number().optional().describe("Milestone ID"), // 标签 labels: z.array(z.string()).optional().describe("Labels"), // Issue 状态 state: z.enum(["open", "closed", "progressing"]).optional().describe("Issue state"), }); export const IssueCommentSchema = z.object({ // 仓库所属空间地址 (企业、组织或个人的地址 path) owner: z.string().describe("Repository owner path (enterprise, organization, or personal path)"), // 仓库路径 (path) repo: z.string().describe("Repository path"), // Issue 编号 issue_number: z.union([z.number(), z.string()]).describe("Issue number"), // 评论内容 body: z.string().describe("Comment content"), }); // Type exports export type CreateIssueOptions = z.infer<typeof CreateIssueSchema>; export type ListIssuesOptions = z.infer<typeof ListIssuesOptionsSchema>; export type GetIssueOptions = z.infer<typeof GetIssueSchema>; export type UpdateIssueOptions = z.infer<typeof UpdateIssueOptionsSchema>; export type IssueCommentOptions = z.infer<typeof IssueCommentSchema>; // Function implementations export async function createIssue( owner: string, repo: string, options: Omit<CreateIssueOptions, "owner" | "repo"> ) { owner = validateOwnerName(owner); repo = validateRepositoryName(repo); // Create the request body const body: Record<string, any> = { ...options, repo: repo, }; // If `assignees` is an array, convert it to a comma-separated string. if (Array.isArray(body.assignees) && body.assignees.length > 0) { body.assignees = body.assignees.join(','); } else if (Array.isArray(body.assignees) && body.assignees.length === 0) { // If `assignees` is an empty array, delete the field. delete body.assignees; } // If `labels` is an array, convert it to a comma-separated string. if (Array.isArray(body.labels) && body.labels.length > 0) { body.labels = body.labels.join(','); } else if (Array.isArray(body.labels) && body.labels.length === 0) { // If `labels` is an empty array, delete the field. delete body.labels; } const url = `/repos/${owner}/${repo}/issues`; const response = await giteeRequest(url, "POST", body); return GiteeIssueSchema.parse(response); } export async function listIssues( owner: string, repo: string, options: Omit<ListIssuesOptions, "owner" | "repo"> ) { owner = validateOwnerName(owner); repo = validateRepositoryName(repo); const url = new URL(`${getGiteeApiBaseUrl()}/repos/${owner}/${repo}/issues`); // Add query parameters Object.entries(options).forEach(([key, value]) => { if (value !== undefined) { url.searchParams.append(key, value.toString()); } }); const response = await giteeRequest(url.toString()); return z.array(GiteeIssueSchema).parse(response); } export async function getIssue( owner: string, repo: string, issueNumber: number | string ) { owner = validateOwnerName(owner); repo = validateRepositoryName(repo); const url = `/repos/${owner}/${repo}/issues/${issueNumber}`; const response = await giteeRequest(url); return GiteeIssueSchema.parse(response); } export async function updateIssue( owner: string, repo: string, issueNumber: number | string, options: Omit<UpdateIssueOptions, "owner" | "repo" | "issue_number"> ) { owner = validateOwnerName(owner); repo = validateRepositoryName(repo); // Create the request body const body: Record<string, any> = { ...options, repo: repo // Add repo as a request body parameter }; // If `assignees` is an array, convert it to a comma-separated string. if (Array.isArray(body.assignees) && body.assignees.length > 0) { body.assignees = body.assignees.join(','); } else if (Array.isArray(body.assignees) && body.assignees.length === 0) { // If `assignees` is an empty array, delete the field. delete body.assignees; } // If `labels` is an array, convert it to a comma-separated string. if (Array.isArray(body.labels) && body.labels.length > 0) { body.labels = body.labels.join(','); } else if (Array.isArray(body.labels) && body.labels.length === 0) { // If `labels` is an empty array, delete the field. delete body.labels; } // Note: In the Gitee API's update issue interface, `repo` is passed as a form parameter, not as a path parameter. const url = `/repos/${owner}/issues/${issueNumber}`; const response = await giteeRequest(url, "PATCH", body); return GiteeIssueSchema.parse(response); } export async function addIssueComment( owner: string, repo: string, issueNumber: number | string, body: string ) { owner = validateOwnerName(owner); repo = validateRepositoryName(repo); const url = `/repos/${owner}/${repo}/issues/${issueNumber}/comments`; const response = await giteeRequest(url, "POST", { body }); return GiteeIssueCommentSchema.parse(response); }