fix: Use total count to filter datasets (#36135)

Co-authored-by: Diego Pucci <diegopucci.me@gmail.com>
This commit is contained in:
Enzo Martellucci
2025-11-17 17:01:54 +01:00
committed by GitHub
parent dad469297c
commit 962faa2196
2 changed files with 121 additions and 54 deletions

View File

@@ -24,7 +24,8 @@ import {
within,
} from 'spec/helpers/testing-library';
import fetchMock from 'fetch-mock';
import DatasetSelect from './DatasetSelect';
import DatasetSelect, { loadDatasetOptions } from './DatasetSelect';
import { supersetGetCache } from 'src/utils/cachedSupersetGet';
const DATASETS = [
{
@@ -201,3 +202,66 @@ test('includes table_name field in option data structure', async () => {
expect(callArg).toHaveProperty('table_name', 'birth_names');
});
});
test('uses API count instead of filteredResult.length', async () => {
supersetGetCache.clear();
fetchMock.restore();
fetchMock.get('glob:*/api/v1/dataset/*', {
result: [
{
id: 1,
table_name: 'ds1',
database: { database_name: 'db' },
schema: 'public',
},
{
id: 2,
table_name: 'ds2',
database: { database_name: 'db' },
schema: 'public',
},
],
count: 500,
});
const result = await loadDatasetOptions('', 0, 100, [1, 2]);
expect(result.totalCount).toBe(500);
expect(result.totalCount).not.toBe(0);
});
test('returns total count from API when data is filtered', async () => {
supersetGetCache.clear();
fetchMock.restore();
fetchMock.get('glob:*/api/v1/dataset/*', {
result: [
{
id: 1,
table_name: 'birth_names',
database: { database_name: 'examples' },
schema: 'public',
},
{
id: 2,
table_name: 'birth_registry',
database: { database_name: 'examples' },
schema: 'public',
},
{
id: 3,
table_name: 'birth_rates',
database: { database_name: 'examples' },
schema: 'public',
},
],
count: 25,
});
// Search for 'birth' and exclude dataset id 2 (client-side filtering)
const result = await loadDatasetOptions('birth', 0, 100, [2]);
expect(result.totalCount).toBe(25);
expect(result.data).toHaveLength(2);
expect(result.data.find(item => item.value === 2)).toBeUndefined();
});

View File

@@ -37,70 +37,73 @@ interface DatasetSelectProps {
excludeDatasetIds?: number[];
}
const getErrorMessage = ({ error, message }: ClientErrorObject) => {
let errorText = message || error || t('An error has occurred');
if (message === 'Forbidden') {
errorText = t('You do not have permission to edit this dashboard');
}
return errorText;
};
export const loadDatasetOptions = async (
search: string,
page: number,
pageSize: number,
excludeDatasetIds: number[] = [],
) => {
const query = rison.encode({
columns: ['id', 'table_name', 'database.database_name', 'schema'],
filters: [{ col: 'table_name', opr: 'ct', value: search }],
page,
page_size: pageSize,
order_column: 'table_name',
order_direction: 'asc',
});
return cachedSupersetGet({
endpoint: `/api/v1/dataset/?q=${query}`,
})
.then((response: JsonResponse) => {
const filteredResult = response.json.result.filter(
(item: Dataset) => !excludeDatasetIds.includes(item.id),
);
const list: {
label: string | ReactNode;
value: string | number;
table_name: string;
}[] = filteredResult.map((item: Dataset) => ({
...item,
label: DatasetSelectLabel(item),
value: item.id,
table_name: item.table_name,
}));
return {
data: list,
totalCount: response.json.count ?? 0,
};
})
.catch(async error => {
const errorMessage = getErrorMessage(await getClientErrorObject(error));
throw new Error(errorMessage);
});
};
const DatasetSelect = ({
onChange,
value,
excludeDatasetIds = [],
}: DatasetSelectProps) => {
const getErrorMessage = useCallback(
({ error, message }: ClientErrorObject) => {
let errorText = message || error || t('An error has occurred');
if (message === 'Forbidden') {
errorText = t('You do not have permission to edit this dashboard');
}
return errorText;
},
[],
);
const loadDatasetOptions = useCallback(
async (search: string, page: number, pageSize: number) => {
const query = rison.encode({
columns: ['id', 'table_name', 'database.database_name', 'schema'],
filters: [{ col: 'table_name', opr: 'ct', value: search }],
page,
page_size: pageSize,
order_column: 'table_name',
order_direction: 'asc',
});
return cachedSupersetGet({
endpoint: `/api/v1/dataset/?q=${query}`,
})
.then((response: JsonResponse) => {
const filteredResult = response.json.result.filter(
(item: Dataset) => !excludeDatasetIds.includes(item.id),
);
const list: {
label: string | ReactNode;
value: string | number;
table_name: string;
}[] = filteredResult.map((item: Dataset) => ({
...item,
label: DatasetSelectLabel(item),
value: item.id,
table_name: item.table_name,
}));
return {
data: list,
totalCount: filteredResult.length,
};
})
.catch(async error => {
const errorMessage = getErrorMessage(
await getClientErrorObject(error),
);
throw new Error(errorMessage);
});
},
[excludeDatasetIds, getErrorMessage],
const loadDatasetOptionsCallback = useCallback(
(search: string, page: number, pageSize: number) =>
loadDatasetOptions(search, page, pageSize, excludeDatasetIds),
[excludeDatasetIds],
);
return (
<AsyncSelect
ariaLabel={t('Dataset')}
value={value}
options={loadDatasetOptions}
options={loadDatasetOptionsCallback}
onChange={onChange}
optionFilterProps={['table_name']}
notFoundContent={t('No compatible datasets found')}