Skip to content

Commit

Permalink
[Stack monitoring] Elasticsearch nodes view migration (#112422) (#113004
Browse files Browse the repository at this point in the history
)

* wip nodes view migration

* Save table pagination with localStorage and add some types

* fix types

* fix pagination

* fix table query

* remove async

* fix eslint

Co-authored-by: neptunian <sandra.gonzales@elastic.co>

Co-authored-by: Ester Martí Vilaseca <ester.martivilaseca@elastic.co>
Co-authored-by: neptunian <sandra.gonzales@elastic.co>
  • Loading branch information
3 people authored Sep 23, 2021
1 parent 167fa4b commit b03e39d
Show file tree
Hide file tree
Showing 5 changed files with 283 additions and 0 deletions.
162 changes: 162 additions & 0 deletions x-pack/plugins/monitoring/public/application/hooks/use_table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { useState, useCallback } from 'react';
import { EUI_SORT_ASCENDING } from '../../../common/constants';
import { euiTableStorageGetter, euiTableStorageSetter } from '../../components/table';
import { Storage } from '../../../../../../src/plugins/kibana_utils/public';

interface Pagination {
pageSize: number;
initialPageSize: number;
pageIndex: number;
initialPageIndex: number;
pageSizeOptions: number[];
totalItemCount: number;
}

interface Page {
size: number;
index: number;
}

interface Sorting {
sort: {
field: string;
direction: string;
};
}

const PAGE_SIZE_OPTIONS = [5, 10, 20, 50];

const DEFAULT_PAGINATION = {
pageSize: 20,
initialPageSize: 20,
pageIndex: 0,
initialPageIndex: 0,
pageSizeOptions: PAGE_SIZE_OPTIONS,
totalItemCount: 0,
};

const getPaginationInitialState = (page: Page | undefined) => {
const pagination = DEFAULT_PAGINATION;

if (page) {
pagination.initialPageSize = page.size;
pagination.pageSize = page.size;
pagination.initialPageIndex = page.index;
pagination.pageIndex = page.index;
}

return {
...pagination,
pageSizeOptions: PAGE_SIZE_OPTIONS,
};
};

