Google 用户:列出特定群组的用户数据

问题描述

我需要列出属于组织内特定组的用户数据。 documentation 没有指定这是否可行。我真的希望可以有某种查询允许这样做。例如email in ([email protected],[email protected])。但是,我认为这是不可能的。我能想到的唯一方法是:

  1. 获取群组中所有成员的列表 (https://developers.google.com/admin-sdk/directory/reference/rest/v1/members/list)

  2. 通过电子邮件 (https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/get) 获取每个用户数据

上述方法的问题在于,如果一个组包含 50 多个成员,这意味着我必须发出所有这些请求,这会适得其反。想象一下这需要多长时间。

有什么想法吗?非常感谢。

解决方法

不幸的是,我认为您不能跳过这两个步骤的过程,但您可以使用 batch requests 加快速度。这 允许您在单个请求中请求最多 1000 个呼叫。步骤是:

  1. 发出批量请求以获取您想要的所有群组的所有成员(使用 members.list)。
  2. 发出批处理请求以使用他们的 ID(使用 user.get)获取您需要的所有用户信息。

注意,结果中的数据不会被排序,但会被标记为Content-ID

参考文献

,

我在发布问题几个小时后考虑了批处理请求。 Node JS 的问题在于它没有内置对批处理请求的支持,例如与 php 客户端库不同;因此,由于找不到任何示例,我不得不花一些时间自己实现对它的支持。我会分享解决方案,以防它对其他人有帮助或供我将来参考。

async function getGroupMembersData(){
  const groupEmail = "[email protected]"; //google group email
  const groupMembers = await getGroupMembers(groupEmail).catch(error=>{
    console.error(`Error querying group members: ${error.toString()}`);
  });
  if(!groupMembers){ return; }
  const url = "https://www.googleapis.com/batch/admin/directory_v1";
  const scopes = ["https://www.googleapis.com/auth/admin.directory.user.readonly"];
  const requests = [];
  for(let i=0; i<groupMembers.length; ++i){
    const user = groupMembers[i];
    const request = {
      email: user,endpoint: `GET directory_v1/admin/directory/v1/users/${user}?fields=*`
    };
    requests.push(request);
  }
  const batchRequestData = await batchProcess(url,scopes,requests).catch(error=>{
    console.error(`Error processing batch request: ${error.toString()}`);
  });
  if(!batchRequestData){ return; }
  const usersList = batchRequestData.map(i=>{
    return i.responseBody;
  });
  console.log(usersList);
}

//get group members using group email address
async function getGroupMembers(groupKey){
  const client = await getClient(scopes); //function to get an authorized client,you have to implement on your own
  const service = google.admin({version: "directory_v1",auth: client});
  const request = await service.members.list({
    groupKey,fields: "members(email)",maxResults: 200
  });
  const members = !!request.data.members ? request.data.members.map(i=>i.email) : [];
  return members;
}

//batch request processing in groups of 100
async function batchProcess(batchUrl,requests){
  const client = await getClient(scopes); //function to get an authorized client,you have to implement on your own
  let results = [];
  const boundary = "foobar99998888"; //boundary line definition
  let batchBody = ""; const nl = "\n";
  const batchLimit = 100; //define batch limit (max supported = 100)
  const totalRounds = Math.ceil(requests.length / batchLimit);
  let batchRound = 1;
  let batchItem = 0;
  let roundLimit = batchLimit;
  do{
    roundLimit = roundLimit < requests.length ? roundLimit : requests.length;
    //build the batch request body
    for(batchItem; batchItem<roundLimit; batchItem++){
      const requestData = requests[batchItem];
      batchBody += `--${boundary}${nl}`;
      batchBody += `Content-Type: application/http${nl}`;
      batchBody += `Content-Id: <myapprequest-${requestData.email}>${nl}${nl}`;
      batchBody += `${requestData.endpoint}${nl}`;
    }
    batchBody += `--${boundary}--`;
    //send the batch request
    const batchRequest = await client.request({
      url: batchUrl,method: "POST",headers: {
        "Content-Type": `multipart/mixed; boundary=${boundary}`
      },body: batchBody
    }).catch(error=>{
      console.log("Error processing batch request: " + error);
    });
    //parse the batch request response
    if(!!batchRequest){
      const batchResponseData = batchRequest.data;
      const responseBoundary = batchRequest.headers["content-type"].split("; ")[1].replace("boundary=","");
      const httpResponses = batchResponseParser(batchResponseData,responseBoundary);
      results.push(...httpResponses);
    }
    batchRound++;
    roundLimit += batchLimit;
  } while(batchRound <= totalRounds);
  return results;
};

//batch response parser
function batchResponseParser(data,boundary){
  const nl = "\r\n";
  data = data.replace(`--${boundary}--`,"");
  const responses = data.split(`--${boundary}`);
  responses.shift();
  const formattedResponses = responses.map(i=>{
    const parts = i.split(`${nl}${nl}`);
    const responseMetaParts = (parts[0].replace(nl,"")).split(nl);
    let responseMeta = {};
    responseMetaParts.forEach(part=>{
      const objectParts = part.split(":");
      responseMeta[objectParts[0].trim()] = objectParts[1].trim();
    });
    const responseHeadersParts = parts[1].split(nl);
    let responseHeaders = {};
    responseHeadersParts.forEach(part=>{
      if(part.indexOf("HTTP/1.1") > -1){
        responseHeaders.status = part;
      } else {
        const objectParts = part.split(":");
        responseHeaders[objectParts[0].trim()] = objectParts[1].trim();
      }
    });
    const reg = new RegExp(`${nl}`,"g");
    const responseBody = JSON.parse(parts[2].replace(reg,""));
    const formatted = {
      responseMeta: responseMeta,responseHeaders: responseHeaders,responseBody: responseBody
    };
    return formatted;
  });
  return formattedResponses;
}