如何通过TypeScript调用Azure DevOps REST API获取指定项目的所有用户列表
如何通过TypeScript调用Azure DevOps REST API获取指定项目的所有用户列表
嗨,我来帮你搞定这个问题!你说得没错,目前Azure DevOps的扩展API里确实没有直接的listUsers(projectId)方法,但其实有两种靠谱的方式实现获取指定项目所有用户的需求,咱们一步步来:
一、使用官方azure-devops-extension-api客户端实现(推荐)
其实你可能没注意到,azure-devops-extension-api里已经提供了相关的客户端方法,咱们可以通过项目的内置安全组来间接获取所有成员——因为每个项目都会自动创建一个「Project Valid Users」组,所有项目成员(包括直接添加和通过团队加入的)都会在这个组里。
具体步骤:
获取项目内置安全组的ID
先调用GraphRestClient的方法找到项目对应的「Project Valid Users」组:import { GraphRestClient, GraphGroup } from "azure-devops-extension-api/Graph"; import { getClient } from "azure-devops-extension-api"; async function getProjectValidUsersGroupId(projectId: string): Promise<string> { const graphClient = getClient(GraphRestClient); // 过滤出项目的内置安全组 const groups = await graphClient.getGroups({ projectId: projectId, filter: "groupType eq 'ProjectSecurity'" }); // 找到「Project Valid Users」组 const validUsersGroup = groups.find(group => group.displayName.includes("Valid Users")); if (!validUsersGroup) { throw new Error("未找到项目的Valid Users安全组"); } return validUsersGroup.descriptor!; }获取安全组的所有成员
用MembershipRestClient获取组内成员,同时递归展开子组(避免遗漏通过嵌套组加入的用户):import { MembershipRestClient, GraphMember } from "azure-devops-extension-api/Graph"; async function getGroupMembers(groupDescriptor: string, graphClient: GraphRestClient): Promise<GraphMember[]> { const membershipClient = getClient(MembershipRestClient); // 获取组的直接成员 const memberships = await membershipClient.getMemberships(groupDescriptor, "direct"); const members: GraphMember[] = []; for (const membership of Object.values(memberships)) { if (membership.memberDescriptor) { const member = await graphClient.getMember(membership.memberDescriptor); // 区分用户和子组,子组递归处理 if (member.subjectKind === "user") { members.push(member); } else if (member.subjectKind === "group") { const nestedMembers = await getGroupMembers(member.descriptor!, graphClient); members.push(...nestedMembers); } } } return members; }整合完整的业务函数
把上面的方法组合起来,就是你需要的getAllProjectMembers:export async function getAllProjectMembers(): Promise<GraphMember[]> { const projectId = await getCurrentProjectId(); // 你已有的获取当前项目ID的方法 const graphClient = getClient(GraphRestClient); const validUsersGroupId = await getProjectValidUsersGroupId(projectId); const projectMembers = await getGroupMembers(validUsersGroupId, graphClient); // 去重,避免同一用户通过多个组加入的重复情况 const uniqueMembers = Array.from(new Map(projectMembers.map(m => [m.descriptor, m])).values()); return uniqueMembers; }
二、直接构造HTTP请求实现(适合特殊场景)
如果官方客户端的方法在你的API版本里有差异,或者你更倾向于直接调用REST API,也可以用TypeScript的fetch方法手动实现:
import { getCurrentProjectId } from "./your-utils"; // 替换成你的工具方法路径 export async function getAllProjectMembers(): Promise<any[]> { const projectId = await getCurrentProjectId(); const accessToken = await VSS.getAccessToken(); const orgUrl = VSS.getWebContext().collection.uri; // 第一步:获取项目的Valid Users组 const groupsUrl = `${orgUrl}_apis/graph/groups?api-version=7.1-preview.1&projectId=${projectId}&$filter=groupType eq 'ProjectSecurity'`; const groupsResponse = await fetch(groupsUrl, { headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json" } }); const groupsData = await groupsResponse.json(); const validUsersGroup = groupsData.value.find((group: any) => group.displayName.includes("Valid Users")); if (!validUsersGroup) { throw new Error("未找到项目的Valid Users安全组"); } const groupDescriptor = validUsersGroup.descriptor; // 第二步:递归获取组内所有成员 async function fetchGroupMembers(descriptor: string): Promise<any[]> { const membersUrl = `${orgUrl}_apis/graph/memberships/${descriptor}?api-version=7.1-preview.1&direction=down`; const membersResponse = await fetch(membersUrl, { headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json" } }); const membersData = await membersResponse.json(); const members: any[] = []; for (const membership of Object.values(membersData.value)) { const memberDescriptor = (membership as any).memberDescriptor; // 获取成员详情 const memberUrl = `${orgUrl}_apis/graph/users/${memberDescriptor}?api-version=7.1-preview.1`; const memberResponse = await fetch(memberUrl, { headers: { "Authorization": `Bearer ${accessToken}` } }); const member = await memberResponse.json(); if (member.subjectKind === "user") { members.push(member); } else if (member.subjectKind === "group") { const nestedMembers = await fetchGroupMembers(member.descriptor); members.push(...nestedMembers); } } return members; } const allMembers = await fetchGroupMembers(groupDescriptor); // 去重处理 const uniqueMembers = Array.from(new Map(allMembers.map(m => [m.descriptor, m])).values()); return uniqueMembers; }
补充说明:
为什么不直接调用组织级的用户列表API?因为Azure DevOps的用户是组织维度的,项目成员本质是通过安全组关联的,通过项目内置组获取的成员列表是最准确的,能覆盖所有直接或间接加入项目的用户。如果只需要某个团队的成员,替换成团队对应的安全组即可,逻辑完全一致。
备注:内容来源于stack exchange,提问作者Tomasz Małachowski
相关产品推荐
相关产品推荐

