mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-12 02:15:01 +00:00
remove all uses of fetchUsers() v1 (#64719)
This commit is contained in:
@@ -187,6 +187,7 @@ export function splitSelectProps<
|
||||
customProps,
|
||||
defaultValue,
|
||||
elevated,
|
||||
getOptionValue,
|
||||
helperText,
|
||||
inputId,
|
||||
inputValue,
|
||||
@@ -236,6 +237,7 @@ export function splitSelectProps<
|
||||
customProps,
|
||||
defaultValue,
|
||||
elevated,
|
||||
getOptionValue,
|
||||
inputValue,
|
||||
isClearable,
|
||||
isDisabled,
|
||||
@@ -284,6 +286,7 @@ type KeysRemovedFromOthers =
|
||||
| 'customProps'
|
||||
| 'defaultValue'
|
||||
| 'elevated'
|
||||
| 'getOptionValue'
|
||||
| 'helperText'
|
||||
| 'inputId'
|
||||
| 'inputValue'
|
||||
|
||||
+16
-1
@@ -36,6 +36,7 @@ import type {
|
||||
import { Desktop } from 'teleport/services/desktops';
|
||||
import { Node } from 'teleport/services/nodes';
|
||||
import { RoleResource } from 'teleport/services/resources';
|
||||
import { User } from 'teleport/services/user';
|
||||
import Ctx from 'teleport/teleportContext';
|
||||
import useTeleport from 'teleport/useTeleport';
|
||||
|
||||
@@ -44,6 +45,7 @@ import { ServerSideListProps, TableWrapper } from '../common';
|
||||
import { Desktops } from './Desktops';
|
||||
import { Nodes } from './Nodes';
|
||||
import { Roles } from './Roles';
|
||||
import Users from './Users';
|
||||
|
||||
export function ServerSideSupportedList(props: CommonListProps) {
|
||||
const ctx = useTeleport();
|
||||
@@ -119,6 +121,8 @@ export function ServerSideSupportedList(props: CommonListProps) {
|
||||
};
|
||||
|
||||
switch (props.selectedResourceKind) {
|
||||
case 'user':
|
||||
return <Users users={resources as User[]} {...listProps} />;
|
||||
case 'role':
|
||||
return <Roles roles={resources as RoleResource[]} {...listProps} />;
|
||||
case 'node':
|
||||
@@ -148,7 +152,11 @@ export function ServerSideSupportedList(props: CommonListProps) {
|
||||
to: pageIndicators.to,
|
||||
total: pageIndicators.totalCount,
|
||||
}}
|
||||
hideAdvancedSearch={props.selectedResourceKind === 'role'} // Roles don't support advanced search.
|
||||
// Roles and users don't support advanced search.
|
||||
hideAdvancedSearch={
|
||||
props.selectedResourceKind === 'role' ||
|
||||
props.selectedResourceKind === 'user'
|
||||
}
|
||||
filter={resourceFilter}
|
||||
disableSearch={fetchStatus === 'loading'}
|
||||
/>
|
||||
@@ -194,6 +202,13 @@ function getFetchFuncForServerSidePaginating(
|
||||
clusterId: string,
|
||||
params: UrlResourcesParams
|
||||
) => Promise<ResourcesResponse<unknown>> {
|
||||
if (resourceKind === 'user') {
|
||||
return async (clusterId, params) => {
|
||||
const { items, startKey } = await ctx.userService.fetchUsersV2(params);
|
||||
return { agents: items, startKey };
|
||||
};
|
||||
}
|
||||
|
||||
if (resourceKind === 'role') {
|
||||
return async (clusterId, params) => {
|
||||
const { items, startKey } = await ctx.resourceService.fetchRoles(params);
|
||||
|
||||
+5
-20
@@ -20,9 +20,11 @@ import Table, { Cell, LabelCell } from 'design/DataTable';
|
||||
|
||||
import { User } from 'teleport/services/user';
|
||||
|
||||
import { renderActionCell, SimpleListProps } from '../common';
|
||||
import { renderActionCell, ServerSideListProps } from '../common';
|
||||
|
||||
export default function UserList(props: SimpleListProps & { users: User[] }) {
|
||||
export default function UserList(
|
||||
props: ServerSideListProps & { users: User[] }
|
||||
) {
|
||||
const {
|
||||
users = [],
|
||||
selectedResources,
|
||||
@@ -37,31 +39,15 @@ export default function UserList(props: SimpleListProps & { users: User[] }) {
|
||||
{
|
||||
key: 'name',
|
||||
headerText: 'Name',
|
||||
isSortable: true,
|
||||
},
|
||||
{
|
||||
key: 'roles',
|
||||
headerText: 'Roles',
|
||||
isSortable: true,
|
||||
onSort: (a, b) => {
|
||||
const aStr = a.roles.toString();
|
||||
const bStr = b.roles.toString();
|
||||
|
||||
if (aStr < bStr) {
|
||||
return -1;
|
||||
}
|
||||
if (aStr > bStr) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
},
|
||||
render: ({ roles }) => <LabelCell data={roles} />,
|
||||
},
|
||||
{
|
||||
key: 'authType',
|
||||
headerText: 'Type',
|
||||
isSortable: true,
|
||||
render: ({ authType }) => (
|
||||
<Cell style={{ textTransform: 'capitalize' }}>
|
||||
{renderAuthType(authType)}
|
||||
@@ -77,8 +63,7 @@ export default function UserList(props: SimpleListProps & { users: User[] }) {
|
||||
},
|
||||
]}
|
||||
emptyText="No Users Found"
|
||||
isSearchable
|
||||
pagination={{ pageSize: props.pageSize }}
|
||||
disableFilter
|
||||
fetching={{
|
||||
fetchStatus,
|
||||
}}
|
||||
@@ -19,13 +19,11 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { MfaDevice } from 'teleport/services/mfa';
|
||||
import { User } from 'teleport/services/user';
|
||||
import useTeleport from 'teleport/useTeleport';
|
||||
|
||||
import { CommonListProps, LockResourceKind } from '../../common';
|
||||
import { SimpleListProps, TableWrapper } from '../common';
|
||||
import { MfaDevices } from './MfaDevices';
|
||||
import Users from './Users';
|
||||
|
||||
export type SimpleListOpts = {
|
||||
getFetchFn(
|
||||
@@ -45,9 +43,6 @@ export function SimpleList(props: CommonListProps & { opts: SimpleListOpts }) {
|
||||
useEffect(() => {
|
||||
let fetchFn;
|
||||
switch (props.selectedResourceKind) {
|
||||
case 'user':
|
||||
fetchFn = ctx.userService.fetchUsers;
|
||||
break;
|
||||
case 'mfa_device':
|
||||
fetchFn = ctx.mfaService.fetchDevices;
|
||||
break;
|
||||
@@ -81,8 +76,6 @@ export function SimpleList(props: CommonListProps & { opts: SimpleListOpts }) {
|
||||
toggleSelectResource: props.toggleSelectResource,
|
||||
};
|
||||
switch (props.selectedResourceKind) {
|
||||
case 'user':
|
||||
return <Users users={resources as User[]} {...listProps} />;
|
||||
case 'mfa_device':
|
||||
return (
|
||||
<MfaDevices mfaDevices={resources as MfaDevice[]} {...listProps} />
|
||||
|
||||
@@ -102,7 +102,7 @@ export const baseResourceKindOpts: LockResourceOption[] = [
|
||||
{
|
||||
value: 'user',
|
||||
label: 'Users',
|
||||
listKind: 'simple',
|
||||
listKind: 'server-side',
|
||||
},
|
||||
{
|
||||
value: 'role',
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Alert, Box, ButtonPrimary, ButtonSecondary } from 'design';
|
||||
@@ -46,7 +46,6 @@ import userService, {
|
||||
type CreateUserVariables,
|
||||
type User,
|
||||
} from 'teleport/services/user';
|
||||
import { GetUsersQueryKey } from 'teleport/services/user/hooks';
|
||||
|
||||
import UserTokenLink from './../UserTokenLink';
|
||||
|
||||
@@ -67,8 +66,6 @@ export function UserAddEdit({
|
||||
}: UserAddEditProps) {
|
||||
const ctx = useTeleport();
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const createUser = useMutation({
|
||||
mutationFn: async (variables: CreateUserVariables) => {
|
||||
const mfaResponse =
|
||||
@@ -87,30 +84,13 @@ export function UserAddEdit({
|
||||
token,
|
||||
};
|
||||
},
|
||||
onSuccess: ({ token, user }) => {
|
||||
onSuccess: ({ token }) => {
|
||||
setToken(token);
|
||||
|
||||
queryClient.setQueryData(GetUsersQueryKey, previous => {
|
||||
if (!previous) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [user, ...previous];
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const updateUser = useMutation({
|
||||
mutationFn: userService.updateUser,
|
||||
onSuccess: data => {
|
||||
queryClient.setQueryData(GetUsersQueryKey, previous => {
|
||||
if (!previous) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [data, ...previous.filter(i => i.name !== data.name)];
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const [name, setName] = useState(user.name);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
|
||||
import { Alert, ButtonSecondary, ButtonWarning, Text } from 'design';
|
||||
import Dialog, {
|
||||
@@ -28,7 +28,6 @@ import Dialog, {
|
||||
|
||||
import { ResourcesResponse } from 'teleport/services/agents';
|
||||
import userService, { User } from 'teleport/services/user';
|
||||
import { GetUsersQueryKey } from 'teleport/services/user/hooks';
|
||||
|
||||
interface UserDeleteProps {
|
||||
username: string;
|
||||
@@ -43,19 +42,8 @@ export function UserDelete({
|
||||
onClose,
|
||||
modifyFetchedData,
|
||||
}: UserDeleteProps) {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const deleteUser = useMutation({
|
||||
mutationFn: userService.deleteUser,
|
||||
onSuccess: (_, name) => {
|
||||
queryClient.setQueryData(GetUsersQueryKey, previous => {
|
||||
if (!previous) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return previous.filter(user => user.name !== name);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
async function handleDelete() {
|
||||
|
||||
@@ -1287,7 +1287,7 @@ const cfg = {
|
||||
return generatePath(cfg.routes.ssoConnector.create, { connectorType });
|
||||
},
|
||||
|
||||
// TODO(rudream): DELETE IN V21.0.0
|
||||
// TODO(rudream): DELETE IN v20.0
|
||||
getUsersUrl() {
|
||||
return cfg.api.usersPath;
|
||||
},
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2025 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { createQueryHook } from 'teleport/services/queryHelpers';
|
||||
import userService from 'teleport/services/user';
|
||||
|
||||
export const { queryKey: GetUsersQueryKey, useQuery: useGetUsers } =
|
||||
createQueryHook(['users', 'get'], userService.fetchUsers);
|
||||
@@ -66,11 +66,29 @@ const service = {
|
||||
.then(makeUser);
|
||||
},
|
||||
|
||||
// TODO(rudream): DELETE IN v21.0
|
||||
/**
|
||||
* @deprecated Use {@link service.fetchUsersV2} instead.
|
||||
*/
|
||||
// TODO(rudream): DELETE IN v20.0
|
||||
fetchUsers(signal?: AbortSignal) {
|
||||
return api.get(cfg.getUsersUrl(), signal).then(makeUsers);
|
||||
},
|
||||
|
||||
/**
|
||||
* fetchAllUsers fetches all users page by page until there are no more.
|
||||
* Only use this in cases where all users are needed upfront, otherwise, use {@link service.fetchUsersV2}.
|
||||
*/
|
||||
async fetchAllUsers(signal?: AbortSignal): Promise<User[]> {
|
||||
const allUsers: User[] = [];
|
||||
let startKey = '';
|
||||
do {
|
||||
const page = await service.fetchUsersV2({ limit: 200, startKey }, signal);
|
||||
allUsers.push(...page.items);
|
||||
startKey = page.startKey;
|
||||
} while (startKey);
|
||||
return allUsers;
|
||||
},
|
||||
|
||||
async fetchUsersV2(
|
||||
params?: UrlListUsersParams,
|
||||
signal?: AbortSignal
|
||||
@@ -89,11 +107,11 @@ const service = {
|
||||
.catch(err => {
|
||||
// If this v2 paginated endpoint isn't found, fallback to the v1 endpoint but paginate locally in order to
|
||||
// maintain compatibility with the paginated table component which expects a paginated response.
|
||||
// TODO(rudream): DELETE IN v21.0
|
||||
// TODO(rudream): DELETE IN v20.0
|
||||
if (isPathNotFoundError(err)) {
|
||||
return this.fetchUsers().then(users =>
|
||||
makeUsersPageLocally(params, users)
|
||||
);
|
||||
return service
|
||||
.fetchUsers()
|
||||
.then(users => makeUsersPageLocally(params, users));
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
@@ -202,7 +220,7 @@ function withExcludedField(user: User, excludeUserField: ExcludeUserField) {
|
||||
* makeUsersPageLocally mocks a paginated response for users so that a list of all users
|
||||
* can be handled by a serverside paginated table component.
|
||||
*/
|
||||
// TODO(rudream): DELETE IN v21.0
|
||||
// TODO(rudream): DELETE IN v20.0
|
||||
function makeUsersPageLocally(
|
||||
params: UrlListUsersParams,
|
||||
allUsers: User[]
|
||||
@@ -210,18 +228,18 @@ function makeUsersPageLocally(
|
||||
items: User[];
|
||||
startKey: string;
|
||||
} {
|
||||
if (params.search) {
|
||||
if (params?.search) {
|
||||
allUsers = allUsers.filter(u =>
|
||||
u.name.toLowerCase().includes(params.search.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
if (params.startKey) {
|
||||
if (params?.startKey) {
|
||||
const startIndex = allUsers.findIndex(p => p.name === params.startKey);
|
||||
allUsers = allUsers.slice(startIndex);
|
||||
}
|
||||
|
||||
const limit = params.limit || 200;
|
||||
const limit = params?.limit || 200;
|
||||
const nextKey = allUsers.at(limit)?.name;
|
||||
allUsers = allUsers.slice(0, limit);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user