Skip to content

Commit

Permalink
feat: add test path filtering
Browse files Browse the repository at this point in the history
Part of #524
  • Loading branch information
MarceloRobert committed Nov 22, 2024
1 parent 0a2cc22 commit dc85aac
Show file tree
Hide file tree
Showing 14 changed files with 158 additions and 34 deletions.
7 changes: 7 additions & 0 deletions backend/kernelCI_app/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,11 @@ class FilterParams:
"gte": ["gte", ">="],
"lt": ["lt", "<"],
"lte": ["lte", "<="],
"like": ["like", "LIKE"],
}

string_like_filters = ["test.path"]

def __init__(self, request):
self.filters = []
self.create_filters_from_req(request)
Expand All @@ -129,6 +132,10 @@ def create_filters_from_req(self, request):
self.add_filter(field, value, "in")
continue

if filter_term in self.string_like_filters:
self.add_filter(filter_term, request.GET.get(k), "like")
continue

match = self.filter_reg.match(filter_term)
if match:
field = match.group(1)
Expand Down
3 changes: 3 additions & 0 deletions backend/kernelCI_app/views/treeCommitsHistory.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ def __treat_unknown_filter(self, table_field, op, value_name, filter):
self.field_values[value_name] = filter['value']
if op == "IN":
clause += f" = ANY(%({value_name})s)"
elif op == "LIKE":
self.field_values[value_name] = f"%{filter['value']}%"
clause += f" {op} %({value_name})s"
else:
clause += f" {op} %({value_name})s"

Expand Down
10 changes: 10 additions & 0 deletions backend/kernelCI_app/views/treeDetailsSlowView.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def __init__(self):
self.filterTreeDetailsCompiler = set()
self.filterArchitecture = set()
self.filterHardware = set()
self.filterPath = ""
self.filter_handlers = {
"boot.status": self.__handle_boot_status,
"boot.duration": self.__handle_boot_duration,
Expand All @@ -36,6 +37,7 @@ def __init__(self):
"compiler": self.__handle_compiler,
"architecture": self.__handle_architecture,
"test.hardware": self.__handle_hardware,
"test.path": self.__handle_path,
}

self.testHistory = []
Expand Down Expand Up @@ -95,6 +97,9 @@ def __handle_architecture(self, current_filter):
def __handle_hardware(self, current_filter):
self.filterHardware.add(current_filter["value"])

def __handle_path(self, current_filter):
self.filterPath = current_filter["value"]

def __processFilters(self, request):
try:
filter_params = FilterParams(request)
Expand Down Expand Up @@ -470,8 +475,13 @@ def get(self, request, commit_hash: str | None):
) = currentRowData

self.hardwareUsed.add(testEnvironmentCompatible)

