Unverified Commit d6a3ee73 authored by Joaquim Rocha's avatar Joaquim Rocha Committed by GitHub
Browse files

Merge pull request #382 from kinvolk/fix-advanced-filtered-search

frontend: Simplify and fix advanced search
parents 4614e6e2 0b5cd7b0
Showing with 45 additions and 36 deletions
+45 -36
......@@ -13,11 +13,8 @@ import TableCell from '@material-ui/core/TableCell';
import TableHead from '@material-ui/core/TableHead';
import TablePagination from '@material-ui/core/TablePagination';
import TableRow from '@material-ui/core/TableRow';
import _ from 'lodash';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { KubeObjectInterface } from '../../lib/k8s/cluster';
import { useTypedSelector } from '../../redux/reducers/reducers';
import Empty from './EmptyContent';
import { ValueLabel } from './Label';
import Loader from './Loader';
......@@ -80,7 +77,6 @@ export interface SimpleTableProps {
emptyMessage?: string;
errorMessage?: string | null;
defaultSortingColumn?: number;
advancedFilters?: [string];
}
interface ColumnSortButtonProps {
......@@ -119,7 +115,6 @@ export default function SimpleTable(props: SimpleTableProps) {
emptyMessage = null,
errorMessage = null,
defaultSortingColumn,
advancedFilters,
} = props;
const [page, setPage] = React.useState(0);
const [currentData, setCurrentData] = React.useState(data);
......@@ -127,7 +122,6 @@ export default function SimpleTable(props: SimpleTableProps) {
const rowsPerPageOptions = props.rowsPerPage || [5, 10, 50];
const [rowsPerPage, setRowsPerPage] = React.useState(rowsPerPageOptions[0]);
const classes = useTableStyle();
const filter = useTypedSelector(state => state.filter);
const [isIncreasingOrder, setIsIncreasingOrder] = React.useState(
!defaultSortingColumn || defaultSortingColumn > 0
);
......@@ -247,22 +241,8 @@ export default function SimpleTable(props: SimpleTableProps) {
let filteredData = displayData;
if (filterFunction) {
filteredData = displayData.filter(filterFunction);
if (advancedFilters) {
for (let i = 0; i < advancedFilters.length; i++) {
// attach all data from previous filter so that we don't lose data from any filter
filteredData = filteredData.concat(
displayData.filter((item: KubeObjectInterface) =>
_.get(item, advancedFilters[i]).toLowerCase().includes(filter.search.toLowerCase())
)
);
}
//remove duplicate entries
filteredData = _.uniqBy(
filteredData as KubeObjectInterface[],
(val: KubeObjectInterface) => val.metadata.uid
);
}
}
function sortClickHandler(isIncreasingOrder: boolean, index: number) {
setIsIncreasingOrder(isIncreasingOrder);
setSortColIndex(index);
......
......@@ -18,7 +18,7 @@ export default function RoleBindingList() {
const [clusterRoleBindingError, onClusterRoleBindingError] =
useErrorState(setupClusterRoleBindings);
const { t } = useTranslation('glossary');
const filterFunc = useFilterFunc();
const filterFunc = useFilterFunc(['.kind']);
function setRoleBindings(newBindings: RoleBinding[] | null, kind: string) {
const currentBindings: RoleBindingDict = bindings || {};
......@@ -70,7 +70,6 @@ export default function RoleBindingList() {
<SimpleTable
rowsPerPage={[15, 25, 50]}
filterFunction={filterFunc}
advancedFilters={['kind']}
errorMessage={getErrorMessage()}
columns={[
{
......
......@@ -17,7 +17,7 @@ export default function RoleList() {
const [roleError, setRolesError] = useErrorState(setupRoles);
const [clusterRoleError, setClusterRolesError] = useErrorState(setupClusterRoles);
const { t } = useTranslation('glossary');
const filterFunc = useFilterFunc();
const filterFunc = useFilterFunc(['.jsonData.kind']);
function setupRolesWithKind(newRoles: Role[] | null, kind: string) {
setRoles(oldRoles => ({ ...(oldRoles || {}), [kind]: newRoles }));
......@@ -65,7 +65,6 @@ export default function RoleList() {
<SimpleTable
rowsPerPage={[15, 25, 50]}
filterFunction={filterFunc}
advancedFilters={['kind']}
errorMessage={getErrorMessage()}
columns={[
{
......
......@@ -9,7 +9,7 @@ import SimpleTable from '../common/SimpleTable';
export default function SecretList() {
const [secrets, error] = Secret.useList();
const filterFunc = useFilterFunc();
const filterFunc = useFilterFunc(['.jsonData.type']);
const { t } = useTranslation('glossary');
return (
......@@ -18,7 +18,6 @@ export default function SecretList() {
rowsPerPage={[15, 25, 50]}
filterFunction={filterFunc}
errorMessage={Secret.getErrorMessage(error)}
advancedFilters={['type']}
columns={[
{
label: t('frequent|Name'),
......
......@@ -9,7 +9,7 @@ import SimpleTable from '../common/SimpleTable';
export default function ServiceList() {
const [services, error] = Service.useList();
const filterFunc = useFilterFunc();
const filterFunc = useFilterFunc(['.jsonData.spec.type']);
const { t } = useTranslation('glossary');
return (
......@@ -17,7 +17,6 @@ export default function ServiceList() {
<SimpleTable
rowsPerPage={[15, 25, 50]}
filterFunction={filterFunc}
advancedFilters={['spec.type']}
errorMessage={Service.getErrorMessage(error)}
columns={[
{
......
......@@ -23,7 +23,7 @@ interface WorkloadDict {
export default function Overview() {
const [workloadsData, dispatch] = React.useReducer(setWorkloads, {});
const location = useLocation();
const filterFunc = useFilterFunc();
const filterFunc = useFilterFunc(['.jsonData.kind']);
const { t } = useTranslation('glossary');
function setWorkloads(
......@@ -85,7 +85,6 @@ export default function Overview() {
<SimpleTable
rowsPerPage={[15, 25, 50]}
filterFunction={filterFunc}
advancedFilters={['kind']}
columns={[
{
label: t('Type'),
......
import TimeAgo from 'javascript-time-ago';
import en from 'javascript-time-ago/locale/en';
import { JSONPath } from 'jsonpath-plus';
import React from 'react';
import { matchPath } from 'react-router';
import helpers from '../helpers';
......@@ -81,7 +82,11 @@ export interface FilterState {
search: string;
}
export function filterResource(item: KubeObjectInterface, filter: FilterState) {
export function filterResource(
item: KubeObjectInterface,
filter: FilterState,
matchCriteria?: string[]
) {
let matches: boolean = true;
if (item.metadata.namespace && filter.namespaces.size > 0) {
......@@ -90,22 +95,51 @@ export function filterResource(item: KubeObjectInterface, filter: FilterState) {
if (matches && filter.search) {
const filterString = filter.search.toLowerCase();
const matchCriteria = [
const usedMatchCriteria = [
item.metadata.namespace ? item.metadata.namespace.toLowerCase() : '',
item.metadata.name.toLowerCase(),
...Object.keys(item.metadata.labels || {}).map(item => item.toLowerCase()),
...Object.values(item.metadata.labels || {}).map(item => item.toLowerCase()),
];
matches = !!matchCriteria.find(item => item.includes(filterString));
// Use the custom matchCriteria if any
(matchCriteria || []).forEach(jsonPath => {
let values: any[];
try {
values = JSONPath({ path: '$' + jsonPath, json: item });
} catch (err) {
console.debug(
`Failed to get value from JSONPath when filtering ${jsonPath} on item ${item}; skipping criteria`
);
return;
}
// Include matches values in the criteria
values.forEach((value: any) => {
if (typeof value === 'string') {
// Don't use empty string, otherwise it'll match everything
if (value !== '') {
usedMatchCriteria.push(value.toLowerCase());
}
} else if (Array.isArray(value)) {
value.forEach((elem: any) => {
if (!!elem && typeof elem === 'string') {
usedMatchCriteria.push(elem.toLowerCase());
}
});
}
});
});
matches = !!usedMatchCriteria.find(item => item.includes(filterString));
}
return matches;
}
export function useFilterFunc() {
export function useFilterFunc(matchCriteria?: string[]) {
const filter = useTypedSelector(state => state.filter);
return (item: KubeObjectInterface) => filterResource(item, filter);
return (item: KubeObjectInterface) => filterResource(item, filter, matchCriteria);
}
export function getClusterPrefixedPath(path?: string | null) {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment