mirror of
https://github.com/logto-io/logto.git
synced 2025-03-31 22:51:25 -05:00
feat(core): count role users and featured users (#2866)
This commit is contained in:
parent
8b179c0567
commit
225ccfed0a
5 changed files with 53 additions and 7 deletions
|
@ -1,6 +1,6 @@
|
|||
import type { UsersRole } from '@logto/schemas';
|
||||
import { UsersRoles } from '@logto/schemas';
|
||||
import { convertToIdentifiers } from '@logto/shared';
|
||||
import { conditionalSql, convertToIdentifiers } from '@logto/shared';
|
||||
import { sql } from 'slonik';
|
||||
|
||||
import envSet from '#src/env-set/index.js';
|
||||
|
@ -14,11 +14,19 @@ export const findUsersRolesByUserId = async (userId: string) =>
|
|||
where ${fields.userId}=${userId}
|
||||
`);
|
||||
|
||||
export const findUsersRolesByRoleId = async (roleId: string) =>
|
||||
export const findUsersRolesByRoleId = async (roleId: string, limit?: number) =>
|
||||
envSet.pool.any<UsersRole>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
from ${table}
|
||||
where ${fields.roleId}=${roleId}
|
||||
${conditionalSql(limit, (value) => sql`limit ${value}`)}
|
||||
`);
|
||||
|
||||
export const countUsersRolesByRoleId = async (roleId: string) =>
|
||||
envSet.pool.one<{ count: number }>(sql`
|
||||
select count(*)
|
||||
from ${table}
|
||||
where ${fields.roleId}=${roleId}
|
||||
`);
|
||||
|
||||
export const findFirstUsersRolesByRoleIdAndUserIds = async (roleId: string, userIds: string[]) =>
|
||||
|
|
|
@ -51,8 +51,10 @@ const {
|
|||
findUsersRolesByRoleId,
|
||||
deleteUsersRolesByUserIdAndRoleId,
|
||||
findFirstUsersRolesByRoleIdAndUserIds,
|
||||
countUsersRolesByRoleId,
|
||||
} = await mockEsmWithActual('#src/queries/users-roles.js', () => ({
|
||||
insertUsersRoles: jest.fn(),
|
||||
countUsersRolesByRoleId: jest.fn(),
|
||||
findUsersRolesByRoleId: jest.fn(),
|
||||
findFirstUsersRolesByRoleIdAndUserIds: jest.fn(),
|
||||
deleteUsersRolesByUserIdAndRoleId: jest.fn(),
|
||||
|
@ -62,10 +64,24 @@ const roleRoutes = await pickDefault(import('./role.js'));
|
|||
describe('role routes', () => {
|
||||
const roleRequester = createRequester({ authedRoutes: roleRoutes });
|
||||
|
||||
it('GET /roles', async () => {
|
||||
const response = await roleRequester.get('/roles');
|
||||
it('GET /roles?page=1', async () => {
|
||||
countUsersRolesByRoleId.mockResolvedValueOnce({ count: 1 });
|
||||
findUsersByIds.mockResolvedValueOnce([mockUser]);
|
||||
findUsersRolesByRoleId.mockResolvedValueOnce([]);
|
||||
const response = await roleRequester.get('/roles?page=1&page_size=20');
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual([mockRole]);
|
||||
expect(response.body).toEqual([
|
||||
{
|
||||
...mockRole,
|
||||
usersCount: 1,
|
||||
featuredUsers: [
|
||||
{
|
||||
id: mockUser.id,
|
||||
avatar: mockUser.avatar,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('POST /roles', async () => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { buildIdGenerator } from '@logto/core-kit';
|
||||
import type { ScopeResponse } from '@logto/schemas';
|
||||
import type { RoleResponse, ScopeResponse } from '@logto/schemas';
|
||||
import { Roles } from '@logto/schemas';
|
||||
import { tryThat } from '@logto/shared';
|
||||
import { object, string, z } from 'zod';
|
||||
|
@ -25,6 +25,7 @@ import {
|
|||
import { findScopeById, findScopesByIds } from '#src/queries/scope.js';
|
||||
import { findUserById, findUsersByIds } from '#src/queries/user.js';
|
||||
import {
|
||||
countUsersRolesByRoleId,
|
||||
deleteUsersRolesByUserIdAndRoleId,
|
||||
findFirstUsersRolesByRoleIdAndUserIds,
|
||||
findUsersRolesByRoleId,
|
||||
|
@ -57,9 +58,23 @@ export default function roleRoutes<T extends AuthedRouter>(router: T) {
|
|||
findRoles(search, limit, offset),
|
||||
]);
|
||||
|
||||
const rolesResponse: RoleResponse[] = await Promise.all(
|
||||
roles.map(async (role) => {
|
||||
const { count } = await countUsersRolesByRoleId(role.id);
|
||||
const usersRoles = await findUsersRolesByRoleId(role.id, 3);
|
||||
const users = await findUsersByIds(usersRoles.map(({ userId }) => userId));
|
||||
|
||||
return {
|
||||
...role,
|
||||
usersCount: count,
|
||||
featuredUsers: users.map(({ id, avatar }) => ({ id, avatar })),
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
// Return totalCount to pagination middleware
|
||||
ctx.pagination.totalCount = count;
|
||||
ctx.body = roles;
|
||||
ctx.body = rolesResponse;
|
||||
|
||||
return next();
|
||||
},
|
||||
|
|
|
@ -7,3 +7,4 @@ export * from './interactions.js';
|
|||
export * from './search.js';
|
||||
export * from './resource.js';
|
||||
export * from './scope.js';
|
||||
export * from './role.js';
|
||||
|
|
6
packages/schemas/src/types/role.ts
Normal file
6
packages/schemas/src/types/role.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import type { Role, User } from '../db-entries/index.js';
|
||||
|
||||
export type RoleResponse = Role & {
|
||||
usersCount: number;
|
||||
featuredUsers: Array<Pick<User, 'avatar' | 'id'>>;
|
||||
};
|
Loading…
Add table
Reference in a new issue