mirror of
https://github.com/logto-io/logto.git
synced 2025-01-06 20:40:08 -05:00
feat(core): add pagination and search to role users (#2927)
This commit is contained in:
parent
6d874d5250
commit
900b87b9a5
3 changed files with 52 additions and 10 deletions
|
@ -105,7 +105,7 @@ export const createUserQueries = (pool: CommonQueryMethods) => {
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
|
|
||||||
const buildUserConditions = (search: Search, excludeUserIds: string[]) => {
|
const buildUserConditions = (search: Search, excludeUserIds: string[], userIds?: string[]) => {
|
||||||
const hasSearch = search.matches.length > 0;
|
const hasSearch = search.matches.length > 0;
|
||||||
const searchFields = [
|
const searchFields = [
|
||||||
Users.fields.id,
|
Users.fields.id,
|
||||||
|
@ -127,6 +127,16 @@ export const createUserQueries = (pool: CommonQueryMethods) => {
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (userIds) {
|
||||||
|
return sql`
|
||||||
|
where ${fields.id} in (${userIds.length > 0 ? sql.join(userIds, sql`, `) : sql`null`})
|
||||||
|
${conditionalSql(
|
||||||
|
hasSearch,
|
||||||
|
() => sql`and (${buildConditionsFromSearch(search, searchFields)})`
|
||||||
|
)}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
return conditionalSql(
|
return conditionalSql(
|
||||||
hasSearch,
|
hasSearch,
|
||||||
() => sql`where ${buildConditionsFromSearch(search, searchFields)}`
|
() => sql`where ${buildConditionsFromSearch(search, searchFields)}`
|
||||||
|
@ -135,18 +145,23 @@ export const createUserQueries = (pool: CommonQueryMethods) => {
|
||||||
|
|
||||||
const defaultUserSearch = { matches: [], isCaseSensitive: false, joint: SearchJointMode.Or };
|
const defaultUserSearch = { matches: [], isCaseSensitive: false, joint: SearchJointMode.Or };
|
||||||
|
|
||||||
const countUsers = async (search: Search = defaultUserSearch, excludeUserIds: string[] = []) =>
|
const countUsers = async (
|
||||||
|
search: Search = defaultUserSearch,
|
||||||
|
excludeUserIds: string[] = [],
|
||||||
|
userIds?: string[]
|
||||||
|
) =>
|
||||||
pool.one<{ count: number }>(sql`
|
pool.one<{ count: number }>(sql`
|
||||||
select count(*)
|
select count(*)
|
||||||
from ${table}
|
from ${table}
|
||||||
${buildUserConditions(search, excludeUserIds)}
|
${buildUserConditions(search, excludeUserIds, userIds)}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const findUsers = async (
|
const findUsers = async (
|
||||||
limit: number,
|
limit: number,
|
||||||
offset: number,
|
offset: number,
|
||||||
search: Search,
|
search: Search,
|
||||||
excludeUserIds: string[] = []
|
excludeUserIds: string[] = [],
|
||||||
|
userIds?: string[]
|
||||||
) =>
|
) =>
|
||||||
pool.any<User>(
|
pool.any<User>(
|
||||||
sql`
|
sql`
|
||||||
|
@ -155,7 +170,7 @@ export const createUserQueries = (pool: CommonQueryMethods) => {
|
||||||
sql`,`
|
sql`,`
|
||||||
)}
|
)}
|
||||||
from ${table}
|
from ${table}
|
||||||
${buildUserConditions(search, excludeUserIds)}
|
${buildUserConditions(search, excludeUserIds, userIds)}
|
||||||
limit ${limit}
|
limit ${limit}
|
||||||
offset ${offset}
|
offset ${offset}
|
||||||
`
|
`
|
||||||
|
|
|
@ -44,6 +44,8 @@ const { insertRolesScopes } = rolesScopes;
|
||||||
const users = {
|
const users = {
|
||||||
findUsersByIds: jest.fn(),
|
findUsersByIds: jest.fn(),
|
||||||
findUserById: jest.fn(),
|
findUserById: jest.fn(),
|
||||||
|
countUsers: jest.fn(async () => ({ count: 1 })),
|
||||||
|
findUsers: jest.fn(async () => [mockUser]),
|
||||||
};
|
};
|
||||||
const { findUsersByIds } = users;
|
const { findUsersByIds } = users;
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ export default function roleRoutes<T extends AuthedRouter>(
|
||||||
updateRoleById,
|
updateRoleById,
|
||||||
},
|
},
|
||||||
scopes: { findScopeById },
|
scopes: { findScopeById },
|
||||||
users: { findUserById, findUsersByIds },
|
users: { findUserById, findUsersByIds, countUsers, findUsers },
|
||||||
usersRoles: {
|
usersRoles: {
|
||||||
countUsersRolesByRoleId,
|
countUsersRolesByRoleId,
|
||||||
deleteUsersRolesByUserIdAndRoleId,
|
deleteUsersRolesByUserIdAndRoleId,
|
||||||
|
@ -190,6 +190,7 @@ export default function roleRoutes<T extends AuthedRouter>(
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
'/roles/:id/users',
|
'/roles/:id/users',
|
||||||
|
koaPagination(),
|
||||||
koaGuard({
|
koaGuard({
|
||||||
params: object({ id: string().min(1) }),
|
params: object({ id: string().min(1) }),
|
||||||
}),
|
}),
|
||||||
|
@ -197,13 +198,37 @@ export default function roleRoutes<T extends AuthedRouter>(
|
||||||
const {
|
const {
|
||||||
params: { id },
|
params: { id },
|
||||||
} = ctx.guard;
|
} = ctx.guard;
|
||||||
|
const { limit, offset } = ctx.pagination;
|
||||||
|
const { searchParams } = ctx.request.URL;
|
||||||
|
|
||||||
await findRoleById(id);
|
await findRoleById(id);
|
||||||
const usersRoles = await findUsersRolesByRoleId(id);
|
|
||||||
const users = await findUsersByIds(usersRoles.map(({ userId }) => userId));
|
|
||||||
ctx.body = users.map((user) => pick(user, ...userInfoSelectFields));
|
|
||||||
|
|
||||||
return next();
|
return tryThat(
|
||||||
|
async () => {
|
||||||
|
const search = parseSearchParamsForSearch(searchParams);
|
||||||
|
const usersRoles = await findUsersRolesByRoleId(id);
|
||||||
|
const userIds = usersRoles.map(({ userId }) => userId);
|
||||||
|
|
||||||
|
const [{ count }, users] = await Promise.all([
|
||||||
|
countUsers(search, undefined, userIds),
|
||||||
|
findUsers(limit, offset, search, undefined, userIds),
|
||||||
|
]);
|
||||||
|
|
||||||
|
ctx.pagination.totalCount = count;
|
||||||
|
ctx.body = users.map((user) => pick(user, ...userInfoSelectFields));
|
||||||
|
|
||||||
|
return next();
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
if (error instanceof TypeError) {
|
||||||
|
throw new RequestError(
|
||||||
|
{ code: 'request.invalid_input', details: error.message },
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue