Skip to content

Commit

Permalink
added actions on datanode datails page (#17840)
Browse files Browse the repository at this point in the history
* added actions on datanode datails page

* changelog file

* use fetchDataNode instead of the list api call for details page

* reuse DataNodeActions component

* defaultProp displayAs: 'dropdown'

---------

Co-authored-by: Ousmane SAMBA <ousmane@graylog.com>
  • Loading branch information
gally47 and ousmaneo committed Jan 10, 2024
1 parent 3f02c91 commit ca5ca2c
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 25 deletions.
5 changes: 5 additions & 0 deletions changelog/unreleased/issue-17705.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type = "fixed"
message = "added actions on datanode datails page"

issues = ["17705"]
pulls = ["17840"]
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@
*/
import * as React from 'react';
import { useState } from 'react';
import styled from 'styled-components';

import type { DataNode } from 'preflight/types';
import { ConfirmDialog } from 'components/common';
import { MenuItem } from 'components/bootstrap';
import { Button, MenuItem } from 'components/bootstrap';
import OverlayDropdownButton from 'components/common/OverlayDropdownButton';
import { MORE_ACTIONS_TITLE, MORE_ACTIONS_HOVER_TITLE } from 'components/common/EntityDataTable/Constants';
import type { DataNode } from 'preflight/types';

import {
rejoinDataNode,
Expand All @@ -31,8 +32,13 @@ import {
startDataNode,
} from '../hooks/useDataNodes';

const ActionButton = styled(Button)`
margin-left: 4px;
`;

type Props = {
dataNode: DataNode,
displayAs?: 'dropdown'|'buttons',
};

const DIALOG_TYPES = {
Expand All @@ -57,7 +63,7 @@ const DIALOG_TEXT = {
},
};

const DataNodeActions = ({ dataNode }: Props) => {
const DataNodeActions = ({ dataNode, displayAs }: Props) => {
const [showDialog, setShowDialog] = useState(false);
const [dialogType, setDialogType] = useState(null);

Expand Down Expand Up @@ -120,17 +126,27 @@ const DataNodeActions = ({ dataNode }: Props) => {

return (
<>
<OverlayDropdownButton title={MORE_ACTIONS_TITLE}
bsSize="xsmall"
buttonTitle={MORE_ACTIONS_HOVER_TITLE}
disabled={false}
dropdownZIndex={1000}>
<MenuItem onSelect={() => renewDatanodeCertificate(dataNode.node_id)}>Renew certificate</MenuItem>
{!isDatanodeRunning && <MenuItem onSelect={() => startDataNode(dataNode.node_id)}>Start</MenuItem>}
{isDatanodeRunning && <MenuItem onSelect={() => handleAction(DIALOG_TYPES.STOP)}>Stop</MenuItem>}
{isDatanodeRemoved && <MenuItem onSelect={() => handleAction(DIALOG_TYPES.REJOIN)}>Rejoin</MenuItem>}
{(!isDatanodeRemoved || isRemovingDatanode) && <MenuItem onSelect={() => handleAction(DIALOG_TYPES.REMOVE)}>Remove</MenuItem>}
</OverlayDropdownButton>
{displayAs === 'dropdown' && (
<OverlayDropdownButton title={MORE_ACTIONS_TITLE}
bsSize="xsmall"
buttonTitle={MORE_ACTIONS_HOVER_TITLE}
disabled={false}
dropdownZIndex={1000}>
<MenuItem onSelect={() => renewDatanodeCertificate(dataNode.node_id)}>Renew certificate</MenuItem>
{!isDatanodeRunning && <MenuItem onSelect={() => startDataNode(dataNode.node_id)}>Start</MenuItem>}
{isDatanodeRunning && <MenuItem onSelect={() => handleAction(DIALOG_TYPES.STOP)}>Stop</MenuItem>}
{isDatanodeRemoved && <MenuItem onSelect={() => handleAction(DIALOG_TYPES.REJOIN)}>Rejoin</MenuItem>}
{(!isDatanodeRemoved || isRemovingDatanode) && <MenuItem onSelect={() => handleAction(DIALOG_TYPES.REMOVE)}>Remove</MenuItem>}
</OverlayDropdownButton>
)}
{displayAs === 'buttons' && (
<>
{!isDatanodeRunning && <ActionButton onClick={() => startDataNode(dataNode.node_id)} bsSize="small">Start</ActionButton>}
{isDatanodeRunning && <ActionButton onClick={() => handleAction(DIALOG_TYPES.STOP)} bsSize="small">Stop</ActionButton>}
{isDatanodeRemoved && <ActionButton onClick={() => handleAction(DIALOG_TYPES.REJOIN)} bsSize="small">Rejoin</ActionButton>}
{(!isDatanodeRemoved || isRemovingDatanode) && <ActionButton onClick={() => handleAction(DIALOG_TYPES.REMOVE)} bsSize="small">Remove</ActionButton>}
</>
)}
{showDialog && (
<ConfirmDialog title={DIALOG_TEXT[dialogType].dialogTitle}
show
Expand All @@ -143,4 +159,8 @@ const DataNodeActions = ({ dataNode }: Props) => {
);
};

DataNodeActions.defaultProps = {
displayAs: 'dropdown',
};

export default DataNodeActions;
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2020 Graylog, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* 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
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
import { useQuery } from '@tanstack/react-query';

import { qualifyUrl } from 'util/URLUtils';
import type { DataNode } from 'preflight/types';
import UserNotification from 'util/UserNotification';
import fetch from 'logic/rest/FetchProvider';

const fetchDataNode = async (datanodeId: string) => fetch('GET', qualifyUrl(`/datanode/${datanodeId}`));

const useDataNode = (datanodeId: string) : {
data: DataNode,
refetch: () => void,
isInitialLoading: boolean,
error: any,
} => {
const { data, refetch, isInitialLoading, error } = useQuery(
['datanode'],
() => fetchDataNode(datanodeId),
{
onError: (errorThrown) => {
UserNotification.error(`Loading Data Node failed with status: ${errorThrown}`,
'Could not load Data Node');
},
notifyOnChangeProps: ['data', 'error'],
refetchInterval: 5000,
},
);

return ({
data,
refetch,
isInitialLoading,
error,
});
};

export default useDataNode;
30 changes: 19 additions & 11 deletions graylog2-web-interface/src/pages/DataNodePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@
import * as React from 'react';
import styled, { css } from 'styled-components';

import type { DataNode } from 'preflight/types';
import useParams from 'routing/useParams';
import useDataNodes from 'components/datanode/hooks/useDataNodes';
import DataNodesPageNavigation from 'components/datanode/DataNodePageNavigation';
import DocsHelper from 'util/DocsHelper';
import { Row, Col, Label } from 'components/bootstrap';
import { DocumentTitle, PageHeader, RelativeTime, Spinner } from 'components/common';
import type { SearchParams } from 'stores/PaginationTypes';
import { DocumentTitle, NoSearchResult, PageHeader, RelativeTime, Spinner } from 'components/common';
import { CertRenewalButton } from 'components/datanode/DataNodeConfiguration/CertificateRenewal';
import Icon from 'components/common/Icon';
import useDataNode from 'components/datanode/hooks/useDataNode';
import DataNodeActions from 'components/datanode/DataNodeList/DataNodeActions';

const StyledHorizontalDl = styled.dl(({ theme }) => css`
margin: ${theme.spacings.md} 0;
Expand Down Expand Up @@ -57,19 +58,23 @@ const BooleanValue = ({ value }: { value: boolean }) => (
<><BooleanIcon name={value ? 'check-circle' : 'times-circle'} value={value} /> {value ? 'yes' : 'no'}</>
);

const ActionsCol = styled(Col)`
text-align: right;
`;

const DataNodePage = () => {
const { dataNodeId } = useParams();
const { data: { elements }, isInitialLoading } = useDataNodes({
query: '',
page: 1,
pageSize: 0,
} as SearchParams);
const { data, isInitialLoading, error } = useDataNode(dataNodeId);

if (isInitialLoading) {
return <Spinner />;
}

const datanode = elements.find((node) => node.node_id === dataNodeId);
if (!isInitialLoading && (!data || error)) {
return <NoSearchResult>Error: {error?.message}</NoSearchResult>;
}

const datanode = data as DataNode;
const datanodeDisabled = datanode.data_node_status !== 'AVAILABLE';

return (
Expand All @@ -81,8 +86,8 @@ const DataNodePage = () => {
path: DocsHelper.PAGES.GRAYLOG_DATA_NODE,
}} />
<Row className="content">
<Col md={12}>
<Col md={5}>
<Col xs={12}>
<Col xs={9}>
<h2>Details:</h2>
<StyledHorizontalDl>
<dt>Hostname:</dt>
Expand All @@ -104,6 +109,9 @@ const DataNodePage = () => {
<dd><RelativeTime dateTime={datanode.cert_valid_until} /> <CertRenewalButton nodeId={datanode.node_id} status={datanode.status} /></dd>
</StyledHorizontalDl>
</Col>
<ActionsCol xs={3}>
<DataNodeActions dataNode={datanode} displayAs="buttons" />
</ActionsCol>
</Col>
</Row>
</DocumentTitle>
Expand Down

0 comments on commit ca5ca2c

Please sign in to comment.