fix(sqllab): save datasets with template parameters (#33195)

This commit is contained in:
ethan-l-geotab
2025-05-30 18:57:33 -04:00
committed by GitHub
parent 401ce56fa1
commit c09f8f6f76
5 changed files with 130 additions and 9 deletions

View File

@@ -29,6 +29,7 @@ import fetchMock from 'fetch-mock';
import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal';
import { createDatasource } from 'src/SqlLab/actions/sqlLab';
import { user, testQuery, mockdatasets } from 'src/SqlLab/fixtures';
import { FeatureFlag } from '@superset-ui/core';
const mockedProps = {
visible: true,
@@ -250,4 +251,88 @@ describe('SaveDatasetModal', () => {
templateParams: undefined,
});
});
it('does not renders a checkbox button when template processing is disabled', () => {
render(<SaveDatasetModal {...mockedProps} />, { useRedux: true });
expect(screen.queryByRole('checkbox')).not.toBeInTheDocument();
});
it('renders a checkbox button when template processing is enabled', () => {
// @ts-ignore
global.featureFlags = {
[FeatureFlag.EnableTemplateProcessing]: true,
};
render(<SaveDatasetModal {...mockedProps} />, { useRedux: true });
expect(screen.getByRole('checkbox')).toBeInTheDocument();
});
it('correctly includes template parameters when template processing is enabled', () => {
// @ts-ignore
global.featureFlags = {
[FeatureFlag.EnableTemplateProcessing]: true,
};
const propsWithTemplateParam = {
...mockedProps,
datasource: {
...testQuery,
templateParams: JSON.stringify({ my_param: 12 }),
},
};
render(<SaveDatasetModal {...propsWithTemplateParam} />, {
useRedux: true,
});
const inputFieldText = screen.getByDisplayValue(/unimportant/i);
fireEvent.change(inputFieldText, { target: { value: 'my dataset' } });
userEvent.click(screen.getByRole('checkbox'));
const saveConfirmationBtn = screen.getByRole('button', {
name: /save/i,
});
userEvent.click(saveConfirmationBtn);
expect(createDatasource).toHaveBeenCalledWith({
datasourceName: 'my dataset',
dbId: 1,
catalog: null,
schema: 'main',
sql: 'SELECT *',
templateParams: JSON.stringify({ my_param: 12 }),
});
});
it('correctly excludes template parameters when template processing is enabled', () => {
// @ts-ignore
global.featureFlags = {
[FeatureFlag.EnableTemplateProcessing]: true,
};
const propsWithTemplateParam = {
...mockedProps,
datasource: {
...testQuery,
templateParams: JSON.stringify({ my_param: 12 }),
},
};
render(<SaveDatasetModal {...propsWithTemplateParam} />, {
useRedux: true,
});
const inputFieldText = screen.getByDisplayValue(/unimportant/i);
fireEvent.change(inputFieldText, { target: { value: 'my dataset' } });
userEvent.click(screen.getByRole('checkbox'));
const saveConfirmationBtn = screen.getByRole('button', {
name: /save/i,
});
userEvent.click(saveConfirmationBtn);
expect(createDatasource).toHaveBeenCalledWith({
datasourceName: 'my dataset',
dbId: 1,
catalog: null,
schema: 'main',
sql: 'SELECT *',
templateParams: undefined,
});
});
});

View File

@@ -24,6 +24,7 @@ import { AsyncSelect } from 'src/components';
import { Input } from 'src/components/Input';
import StyledModal from 'src/components/Modal';
import Button from 'src/components/Button';
import Checkbox from 'src/components/Checkbox';
import {
styled,
t,
@@ -33,6 +34,8 @@ import {
QueryResponse,
QueryFormData,
VizType,
FeatureFlag,
isFeatureEnabled,
} from '@superset-ui/core';
import { useSelector, useDispatch } from 'react-redux';
import dayjs from 'dayjs';
@@ -185,6 +188,8 @@ export const SaveDatasetModal = ({
const user = useSelector<SqlLabRootState, User>(state => state.user);
const dispatch = useDispatch<(dispatch: any) => Promise<JsonObject>>();
const [includeTemplateParameters, setIncludeTemplateParameters] =
useState(false);
const createWindow = (url: string) => {
if (openWindow) {
@@ -285,14 +290,21 @@ export const SaveDatasetModal = ({
// Remove the special filters entry from the templateParams
// before saving the dataset.
let templateParams;
if (typeof datasource?.templateParams === 'string') {
const p = JSON.parse(datasource.templateParams);
/* eslint-disable-next-line no-underscore-dangle */
if (p._filters) {
if (
typeof datasource?.templateParams === 'string' &&
includeTemplateParameters
) {
try {
const p = JSON.parse(datasource.templateParams);
/* eslint-disable-next-line no-underscore-dangle */
delete p._filters;
// eslint-disable-next-line no-param-reassign
if (p._filters) {
/* eslint-disable-next-line no-underscore-dangle */
delete p._filters;
}
templateParams = JSON.stringify(p);
} catch (e) {
// malformed templateParams, do not include it
templateParams = undefined;
}
}
@@ -362,7 +374,27 @@ export const SaveDatasetModal = ({
title={t('Save or Overwrite Dataset')}
onHide={onHide}
footer={
<>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
gap: '8px',
}}
>
{isFeatureEnabled(FeatureFlag.EnableTemplateProcessing) && (
<div style={{ display: 'flex', alignItems: 'center' }}>
<Checkbox
checked={includeTemplateParameters}
onChange={checked =>
setIncludeTemplateParameters(checked ?? false)
}
/>
<span style={{ marginLeft: '5px' }}>
{t('Include Template Parameters')}
</span>
</div>
)}
{newOrOverwrite === DatasetRadioState.SaveNew && (
<Button
disabled={disableSaveAndExploreBtn}
@@ -389,7 +421,7 @@ export const SaveDatasetModal = ({
</Button>
</>
)}
</>
</div>
}
>
<Styles>