问题描述
我需要列出属于组织内特定组的用户数据。 documentation 没有指定这是否可行。我真的希望可以有某种查询允许这样做。例如email in ([email protected],[email protected])
。但是,我认为这是不可能的。我能想到的唯一方法是:
-
获取群组中所有成员的列表 (https://developers.google.com/admin-sdk/directory/reference/rest/v1/members/list)
-
通过电子邮件 (https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/get) 获取每个用户数据
上述方法的问题在于,如果一个组包含 50 多个成员,这意味着我必须发出所有这些请求,这会适得其反。想象一下这需要多长时间。
有什么想法吗?非常感谢。
解决方法
不幸的是,我认为您不能跳过这两个步骤的过程,但您可以使用 batch requests 加快速度。这 允许您在单个请求中请求最多 1000 个呼叫。步骤是:
- 发出批量请求以获取您想要的所有群组的所有成员(使用
members.list
)。 - 发出批处理请求以使用他们的 ID(使用
user.get
)获取您需要的所有用户信息。
注意,结果中的数据不会被排序,但会被标记为Content-ID
。
参考文献
- Sending Batch Requests (Directory API)
- Method: members.list (Directory API)
- Method: users.get (Directory API)
我在发布问题几个小时后考虑了批处理请求。 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;
}