mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
feat(core): list roles filter by userId (#2893)
This commit is contained in:
parent
751d6117c1
commit
5d862f617f
3 changed files with 27 additions and 6 deletions
|
@ -1,7 +1,7 @@
|
||||||
import type { CreateRole, Role } from '@logto/schemas';
|
import type { CreateRole, Role } from '@logto/schemas';
|
||||||
import { adminConsoleAdminRoleId, SearchJointMode, Roles } from '@logto/schemas';
|
import { adminConsoleAdminRoleId, SearchJointMode, Roles } from '@logto/schemas';
|
||||||
import type { OmitAutoSetFields } from '@logto/shared';
|
import type { OmitAutoSetFields } from '@logto/shared';
|
||||||
import { conditionalSql, convertToIdentifiers } from '@logto/shared';
|
import { conditionalArraySql, conditionalSql, convertToIdentifiers } from '@logto/shared';
|
||||||
import type { CommonQueryMethods } from 'slonik';
|
import type { CommonQueryMethods } from 'slonik';
|
||||||
import { sql } from 'slonik';
|
import { sql } from 'slonik';
|
||||||
|
|
||||||
|
@ -28,20 +28,33 @@ const buildRoleConditions = (search: Search) => {
|
||||||
export const defaultUserSearch = { matches: [], isCaseSensitive: false, joint: SearchJointMode.Or };
|
export const defaultUserSearch = { matches: [], isCaseSensitive: false, joint: SearchJointMode.Or };
|
||||||
|
|
||||||
export const createRolesQueries = (pool: CommonQueryMethods) => {
|
export const createRolesQueries = (pool: CommonQueryMethods) => {
|
||||||
const countRoles = async (search: Search = defaultUserSearch) =>
|
const countRoles = async (search: Search = defaultUserSearch, excludeRoleIds: string[] = []) =>
|
||||||
pool.one<{ count: number }>(sql`
|
pool.one<{ count: number }>(sql`
|
||||||
select count(*)
|
select count(*)
|
||||||
from ${table}
|
from ${table}
|
||||||
where ${fields.id}<>${adminConsoleAdminRoleId}
|
where ${fields.id}<>${adminConsoleAdminRoleId}
|
||||||
|
${conditionalArraySql(
|
||||||
|
excludeRoleIds,
|
||||||
|
(value) => sql`and ${fields.id} not in (${sql.join(value, sql`, `)})`
|
||||||
|
)}
|
||||||
${buildRoleConditions(search)}
|
${buildRoleConditions(search)}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const findRoles = async (search: Search, limit?: number, offset?: number) =>
|
const findRoles = async (
|
||||||
|
search: Search,
|
||||||
|
excludeRoleIds: string[] = [],
|
||||||
|
limit?: number,
|
||||||
|
offset?: number
|
||||||
|
) =>
|
||||||
pool.any<Role>(
|
pool.any<Role>(
|
||||||
sql`
|
sql`
|
||||||
select ${sql.join(Object.values(fields), sql`, `)}
|
select ${sql.join(Object.values(fields), sql`, `)}
|
||||||
from ${table}
|
from ${table}
|
||||||
where ${fields.id}<>${adminConsoleAdminRoleId}
|
where ${fields.id}<>${adminConsoleAdminRoleId}
|
||||||
|
${conditionalArraySql(
|
||||||
|
excludeRoleIds,
|
||||||
|
(value) => sql`and ${fields.id} not in (${sql.join(value, sql`, `)})`
|
||||||
|
)}
|
||||||
${buildRoleConditions(search)}
|
${buildRoleConditions(search)}
|
||||||
${conditionalSql(limit, (value) => sql`limit ${value}`)}
|
${conditionalSql(limit, (value) => sql`limit ${value}`)}
|
||||||
${conditionalSql(offset, (value) => sql`offset ${value}`)}
|
${conditionalSql(offset, (value) => sql`offset ${value}`)}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import {
|
||||||
deleteUsersRolesByUserIdAndRoleId,
|
deleteUsersRolesByUserIdAndRoleId,
|
||||||
findFirstUsersRolesByRoleIdAndUserIds,
|
findFirstUsersRolesByRoleIdAndUserIds,
|
||||||
findUsersRolesByRoleId,
|
findUsersRolesByRoleId,
|
||||||
|
findUsersRolesByUserId,
|
||||||
insertUsersRoles,
|
insertUsersRoles,
|
||||||
} from '#src/queries/users-roles.js';
|
} from '#src/queries/users-roles.js';
|
||||||
import assertThat from '#src/utils/assert-that.js';
|
import assertThat from '#src/utils/assert-that.js';
|
||||||
|
@ -46,16 +47,19 @@ export default function roleRoutes<T extends AuthedRouter>(...[router]: RouterIn
|
||||||
return tryThat(
|
return tryThat(
|
||||||
async () => {
|
async () => {
|
||||||
const search = parseSearchParamsForSearch(searchParams);
|
const search = parseSearchParamsForSearch(searchParams);
|
||||||
|
const excludeUserId = searchParams.get('excludeUserId');
|
||||||
|
const usersRoles = excludeUserId ? await findUsersRolesByUserId(excludeUserId) : [];
|
||||||
|
const excludeRoleIds = usersRoles.map(({ roleId }) => roleId);
|
||||||
|
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
ctx.body = await findRoles(search);
|
ctx.body = await findRoles(search, excludeRoleIds);
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
const [{ count }, roles] = await Promise.all([
|
const [{ count }, roles] = await Promise.all([
|
||||||
countRoles(search),
|
countRoles(search, excludeRoleIds),
|
||||||
findRoles(search, limit, offset),
|
findRoles(search, excludeRoleIds, limit, offset),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const rolesResponse: RoleResponse[] = await Promise.all(
|
const rolesResponse: RoleResponse[] = await Promise.all(
|
||||||
|
|
|
@ -8,6 +8,10 @@ import type { FieldIdentifiers, Table } from './types.js';
|
||||||
|
|
||||||
export const conditionalSql = <T>(value: T, buildSql: (value: Exclude<T, Falsy>) => SqlSqlToken) =>
|
export const conditionalSql = <T>(value: T, buildSql: (value: Exclude<T, Falsy>) => SqlSqlToken) =>
|
||||||
notFalsy(value) ? buildSql(value) : sql``;
|
notFalsy(value) ? buildSql(value) : sql``;
|
||||||
|
export const conditionalArraySql = <T>(
|
||||||
|
value: T[],
|
||||||
|
buildSql: (value: Exclude<T[], Falsy>) => SqlSqlToken
|
||||||
|
) => (value.length > 0 ? buildSql(value) : sql``);
|
||||||
|
|
||||||
export const autoSetFields = Object.freeze(['createdAt', 'updatedAt'] as const);
|
export const autoSetFields = Object.freeze(['createdAt', 'updatedAt'] as const);
|
||||||
export type OmitAutoSetFields<T> = Omit<T, typeof autoSetFields[number]>;
|
export type OmitAutoSetFields<T> = Omit<T, typeof autoSetFields[number]>;
|
||||||
|
|
Loading…
Reference in a new issue