-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ui-metrics] Converted the Metrics page within Admin Server in ReactJ…
…S (#3707) * [ui-metrics] Converted the Metrics page within Admin Server in ReactJS * Fixing issues for removing the errors * Fixing the linting issues * metricsNew * Fixing lints in MetricsTable * Fixing the tests-unnecessary changes * Fixing the unit test, missing return type errors, any return type error * Not hardcoding the metrics keys and generating dynamically * WIP * Updated the test for the review * WIP * fix unit test Change-Id: I641dfe1152e184e8501dc718e49f4e038bbe69c4 * Fixed unit test for select with proper linting * Fixing the style issues * replace click with mouseDown for clicking the dropDown * Filtering the metrics and removing python.gc, multiprocessing, auth starting metrics * Fixing the linting issues * Removing matchMedia as it is not required anymore * Remove the unneccessary comments * Proper heading to h4 tag * remove the snake_case and add i18n * Fixing the linting issues * Fixing based on Bjorn's review comments * Updating based on Tabriz's PR review comments * Fixing linting issues * Changing the metrics only to the required fields in metricsTable * Fixing style issues * Removing the shadow within the input filter * Filtering done inside useEffect hook only and removed from elsewhere after saving state * Revertung box shadow changes * [ui-metrics] Converted the Metrics page within Admin Server in ReactJS * Fixing issues for removing the errors * Fixing the linting issues * metricsNew * Fixing lints in MetricsTable * Fixing the tests-unnecessary changes * Fixing the unit test, missing return type errors, any return type error * Not hardcoding the metrics keys and generating dynamically * WIP * Updated the test for the review * WIP * fix unit test Change-Id: I641dfe1152e184e8501dc718e49f4e038bbe69c4 * Fixed unit test for select with proper linting * Fixing the style issues * replace click with mouseDown for clicking the dropDown * Filtering the metrics and removing python.gc, multiprocessing, auth starting metrics * Fixing the linting issues * Removing matchMedia as it is not required anymore * Remove the unneccessary comments * Proper heading to h4 tag * remove the snake_case and add i18n * Fixing the linting issues * Fixing based on Bjorn's review comments * Updating based on Tabriz's PR review comments * Fixing linting issues * Changing the metrics only to the required fields in metricsTable * Fixing style issues * Removing the shadow within the input filter * Filtering done inside useEffect hook only and removed from elsewhere after saving state * Revertung box shadow changes * grey line removal WIP * Box shadow final removal * fixing indentation * Removing unneccesary comments --------- Co-authored-by: tabraiz <[email protected]>
- Loading branch information
1 parent
e118249
commit bdeccbd
Showing
6 changed files
with
434 additions
and
70 deletions.
There are no files selected for viewing
52 changes: 52 additions & 0 deletions
52
desktop/core/src/desktop/js/reactComponents/MetricsComponent/MetricsComponent.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// Licensed to Cloudera, Inc. under one | ||
// or more contributor license agreements. See the NOTICE file | ||
// distributed with this work for additional information | ||
// regarding copyright ownership. Cloudera, Inc. licenses this file | ||
// to you under the Apache License, Version 2.0 (the | ||
// 'License'); you may not use this file except in compliance | ||
// with the License. You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an 'AS IS' BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
@import '../../components/styles/variables'; | ||
|
||
.metrics-component { | ||
background-color: $fluidx-gray-100; | ||
padding: 0 24px 24px 24px; | ||
|
||
.metrics-heading { | ||
color: $fluidx-gray-700; | ||
padding-left: $font-size-lg; | ||
font-size: $fluidx-heading-h4-size; | ||
line-height: $fluidx-heading-h4-line-height; | ||
font-weight: $fluidx-heading-h4-weight; | ||
} | ||
|
||
.metrics-filter { | ||
margin: $font-size-sm; | ||
width: 30%; | ||
|
||
input { | ||
box-shadow: none; | ||
-webkit-box-shadow: none; | ||
} | ||
} | ||
|
||
.metrics-table th { | ||
width: 30%; | ||
} | ||
|
||
.metrics-select { | ||
border: 1px solid $fluidx-gray-600; | ||
border-radius: $border-radius-base; | ||
background-color: $fluidx-white; | ||
min-width: 200px; | ||
height: 32px; | ||
} | ||
} |
99 changes: 99 additions & 0 deletions
99
desktop/core/src/desktop/js/reactComponents/MetricsComponent/MetricsComponent.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
// Licensed to Cloudera, Inc. under one | ||
// or more contributor license agreements. See the NOTICE file | ||
// distributed with this work for additional information | ||
// regarding copyright ownership. Cloudera, Inc. licenses this file | ||
// to you under the Apache License, Version 2.0 (the | ||
// 'License'); you may not use this file except in compliance | ||
// with the License. You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an 'AS IS' BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
import React from 'react'; | ||
import { render, waitFor, screen, fireEvent } from '@testing-library/react'; | ||
import '@testing-library/jest-dom'; | ||
import MetricsComponent from './MetricsComponent'; | ||
|
||
// Mock the API call to return sample metrics data | ||
jest.mock('api/utils', () => ({ | ||
get: jest.fn(() => | ||
Promise.resolve({ | ||
metric: { | ||
'queries.number': { value: 10 }, | ||
'requests.active': { count: 5 }, | ||
'requests.exceptions': { count: 2 }, | ||
'requests.response-time': { | ||
'15m_rate': 20, | ||
'1m_rate': 15, | ||
'5m_rate': 18, | ||
'75_percentile': 50, | ||
'95_percentile': 60, | ||
'999_percentile': 70, | ||
'99_percentile': 55, | ||
avg: 25, | ||
count: 100, | ||
max: 30, | ||
mean_rate: 22, | ||
min: 20, | ||
std_dev: 5, | ||
sum: 2500 | ||
}, | ||
'threads.daemon': { value: 3 }, | ||
'threads.total': { value: 6 }, | ||
users: { value: 50 }, | ||
'users.active': { value: 30 }, | ||
'users.active.total': { value: 40 } | ||
} | ||
}) | ||
) | ||
})); | ||
|
||
describe('MetricsComponent', () => { | ||
// Test for filtering metrics based on input | ||
test('Filtering metrics based on name column value', async () => { | ||
render(<MetricsComponent />); | ||
|
||
const filterInput = screen.getByPlaceholderText('Filter metrics...'); | ||
fireEvent.change(filterInput, { target: { value: 'value' } }); | ||
|
||
await waitFor(() => { | ||
expect(screen.getByText('queries.number')).toBeInTheDocument(); | ||
expect(screen.getByText('threads.daemon')).toBeInTheDocument(); | ||
expect(screen.getByText('threads.total')).toBeInTheDocument(); | ||
expect(screen.getByText('users')).toBeInTheDocument(); | ||
expect(screen.getByText('users.active')).toBeInTheDocument(); | ||
expect(screen.getByText('users.active.total')).toBeInTheDocument(); | ||
expect(screen.queryByText('requests.active')).not.toBeInTheDocument(); | ||
expect(screen.queryByText('requests.exceptions')).toBeNull(); | ||
expect(screen.queryByText('requests.response-time')).toBeNull(); | ||
}); | ||
}); | ||
|
||
// Test for selecting a specific metric from the dropdown | ||
test('selecting a specific metric from the dropdown filters the data using click events', async () => { | ||
render(<MetricsComponent />); | ||
|
||
await waitFor(() => screen.getByText('queries.number')); | ||
|
||
const select = screen.getByTestId('metric-select').firstElementChild; | ||
if (select) { | ||
fireEvent.mouseDown(select); | ||
} | ||
|
||
const dropdown = document.querySelector('.ant-select'); | ||
|
||
const secondOption = dropdown?.querySelectorAll('.ant-select-item')[1]; | ||
if (secondOption) { | ||
fireEvent.click(secondOption); | ||
await waitFor(() => { | ||
const headings = screen.queryAllByRole('heading', { level: 4 }); | ||
expect(headings).toHaveLength(1); | ||
}); | ||
} | ||
}); | ||
}); |
142 changes: 142 additions & 0 deletions
142
desktop/core/src/desktop/js/reactComponents/MetricsComponent/MetricsComponent.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
// Licensed to Cloudera, Inc. under one | ||
// or more contributor license agreements. See the NOTICE file | ||
// distributed with this work for additional information | ||
// regarding copyright ownership. Cloudera, Inc. licenses this file | ||
// to you under the Apache License, Version 2.0 (the | ||
// 'License'); you may not use this file except in compliance | ||
// with the License. You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an 'AS IS' BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
import React, { useState, useEffect, useRef } from 'react'; | ||
import MetricsTable, { MetricsResponse } from './MetricsTable'; | ||
import { Spin, Input, Select, Alert } from 'antd'; | ||
import { get } from 'api/utils'; | ||
import { SearchOutlined } from '@ant-design/icons'; | ||
import './MetricsComponent.scss'; | ||
|
||
const { Option } = Select; | ||
|
||
const MetricsComponent: React.FC = (): JSX.Element => { | ||
const [metrics, setMetrics] = useState<MetricsResponse>(); | ||
const [filteredKeys, setFilteredKeys] = useState<string[]>([]); | ||
const [loading, setLoading] = useState(true); | ||
const [error, setError] = useState<string>(); | ||
const [searchQuery, setSearchQuery] = useState<string>(''); | ||
const [selectedMetric, setSelectedMetric] = useState<string>(''); | ||
const [showAllTables, setShowAllTables] = useState(true); | ||
const [filteredMetricsData, setFilteredMetricsData] = useState<MetricsData[]>([]); | ||
const dropdownRef = useRef(null); | ||
|
||
useEffect(() => { | ||
const fetchData = async () => { | ||
try { | ||
const response = await get<MetricsResponse>('/desktop/metrics/', { format: 'json' }); | ||
setMetrics(response); | ||
const keys = Object.keys(response.metric).filter( | ||
key => | ||
!key.startsWith('auth') && | ||
!key.startsWith('multiprocessing') && | ||
!key.startsWith('python.gc') | ||
); | ||
setFilteredKeys(keys); | ||
} catch (error) { | ||
setError(error.message); | ||
} finally { | ||
setLoading(false); | ||
} | ||
}; | ||
|
||
fetchData(); | ||
}, []); | ||
|
||
useEffect(() => { | ||
if (!metrics) { | ||
return; | ||
} | ||
|
||
const filteredData = parseMetricsData(metrics).filter(tableData => | ||
tableData.dataSource.some(data => data.name.toLowerCase().includes(searchQuery.toLowerCase())) | ||
); | ||
|
||
setFilteredMetricsData(filteredData); | ||
}, [searchQuery, metrics]); | ||
|
||
const parseMetricsData = (data: MetricsResponse) => { | ||
return filteredKeys.map(key => ({ | ||
caption: key, | ||
dataSource: Object.keys(data.metric[key]).map(subKey => ({ | ||
name: subKey, | ||
value: data.metric[key][subKey] | ||
})) | ||
})); | ||
}; | ||
const handleMetricChange = (value: string) => { | ||
setSelectedMetric(value); | ||
setShowAllTables(value === ''); | ||
}; | ||
|
||
const handleFilterInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
setSearchQuery(e.target.value); | ||
}; | ||
|
||
return ( | ||
<div className="cuix antd metrics-component"> | ||
<Spin spinning={loading}> | ||
{!error && ( | ||
<> | ||
<Input | ||
className="metrics-filter" | ||
placeholder="Filter metrics..." | ||
value={searchQuery} | ||
onChange={handleFilterInputChange} | ||
prefix={<SearchOutlined />} | ||
/> | ||
|
||
<Select | ||
className="metrics-select" | ||
//to make sure antd class gets applied | ||
getPopupContainer={triggerNode => triggerNode.parentElement} | ||
ref={dropdownRef} | ||
value={selectedMetric} | ||
onChange={handleMetricChange} | ||
data-testid="metric-select" | ||
> | ||
<Option value="">All</Option> | ||
{filteredKeys.map(key => ( | ||
<Option key={key} value={key}> | ||
{key} | ||
</Option> | ||
))} | ||
</Select> | ||
</> | ||
)} | ||
|
||
{error && ( | ||
<Alert | ||
message={`Error: ${error}`} | ||
description="Error in displaying the Metrics!" | ||
type="error" | ||
/> | ||
)} | ||
|
||
{!error && | ||
filteredMetricsData.map((tableData, index) => ( | ||
<div key={index}> | ||
{(showAllTables || selectedMetric === tableData.caption) && ( | ||
<MetricsTable caption={tableData.caption} dataSource={tableData.dataSource} /> | ||
)} | ||
</div> | ||
))} | ||
</Spin> | ||
</div> | ||
); | ||
}; | ||
|
||
export default MetricsComponent; |
Oops, something went wrong.