Skip to content

Commit

Permalink
Display license related graph and metrics on system overview page (#2…
Browse files Browse the repository at this point in the history
  • Loading branch information
grotlue authored Aug 7, 2024
1 parent 55ce9b2 commit 19203e2
Show file tree
Hide file tree
Showing 12 changed files with 451 additions and 239 deletions.
12 changes: 12 additions & 0 deletions graylog2-web-interface/src/@types/graylog-web-plugin/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,17 @@ type DataTiering = {
WarmTierReadinessInfo: React.ComponentType,
}

type License = {
EnterpriseTrafficGraph: React.ComponentType,
LicenseGraphWithMetrics: React.ComponentType,
EnterpriseProductLink: React.ComponentType<{
children: React.ReactNode,
href: string,
clusterId: string,
licenseSubject?: string
}>,
}

type FieldValueProvider = {
type: string,
displayName: string,
Expand Down Expand Up @@ -191,6 +202,7 @@ declare module 'graylog-web-plugin/plugin' {
navigationItems?: Array<PluginNavigationItems>;
globalNotifications?: Array<GlobalNotification>;
fieldValueProviders?:Array<FieldValueProvider>;
license?: Array<License>,
// Global context providers allow to fetch and process data once
// and provide the result for all components in your plugin.
globalContextProviders?: Array<React.ComponentType<React.PropsWithChildrean<{}>>>,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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 * as React from 'react';

import { useStore } from 'stores/connect';
import { ClusterTrafficActions, ClusterTrafficStore } from 'stores/cluster/ClusterTrafficStore';
import { TrafficGraphWithDaySelect } from 'components/common/Graph';

const ClusterTrafficGraph = () => {
const { traffic } = useStore(ClusterTrafficStore);

return (
<TrafficGraphWithDaySelect traffic={traffic}
getTraffic={ClusterTrafficActions.getTraffic} />
);
};

export default ClusterTrafficGraph;
151 changes: 19 additions & 132 deletions graylog2-web-interface/src/components/cluster/GraylogClusterOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,61 +16,24 @@
*/

import * as React from 'react';
import { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import reduce from 'lodash/reduce';
import styled, { css } from 'styled-components';
import { PluginStore } from 'graylog-web-plugin/plugin';

import EventHandlersThrottler from 'util/EventHandlersThrottler';
import NumberUtils from 'util/NumberUtils';
import { useStore } from 'stores/connect';
import { Col, Row, Input } from 'components/bootstrap';
import useCurrentUser from 'hooks/useCurrentUser';
import { isPermitted } from 'util/PermissionsMixin';
import { Col, Row } from 'components/bootstrap';
import { Spinner } from 'components/common';
import { ClusterTrafficActions, ClusterTrafficStore } from 'stores/cluster/ClusterTrafficStore';
import { NodesStore } from 'stores/nodes/NodesStore';
import { formatTrafficData } from 'util/TrafficUtils';
import { isPermitted } from 'util/PermissionsMixin';
import useCurrentUser from 'hooks/useCurrentUser';
import useSendTelemetry from 'logic/telemetry/useSendTelemetry';
import { TELEMETRY_EVENT_TYPE } from 'logic/telemetry/Constants';

import TrafficGraph from './TrafficGraph';

const DAYS = [
30,
90,
180,
365,
];

const Wrapper = styled.div`
margin-bottom: 5px;
.control-label {
padding-top: 0;
}
.graph-days-select {
display: flex;
align-items: baseline;
select {
padding-top: 3px;
height: 28px;
}
}
`;
import ClusterTrafficGraph from 'components/cluster/ClusterTrafficGraph';

const StyledDl = styled.dl`
margin-bottom: 0;
`;
const StyledH2 = styled.h2(({ theme }) => css`
margin-bottom: ${theme.spacings.sm};
`);
const StyledH3 = styled.h3(({ theme }) => css`
margin-bottom: ${theme.spacings.sm};
`);

const Header = () => <StyledH2>Graylog cluster</StyledH2>;

Expand All @@ -93,99 +56,19 @@ const ClusterInfo = () => {
);
};

export const GraylogClusterTrafficGraph = () => {
const { traffic } = useStore(ClusterTrafficStore);
const [graphDays, setGraphDays] = useState(DAYS[0]);
const [graphWidth, setGraphWidth] = useState(600);
const eventThrottler = useRef(new EventHandlersThrottler());
const containerRef = useRef(null);
const licensePlugin = PluginStore.exports('license');
const currentUser = useCurrentUser();
const sendTelemetry = useSendTelemetry();

const onGraphDaysChange = (event: React.ChangeEvent<HTMLOptionElement>): void => {
event.preventDefault();
const newDays = Number(event.target.value);

setGraphDays(newDays);

sendTelemetry(TELEMETRY_EVENT_TYPE.SYSTEM_OVERVIEW_OUTGOING_TRAFFIC_DAYS_CHANGED, {
app_pathname: 'system-overview',
app_section: 'outgoing-traffic',
app_action_value: 'trafficgraph-days-button',
event_details: { value: newDays },
});
};

useEffect(() => {
ClusterTrafficActions.getTraffic(graphDays);
}, [graphDays]);

useEffect(() => {
const _resizeGraphs = () => {
const { clientWidth } = containerRef.current;

setGraphWidth(clientWidth);
};

const _onResize = () => {
eventThrottler.current.throttle(() => _resizeGraphs());
};

window.addEventListener('resize', _onResize);

if (containerRef.current) {
_resizeGraphs();
}

return () => {
window.removeEventListener('resize', _onResize);
};
}, []);

const TrafficGraphComponent = (isPermitted(currentUser.permissions, ['licenses:read']) && licensePlugin[0]?.EnterpriseTrafficGraph) || TrafficGraph;
let sumOutput = null;
let trafficGraph = <Spinner />;

if (traffic) {
const bytesOut = reduce(traffic.output, (result, value) => result + value);

sumOutput = <small>Last {graphDays} days: {NumberUtils.formatBytes(bytesOut)}</small>;

const unixTraffic = formatTrafficData(traffic.output);

trafficGraph = (
<TrafficGraphComponent traffic={unixTraffic}
width={graphWidth} />
);
}

return (
<>
<Wrapper className="form-inline graph-days pull-right">
<Input id="graph-days"
type="select"
bsSize="small"
label="Days"
value={graphDays}
onChange={onGraphDaysChange}
formGroupClassName="graph-days-select">
{DAYS.map((size) => <option key={`option-${size}`} value={size}>{size}</option>)}
</Input>
</Wrapper>

<StyledH3 ref={containerRef}>Outgoing traffic {sumOutput}</StyledH3>
{trafficGraph}
</>
);
};

type Props = {
layout?: 'default' | 'compact',
children: React.ReactNode
children: React.ReactNode,
showLicenseGraph?: boolean,
}

const GraylogClusterOverview = ({ layout, children }: Props) => {
const GraylogClusterOverview = ({ layout, children, showLicenseGraph }: Props) => {
const licensePlugin = PluginStore.exports('license');
const currentUser = useCurrentUser();

const LicenseGraphComponent = (isPermitted(currentUser.permissions, ['licenses:read']) && licensePlugin[0]?.LicenseGraphWithMetrics) || ClusterTrafficGraph;
const EnterpriseGraphComponent = (isPermitted(currentUser.permissions, ['licenses:read']) && licensePlugin[0]?.EnterpriseTrafficGraph) || ClusterTrafficGraph;

if (layout === 'compact') {
return (
<Row className="content">
Expand All @@ -198,7 +81,8 @@ const GraylogClusterOverview = ({ layout, children }: Props) => {
{children}
</Col>
<Col md={6}>
<GraylogClusterTrafficGraph />
{showLicenseGraph ? (<LicenseGraphComponent />
) : (<EnterpriseGraphComponent />)}
</Col>
</Row>
</Col>
Expand All @@ -215,7 +99,8 @@ const GraylogClusterOverview = ({ layout, children }: Props) => {
{children}
<Row>
<Col md={12}>
<GraylogClusterTrafficGraph />
{showLicenseGraph ? (<LicenseGraphComponent />
) : (<EnterpriseGraphComponent />)}
</Col>
</Row>
</Col>
Expand All @@ -226,11 +111,13 @@ const GraylogClusterOverview = ({ layout, children }: Props) => {
GraylogClusterOverview.propTypes = {
layout: PropTypes.oneOf(['default', 'compact']),
children: PropTypes.node,
showLicenseGraph: PropTypes.bool,
};

GraylogClusterOverview.defaultProps = {
layout: 'default',
children: null,
showLicenseGraph: false,
};

export default GraylogClusterOverview;
84 changes: 0 additions & 84 deletions graylog2-web-interface/src/components/cluster/TrafficGraph.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,11 @@
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
import type * as React from 'react';

interface EnterpriseTrafficGraph {
layout: string;
children: React.ReactNode;
}

interface LicensePlugin {
EnterpriseTrafficGraph?: React.ComponentType<EnterpriseTrafficGraph>;
}

declare module 'graylog-web-plugin/plugin' {
interface PluginExports {
license?: LicensePlugin;
}
export type Traffic = {
from: string,
to: string,
output: Record<string, number>,
input: Record<string, number>,
decoded: Record<string, number>,
}
Loading

0 comments on commit 19203e2

Please sign in to comment.