UNPKG

@iflow-mcp/alibabacloud-devops-mcp-server

Version:

MCP Server for using the alibabacloud-devops API: allows AI assistants to directly participate in development collaboration, helping teams optimize development processes and improve efficiency.

446 lines (445 loc) 15.9 kB
import { buildUrl, yunxiaoRequest } from "../../common/utils.js"; import { WorkItemSchema } from "../../common/types.js"; import { getCurrentUserFunc } from "../organization/organization.js"; export async function getWorkItemFunc(organizationId, workItemId) { const url = `/oapi/v1/projex/organizations/${organizationId}/workitems/${workItemId}`; const response = await yunxiaoRequest(url, { method: "GET", }); return WorkItemSchema.parse(response); } export async function searchWorkitemsFunc(organizationId, category, spaceId, subject, status, createdAfter, createdBefore, updatedAfter, updatedBefore, creator, assignedTo, advancedConditions, orderBy = "gmtCreate", includeDetails = false // 新增参数:是否自动补充缺失的description等详细信息 ) { // 处理assignedTo为"self"的情况,自动获取当前用户ID let finalAssignedTo = assignedTo; let finalCreator = creator; if (assignedTo === "self" || creator === "self") { try { const currentUser = await getCurrentUserFunc(); if (currentUser.id) { if (assignedTo === "self") { finalAssignedTo = currentUser.id; } if (creator === "self") { finalCreator = currentUser.id; } } else { finalAssignedTo = assignedTo; finalCreator = creator; } } catch (error) { finalAssignedTo = assignedTo; finalCreator = creator; } } const url = `/oapi/v1/projex/organizations/${organizationId}/workitems:search`; const payload = { category: category, spaceId: spaceId, }; const conditions = buildWorkitemConditions({ subject, status, createdAfter, createdBefore, updatedAfter, updatedBefore, creator: finalCreator, assignedTo: finalAssignedTo, advancedConditions }); if (conditions) { payload.conditions = conditions; } payload.orderBy = orderBy; const response = await yunxiaoRequest(url, { method: "POST", body: payload, }); if (!Array.isArray(response)) { return []; } const workItems = response.map(workitem => WorkItemSchema.parse(workitem)); // 如果需要补充详细信息,使用分批并发方式获取 if (includeDetails) { const itemsNeedingDetails = workItems.filter(item => item.id.length > 0 && (item.description === null || item.description === undefined || item.description === "")); if (itemsNeedingDetails.length > 0) { // 分批并发获取详情 const descriptionMap = await batchGetWorkItemDetails(organizationId, itemsNeedingDetails); // 更新workItems中的description return workItems.map(item => { if (descriptionMap.has(item.id)) { return { ...item, description: descriptionMap.get(item.id) || item.description }; } return item; }); } } return workItems; } // 分批并发获取工作项详情 async function batchGetWorkItemDetails(organizationId, workItems, batchSize = 10, // 每批处理10个 maxItems = 100 // 最多处理100个 ) { const descriptionMap = new Map(); // 限制处理数量 const limitedItems = workItems.slice(0, maxItems); // 分批处理 for (let i = 0; i < limitedItems.length; i += batchSize) { const batch = limitedItems.slice(i, i + batchSize); // 批次内并发执行 const batchResults = await Promise.allSettled(batch.map(async (item) => { // 再次检查item.id是否为有效字符串 if (typeof item.id !== 'string' || item.id.length === 0) { return { id: item.id || 'unknown', description: null, success: false }; } const itemId = item.id; try { const detailedItem = await getWorkItemFunc(organizationId, itemId); return { id: itemId, description: detailedItem.description, success: true }; } catch (error) { return { id: itemId, description: null, success: false }; } })); // 处理批次结果 batchResults.forEach((result) => { if (result.status === 'fulfilled') { // 确保description类型正确,将undefined转换为null const description = result.value.description === undefined ? null : result.value.description; descriptionMap.set(result.value.id, description); } }); } return descriptionMap; } function buildWorkitemConditions(args) { if (args.advancedConditions) { return args.advancedConditions; } const filterConditions = []; if (args.subject) { filterConditions.push({ className: "string", fieldIdentifier: "subject", format: "input", operator: "CONTAINS", toValue: null, value: [args.subject], }); } if (args.status) { const statusValues = args.status.split(","); const values = statusValues.map(v => v.trim()); filterConditions.push({ className: "status", fieldIdentifier: "status", format: "list", operator: "CONTAINS", toValue: null, value: values, }); } if (args.createdAfter) { const createdBefore = args.createdBefore ? `${args.createdBefore} 23:59:59` : null; filterConditions.push({ className: "dateTime", fieldIdentifier: "gmtCreate", format: "input", operator: "BETWEEN", toValue: createdBefore, value: [`${args.createdAfter} 00:00:00`], }); } if (args.updatedAfter) { const updatedBefore = args.updatedBefore ? `${args.updatedBefore} 23:59:59` : null; filterConditions.push({ className: "dateTime", fieldIdentifier: "gmtModified", format: "input", operator: "BETWEEN", toValue: updatedBefore, value: [`${args.updatedAfter} 00:00:00`], }); } if (args.creator) { const creatorValues = args.creator.split(","); const values = creatorValues.map(v => v.trim()); filterConditions.push({ className: "user", fieldIdentifier: "creator", format: "list", operator: "CONTAINS", toValue: null, value: values, }); } if (args.assignedTo) { const assignedToValues = args.assignedTo.split(","); const values = assignedToValues.map(v => v.trim()); filterConditions.push({ className: "user", fieldIdentifier: "assignedTo", format: "list", operator: "CONTAINS", toValue: null, value: values, }); } if (filterConditions.length === 0) { return undefined; } const conditions = { conditionGroups: [filterConditions], }; return JSON.stringify(conditions); } export async function createWorkItemFunc(organizationId, assignedTo, spaceId, subject, workitemTypeId, customFieldValues, description, labels, parentId, participants, sprint, trackers, verifier, versions) { const url = `/oapi/v1/projex/organizations/${organizationId}/workitems`; const payload = { assignedTo, spaceId, subject, workitemTypeId }; if (customFieldValues) { payload.customFieldValues = customFieldValues; } if (description !== undefined) { payload.description = description; } if (labels && labels.length > 0) { payload.labels = labels; } if (parentId !== undefined) { payload.parentId = parentId; } if (participants && participants.length > 0) { payload.participants = participants; } if (sprint !== undefined) { payload.sprint = sprint; } if (trackers && trackers.length > 0) { payload.trackers = trackers; } if (verifier !== undefined) { payload.verifier = verifier; } if (versions && versions.length > 0) { payload.versions = versions; } const response = await yunxiaoRequest(url, { method: "POST", body: payload, }); return WorkItemSchema.parse(response); } export async function getWorkItemTypesFunc(organizationId, id, // 项目唯一标识 category // 工作项类型,可选值为 Req,Bug,Task 等。 ) { const url = `/oapi/v1/projex/organizations/${organizationId}/projects/${id}/workitemTypes?category=${encodeURIComponent(category)}`; const response = await yunxiaoRequest(url, { method: "GET", }); return response; } /** * 列出所有工作项类型 * @param organizationId 企业ID * @returns 工作项类型列表 */ export async function listAllWorkItemTypesFunc(organizationId) { const url = `/oapi/v1/projex/organizations/${organizationId}/workitemTypes`; const response = await yunxiaoRequest(url, { method: "GET", }); // 确保返回的是数组格式 if (Array.isArray(response)) { return response; } // 如果响应中包含result字段,则返回result中的数据 if (response && typeof response === 'object' && 'result' in response && Array.isArray(response.result)) { return response.result; } // 其他情况返回空数组 return []; } /** * 列出工作项类型 * @param organizationId 企业ID * @param spaceIdentifier 项目唯一标识 * @param category 工作项类型分类(可选) * @returns 工作项类型列表 */ export async function listWorkItemTypesFunc(organizationId, spaceIdentifier, category) { let url = `/oapi/v1/projex/organizations/${organizationId}/projects/${spaceIdentifier}/workitemTypes`; // 如果提供了category参数,则添加到URL中 if (category) { url += `?category=${encodeURIComponent(category)}`; } const response = await yunxiaoRequest(url, { method: "GET", }); // 确保返回的是数组格式 if (Array.isArray(response)) { return response; } // 如果响应中包含result字段,则返回result中的数据 if (response && typeof response === 'object' && 'result' in response && Array.isArray(response.result)) { return response.result; } // 其他情况返回空数组 return []; } /** * 获取工作项类型详情 * @param organizationId 企业ID * @param spaceIdentifier 项目唯一标识 * @param id 工作项类型ID * @returns 工作项类型详情 */ export async function getWorkItemTypeFunc(organizationId, id) { const url = `/oapi/v1/projex/organizations/${organizationId}/workitemTypes/${id}`; const response = await yunxiaoRequest(url, { method: "GET", }); // 如果响应中包含result字段,则返回result中的数据 if (response && typeof response === 'object' && 'result' in response) { return response.result; } // 否则直接返回响应 return response; } /** * 列出工作项关联的工作项类型 * @param organizationId 企业ID * @param spaceIdentifier 项目唯一标识 * @param workItemTypeId 工作项ID * @param relationType 关联类型 (BLOCK, RELATE, DUPLICATE, CHILD) * @returns 关联的工作项类型列表 */ export async function listWorkItemRelationWorkItemTypesFunc(organizationId, workItemTypeId, relationType) { const url = `/oapi/v1/projex/organizations/${organizationId}/workitemTypes/${workItemTypeId}/relationWorkitemTypes`; const queryParams = {}; if (relationType != null) { queryParams.relationType = relationType; } let finalUrl = buildUrl(url, queryParams); const response = await yunxiaoRequest(finalUrl, { method: "GET", }); // 确保返回的是数组格式 if (Array.isArray(response)) { return response; } // 如果响应中包含result字段,则返回result中的数据 if (response && typeof response === 'object' && 'result' in response && Array.isArray(response.result)) { return response.result; } // 其他情况返回空数组 return []; } /** * 获取工作项类型字段配置 * @param organizationId 企业ID * @param projectId 项目唯一标识 * @param workItemTypeId 工作项类型ID * @returns 工作项类型字段配置 */ export async function getWorkItemTypeFieldConfigFunc(organizationId, projectId, workItemTypeId) { const url = `/oapi/v1/projex/organizations/${organizationId}/projects/${projectId}/workitemTypes/${workItemTypeId}/fields`; const response = await yunxiaoRequest(url, { method: "GET", }); // 如果响应中包含result字段,则返回result中的数据 if (response && typeof response === 'object' && 'result' in response) { return response.result; } // 否则直接返回响应 return response; } /** * 获取工作项工作流 * @param organizationId 企业ID * @param projectId 项目唯一标识 * @param workItemTypeId 工作项类型ID * @returns 工作项工作流信息 */ export async function getWorkItemWorkflowFunc(organizationId, projectId, workItemTypeId) { const url = `/oapi/v1/projex/organizations/${organizationId}/projects/${projectId}/workitemTypes/${workItemTypeId}/workflows`; const response = await yunxiaoRequest(url, { method: "GET", }); // 如果响应中包含result字段,则返回result中的数据 if (response && typeof response === 'object' && 'result' in response) { return response.result; } // 否则直接返回响应 return response; } /** * 列出工作项评论 * @param organizationId 企业ID * @param workItemId 工作项ID * @param page 页码 * @param perPage 每页条数 * @returns 工作项评论列表 */ export async function listWorkItemCommentsFunc(organizationId, workItemId, page = 1, perPage = 20) { const url = `/oapi/v1/projex/organizations/${organizationId}/workitems/${workItemId}/comments?page=${page}&perPage=${perPage}`; const response = await yunxiaoRequest(url, { method: "GET", }); // 确保返回的是数组格式 if (Array.isArray(response)) { return response; } // 如果响应中包含result字段,则返回result中的数据 if (response && typeof response === 'object' && 'result' in response && Array.isArray(response.result)) { return response.result; } // 其他情况返回空数组 return []; } /** * 创建工作项评论 * @param organizationId 企业ID * @param workItemId 工作项ID * @param content 评论内容 * @returns 创建的评论信息 */ export async function createWorkItemCommentFunc(organizationId, workItemId, content) { const url = `/oapi/v1/projex/organizations/${organizationId}/workitems/${workItemId}/comments`; const payload = { content: content }; const response = await yunxiaoRequest(url, { method: "POST", body: payload, }); // 如果响应中包含result字段,则返回result中的数据 if (response && typeof response === 'object' && 'result' in response) { return response.result; } // 否则直接返回响应 return response; }