if (
(
self.filterPath != ""
and (self.filterPath not in path)
)
or (
len(self.filterHardware) > 0
and (testEnvironmentCompatible not in self.filterHardware)
)
Expand Down
46 changes: 30 additions & 16 deletions dashboard/src/components/BootsTable/BootsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ interface IBootsTable {
filter: TestsTableFilter;
getRowLink: (testId: TestHistory['id']) => LinkProps;
onClickFilter: (newFilter: TestsTableFilter) => void;
updatePathFilter: (pathFilter: string) => void;
}

const TableCellComponent = ({
Expand Down Expand Up @@ -196,6 +197,7 @@ export function BootsTable({
filter,
getRowLink,
onClickFilter,
updatePathFilter,
}: IBootsTable): JSX.Element {
const [sorting, setSorting] = useState<SortingState>([]);
const [pagination, setPagination] = useState<PaginationState>({
Expand Down Expand Up @@ -311,20 +313,40 @@ export function BootsTable({
?.setFilterValue(filter !== 'all' ? filter : undefined);
}, [filter, table]);

// TODO: there should be a filtering for the frontend before the backend AND that filtering should consider the individual tests inside each test "batch" (the data from individualTestsTables), not only the rows of the external table
const onSearchChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) =>
table.setGlobalFilter(String(e.target.value)),
[table],
(e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.value !== undefined) {
updatePathFilter(e.target.value);
}
// TODO: only use the frontend filtering when the backend filter function is undefined (like in BuildDetails page)
table.setGlobalFilter(String(e.target.value));
},
[table, updatePathFilter],
);

const groupHeaders = table.getHeaderGroups()[0]?.headers;
const tableHeaders = useMemo((): JSX.Element[] => {
return groupHeaders.map(header => {
const headerComponent = header.isPlaceholder
? null
: flexRender(header.column.columnDef.header, header.getContext());
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(header.column.columnDef.header, header.getContext())}
<TableHead key={header.id} className="border-b px-2 font-bold">
{header.id === 'path' ? (
<div className="flex items-center">
{headerComponent}
{/* TODO: add startingValue with the currentPathFilter from the diffFilter param, same for TestsTable */}
<DebounceInput
debouncedSideEffect={onSearchChange}
className="w-50 font-normal"
type="text"
placeholder={intl.formatMessage({ id: 'global.search' })}
/>
</div>
) : (
headerComponent
)}
</TableHead>
);
});
Expand Down Expand Up @@ -416,15 +438,7 @@ export function BootsTable({
navigationLogsActions={navigationLogsActions}
onOpenChange={onOpenChange}
>
<div className="flex justify-between">
<TableStatusFilter filters={filters} onClickTest={onClickFilter} />
<DebounceInput
debouncedSideEffect={onSearchChange}
className="w-50"
type="text"
placeholder={intl.formatMessage({ id: 'global.search' })}
/>
</div>
<TableStatusFilter filters={filters} onClickTest={onClickFilter} />
<BaseTable headerComponents={tableHeaders}>
<TableBody>{tableRows}</TableBody>
</BaseTable>
Expand Down
45 changes: 29 additions & 16 deletions dashboard/src/components/TestsTable/TestsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,17 @@ export interface ITestsTable {
columns?: ColumnDef<TPathTests>[];
innerColumns?: ColumnDef<TIndividualTest>[];
getRowLink: (testId: TestHistory['id']) => LinkProps;
updatePathFilter?: (pathFilter: string) => void;
}

export function TestsTable({
testHistory,
onClickFilter,
filter,
getRowLink,
columns = defaultColumns,
innerColumns = defaultInnerColumns,
getRowLink,
updatePathFilter,
}: ITestsTable): JSX.Element {
const [sorting, setSorting] = useState<SortingState>([]);
const [expanded, setExpanded] = useState<ExpandedState>({});
Expand Down Expand Up @@ -253,20 +255,39 @@ export function TestsTable({
[filterCount, intl, filter],
);

// TODO: there should be a filtering for the frontend before the backend AND that filtering should consider the individual tests inside each test "batch" (the data from individualTestsTables), not only the rows of the external table
const onSearchChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) =>
table.setGlobalFilter(String(e.target.value)),
[table],
(e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.value !== undefined && updatePathFilter) {
updatePathFilter(e.target.value);
}
// TODO: remove this frontend filter when the hardwareDetails backend filtering gets in place
table.setGlobalFilter(String(e.target.value));
},
[table, updatePathFilter],
);

const groupHeaders = table.getHeaderGroups()[0]?.headers;
const tableHeaders = useMemo((): JSX.Element[] => {
return groupHeaders.map(header => {
const headerComponent = header.isPlaceholder
? null
: flexRender(header.column.columnDef.header, header.getContext());
return (
<TableHead key={header.id} className="border-b px-2 font-bold">
{header.isPlaceholder
? null
: flexRender(header.column.columnDef.header, header.getContext())}
{header.id === 'path_group' ? (
<div className="flex items-center">
{headerComponent}
<DebounceInput
debouncedSideEffect={onSearchChange}
className="w-50 font-normal"
type="text"
placeholder={intl.formatMessage({ id: 'global.search' })}
/>
</div>
) : (
headerComponent
)}
</TableHead>
);
});
Expand Down Expand Up @@ -316,15 +337,7 @@ export function TestsTable({

return (
<div className="flex flex-col gap-6 pb-4">
<div className="flex justify-between">
<TableStatusFilter filters={filters} onClickTest={onClickFilter} />
<DebounceInput
debouncedSideEffect={onSearchChange}
className="w-50"
type="text"
placeholder={intl.formatMessage({ id: 'global.search' })}
/>
</div>
<TableStatusFilter filters={filters} onClickTest={onClickFilter} />
<BaseTable headerComponents={tableHeaders}>
<TableBody>{tableRows}</TableBody>
</BaseTable>
Expand Down
16 changes: 16 additions & 0 deletions dashboard/src/pages/TreeDetails/Tabs/Boots/BootsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,21 @@ const BootsTab = ({ reqFilter }: BootsTabProps): JSX.Element => {

const navigate = useNavigate({ from: '/tree/$treeId/' });

const updatePathFilter = useCallback(
(pathFilter: string) => {
navigate({
search: previousSearch => ({
...previousSearch,
diffFilter: {
...previousSearch.diffFilter,
path: pathFilter === '' ? undefined : { [pathFilter]: true },
},
}),
});
},
[navigate],
);

const onClickFilter = useCallback(
(newFilter: TestsTableFilter): void => {
navigate({
Expand Down Expand Up @@ -165,6 +180,7 @@ const BootsTab = ({ reqFilter }: BootsTabProps): JSX.Element => {
onClickFilter={onClickFilter}
testHistory={data.bootHistory}
getRowLink={getRowLink}
updatePathFilter={updatePathFilter}
/>
</div>
);
Expand Down
16 changes: 16 additions & 0 deletions dashboard/src/pages/TreeDetails/Tabs/Tests/TestsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,21 @@ const TestsTab = ({ reqFilter }: TestsTabProps): JSX.Element => {

const navigate = useNavigate({ from: '/tree/$treeId' });

const updatePathFilter = useCallback(
(pathFilter: string) => {
navigate({
search: previousSearch => ({
...previousSearch,
diffFilter: {
...previousSearch.diffFilter,
path: pathFilter === '' ? undefined : { [pathFilter]: true },
},
}),
});
},
[navigate],
);

const getRowLink = useCallback(
(bootId: string): LinkProps => {
return {
Expand Down Expand Up @@ -170,6 +185,7 @@ const TestsTab = ({ reqFilter }: TestsTabProps): JSX.Element => {
onClickFilter={onClickFilter}
filter={tableFilter.testsTable}
getRowLink={getRowLink}
updatePathFilter={updatePathFilter}
/>
</div>
);
Expand Down
1 change: 1 addition & 0 deletions dashboard/src/pages/TreeDetails/TreeDetailsFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const filterFieldMap = {
'test.duration_[gte]': 'testDurationMin',
'test.duration_[lte]': 'testDurationMax',
'test.hardware': 'hardware',
'test.path': 'path',
} as const satisfies Record<TRequestFiltersValues, TFilterKeys>;

export const mapFilterToReq = (
Expand Down
20 changes: 19 additions & 1 deletion dashboard/src/pages/hardwareDetails/Tabs/Boots/BootsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,9 @@ const ErrorsSummary = ({
export const MemoizedErrorsSummary = memo(ErrorsSummary);

const BootsTab = ({ boots, hardwareId }: TBootsTab): JSX.Element => {
const { tableFilter } = useSearch({ from: '/hardware/$hardwareId' });
const { tableFilter } = useSearch({
from: '/hardware/$hardwareId',
});

const getRowLink = useCallback(
(bootId: string): LinkProps => ({
Expand All @@ -161,6 +163,21 @@ const BootsTab = ({ boots, hardwareId }: TBootsTab): JSX.Element => {

const navigate = useNavigate({ from: '/hardware/$hardwareId' });

const updatePathFilter = useCallback(
(pathFilter: string) => {
navigate({
search: previousSearch => ({
...previousSearch,
diffFilter: {
...previousSearch.diffFilter,
path: pathFilter === '' ? undefined : { [pathFilter]: true },
},
}),
});
},
[navigate],
);

const onClickFilter = useCallback(
(newFilter: TestsTableFilter): void => {
navigate({
Expand Down Expand Up @@ -227,6 +244,7 @@ const BootsTab = ({ boots, hardwareId }: TBootsTab): JSX.Element => {
filter={tableFilter.bootsTable}
testHistory={boots.history}
onClickFilter={onClickFilter}
updatePathFilter={updatePathFilter}
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ const HardwareDetailsTestTable = ({
onClickFilter,
testHistory,
hardwareId,
updatePathFilter,
}: IHardwareDetailsTestTable): JSX.Element => {
const getRowLink = useCallback(
(bootId: string): LinkProps => ({
Expand All @@ -114,6 +115,7 @@ const HardwareDetailsTestTable = ({
testHistory={testHistory}
innerColumns={innerColumns}
getRowLink={getRowLink}
updatePathFilter={updatePathFilter}
/>
);
};
Expand Down
21 changes: 20 additions & 1 deletion dashboard/src/pages/hardwareDetails/Tabs/Tests/TestsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,28 @@ interface TTestsTab {
}

const TestsTab = ({ tests, hardwareId }: TTestsTab): JSX.Element => {
const { tableFilter } = useSearch({ from: '/hardware/$hardwareId' });
const { tableFilter } = useSearch({
from: '/hardware/$hardwareId',
});

const navigate = useNavigate({ from: '/hardware/$hardwareId' });

// TODO: move inside the same hook for the tables, passing navigate, in order to not repeat the same code in each tab of each monitor
const updatePathFilter = useCallback(
(pathFilter: string) => {
navigate({
search: previousSearch => ({
...previousSearch,
diffFilter: {
...previousSearch.diffFilter,
path: pathFilter === '' ? undefined : { [pathFilter]: true },
},
}),
});
},
[navigate],
);

const onClickFilter = useCallback(
(newFilter: TestsTableFilter): void => {
navigate({
Expand Down Expand Up @@ -97,6 +115,7 @@ const TestsTab = ({ tests, hardwareId }: TTestsTab): JSX.Element => {
filter={tableFilter.testsTable}
hardwareId={hardwareId}
onClickFilter={onClickFilter}
updatePathFilter={updatePathFilter}
/>
</div>
);
Expand Down
Loading

0 comments on commit dc85aac

Please sign in to comment.