JavaScript 中 Amazon Cognito API 的所有用户 select 详尽无遗的安全且可扩展的方法是什么?
What is a safe and scalable way to exhaustively select all users from Amazon Cognito API in JavaScript?
我是一个小团队的一员,在一个有用户帐户的相当小的网站上工作;此时大约有 100 个用户。我们正在使用 Amazon Cognito 进行用户管理。在我们的网站上有一个摘要页面,其中显示了所有用户和各种属性的 list/table。但是,there's a hard limit 由 Amazon Cognito listUsers API 调用 return 编辑的项目数量,在本例中为 60。
幸运的是,API 调用还 return 是一个令牌,用于在有更多用户时进行后续调用。使用这些令牌,我们能够请求所有用户的完整列表。
另外,我们的网站使用了 react-redux 和 javascript 库 bluebird。在这个问题的上下文中,请求所有用户列表的组件也分派操作(redux 片段)并且来自亚马逊 aws-sdk 的 CognitoIdentityServiceProvider 被传递给 bluebird Promise.promisfyAll(将异步后缀添加到 listUser 调用)。我正在修改此组件以获取整个用户列表。
作为请求整个用户列表的粗略草稿,我使用递归函数将承诺链接在一起,直到来自亚马逊的分页令牌 return 未定义并将结果存储在一些 class 变量。我用这个 forum post as reference.
这行得通,但我对实施感觉不太好;现在在我们的用例中应该没问题,因为我们有大约 100 个用户,但我不知道这是否可以很好地扩展到数千个或更多用户。我知道递归足够危险,因为我不知道当 promises/calls 的数量增加时这种技术是否会导致问题。我想到了开销和内存管理的问题。但是,我们不必担心用户数量至少在短时间内飙升至数千,但我仍然想了解可能更可取的 and/or 更安全的方法来实现同样的目标。
以下片段来自请求用户列表的组件:
import Promise from 'bluebird';
import { CognitoIdentityServiceProvider } from './services/aws';
export const fetchRegisteredUsers = () => (dispatch) => {
...
let allUsersTemp = [];
let paginationToken;
const getUserList = () => {
let tempUserTest = CognitoIdentityServiceProvider.listUsersAsync({
UserPoolId: process.env.COGNITO_USER_POOL_ID,
PaginationToken: paginationToken
});
return tempUserTest.then((tempUser) => {
allUsersTemp = allUsersTemp.concat(tempUser.Users);
paginationToken = tempUser.PaginationToken;
if(paginationToken) {
return getUserList();
} else {
return;
}
})
}
const adminUsers = CognitoIdentityServiceProvider.listUsersInGroupAsync(adminParams);
return Promise.all([adminUsers]).then(
([{ Users: adminUsers }]) => {
getUserList().then((item) => {
let allUsers = allUsersTemp;
const adminUsernames = adminUsers.map(user => user.Username);
allUsers.forEach(user => {
let mappedAttributes = {};
user.Attributes.forEach(attribute => mappedAttributes[attribute.Name] = attribute.Value);
user.Attributes = mappedAttributes;
user.isAdmin = adminUsernames.includes(user.Username);
});
dispatch({
type: FETCH_REGISTERED_USERS_SUCCESS,
payload: allUsers
});
});
}, ...
根据我的理解,bluebird Promise.all 调用会等待传递给它的每个项目都可以解析,然后再执行 .then 部分中的内容;在对代码进行任何更改之前,有两个列表,一个用于用户,一个用于管理员。 Promise.all 等到两个承诺都完成后,对数据进行了一些基本处理,然后 return 在动作调度负载中编辑了数据。
经过我的更改,处理和return用户列表的逻辑在递归承诺链 (getUserList) 完成后执行。
我的问题: 这种技术可以吗?我可以按原样使用吗?还是它不安全,如果是,具体是什么问题?还有更好的方法吗?
我的弱点是递归和 promises/bluebird 所以请随时批评与这些主题相关的任何代码
I understand recursion just enough to be dangerous in that I do not know if this technique can cause problems when the number of promises/calls goes up. Questions of overhead and memory management come to mind
.
唯一可能的内存问题是您的 allUsersTemp
数组可能会变得非常大,直到超过浏览器的限制。然而,您应该很久以前就想知道在一个大的 table 中显示百分之一万的用户条目是否真的有用。在内存会出现问题的规模上,无论如何,您都需要更有效的工具来管理您的用户群,而不仅仅是列出所有用户。
关于代码风格,我建议不要将 allUsersTemp
和 paginationToken
声明为 mutable 更高范围的变量。而是将它们作为递归函数的参数并用结果履行承诺:
function getUserList (paginationToken, allUsersTemp = []) => {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
return tempUserTest = CognitoIdentityServiceProvider.listUsersAsync({
UserPoolId: process.env.COGNITO_USER_POOL_ID,
PaginationToken: paginationToken
}).then(tempUser => {
const allUsers = allUsersTemp.concat(tempUser.Users);
const nextToken = tempUser.PaginationToken;
if (nextToken) {
return getUserList(nextToken, allUsers);
// ^^^^^^^^^^^^^^^^^^^
} else {
return allUsers;
// ^^^^^^^^
}
});
}
The Promise.all
waited until both promises were done
不,您将只有一个承诺的数组传递给 Promise.all
,并在第一个函数完成后调用第二个函数。要同时制作 运行,它应该看起来像
return Promise.all([
CognitoIdentityServiceProvider.listUsersInGroupAsync(adminParams),
getUserList()
]).then(([{ Users: adminUsers }, allUsers]) => {
…
});
我知道已经有一段时间了,但这就是我在 Cognito 中扫描所有用户的方式。希望这会有所帮助
const getListUserCognito = async () => {
try {
const params = {
UserPoolId: userPoolID
};
console.log('params', JSON.stringify(params));
const listUserResp = await getAllUserCognito(params);
console.log('listUserResp', JSON.stringify(listUserResp));
return listUserResp.Users;
} catch (error) {
return null;
}
};
const getAllUserCognito = async (params) => {
try {
// string must not be empty
let paginationToken = 'notEmpty';
let itemsAll = {
Users: []
};
while (paginationToken) {
const data = await cognitoidentityserviceprovider
.listUsers(params)
.promise();
const { Users } = data;
itemsAll = {
...data,
...{ Users: [...itemsAll.Users, ...(Users ? [...Users] : [])] }
};
paginationToken = data.PaginationToken;
if (paginationToken) {
params.PaginationToken = paginationToken;
}
}
return itemsAll;
} catch (err) {
console.error(
'Unable to scan the cognito pool users. Error JSON:',
JSON.stringify(err, null, 2)
);
}
};
我是一个小团队的一员,在一个有用户帐户的相当小的网站上工作;此时大约有 100 个用户。我们正在使用 Amazon Cognito 进行用户管理。在我们的网站上有一个摘要页面,其中显示了所有用户和各种属性的 list/table。但是,there's a hard limit 由 Amazon Cognito listUsers API 调用 return 编辑的项目数量,在本例中为 60。
幸运的是,API 调用还 return 是一个令牌,用于在有更多用户时进行后续调用。使用这些令牌,我们能够请求所有用户的完整列表。
另外,我们的网站使用了 react-redux 和 javascript 库 bluebird。在这个问题的上下文中,请求所有用户列表的组件也分派操作(redux 片段)并且来自亚马逊 aws-sdk 的 CognitoIdentityServiceProvider 被传递给 bluebird Promise.promisfyAll(将异步后缀添加到 listUser 调用)。我正在修改此组件以获取整个用户列表。
作为请求整个用户列表的粗略草稿,我使用递归函数将承诺链接在一起,直到来自亚马逊的分页令牌 return 未定义并将结果存储在一些 class 变量。我用这个 forum post as reference.
这行得通,但我对实施感觉不太好;现在在我们的用例中应该没问题,因为我们有大约 100 个用户,但我不知道这是否可以很好地扩展到数千个或更多用户。我知道递归足够危险,因为我不知道当 promises/calls 的数量增加时这种技术是否会导致问题。我想到了开销和内存管理的问题。但是,我们不必担心用户数量至少在短时间内飙升至数千,但我仍然想了解可能更可取的 and/or 更安全的方法来实现同样的目标。
以下片段来自请求用户列表的组件:
import Promise from 'bluebird';
import { CognitoIdentityServiceProvider } from './services/aws';
export const fetchRegisteredUsers = () => (dispatch) => {
...
let allUsersTemp = [];
let paginationToken;
const getUserList = () => {
let tempUserTest = CognitoIdentityServiceProvider.listUsersAsync({
UserPoolId: process.env.COGNITO_USER_POOL_ID,
PaginationToken: paginationToken
});
return tempUserTest.then((tempUser) => {
allUsersTemp = allUsersTemp.concat(tempUser.Users);
paginationToken = tempUser.PaginationToken;
if(paginationToken) {
return getUserList();
} else {
return;
}
})
}
const adminUsers = CognitoIdentityServiceProvider.listUsersInGroupAsync(adminParams);
return Promise.all([adminUsers]).then(
([{ Users: adminUsers }]) => {
getUserList().then((item) => {
let allUsers = allUsersTemp;
const adminUsernames = adminUsers.map(user => user.Username);
allUsers.forEach(user => {
let mappedAttributes = {};
user.Attributes.forEach(attribute => mappedAttributes[attribute.Name] = attribute.Value);
user.Attributes = mappedAttributes;
user.isAdmin = adminUsernames.includes(user.Username);
});
dispatch({
type: FETCH_REGISTERED_USERS_SUCCESS,
payload: allUsers
});
});
}, ...
根据我的理解,bluebird Promise.all 调用会等待传递给它的每个项目都可以解析,然后再执行 .then 部分中的内容;在对代码进行任何更改之前,有两个列表,一个用于用户,一个用于管理员。 Promise.all 等到两个承诺都完成后,对数据进行了一些基本处理,然后 return 在动作调度负载中编辑了数据。
经过我的更改,处理和return用户列表的逻辑在递归承诺链 (getUserList) 完成后执行。
我的问题: 这种技术可以吗?我可以按原样使用吗?还是它不安全,如果是,具体是什么问题?还有更好的方法吗?
我的弱点是递归和 promises/bluebird 所以请随时批评与这些主题相关的任何代码
I understand recursion just enough to be dangerous in that I do not know if this technique can cause problems when the number of promises/calls goes up. Questions of overhead and memory management come to mind
唯一可能的内存问题是您的 allUsersTemp
数组可能会变得非常大,直到超过浏览器的限制。然而,您应该很久以前就想知道在一个大的 table 中显示百分之一万的用户条目是否真的有用。在内存会出现问题的规模上,无论如何,您都需要更有效的工具来管理您的用户群,而不仅仅是列出所有用户。
关于代码风格,我建议不要将 allUsersTemp
和 paginationToken
声明为 mutable 更高范围的变量。而是将它们作为递归函数的参数并用结果履行承诺:
function getUserList (paginationToken, allUsersTemp = []) => {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
return tempUserTest = CognitoIdentityServiceProvider.listUsersAsync({
UserPoolId: process.env.COGNITO_USER_POOL_ID,
PaginationToken: paginationToken
}).then(tempUser => {
const allUsers = allUsersTemp.concat(tempUser.Users);
const nextToken = tempUser.PaginationToken;
if (nextToken) {
return getUserList(nextToken, allUsers);
// ^^^^^^^^^^^^^^^^^^^
} else {
return allUsers;
// ^^^^^^^^
}
});
}
The
Promise.all
waited until both promises were done
不,您将只有一个承诺的数组传递给 Promise.all
,并在第一个函数完成后调用第二个函数。要同时制作 运行,它应该看起来像
return Promise.all([
CognitoIdentityServiceProvider.listUsersInGroupAsync(adminParams),
getUserList()
]).then(([{ Users: adminUsers }, allUsers]) => {
…
});
我知道已经有一段时间了,但这就是我在 Cognito 中扫描所有用户的方式。希望这会有所帮助
const getListUserCognito = async () => {
try {
const params = {
UserPoolId: userPoolID
};
console.log('params', JSON.stringify(params));
const listUserResp = await getAllUserCognito(params);
console.log('listUserResp', JSON.stringify(listUserResp));
return listUserResp.Users;
} catch (error) {
return null;
}
};
const getAllUserCognito = async (params) => {
try {
// string must not be empty
let paginationToken = 'notEmpty';
let itemsAll = {
Users: []
};
while (paginationToken) {
const data = await cognitoidentityserviceprovider
.listUsers(params)
.promise();
const { Users } = data;
itemsAll = {
...data,
...{ Users: [...itemsAll.Users, ...(Users ? [...Users] : [])] }
};
paginationToken = data.PaginationToken;
if (paginationToken) {
params.PaginationToken = paginationToken;
}
}
return itemsAll;
} catch (err) {
console.error(
'Unable to scan the cognito pool users. Error JSON:',
JSON.stringify(err, null, 2)
);
}
};