Skip to content

feat(RunningQueries): add userSID search #1462

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {ResponseError} from '../../../../components/Errors/ResponseError';
import {ResizeableDataTable} from '../../../../components/ResizeableDataTable/ResizeableDataTable';
import {TableWithControlsLayout} from '../../../../components/TableWithControlsLayout/TableWithControlsLayout';
import {topQueriesApi} from '../../../../store/reducers/executeTopQueries/executeTopQueries';
import type {KeyValueRow} from '../../../../types/api/query';
import {useAutoRefreshInterval, useTypedSelector} from '../../../../utils/hooks';
import {parseQueryErrorToString} from '../../../../utils/query';
import {QUERY_TABLE_SETTINGS} from '../../utils/constants';
Expand All @@ -16,12 +17,14 @@ import i18n from './i18n';

interface Props {
database: string;
onRowClick: (query: string) => void;
rowClassName: string;
}

export const RunningQueriesData = ({database}: Props) => {
export const RunningQueriesData = ({database, onRowClick, rowClassName}: Props) => {
const [autoRefreshInterval] = useAutoRefreshInterval();
const filters = useTypedSelector((state) => state.executeTopQueries);
const {currentData, isFetching, error} = topQueriesApi.useGetRunningQueriesQuery(
const {currentData, isLoading, error} = topQueriesApi.useGetRunningQueriesQuery(
{
database,
filters,
Expand All @@ -31,16 +34,22 @@ export const RunningQueriesData = ({database}: Props) => {

const data = currentData?.resultSets?.[0].result || [];

const handleRowClick = (row: KeyValueRow) => {
return onRowClick(row.QueryText as string);
};

return (
<React.Fragment>
{error ? <ResponseError error={parseQueryErrorToString(error)} /> : null}
<TableWithControlsLayout.Table loading={isFetching && data === undefined}>
<TableWithControlsLayout.Table loading={isLoading}>
<ResizeableDataTable
emptyDataMessage={i18n('no-data')}
columnsWidthLSKey={RUNNING_QUERIES_COLUMNS_WIDTH_LS_KEY}
columns={RUNNING_QUERIES_COLUMNS}
data={data}
settings={QUERY_TABLE_SETTINGS}
onRowClick={handleRowClick}
rowClassName={() => rowClassName}
/>
</TableWithControlsLayout.Table>
</React.Fragment>
Expand Down
8 changes: 3 additions & 5 deletions src/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ export const TopQueries = ({tenantName}: TopQueriesProps) => {
dispatch(setTopQueriesFilters(value));
};

const DataComponent = isTopQueries ? TopQueriesData : RunningQueriesData;

return (
<TableWithControlsLayout>
<TableWithControlsLayout.Controls>
Expand All @@ -115,11 +117,7 @@ export const TopQueries = ({tenantName}: TopQueriesProps) => {
/>
) : null}
</TableWithControlsLayout.Controls>
{isTopQueries ? (
<TopQueriesData database={tenantName} onRowClick={onRowClick} />
) : (
<RunningQueriesData database={tenantName} />
)}
<DataComponent database={tenantName} onRowClick={onRowClick} rowClassName={b('row')} />
</TableWithControlsLayout>
);
};
12 changes: 5 additions & 7 deletions src/containers/Tenant/Diagnostics/TopQueries/TopQueriesData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {ResizeableDataTable} from '../../../../components/ResizeableDataTable/Re
import {TableWithControlsLayout} from '../../../../components/TableWithControlsLayout/TableWithControlsLayout';
import {topQueriesApi} from '../../../../store/reducers/executeTopQueries/executeTopQueries';
import type {KeyValueRow} from '../../../../types/api/query';
import {cn} from '../../../../utils/cn';
import {isSortableTopQueriesProperty} from '../../../../utils/diagnostics';
import {useAutoRefreshInterval, useTypedSelector} from '../../../../utils/hooks';
import {parseQueryErrorToString} from '../../../../utils/query';
Expand All @@ -14,17 +13,16 @@ import {QUERY_TABLE_SETTINGS} from '../../utils/constants';
import {TOP_QUERIES_COLUMNS, TOP_QUERIES_COLUMNS_WIDTH_LS_KEY} from './getTopQueriesColumns';
import i18n from './i18n';

const b = cn('kv-top-queries');

interface Props {
database: string;
onRowClick: (query: string) => void;
rowClassName: string;
}

export const TopQueriesData = ({database, onRowClick}: Props) => {
export const TopQueriesData = ({database, onRowClick, rowClassName}: Props) => {
const [autoRefreshInterval] = useAutoRefreshInterval();
const filters = useTypedSelector((state) => state.executeTopQueries);
const {currentData, isFetching, error} = topQueriesApi.useGetTopQueriesQuery(
const {currentData, isLoading, error} = topQueriesApi.useGetTopQueriesQuery(
{
database,
filters,
Expand All @@ -46,15 +44,15 @@ export const TopQueriesData = ({database, onRowClick}: Props) => {
return (
<React.Fragment>
{error ? <ResponseError error={parseQueryErrorToString(error)} /> : null}
<TableWithControlsLayout.Table loading={isFetching && currentData === undefined}>
<TableWithControlsLayout.Table loading={isLoading}>
<ResizeableDataTable
emptyDataMessage={i18n('no-data')}
columnsWidthLSKey={TOP_QUERIES_COLUMNS_WIDTH_LS_KEY}
columns={columns}
data={data || []}
settings={QUERY_TABLE_SETTINGS}
onRowClick={handleRowClick}
rowClassName={() => b('row')}
rowClassName={() => rowClassName}
/>
</TableWithControlsLayout.Table>
</React.Fragment>
Expand Down
2 changes: 1 addition & 1 deletion src/containers/Tenant/Diagnostics/TopQueries/i18n/en.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"no-data": "No data",
"filter.text.placeholder": "Search by query text...",
"filter.text.placeholder": "Search by query text or userSID...",
"mode_top": "Top",
"mode_running": "Running",
"col_user": "User",
Expand Down
14 changes: 11 additions & 3 deletions src/store/reducers/executeTopQueries/executeTopQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,11 @@ export const topQueriesApi = api.injectEndpoints({
{signal},
) => {
try {
const filterConditions = filters?.text ? `Query ILIKE '%${filters.text}%'` : '';
const commonQueryPart = `SELECT UserSID, QueryStartAt, Query as QueryText, ApplicationName from \`.sys/query_sessions\` WHERE ${filterConditions || 'true'}`;
const filterConditions = filters?.text
? `Query ILIKE '%${filters.text}%' OR UserSID ILIKE '%${filters.text}%'`
: '';

const queryText = `${commonQueryPart} AND Query NOT LIKE '${commonQueryPart}%' ORDER BY SessionStartAt limit 100`;
const queryText = `SELECT UserSID, QueryStartAt, Query as QueryText, ApplicationName from \`.sys/query_sessions\` WHERE ${filterConditions || 'true'} ORDER BY SessionStartAt limit 100`;

const response = await window.api.sendQuery(
{
Expand All @@ -114,6 +115,13 @@ export const topQueriesApi = api.injectEndpoints({

const data = parseQueryAPIExecuteResponse(response);

/* filter running queries query itself */
if (data?.resultSets?.[0]?.result) {
data.resultSets[0].result = data.resultSets[0].result.filter(
(item) => item.QueryText !== queryText,
);
}

return {data};
} catch (error) {
return {error};
Expand Down
4 changes: 3 additions & 1 deletion src/store/reducers/executeTopQueries/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ export function getFiltersConditions(path: string, filters?: TopQueriesFilters)
}

if (filters?.text) {
conditions.push(`QueryText ILIKE '%${filters.text}%'`);
conditions.push(
`(QueryText ILIKE '%${filters.text}%' OR UserSID ILIKE '%${filters.text}%')`,
);
}

return conditions.join(' AND ');
Expand Down
16 changes: 8 additions & 8 deletions tests/suites/tenant/diagnostics/Diagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,15 @@ export class Table {
}

async waitForCellValueByHeader(row: number, header: string, value: string) {
const headers = await this.getHeaders();
const colIndex = headers.indexOf(header);
if (colIndex === -1) {
throw new Error(`Header "${header}" not found`);
}
const cell = this.table.locator(
`tr.data-table__row:nth-child(${row}) td:nth-child(${colIndex + 1})`,
);
await retryAction(async () => {
const headers = await this.getHeaders();
const colIndex = headers.indexOf(header);
if (colIndex === -1) {
throw new Error(`Header "${header}" not found`);
}
const cell = this.table.locator(
`tr.data-table__row:nth-child(${row}) td:nth-child(${colIndex + 1})`,
);
const cellValue = (await cell.innerText()).trim();
if (cellValue === value) {
return true;
Expand Down
1 change: 0 additions & 1 deletion tests/suites/tenant/diagnostics/diagnostics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ test.describe('Diagnostics tab', async () => {
const diagnostics = new Diagnostics(page);
await diagnostics.clickTab(DiagnosticsTab.Queries);
await diagnostics.clickRadioSwitch(QueriesSwitch.Running);
expect(await diagnostics.table.getRowCount()).toBe(1);
expect(
await diagnostics.table.waitForCellValueByHeader(1, 'QueryText', longRunningQuery),
).toBe(true);
Expand Down
Loading