-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cluster-ui: add grants view to v2 db details
This commit adds the db level grants table to the grants tab in the v2 db details page. A new hook, useDatabaseGrants is created to wrap the request with useSWR. For now we simply get all grants on the db on the page, showing 20 results per page on the table. Epic: CRDB-37558 Part of: #131211 Release note (ui change): The grants table in the db details page (e.g. when clicking a db in the databases list) will now show the db level grants. Previously it showed grants per table in the db.
- Loading branch information
Showing
4 changed files
with
223 additions
and
6 deletions.
There are no files selected for viewing
77 changes: 77 additions & 0 deletions
77
pkg/ui/workspaces/cluster-ui/src/api/databases/grantsApi.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// Copyright 2024 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
import useSWRImmutable from "swr/immutable"; | ||
|
||
import { fetchDataJSON } from "../fetchData"; | ||
import { | ||
APIV2ResponseWithPaginationState, | ||
SimplePaginationState, | ||
} from "../types"; | ||
|
||
export type DatabaseGrant = { | ||
grantee: string; | ||
privilege: string; | ||
}; | ||
|
||
export enum GrantsSortOptions { | ||
GRANTEE = "grantee", | ||
PRIVILEGE = "privilege", | ||
} | ||
|
||
type DatabaseGrantsRequest = { | ||
dbId: number; | ||
sortBy?: GrantsSortOptions; | ||
sortOrder?: "asc" | "desc"; | ||
pagination?: SimplePaginationState; | ||
}; | ||
|
||
export type DatabaseGrantsResponse = APIV2ResponseWithPaginationState< | ||
DatabaseGrant[] | ||
>; | ||
|
||
const createDbGrantsPath = (req: DatabaseGrantsRequest): string => { | ||
const { dbId, pagination, sortBy, sortOrder } = req; | ||
const urlParams = new URLSearchParams(); | ||
if (pagination?.pageNum) { | ||
urlParams.append("pageNum", pagination.pageNum.toString()); | ||
} | ||
if (pagination?.pageSize) { | ||
urlParams.append("pageSize", pagination.pageSize.toString()); | ||
} | ||
if (sortBy) { | ||
urlParams.append("sortBy", sortBy); | ||
} | ||
if (sortOrder) { | ||
urlParams.append("sortOrder", sortOrder); | ||
} | ||
return `api/v2/grants/databases/${dbId}/?` + urlParams.toString(); | ||
}; | ||
|
||
const fetchDbGrants = ( | ||
req: DatabaseGrantsRequest, | ||
): Promise<DatabaseGrantsResponse> => { | ||
const path = createDbGrantsPath(req); | ||
return fetchDataJSON(path); | ||
}; | ||
|
||
export const useDatabaseGrantsImmutable = (req: DatabaseGrantsRequest) => { | ||
const { data, isLoading, error } = useSWRImmutable( | ||
createDbGrantsPath(req), | ||
() => fetchDbGrants(req), | ||
); | ||
|
||
return { | ||
databaseGrants: data?.results, | ||
pagination: data?.pagination_info, | ||
isLoading, | ||
error: error, | ||
}; | ||
}; |
118 changes: 118 additions & 0 deletions
118
pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/dbGrantsView.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
// Copyright 2024 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
import React, { useMemo, useState } from "react"; | ||
|
||
import { | ||
GrantsSortOptions, | ||
useDatabaseGrantsImmutable, | ||
} from "src/api/databases/grantsApi"; | ||
import { useRouteParams } from "src/hooks/useRouteParams"; | ||
import { PageSection } from "src/layouts"; | ||
import PageCount from "src/sharedFromCloud/pageCount"; | ||
import { | ||
Table, | ||
TableChangeFn, | ||
TableColumnProps, | ||
} from "src/sharedFromCloud/table"; | ||
|
||
// This type is used by data source for the table. | ||
type GrantsByUser = { | ||
key: string; | ||
grantee: string; | ||
privileges: string[]; | ||
}; | ||
|
||
const COLUMNS: (TableColumnProps<GrantsByUser> & { | ||
sortKey: GrantsSortOptions; | ||
})[] = [ | ||
{ | ||
title: "Grantee", | ||
sorter: (a, b) => a.grantee.localeCompare(b.grantee), | ||
sortKey: GrantsSortOptions.GRANTEE, | ||
render: grant => grant.grantee, | ||
}, | ||
{ | ||
title: "Privileges", | ||
sortKey: GrantsSortOptions.PRIVILEGE, | ||
render: grant => grant.privileges.join(", "), | ||
}, | ||
]; | ||
|
||
const pageSize = 20; | ||
|
||
export const DbGrantsView: React.FC = () => { | ||
const { dbID } = useRouteParams(); | ||
const [currentPage, setCurrentPage] = useState(1); | ||
|
||
const { | ||
databaseGrants, | ||
pagination: paginationRes, | ||
isLoading, | ||
error, | ||
} = useDatabaseGrantsImmutable({ | ||
dbId: parseInt(dbID, 10), | ||
pagination: { | ||
pageSize: 0, // Get all. | ||
pageNum: 0, | ||
}, | ||
}); | ||
|
||
const dataWithKey: GrantsByUser[] = useMemo(() => { | ||
if (!databaseGrants) { | ||
return []; | ||
} | ||
const grantsByUser = {} as Record<string, string[]>; | ||
databaseGrants.forEach(grant => { | ||
if (!grantsByUser[grant.grantee]) { | ||
grantsByUser[grant.grantee] = []; | ||
} | ||
grantsByUser[grant.grantee].push(grant.privilege); | ||
}); | ||
|
||
return Object.entries(grantsByUser).map(([grantee, privileges]) => ({ | ||
key: grantee, | ||
grantee, | ||
privileges, | ||
})); | ||
}, [databaseGrants]); | ||
|
||
const onTableChange: TableChangeFn<GrantsByUser> = pagination => { | ||
if (pagination.current) { | ||
setCurrentPage(pagination.current); | ||
} | ||
}; | ||
|
||
return ( | ||
<PageSection heading={"Grants"}> | ||
<PageCount | ||
page={currentPage} | ||
pageSize={pageSize} | ||
total={paginationRes?.total_results ?? 0} | ||
entity="grants" | ||
/> | ||
<Table | ||
error={error} | ||
loading={isLoading} | ||
dataSource={dataWithKey ?? []} | ||
columns={COLUMNS} | ||
pagination={{ | ||
size: "small", | ||
current: currentPage, | ||
pageSize, | ||
showSizeChanger: false, | ||
position: ["bottomCenter"], | ||
total: paginationRes?.total_results, | ||
}} | ||
onChange={onTableChange} | ||
/> | ||
</PageSection> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters