mirror of
https://github.com/apache/superset.git
synced 2026-04-28 04:25:07 +00:00
refactor(sqllab): migrate share queries via kv by permalink (#29163)
This commit is contained in:
@@ -23,10 +23,9 @@ import fetchMock from 'fetch-mock';
|
||||
import * as uiCore from '@superset-ui/core';
|
||||
import { Provider } from 'react-redux';
|
||||
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
||||
import { render, screen, act } from '@testing-library/react';
|
||||
import { render, screen, act, waitFor } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import * as utils from 'src/utils/common';
|
||||
import ShareSqlLabQuery from 'src/SqlLab/components/ShareSqlLabQuery';
|
||||
import { initialState } from 'src/SqlLab/fixtures';
|
||||
|
||||
@@ -92,20 +91,24 @@ const standardProviderWithUnsaved: FC = ({ children }) => (
|
||||
);
|
||||
|
||||
describe('ShareSqlLabQuery', () => {
|
||||
const storeQueryUrl = 'glob:*/kv/store/';
|
||||
const storeQueryMockId = '123';
|
||||
const storeQueryUrl = 'glob:*/api/v1/sqllab/permalink';
|
||||
const storeQueryMockId = 'ci39c3';
|
||||
|
||||
beforeEach(async () => {
|
||||
fetchMock.post(storeQueryUrl, () => ({ id: storeQueryMockId }), {
|
||||
overwriteRoutes: true,
|
||||
});
|
||||
fetchMock.post(
|
||||
storeQueryUrl,
|
||||
() => ({ key: storeQueryMockId, url: `/p/${storeQueryMockId}` }),
|
||||
{
|
||||
overwriteRoutes: true,
|
||||
},
|
||||
);
|
||||
fetchMock.resetHistory();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
afterAll(fetchMock.reset);
|
||||
|
||||
describe('via /kv/store', () => {
|
||||
describe('via permalink api', () => {
|
||||
beforeAll(() => {
|
||||
isFeatureEnabledMock = jest
|
||||
.spyOn(uiCore, 'isFeatureEnabled')
|
||||
@@ -124,11 +127,13 @@ describe('ShareSqlLabQuery', () => {
|
||||
});
|
||||
const button = screen.getByRole('button');
|
||||
const { id, remoteId, ...expected } = mockQueryEditor;
|
||||
const storeQuerySpy = jest.spyOn(utils, 'storeQuery');
|
||||
userEvent.click(button);
|
||||
expect(storeQuerySpy.mock.calls).toHaveLength(1);
|
||||
expect(storeQuerySpy).toHaveBeenCalledWith(expected);
|
||||
storeQuerySpy.mockRestore();
|
||||
await waitFor(() =>
|
||||
expect(fetchMock.calls(storeQueryUrl)).toHaveLength(1),
|
||||
);
|
||||
expect(
|
||||
JSON.parse(fetchMock.calls(storeQueryUrl)[0][1]?.body as string),
|
||||
).toEqual(expected);
|
||||
});
|
||||
|
||||
it('calls storeQuery() with unsaved changes', async () => {
|
||||
@@ -139,48 +144,13 @@ describe('ShareSqlLabQuery', () => {
|
||||
});
|
||||
const button = screen.getByRole('button');
|
||||
const { id, ...expected } = unsavedQueryEditor;
|
||||
const storeQuerySpy = jest.spyOn(utils, 'storeQuery');
|
||||
userEvent.click(button);
|
||||
expect(storeQuerySpy.mock.calls).toHaveLength(1);
|
||||
expect(storeQuerySpy).toHaveBeenCalledWith(expected);
|
||||
storeQuerySpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('via saved query', () => {
|
||||
beforeAll(() => {
|
||||
isFeatureEnabledMock = jest
|
||||
.spyOn(uiCore, 'isFeatureEnabled')
|
||||
.mockImplementation(() => false);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
isFeatureEnabledMock.mockReset();
|
||||
});
|
||||
|
||||
it('does not call storeQuery() with the query when getCopyUrl() is called and feature is not enabled', async () => {
|
||||
await act(async () => {
|
||||
render(<ShareSqlLabQuery {...defaultProps} />, {
|
||||
wrapper: standardProvider,
|
||||
});
|
||||
});
|
||||
const storeQuerySpy = jest.spyOn(utils, 'storeQuery');
|
||||
const button = screen.getByRole('button');
|
||||
userEvent.click(button);
|
||||
expect(storeQuerySpy.mock.calls).toHaveLength(0);
|
||||
storeQuerySpy.mockRestore();
|
||||
});
|
||||
|
||||
it('button is disabled and there is a request to save the query', async () => {
|
||||
const updatedProps = {
|
||||
queryEditorId: disabled.id,
|
||||
};
|
||||
|
||||
render(<ShareSqlLabQuery {...updatedProps} />, {
|
||||
wrapper: standardProvider,
|
||||
});
|
||||
const button = await screen.findByRole('button', { name: /copy link/i });
|
||||
expect(button).toBeDisabled();
|
||||
await waitFor(() =>
|
||||
expect(fetchMock.calls(storeQueryUrl)).toHaveLength(1),
|
||||
);
|
||||
expect(
|
||||
JSON.parse(fetchMock.calls(storeQueryUrl)[0][1]?.body as string),
|
||||
).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,18 +17,16 @@
|
||||
* under the License.
|
||||
*/
|
||||
import {
|
||||
FeatureFlag,
|
||||
styled,
|
||||
t,
|
||||
useTheme,
|
||||
isFeatureEnabled,
|
||||
getClientErrorObject,
|
||||
SupersetClient,
|
||||
} from '@superset-ui/core';
|
||||
import Button from 'src/components/Button';
|
||||
import Icons from 'src/components/Icons';
|
||||
import withToasts from 'src/components/MessageToasts/withToasts';
|
||||
import CopyToClipboard from 'src/components/CopyToClipboard';
|
||||
import { storeQuery } from 'src/utils/common';
|
||||
import useQueryEditor from 'src/SqlLab/hooks/useQueryEditor';
|
||||
import { LOG_ACTIONS_SQLLAB_COPY_LINK } from 'src/logger/LogUtils';
|
||||
import useLogAction from 'src/logger/useLogAction';
|
||||
@@ -54,23 +52,21 @@ const ShareSqlLabQuery = ({
|
||||
}: ShareSqlLabQueryProps) => {
|
||||
const theme = useTheme();
|
||||
const logAction = useLogAction({ queryEditorId });
|
||||
const { dbId, name, schema, autorun, sql, remoteId, templateParams } =
|
||||
useQueryEditor(queryEditorId, [
|
||||
'dbId',
|
||||
'name',
|
||||
'schema',
|
||||
'autorun',
|
||||
'sql',
|
||||
'remoteId',
|
||||
'templateParams',
|
||||
]);
|
||||
const { dbId, name, schema, autorun, sql, templateParams } = useQueryEditor(
|
||||
queryEditorId,
|
||||
['dbId', 'name', 'schema', 'autorun', 'sql', 'templateParams'],
|
||||
);
|
||||
|
||||
const getCopyUrlForKvStore = (callback: Function) => {
|
||||
const getCopyUrlForPermalink = (callback: Function) => {
|
||||
const sharedQuery = { dbId, name, schema, autorun, sql, templateParams };
|
||||
|
||||
return storeQuery(sharedQuery)
|
||||
.then(shortUrl => {
|
||||
callback(shortUrl);
|
||||
return SupersetClient.post({
|
||||
endpoint: '/api/v1/sqllab/permalink',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(sharedQuery),
|
||||
})
|
||||
.then(({ json }) => {
|
||||
callback(json.url);
|
||||
})
|
||||
.catch(response => {
|
||||
getClientErrorObject(response).then(() => {
|
||||
@@ -79,61 +75,29 @@ const ShareSqlLabQuery = ({
|
||||
});
|
||||
};
|
||||
|
||||
const getCopyUrlForSavedQuery = (callback: Function) => {
|
||||
let savedQueryToastContent;
|
||||
|
||||
if (remoteId) {
|
||||
savedQueryToastContent = `${
|
||||
window.location.origin + window.location.pathname
|
||||
}?savedQueryId=${remoteId}`;
|
||||
callback(savedQueryToastContent);
|
||||
} else {
|
||||
savedQueryToastContent = t('Please save the query to enable sharing');
|
||||
callback(savedQueryToastContent);
|
||||
}
|
||||
};
|
||||
const getCopyUrl = (callback: Function) => {
|
||||
logAction(LOG_ACTIONS_SQLLAB_COPY_LINK, {
|
||||
shortcut: false,
|
||||
});
|
||||
if (isFeatureEnabled(FeatureFlag.ShareQueriesViaKvStore)) {
|
||||
return getCopyUrlForKvStore(callback);
|
||||
}
|
||||
return getCopyUrlForSavedQuery(callback);
|
||||
return getCopyUrlForPermalink(callback);
|
||||
};
|
||||
|
||||
const buildButton = (canShare: boolean) => {
|
||||
const tooltip = canShare
|
||||
? t('Copy query link to your clipboard')
|
||||
: t('Save the query to enable this feature');
|
||||
const buildButton = () => {
|
||||
const tooltip = t('Copy query link to your clipboard');
|
||||
return (
|
||||
<Button buttonSize="small" tooltip={tooltip} disabled={!canShare}>
|
||||
<StyledIcon
|
||||
iconColor={
|
||||
canShare ? theme.colors.primary.base : theme.colors.grayscale.base
|
||||
}
|
||||
iconSize="xl"
|
||||
/>
|
||||
<Button buttonSize="small" tooltip={tooltip}>
|
||||
<StyledIcon iconColor={theme.colors.primary.base} iconSize="xl" />
|
||||
{t('Copy link')}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
const canShare =
|
||||
!!remoteId || isFeatureEnabled(FeatureFlag.ShareQueriesViaKvStore);
|
||||
|
||||
return (
|
||||
<>
|
||||
{canShare ? (
|
||||
<CopyToClipboard
|
||||
getText={getCopyUrl}
|
||||
wrapped={false}
|
||||
copyNode={buildButton(canShare)}
|
||||
/>
|
||||
) : (
|
||||
buildButton(canShare)
|
||||
)}
|
||||
</>
|
||||
<CopyToClipboard
|
||||
getText={getCopyUrl}
|
||||
wrapped={false}
|
||||
copyNode={buildButton()}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user