mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
move resource type filter to backend
This commit is contained in:
@@ -59,7 +59,16 @@ export const registerPamResourceRouter = async (server: FastifyZodProvider) => {
|
||||
limit: z.coerce.number().min(1).max(100).default(100),
|
||||
orderBy: z.nativeEnum(PamResourceOrderBy).default(PamResourceOrderBy.Name),
|
||||
orderDirection: z.nativeEnum(OrderByDirection).default(OrderByDirection.ASC),
|
||||
search: z.string().trim().optional()
|
||||
search: z.string().trim().optional(),
|
||||
filterResourceTypes: z
|
||||
.string()
|
||||
.transform((val) =>
|
||||
val
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
)
|
||||
.optional()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@@ -70,7 +79,7 @@ export const registerPamResourceRouter = async (server: FastifyZodProvider) => {
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const { projectId, limit, offset, search, orderBy, orderDirection } = req.query;
|
||||
const { projectId, limit, offset, search, orderBy, orderDirection, filterResourceTypes } = req.query;
|
||||
|
||||
const { resources, totalCount } = await server.services.pamResource.list({
|
||||
actorId: req.permission.id,
|
||||
@@ -82,7 +91,8 @@ export const registerPamResourceRouter = async (server: FastifyZodProvider) => {
|
||||
offset,
|
||||
search,
|
||||
orderBy,
|
||||
orderDirection
|
||||
orderDirection,
|
||||
filterResourceTypes
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
|
||||
@@ -31,7 +31,8 @@ export const pamResourceDALFactory = (db: TDbClient) => {
|
||||
limit,
|
||||
offset = 0,
|
||||
orderBy = PamResourceOrderBy.Name,
|
||||
orderDirection = OrderByDirection.ASC
|
||||
orderDirection = OrderByDirection.ASC,
|
||||
filterResourceTypes
|
||||
}: {
|
||||
projectId: string;
|
||||
search?: string;
|
||||
@@ -39,6 +40,7 @@ export const pamResourceDALFactory = (db: TDbClient) => {
|
||||
offset?: number;
|
||||
orderBy?: PamResourceOrderBy;
|
||||
orderDirection?: OrderByDirection;
|
||||
filterResourceTypes?: string[];
|
||||
},
|
||||
tx?: Knex
|
||||
) => {
|
||||
@@ -57,6 +59,10 @@ export const pamResourceDALFactory = (db: TDbClient) => {
|
||||
});
|
||||
}
|
||||
|
||||
if (filterResourceTypes && filterResourceTypes.length) {
|
||||
void query.whereIn(`${TableName.PamResource}.resourceType`, filterResourceTypes);
|
||||
}
|
||||
|
||||
const countQuery = query.clone().count("*", { as: "count" }).first();
|
||||
|
||||
void query.select(selectAllTableCols(TableName.PamResource));
|
||||
|
||||
@@ -92,6 +92,7 @@ export const useListPamResources = (
|
||||
|
||||
return data;
|
||||
},
|
||||
placeholderData: (prev) => prev,
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
@@ -59,6 +59,7 @@ export type TListPamResourcesDTO = {
|
||||
orderBy?: PamResourceOrderBy;
|
||||
orderDirection?: OrderByDirection;
|
||||
search?: string;
|
||||
filterResourceTypes?: string;
|
||||
};
|
||||
|
||||
export type TCreatePamResourceDTO = Pick<
|
||||
|
||||
@@ -59,8 +59,8 @@ import { PamDeleteResourceModal } from "./PamDeleteResourceModal";
|
||||
import { PamResourceRow } from "./PamResourceRow";
|
||||
import { PamUpdateResourceModal } from "./PamUpdateResourceModal";
|
||||
|
||||
type Filters = {
|
||||
resourceType: PamResourceType[];
|
||||
type PamResourceFilter = {
|
||||
resourceTypes: PamResourceType[];
|
||||
};
|
||||
|
||||
type Props = {
|
||||
@@ -80,8 +80,8 @@ export const PamResourcesTable = ({ projectId }: Props) => {
|
||||
from: ROUTE_PATHS.Pam.ResourcesPage.id
|
||||
});
|
||||
|
||||
const [filters, setFilters] = useState<Filters>({
|
||||
resourceType: []
|
||||
const [filter, setFilter] = useState<PamResourceFilter>({
|
||||
resourceTypes: []
|
||||
});
|
||||
|
||||
const {
|
||||
@@ -114,7 +114,8 @@ export const PamResourcesTable = ({ projectId }: Props) => {
|
||||
limit: perPage,
|
||||
search: debouncedSearch,
|
||||
orderBy,
|
||||
orderDirection
|
||||
orderDirection,
|
||||
filterResourceTypes: filter.resourceTypes.length ? filter.resourceTypes.join(",") : undefined
|
||||
});
|
||||
|
||||
const resources = data?.resources || [];
|
||||
@@ -125,7 +126,7 @@ export const PamResourcesTable = ({ projectId }: Props) => {
|
||||
resources.filter((resource) => {
|
||||
const { name, resourceType } = resource;
|
||||
|
||||
if (filters.resourceType.length && !filters.resourceType.includes(resourceType)) {
|
||||
if (filter.resourceTypes.length && !filter.resourceTypes.includes(resourceType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -136,7 +137,7 @@ export const PamResourcesTable = ({ projectId }: Props) => {
|
||||
resourceType.toLowerCase().includes(searchValue)
|
||||
);
|
||||
}),
|
||||
[resources, search, filters]
|
||||
[resources, search, filter]
|
||||
);
|
||||
|
||||
const handleSort = (column: PamResourceOrderBy) => {
|
||||
@@ -155,7 +156,7 @@ export const PamResourcesTable = ({ projectId }: Props) => {
|
||||
const getColSortIcon = (col: PamResourceOrderBy) =>
|
||||
orderDirection === OrderByDirection.DESC && orderBy === col ? faArrowUp : faArrowDown;
|
||||
|
||||
const isTableFiltered = Boolean(filters.resourceType.length);
|
||||
const isTableFiltered = Boolean(filter.resourceTypes.length);
|
||||
const isContentEmpty = !filteredResources.length;
|
||||
const isSearchEmpty = isContentEmpty && (Boolean(search) || isTableFiltered);
|
||||
|
||||
@@ -192,43 +193,38 @@ export const PamResourcesTable = ({ projectId }: Props) => {
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="max-h-[70vh] thin-scrollbar overflow-y-auto" align="end">
|
||||
<DropdownMenuLabel>Resource Type</DropdownMenuLabel>
|
||||
{resources.length ? (
|
||||
[...new Set(resources.map(({ resourceType }) => resourceType))].map((type) => {
|
||||
const { name, image } = PAM_RESOURCE_TYPE_MAP[type];
|
||||
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setFilters((prev) => ({
|
||||
...prev,
|
||||
resourceType: prev.resourceType.includes(type)
|
||||
? prev.resourceType.filter((a) => a !== type)
|
||||
: [...prev.resourceType, type]
|
||||
}));
|
||||
}}
|
||||
key={type}
|
||||
icon={
|
||||
filters.resourceType.includes(type) && (
|
||||
<FontAwesomeIcon className="text-primary" icon={faCheckCircle} />
|
||||
)
|
||||
}
|
||||
iconPos="right"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<img
|
||||
alt={`${name} resource type`}
|
||||
src={`/images/integrations/${image}`}
|
||||
className="h-4 w-4"
|
||||
/>
|
||||
<span>{name}</span>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<DropdownMenuItem isDisabled>No Resources</DropdownMenuItem>
|
||||
)}
|
||||
{Object.entries(PAM_RESOURCE_TYPE_MAP).map(([type, { name, image }]) => {
|
||||
const resourceType = type as PamResourceType;
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setFilter((prev) => ({
|
||||
...prev,
|
||||
resourceTypes: prev.resourceTypes.includes(resourceType)
|
||||
? prev.resourceTypes.filter((a) => a !== resourceType)
|
||||
: [...prev.resourceTypes, resourceType]
|
||||
}));
|
||||
}}
|
||||
key={resourceType}
|
||||
icon={
|
||||
filter.resourceTypes.includes(resourceType) && (
|
||||
<FontAwesomeIcon className="text-primary" icon={faCheckCircle} />
|
||||
)
|
||||
}
|
||||
iconPos="right"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<img
|
||||
alt={`${name} resource type`}
|
||||
src={`/images/integrations/${image}`}
|
||||
className="h-4 w-4"
|
||||
/>
|
||||
<span>{name}</span>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<OrgPermissionCan
|
||||
|
||||
Reference in New Issue
Block a user