feature: project and org identity pagination, search and sort

This commit is contained in:
Scott Wilson
2024-09-11 07:22:08 -07:00
parent 1321aa712f
commit 16182a9d1d
22 changed files with 1061 additions and 492 deletions
@@ -4,6 +4,8 @@ import { TDbClient } from "@app/db";
import { TableName, TIdentityOrgMemberships } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import { ormify, selectAllTableCols } from "@app/lib/knex";
import { OrderByDirection } from "@app/lib/types";
import { TListOrgIdentitiesByOrgIdDTO } from "@app/services/identity/identity-types";
export type TIdentityOrgDALFactory = ReturnType<typeof identityOrgDALFactory>;
@@ -27,9 +29,20 @@ export const identityOrgDALFactory = (db: TDbClient) => {
}
};
const find = async (filter: Partial<TIdentityOrgMemberships>, tx?: Knex) => {
const find = async (
{
limit,
offset = 0,
orderBy,
direction = OrderByDirection.ASC,
textFilter,
...filter
}: Partial<TIdentityOrgMemberships> &
Pick<TListOrgIdentitiesByOrgIdDTO, "offset" | "limit" | "orderBy" | "direction" | "textFilter">,
tx?: Knex
) => {
try {
const docs = await (tx || db.replicaNode())(TableName.IdentityOrgMembership)
const query = (tx || db.replicaNode())(TableName.IdentityOrgMembership)
.where(filter)
.join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`)
.leftJoin(TableName.OrgRoles, `${TableName.IdentityOrgMembership}.roleId`, `${TableName.OrgRoles}.id`)
@@ -44,6 +57,30 @@ export const identityOrgDALFactory = (db: TDbClient) => {
.select(db.ref("id").as("identityId").withSchema(TableName.Identity))
.select(db.ref("name").as("identityName").withSchema(TableName.Identity))
.select(db.ref("authMethod").as("identityAuthMethod").withSchema(TableName.Identity));
if (limit) {
void query.offset(offset).limit(limit);
}
if (orderBy) {
switch (orderBy) {
case "name":
void query.orderBy(`${TableName.Identity}.${orderBy}`, direction);
break;
case "role":
void query.orderBy(`${TableName.IdentityOrgMembership}.${orderBy}`, direction);
break;
default:
// do nothing
}
}
if (textFilter?.length) {
void query.whereILike(`${TableName.Identity}.name`, `%${textFilter}%`);
}
const docs = await query;
return docs.map(
({
crId,
@@ -79,5 +116,26 @@ export const identityOrgDALFactory = (db: TDbClient) => {
}
};
return { ...identityOrgOrm, find, findOne };
const countAllOrgIdentities = async (
{ textFilter, ...filter }: Partial<TIdentityOrgMemberships> & Pick<TListOrgIdentitiesByOrgIdDTO, "textFilter">,
tx?: Knex
) => {
try {
const query = (tx || db.replicaNode())(TableName.IdentityOrgMembership)
.where(filter)
.join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`);
if (textFilter?.length) {
void query.whereILike(`${TableName.Identity}.name`, `%${textFilter}%`);
}
const identities = await query;
return identities.length;
} catch (error) {
throw new DatabaseError({ error, name: "countAllOrgIdentities" });
}
};
return { ...identityOrgOrm, find, findOne, countAllOrgIdentities };
};
@@ -6,7 +6,6 @@ import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/pe
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { isAtLeastAsPrivileged } from "@app/lib/casl";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { TOrgPermission } from "@app/lib/types";
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
import { ActorType } from "../auth/auth-type";
@@ -16,6 +15,7 @@ import {
TCreateIdentityDTO,
TDeleteIdentityDTO,
TGetIdentityByIdDTO,
TListOrgIdentitiesByOrgIdDTO,
TListProjectIdentitiesByIdentityIdDTO,
TUpdateIdentityDTO
} from "./identity-types";
@@ -195,14 +195,36 @@ export const identityServiceFactory = ({
return { ...deletedIdentity, orgId: identityOrgMembership.orgId };
};
const listOrgIdentities = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TOrgPermission) => {
const listOrgIdentities = async ({
orgId,
actor,
actorId,
actorAuthMethod,
actorOrgId,
limit,
offset,
orderBy,
direction,
textFilter
}: TListOrgIdentitiesByOrgIdDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity);
const identityMemberships = await identityOrgMembershipDAL.find({
[`${TableName.IdentityOrgMembership}.orgId` as "orgId"]: orgId
[`${TableName.IdentityOrgMembership}.orgId` as "orgId"]: orgId,
limit,
offset,
orderBy,
direction,
textFilter
});
return identityMemberships;
const totalCount = await identityOrgMembershipDAL.countAllOrgIdentities({
[`${TableName.IdentityOrgMembership}.orgId` as "orgId"]: orgId,
textFilter
});
return { identityMemberships, totalCount };
};
const listProjectIdentitiesByIdentityId = async ({
@@ -1,5 +1,5 @@
import { IPType } from "@app/lib/ip";
import { TOrgPermission } from "@app/lib/types";
import { OrderByDirection, TOrgPermission } from "@app/lib/types";
export type TCreateIdentityDTO = {
role: string;
@@ -29,3 +29,16 @@ export interface TIdentityTrustedIp {
export type TListProjectIdentitiesByIdentityIdDTO = {
identityId: string;
} & Omit<TOrgPermission, "orgId">;
export type TListOrgIdentitiesByOrgIdDTO = {
limit?: number;
offset?: number;
orderBy?: OrgIdentityOrderBy;
direction?: OrderByDirection;
textFilter?: string;
} & TOrgPermission;
export enum OrgIdentityOrderBy {
Name = "name",
Role = "role"
}