in superset-frontend/src/pages/SavedQueryList/index.tsx [102:408]
function SavedQueryList({
addDangerToast,
addSuccessToast,
user,
}: SavedQueryListProps) {
const theme = useTheme();
const {
state: {
loading,
resourceCount: queryCount,
resourceCollection: queries,
bulkSelectEnabled,
},
hasPerm,
fetchData,
toggleBulkSelect,
refreshData,
} = useListViewResource<SavedQueryObject>(
'saved_query',
t('Saved queries'),
addDangerToast,
);
const { roles } = useSelector<any, UserWithPermissionsAndRoles>(
state => state.user,
);
const canReadTag = findPermission('can_read', 'Tag', roles);
const [queryCurrentlyDeleting, setQueryCurrentlyDeleting] =
useState<SavedQueryObject | null>(null);
const [savedQueryCurrentlyPreviewing, setSavedQueryCurrentlyPreviewing] =
useState<SavedQueryObject | null>(null);
const [importingSavedQuery, showImportModal] = useState<boolean>(false);
const [passwordFields, setPasswordFields] = useState<string[]>([]);
const [preparingExport, setPreparingExport] = useState<boolean>(false);
const [sshTunnelPasswordFields, setSSHTunnelPasswordFields] = useState<
string[]
>([]);
const [sshTunnelPrivateKeyFields, setSSHTunnelPrivateKeyFields] = useState<
string[]
>([]);
const [
sshTunnelPrivateKeyPasswordFields,
setSSHTunnelPrivateKeyPasswordFields,
] = useState<string[]>([]);
const history = useHistory();
const openSavedQueryImportModal = () => {
showImportModal(true);
};
const closeSavedQueryImportModal = () => {
showImportModal(false);
};
const handleSavedQueryImport = () => {
showImportModal(false);
refreshData();
addSuccessToast(t('Query imported'));
};
const canCreate = hasPerm('can_write');
const canEdit = hasPerm('can_write');
const canDelete = hasPerm('can_write');
const canExport = hasPerm('can_export');
const handleSavedQueryPreview = useCallback(
(id: number) => {
SupersetClient.get({
endpoint: `/api/v1/saved_query/${id}`,
}).then(
({ json = {} }) => {
setSavedQueryCurrentlyPreviewing({ ...json.result });
},
createErrorHandler(errMsg =>
addDangerToast(
t('There was an issue previewing the selected query %s', errMsg),
),
),
);
},
[addDangerToast],
);
const menuData: SubMenuProps = {
activeChild: 'Saved queries',
...commonMenuData,
};
const subMenuButtons: Array<ButtonProps> = [];
if (canDelete) {
subMenuButtons.push({
name: t('Bulk select'),
onClick: toggleBulkSelect,
buttonStyle: 'secondary',
});
}
subMenuButtons.push({
name: (
<Link
to="/sqllab?new=true"
css={css`
display: flex;
&:hover {
color: currentColor;
text-decoration: none;
}
`}
>
<Icons.PlusOutlined
iconColor={theme.colors.primary.light5}
iconSize="m"
css={css`
margin: auto ${theme.gridUnit * 2}px auto 0;
`}
/>
{t('Query')}
</Link>
),
buttonStyle: 'primary',
});
if (canCreate) {
subMenuButtons.push({
name: (
<Tooltip
id="import-tooltip"
title={t('Import queries')}
placement="bottomRight"
data-test="import-tooltip-test"
>
<Icons.DownloadOutlined data-test="import-icon" />
</Tooltip>
),
buttonStyle: 'link',
onClick: openSavedQueryImportModal,
'data-test': 'import-button',
});
}
menuData.buttons = subMenuButtons;
// Action methods
const openInSqlLab = (id: number, openInNewWindow: boolean) => {
if (openInNewWindow) {
window.open(`/sqllab?savedQueryId=${id}`);
} else {
history.push(`/sqllab?savedQueryId=${id}`);
}
};
const copyQueryLink = useCallback(
async (savedQuery: SavedQueryObject) => {
try {
const payload = {
dbId: savedQuery.db_id,
name: savedQuery.label,
schema: savedQuery.schema,
catalog: savedQuery.catalog,
sql: savedQuery.sql,
autorun: false,
templateParams: null,
};
const response = await SupersetClient.post({
endpoint: '/api/v1/sqllab/permalink',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
const { url: permalink } = response.json;
await navigator.clipboard.writeText(permalink);
addSuccessToast(t('Link Copied!'));
} catch (error) {
addDangerToast(t('There was an error generating the permalink.'));
}
},
[addDangerToast, addSuccessToast],
);
const handleQueryDelete = ({ id, label }: SavedQueryObject) => {
SupersetClient.delete({
endpoint: `/api/v1/saved_query/${id}`,
}).then(
() => {
refreshData();
setQueryCurrentlyDeleting(null);
addSuccessToast(t('Deleted: %s', label));
},
createErrorHandler(errMsg =>
addDangerToast(t('There was an issue deleting %s: %s', label, errMsg)),
),
);
};
const handleBulkSavedQueryExport = (
savedQueriesToExport: SavedQueryObject[],
) => {
const ids = savedQueriesToExport.map(({ id }) => id);
handleResourceExport('saved_query', ids, () => {
setPreparingExport(false);
});
setPreparingExport(true);
};
const handleBulkQueryDelete = (queriesToDelete: SavedQueryObject[]) => {
SupersetClient.delete({
endpoint: `/api/v1/saved_query/?q=${rison.encode(
queriesToDelete.map(({ id }) => id),
)}`,
}).then(
({ json = {} }) => {
refreshData();
addSuccessToast(json.message);
},
createErrorHandler(errMsg =>
addDangerToast(
t('There was an issue deleting the selected queries: %s', errMsg),
),
),
);
};
const initialSort = [{ id: 'changed_on_delta_humanized', desc: true }];
const columns = useMemo(
() => [
{
accessor: 'label',
Header: t('Name'),
Cell: ({
row: {
original: { id, label },
},
}: any) => <Link to={`/sqllab?savedQueryId=${id}`}>{label}</Link>,
},
{
accessor: 'description',
Header: t('Description'),
},
{
accessor: 'database.database_name',
Header: t('Database'),
size: 'xl',
},
{
accessor: 'database',
hidden: true,
disableSortBy: true,
},
{
accessor: 'schema',
Header: t('Schema'),
size: 'xl',
},
{
Cell: ({
row: {
original: { sql_tables: tables = [] },
},
}: any) => {
const names = tables.map((table: any) => table.table);
const main = names?.shift() || '';
if (names.length) {
return (
<StyledTableLabel>
<span>{main}</span>
<Popover
placement="right"
title={t('TABLES')}
trigger="click"
content={
<>
{names.map((name: string) => (
<StyledPopoverItem key={name}>{name}</StyledPopoverItem>
))}
</>
}
>
<span className="count">(+{names.length})</span>
</Popover>
</StyledTableLabel>
);
}
return main;
},
accessor: 'sql_tables',
Header: t('Tables'),
size: 'xl',
disableSortBy: true,
},
{
Cell: ({
row: {
original: { tags = [] },
},
}: any) => (
// Only show custom type tags
<TagsList tags={tags.filter((tag: Tag) => tag.type === 1)} />
),
Header: t('Tags'),
accessor: 'tags',
disableSortBy: true,
hidden: !isFeatureEnabled(FeatureFlag.TaggingSystem),
},