export function useTable(storageKey: string) {
const storage = new Storage(window.localStorage);
const getLocalStorageData = euiTableStorageGetter(storageKey);
const setLocalStorageData = euiTableStorageSetter(storageKey);

const storageData = getLocalStorageData(storage);
// get initial state from localstorage
const [pagination, setPagination] = useState<Pagination>(
getPaginationInitialState(storageData.page)
);

const updateTotalItemCount = useCallback(
(num) => {
// only update pagination state if different
if (num === pagination.totalItemCount) return;
setPagination({
...pagination,
totalItemCount: num,
});
},
[setPagination, pagination]
);

// get initial state from localStorage
const [sorting, setSorting] = useState<Sorting>(storageData.sort || { sort: {} });
const cleanSortingData = (sortData: Sorting) => {
const sort = sortData || { sort: {} };

if (!sort.sort.field) {
sort.sort.field = 'name';
}
if (!sort.sort.direction) {
sort.sort.direction = EUI_SORT_ASCENDING;
}

return sort;
};

const [query, setQuery] = useState('');

const onTableChange = () => {
// we are already updating the state in fetchMoreData. We would need to check in react
// if both methods are needed or we can clean one of them
// For now I just keep it so existing react components don't break
};

const getPaginationRouteOptions = useCallback(() => {
if (!pagination || !sorting) {
return {};
}

return {
pagination: {
size: pagination.pageSize,
index: pagination.pageIndex,
},
...sorting,
queryText: query,
};
}, [pagination, query, sorting]);

const getPaginationTableProps = () => {
return {
sorting,
pagination,
onTableChange,
fetchMoreData: ({
page,
sort,
queryText,
}: {
page: Page;
sort: Sorting;
queryText: string;
}) => {
setPagination({
...pagination,
...{
initialPageSize: page.size,
pageSize: page.size,
initialPageIndex: page.index,
pageIndex: page.index,
pageSizeOptions: PAGE_SIZE_OPTIONS,
},
});
setSorting(cleanSortingData(sort));
setQuery(queryText);

setLocalStorageData(storage, {
page,
sort,
});
},
};
};

return {
getPaginationRouteOptions,
getPaginationTableProps,
updateTotalItemCount,
};
}
9 changes: 9 additions & 0 deletions x-pack/plugins/monitoring/public/application/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { createPreserveQueryHistory } from './preserve_query_history';
import { RouteInit } from './route_init';
import { NoDataPage } from './pages/no_data';
import { ElasticsearchOverviewPage } from './pages/elasticsearch/overview';
import { ElasticsearchNodesPage } from './pages/elasticsearch/nodes_page';
import { CODE_PATH_ELASTICSEARCH } from '../../common/constants';
import { MonitoringTimeContainer } from './hooks/use_monitoring_time';
import { BreadcrumbContainer } from './hooks/use_breadcrumbs';
Expand Down Expand Up @@ -77,12 +78,20 @@ const MonitoringApp: React.FC<{
/>

{/* ElasticSearch Views */}
<RouteInit
path="/elasticsearch/nodes"
component={ElasticsearchNodesPage}
codePaths={[CODE_PATH_ELASTICSEARCH]}
fetchAllClusters={false}
/>

<RouteInit
path="/elasticsearch"
component={ElasticsearchOverviewPage}
codePaths={[CODE_PATH_ELASTICSEARCH]}
fetchAllClusters={false}
/>

<Redirect
to={{
pathname: '/loading',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { useContext, useState, useCallback } from 'react';
import { i18n } from '@kbn/i18n';
import { find } from 'lodash';
import { ElasticsearchTemplate } from './elasticsearch_template';
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
import { GlobalStateContext } from '../../global_state_context';
import { ExternalConfigContext } from '../../external_config_context';
import { ElasticsearchNodes } from '../../../components/elasticsearch';
import { ComponentProps } from '../../route_init';
import { SetupModeRenderer } from '../../setup_mode/setup_mode_renderer';
import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context';
import { useTable } from '../../hooks/use_table';

interface SetupModeProps {
setupMode: any;
flyoutComponent: any;
bottomBarComponent: any;
}

export const ElasticsearchNodesPage: React.FC<ComponentProps> = ({ clusters }) => {
const globalState = useContext(GlobalStateContext);
const { showCgroupMetricsElasticsearch } = useContext(ExternalConfigContext);
const { services } = useKibana<{ data: any }>();
const { getPaginationRouteOptions, updateTotalItemCount, getPaginationTableProps } =
useTable('elasticsearch.nodes');
const clusterUuid = globalState.cluster_uuid;
const ccs = globalState.ccs;
const cluster = find(clusters, {
cluster_uuid: clusterUuid,
});
const [data, setData] = useState({} as any);

const title = i18n.translate('xpack.monitoring.elasticsearch.nodes.routeTitle', {
defaultMessage: 'Elasticsearch - Nodes',
});

const pageTitle = i18n.translate('xpack.monitoring.elasticsearch.nodes.pageTitle', {
defaultMessage: 'Elasticsearch nodes',
});

const getPageData = useCallback(async () => {
const bounds = services.data?.query.timefilter.timefilter.getBounds();
const url = `../api/monitoring/v1/clusters/${clusterUuid}/elasticsearch/nodes`;
const response = await services.http?.fetch(url, {
method: 'POST',
body: JSON.stringify({
ccs,
timeRange: {
min: bounds.min.toISOString(),
max: bounds.max.toISOString(),
},
...getPaginationRouteOptions(),
}),
});

setData(response);
updateTotalItemCount(response.totalNodeCount);
}, [
ccs,
clusterUuid,
services.data?.query.timefilter.timefilter,
services.http,
getPaginationRouteOptions,
updateTotalItemCount,
]);

return (
<ElasticsearchTemplate
title={title}
pageTitle={pageTitle}
getPageData={getPageData}
data-test-subj="elasticsearchOverviewPage"
cluster={cluster}
>
<div data-test-subj="elasticsearchNodesListingPage">
<SetupModeRenderer
render={({ setupMode, flyoutComponent, bottomBarComponent }: SetupModeProps) => (
<SetupModeContext.Provider value={{ setupModeSupported: true }}>
{flyoutComponent}
<ElasticsearchNodes
clusterStatus={data.clusterStatus}
clusterUuid={globalState.cluster_uuid}
setupMode={setupMode}
nodes={data.nodes}
alerts={{}}
showCgroupMetricsElasticsearch={showCgroupMetricsElasticsearch}
{...getPaginationTableProps()}
/>
{bottomBarComponent}
</SetupModeContext.Provider>
)}
/>
</div>
</ElasticsearchTemplate>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
*/

export const ElasticsearchOverview: FunctionComponent<Props>;
export const ElasticsearchNodes: FunctionComponent<Props>;
9 changes: 9 additions & 0 deletions x-pack/plugins/monitoring/public/components/table/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export const euiTableStorageGetter: (string) => any;
export const euiTableStorageSetter: (string) => any;

0 comments on commit b03e39d

Please sign in to comment.