Skip to content

Commit

Permalink
Merge branch 'main' into astandrik.fix-query-history
Browse files Browse the repository at this point in the history
  • Loading branch information
astandrik authored Aug 2, 2024
2 parents ce6943f + 70b6dc3 commit 0e3eaa8
Show file tree
Hide file tree
Showing 16 changed files with 164 additions and 244 deletions.
3 changes: 2 additions & 1 deletion src/components/PDiskInfo/PDiskInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {getPDiskPagePath} from '../../routes';
import {selectIsUserAllowedToMakeChanges} from '../../store/reducers/authentication/authentication';
import {useDiskPagesAvailable} from '../../store/reducers/capabilities/hooks';
import {valueIsDefined} from '../../utils';
import {formatBytes} from '../../utils/bytesParsers';
Expand Down Expand Up @@ -194,7 +195,7 @@ export function PDiskInfo<T extends PreparedPDisk>({
withPDiskPageLink,
className,
}: PDiskInfoProps<T>) {
const {isUserAllowedToMakeChanges} = useTypedSelector((state) => state.authentication);
const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges);
const diskPagesAvailable = useDiskPagesAvailable();

const [generalInfo, statusInfo, spaceInfo, additionalInfo] = getPDiskInfo({
Expand Down
63 changes: 29 additions & 34 deletions src/containers/App/Content.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import React from 'react';

import {connect, shallowEqual} from 'react-redux';
import {connect} from 'react-redux';
import type {RedirectProps} from 'react-router-dom';
import {Redirect, Route, Switch} from 'react-router-dom';

import {PageError} from '../../components/Errors/PageError/PageError';
import {LoaderWrapper} from '../../components/LoaderWrapper/LoaderWrapper';
import {useSlots} from '../../components/slots';
import type {SlotMap} from '../../components/slots/SlotMap';
import type {SlotComponent} from '../../components/slots/types';
import routes from '../../routes';
import type {RootState} from '../../store';
import {getUser} from '../../store/reducers/authentication/authentication';
import {authenticationApi} from '../../store/reducers/authentication/authentication';
import {capabilitiesApi} from '../../store/reducers/capabilities/capabilities';
import {nodesListApi} from '../../store/reducers/nodesList';
import {cn} from '../../utils/cn';
import {useTypedDispatch, useTypedSelector} from '../../utils/hooks';
import {lazyComponent} from '../../utils/lazyComponent';
import Authentication from '../Authentication/Authentication';
import {getClusterPath} from '../Cluster/utils';
Expand Down Expand Up @@ -143,43 +144,37 @@ export function Content(props: ContentProps) {
{additionalRoutes?.rendered}
{/* Single cluster routes */}
<Route key="single-cluster">
<GetUser />
<GetNodesList />
<GetCapabilities />
<Header mainPage={mainPage} />
<Switch>
{routesSlots.map((route) => {
return renderRouteSlot(slots, route);
})}
<Route
path={redirectProps.from || redirectProps.path}
exact={redirectProps.exact}
strict={redirectProps.strict}
render={() => <Redirect to={redirectProps.to} push={redirectProps.push} />}
/>
</Switch>
<GetUser>
<GetNodesList />
<GetCapabilities />
<Header mainPage={mainPage} />
<Switch>
{routesSlots.map((route) => {
return renderRouteSlot(slots, route);
})}
<Route
path={redirectProps.from || redirectProps.path}
exact={redirectProps.exact}
strict={redirectProps.strict}
render={() => (
<Redirect to={redirectProps.to} push={redirectProps.push} />
)}
/>
</Switch>
</GetUser>
</Route>
</Switch>
);
}

function GetUser() {
const dispatch = useTypedDispatch();
const {isAuthenticated, isInternalUser} = useTypedSelector(
(state) => ({
isAuthenticated: state.authentication.isAuthenticated,
isInternalUser: Boolean(state.authentication.user),
}),
shallowEqual,
);

React.useEffect(() => {
if (isAuthenticated && !isInternalUser) {
dispatch(getUser());
}
}, [dispatch, isAuthenticated, isInternalUser]);
function GetUser({children}: {children: React.ReactNode}) {
const {isLoading, error} = authenticationApi.useWhoamiQuery(undefined);

return null;
return (
<LoaderWrapper loading={isLoading} size="l">
<PageError error={error}>{children}</PageError>
</LoaderWrapper>
);
}

function GetNodesList() {
Expand Down
3 changes: 2 additions & 1 deletion src/containers/AsideNavigation/AsideNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {MenuItem} from '@gravity-ui/navigation';
import {AsideHeader, FooterItem} from '@gravity-ui/navigation';
import {useHistory} from 'react-router-dom';

import {selectUser} from '../../store/reducers/authentication/authentication';
import {cn} from '../../utils/cn';
import {ASIDE_HEADER_COMPACT_KEY} from '../../utils/constants';
import {useSetting, useTypedSelector} from '../../utils/hooks';
Expand Down Expand Up @@ -65,7 +66,7 @@ export function AsideNavigation(props: AsideNavigationProps) {

const [visiblePanel, setVisiblePanel] = React.useState<Panel>();

const {user: ydbUser} = useTypedSelector((state) => state.authentication);
const ydbUser = useTypedSelector(selectUser);
const [compact, setIsCompact] = useSetting<boolean>(ASIDE_HEADER_COMPACT_KEY);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import {Button, Icon} from '@gravity-ui/uikit';
import {useHistory} from 'react-router-dom';

import routes, {createHref} from '../../../routes';
import {logout} from '../../../store/reducers/authentication/authentication';
import {authenticationApi} from '../../../store/reducers/authentication/authentication';
import {cn} from '../../../utils/cn';
import {useTypedDispatch, useTypedSelector} from '../../../utils/hooks';
import {useTypedSelector} from '../../../utils/hooks';
import i18n from '../i18n';

import './YdbInternalUser.scss';
Expand All @@ -15,16 +15,17 @@ const b = cn('kv-ydb-internal-user');
export function YdbInternalUser() {
const {user: ydbUser} = useTypedSelector((state) => state.authentication);

const [logout] = authenticationApi.useLogoutMutation();

const history = useHistory();
const handleLoginClick = () => {
history.push(
createHref(routes.auth, undefined, {returnUrl: encodeURIComponent(location.href)}),
);
};

const dispatch = useTypedDispatch();
const handleLogout = () => {
dispatch(logout);
logout(undefined);
};

return (
Expand Down
21 changes: 11 additions & 10 deletions src/containers/Authentication/Authentication.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import {Button, Link as ExternalLink, Icon, TextInput} from '@gravity-ui/uikit';
import {useHistory, useLocation} from 'react-router-dom';

import {parseQuery} from '../../routes';
import {authenticate} from '../../store/reducers/authentication/authentication';
import {authenticationApi} from '../../store/reducers/authentication/authentication';
import {cn} from '../../utils/cn';
import {useTypedDispatch, useTypedSelector} from '../../utils/hooks';

import {isPasswordError, isUserError} from './utils';

import ydbLogoIcon from '../../assets/icons/ydb.svg';

Expand All @@ -20,25 +21,24 @@ interface AuthenticationProps {
}

function Authentication({closable = false}: AuthenticationProps) {
const dispatch = useTypedDispatch();
const history = useHistory();
const location = useLocation();

const {returnUrl} = parseQuery(location);
const [authenticate, {error, isLoading}] = authenticationApi.useAuthenticateMutation(undefined);

const {error} = useTypedSelector((state) => state.authentication);
const {returnUrl} = parseQuery(location);

const [login, setLogin] = React.useState('');
const [pass, setPass] = React.useState('');
const [password, setPass] = React.useState('');
const [loginError, setLoginError] = React.useState('');
const [passwordError, setPasswordError] = React.useState('');
const [showPassword, setShowPassword] = React.useState(false);

React.useEffect(() => {
if (error?.data?.error?.includes('user')) {
if (isUserError(error)) {
setLoginError(error.data.error);
}
if (error?.data?.error?.includes('password')) {
if (isPasswordError(error)) {
setPasswordError(error.data.error);
}
}, [error]);
Expand All @@ -54,7 +54,7 @@ function Authentication({closable = false}: AuthenticationProps) {
};

const onLoginClick = () => {
dispatch(authenticate(login, pass)).then(() => {
authenticate({user: login, password}).then(() => {
if (returnUrl) {
const decodedUrl = decodeURIComponent(returnUrl.toString());

Expand Down Expand Up @@ -108,7 +108,7 @@ function Authentication({closable = false}: AuthenticationProps) {
</div>
<div className={b('field-wrapper')}>
<TextInput
value={pass}
value={password}
onUpdate={onPassUpdate}
type={showPassword ? 'text' : 'password'}
placeholder={'Password'}
Expand All @@ -130,6 +130,7 @@ function Authentication({closable = false}: AuthenticationProps) {
width="max"
size="l"
disabled={Boolean(!login || loginError || passwordError)}
loading={isLoading}
className={b('button-sign-in')}
>
Sign in
Expand Down
24 changes: 24 additions & 0 deletions src/containers/Authentication/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
interface AuthError {
data: {
error: string;
};
}

function isAuthError(error: unknown): error is AuthError {
return Boolean(
error &&
typeof error === 'object' &&
'data' in error &&
error.data &&
typeof error.data === 'object' &&
'error' in error.data &&
typeof error.data.error === 'string',
);
}

export function isUserError(error: unknown): error is AuthError {
return isAuthError(error) && error.data.error.includes('user');
}
export function isPasswordError(error: unknown): error is AuthError {
return isAuthError(error) && error.data.error.includes('password');
}
3 changes: 2 additions & 1 deletion src/containers/PDiskPage/PDiskPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {PDiskInfo} from '../../components/PDiskInfo/PDiskInfo';
import {PageMeta} from '../../components/PageMeta/PageMeta';
import {getPDiskPagePath} from '../../routes';
import {api} from '../../store/reducers/api';
import {selectIsUserAllowedToMakeChanges} from '../../store/reducers/authentication/authentication';
import {setHeaderBreadcrumbs} from '../../store/reducers/header/header';
import {pDiskApi} from '../../store/reducers/pdisk/pdisk';
import {valueIsDefined} from '../../utils';
Expand Down Expand Up @@ -55,7 +56,7 @@ const pDiskTabSchema = z.nativeEnum(PDISK_TABS_IDS).catch(PDISK_TABS_IDS.diskDis
export function PDiskPage() {
const dispatch = useTypedDispatch();

const {isUserAllowedToMakeChanges} = useTypedSelector((state) => state.authentication);
const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges);

const [{nodeId, pDiskId, activeTab}] = useQueryParams({
activeTab: StringParam,
Expand Down
3 changes: 2 additions & 1 deletion src/containers/Tablet/TabletControls/TabletControls.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';

import {ButtonWithConfirmDialog} from '../../../components/ButtonWithConfirmDialog/ButtonWithConfirmDialog';
import {selectIsUserAllowedToMakeChanges} from '../../../store/reducers/authentication/authentication';
import {ETabletState} from '../../../types/api/tablet';
import type {TTabletStateInfo} from '../../../types/api/tablet';
import {useTypedSelector} from '../../../utils/hooks';
Expand All @@ -15,7 +16,7 @@ interface TabletControlsProps {
export const TabletControls = ({tablet, fetchData}: TabletControlsProps) => {
const {TabletId, HiveId} = tablet;

const {isUserAllowedToMakeChanges} = useTypedSelector((state) => state.authentication);
const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges);

const _onKillClick = () => {
return window.api.killTablet(TabletId);
Expand Down
3 changes: 2 additions & 1 deletion src/containers/Tablets/Tablets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {InternalLink} from '../../components/InternalLink';
import {ResizeableDataTable} from '../../components/ResizeableDataTable/ResizeableDataTable';
import {TableSkeleton} from '../../components/TableSkeleton/TableSkeleton';
import routes, {createHref} from '../../routes';
import {selectIsUserAllowedToMakeChanges} from '../../store/reducers/authentication/authentication';
import {selectTabletsWithFqdn, tabletsApi} from '../../store/reducers/tablets';
import {ETabletState} from '../../types/api/tablet';
import type {TTabletStateInfo} from '../../types/api/tablet';
Expand Down Expand Up @@ -136,7 +137,7 @@ const columns: DataTableColumn<TTabletStateInfo & {fqdn?: string}>[] = [
function TabletActions(tablet: TTabletStateInfo) {
const isDisabledRestart = tablet.State === ETabletState.Stopped;
const dispatch = useTypedDispatch();
const {isUserAllowedToMakeChanges} = useTypedSelector((state) => state.authentication);
const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges);

return (
<ButtonWithConfirmDialog
Expand Down
3 changes: 2 additions & 1 deletion src/containers/VDiskPage/VDiskPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {InfoViewerSkeleton} from '../../components/InfoViewerSkeleton/InfoViewer
import {PageMeta} from '../../components/PageMeta/PageMeta';
import {VDiskWithDonorsStack} from '../../components/VDisk/VDiskWithDonorsStack';
import {VDiskInfo} from '../../components/VDiskInfo/VDiskInfo';
import {selectIsUserAllowedToMakeChanges} from '../../store/reducers/authentication/authentication';
import {setHeaderBreadcrumbs} from '../../store/reducers/header/header';
import {selectNodesMap} from '../../store/reducers/nodesList';
import {vDiskApi} from '../../store/reducers/vdisk/vdisk';
Expand All @@ -34,7 +35,7 @@ export function VDiskPage() {
const dispatch = useTypedDispatch();

const nodesMap = useTypedSelector(selectNodesMap);
const {isUserAllowedToMakeChanges} = useTypedSelector((state) => state.authentication);
const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges);

const [{nodeId, pDiskId, vDiskSlotId}] = useQueryParams({
nodeId: StringParam,
Expand Down
13 changes: 3 additions & 10 deletions src/services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
// authUrl - external auth service link, after successful auth additional cookies will be appended
// that will allow access to clusters where OIDC proxy is a balancer
if (response && response.status === 401 && response.data?.authUrl) {
return window.location.assign(response.data.authUrl);
window.location.assign(response.data.authUrl);
}

return Promise.reject(error);
Expand Down Expand Up @@ -611,15 +611,8 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
},
);
}
authenticate(user: string, password: string) {
return this.post(
this.getPath('/login'),
{
user,
password,
},
{},
);
authenticate(params: {user: string; password: string}) {
return this.post(this.getPath('/login'), params, {});
}
logout() {
return this.post(this.getPath('/logout'), {}, {});
Expand Down
2 changes: 1 addition & 1 deletion src/store/reducers/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const api = createApi({
*/
endpoints: () => ({}),
invalidationBehavior: 'immediately',
tagTypes: ['All', 'PDiskData'],
tagTypes: ['All', 'PDiskData', 'UserData'],
});

export const _NEVER = Symbol();
Expand Down
Loading

0 comments on commit 0e3eaa8

Please sign in to comment.