Skip to content

Commit 049dde1

Browse files
feat(explore): Explore Saved Query Duplicate (#89237)
Implements the Duplicate button for explore saved queries
1 parent a3d9526 commit 049dde1

File tree

3 files changed

+58
-2
lines changed

3 files changed

+58
-2
lines changed

static/app/views/explore/hooks/useSaveQuery.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,23 @@ export function useSaveQuery() {
9898
return response;
9999
}, [api, organization.slug, id, data, invalidateSavedQueries]);
100100

101+
const saveQueryFromSavedQuery = useCallback(
102+
async (savedQuery: SavedQuery) => {
103+
const response = await api.requestPromise(
104+
`/organizations/${organization.slug}/explore/saved/`,
105+
{
106+
method: 'POST',
107+
data: {
108+
...savedQuery,
109+
},
110+
}
111+
);
112+
invalidateSavedQueries();
113+
return response;
114+
},
115+
[api, organization.slug, invalidateSavedQueries]
116+
);
117+
101118
const updateQueryFromSavedQuery = useCallback(
102119
async (savedQuery: SavedQuery) => {
103120
const response = await api.requestPromise(
@@ -115,5 +132,5 @@ export function useSaveQuery() {
115132
[api, organization.slug, invalidateSavedQueries]
116133
);
117134

118-
return {saveQuery, updateQuery, updateQueryFromSavedQuery};
135+
return {saveQuery, updateQuery, saveQueryFromSavedQuery, updateQueryFromSavedQuery};
119136
}

static/app/views/explore/savedQueries/savedQueriesTable.spec.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ describe('SavedQueriesTable', () => {
99
let deleteQueryMock: jest.Mock;
1010
let starQueryMock: jest.Mock;
1111
let unstarQueryMock: jest.Mock;
12+
let saveQueryMock: jest.Mock;
1213

1314
beforeEach(() => {
1415
getQueriesMock = MockApiClient.addMockResponse({
@@ -42,6 +43,10 @@ describe('SavedQueriesTable', () => {
4243
url: `/organizations/${organization.slug}/explore/saved/2/starred/`,
4344
method: 'POST',
4445
});
46+
saveQueryMock = MockApiClient.addMockResponse({
47+
url: `/organizations/${organization.slug}/explore/saved/`,
48+
method: 'POST',
49+
});
4550
});
4651

4752
afterEach(() => {
@@ -233,4 +238,22 @@ describe('SavedQueriesTable', () => {
233238
})
234239
);
235240
});
241+
242+
it('should duplicate a query', async () => {
243+
render(<SavedQueriesTable mode="owned" />);
244+
await screen.findByText('Query Name');
245+
await userEvent.click(screen.getByLabelText('Query actions'));
246+
await userEvent.click(screen.getByText('Duplicate'));
247+
await waitFor(() =>
248+
expect(saveQueryMock).toHaveBeenCalledWith(
249+
`/organizations/${organization.slug}/explore/saved/`,
250+
expect.objectContaining({
251+
method: 'POST',
252+
data: expect.objectContaining({
253+
name: 'Query Name (Copy)',
254+
}),
255+
})
256+
)
257+
);
258+
});
236259
});

static/app/views/explore/savedQueries/savedQueriesTable.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export function SavedQueriesTable({
8686
const filteredData = data?.filter(row => row.query?.length > 0) ?? [];
8787
const {deleteQuery} = useDeleteQuery();
8888
const {starQuery} = useStarQuery();
89-
const {updateQueryFromSavedQuery} = useSaveQuery();
89+
const {saveQueryFromSavedQuery, updateQueryFromSavedQuery} = useSaveQuery();
9090

9191
const [starredIds, setStarredIds] = useState<number[]>([]);
9292

@@ -126,6 +126,13 @@ export function SavedQueriesTable({
126126
[updateQueryFromSavedQuery]
127127
);
128128

129+
const duplicateQuery = async (savedQuery: SavedQuery) => {
130+
await saveQueryFromSavedQuery({
131+
...savedQuery,
132+
name: `${savedQuery.name} (Copy)`,
133+
});
134+
};
135+
129136
const handleCursor: CursorHandler = (_cursor, pathname, query) => {
130137
navigate({
131138
pathname,
@@ -199,6 +206,15 @@ export function SavedQueriesTable({
199206
{
200207
key: 'duplicate',
201208
label: t('Duplicate'),
209+
onAction: async () => {
210+
addLoadingMessage(t('Duplicating query...'));
211+
try {
212+
await duplicateQuery(row);
213+
addSuccessMessage(t('Query duplicated'));
214+
} catch (error) {
215+
addErrorMessage(t('Unable to duplicate query'));
216+
}
217+
},
202218
},
203219
{
204220
key: 'delete',

0 commit comments

Comments
 (0)