Skip to content

Commit

Permalink
Fix template queries loading and update getSampleQuery interface (#8848)
Browse files Browse the repository at this point in the history
* update sample query impl and fix saved query load

Signed-off-by: Joanne Wang <[email protected]>

* Changeset file for PR #8848 created/updated

* Changeset file for PR #8848 created/updated

* add loading spinner

Signed-off-by: Joanne Wang <[email protected]>

* check if tab is shown based on if sample query isTemplate

Signed-off-by: Joanne Wang <[email protected]>

* added UTs for svaed queries flyout

Signed-off-by: Riya Saxena <[email protected]>

---------

Signed-off-by: Joanne Wang <[email protected]>
Signed-off-by: Riya Saxena <[email protected]>
Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com>
Co-authored-by: Riya Saxena <[email protected]>
  • Loading branch information
3 people authored Nov 13, 2024
1 parent 76cf823 commit 25d3de7
Show file tree
Hide file tree
Showing 9 changed files with 342 additions and 39 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/8848.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fix:
- Fix template queries loading and update getSampleQuery interface ([#8848](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8848))
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,5 @@ export interface DatasetTypeConfig {
/**
* Returns a list of sample queries for this dataset type
*/
getSampleQueries?: (dataset: Dataset, language: string) => any;
getSampleQueries?: (dataset?: Dataset, language?: string) => Promise<any> | any;
}
4 changes: 3 additions & 1 deletion src/plugins/data/public/ui/filter_bar/filter_options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import {
import { FilterEditor } from './filter_editor';
import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public';
import { SavedQueryManagementComponent } from '../saved_query_management';
import { SavedQuery, SavedQueryService } from '../../query';
import { QueryStringManager, SavedQuery, SavedQueryService } from '../../query';
import { SavedQueryMeta } from '../saved_query_form';
import { getUseNewSavedQueriesUI } from '../../services';

Expand All @@ -79,6 +79,7 @@ interface Props {
useSaveQueryMenu: boolean;
isQueryEditorControl: boolean;
saveQuery: (savedQueryMeta: SavedQueryMeta, saveAsNew?: boolean) => Promise<void>;
queryStringManager: QueryStringManager;
}
const maxFilterWidth = 600;

Expand Down Expand Up @@ -310,6 +311,7 @@ const FilterOptionsUI = (props: Props) => {
key={'savedQueryManagement'}
useNewSavedQueryUI={getUseNewSavedQueriesUI()}
saveQuery={props.saveQuery}
queryStringManager={props.queryStringManager}
/>,
]}
data-test-subj="save-query-panel"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
import { OpenSavedQueryFlyout } from './open_saved_query_flyout';
import { createSavedQueryService } from '../../../public/query/saved_query/saved_query_service';
import { applicationServiceMock, uiSettingsServiceMock } from '../../../../../core/public/mocks';
import { SavedQueryAttributes } from '../../../public/query/saved_query/types';
import '@testing-library/jest-dom';
import { queryStringManagerMock } from '../../../../data/public/query/query_string/query_string_manager.mock';

const savedQueryAttributesWithTemplate: SavedQueryAttributes = {
title: 'foo',
description: 'bar',
query: {
language: 'kuery',
query: 'response:200',
dataset: 'my_dataset',
},
};

const mockSavedObjectsClient = {
create: jest.fn(),
error: jest.fn(),
find: jest.fn(),
get: jest.fn(),
delete: jest.fn(),
};

mockSavedObjectsClient.create.mockReturnValue({
id: 'foo',
attributes: {
...savedQueryAttributesWithTemplate,
query: {
...savedQueryAttributesWithTemplate.query,
},
},
});

jest.mock('./saved_query_card', () => ({
SavedQueryCard: ({
savedQuery = {
id: 'foo1',
attributes: savedQueryAttributesWithTemplate,
},
onSelect,
handleQueryDelete,
}) => (
<div>
<div>{savedQuery?.attributes?.title}</div>
<button onClick={() => onSelect(savedQuery)}>Select</button>
<button onClick={() => handleQueryDelete(savedQuery)}>Delete</button>
</div>
),
}));

jest.mock('@osd/i18n', () => ({
i18n: {
translate: jest.fn((id, { defaultMessage }) => defaultMessage),
},
}));

const mockSavedQueryService = createSavedQueryService(
// @ts-ignore
mockSavedObjectsClient,
{
application: applicationServiceMock.create(),
uiSettings: uiSettingsServiceMock.createStartContract(),
}
);

const mockHandleQueryDelete = jest.fn();
const mockOnQueryOpen = jest.fn();
const mockOnClose = jest.fn();

const savedQueries = [
{
id: '1',
attributes: {
title: 'Saved Query 1',
description: 'Description for Query 1',
query: { query: 'SELECT * FROM table1', language: 'sql' },
},
},
{
id: '2',
attributes: {
title: 'Saved Query 2',
description: 'Description for Query 2',
query: { query: 'SELECT * FROM table2', language: 'sql' },
},
},
];

jest.spyOn(mockSavedQueryService, 'getAllSavedQueries').mockResolvedValue(savedQueries);

describe('OpenSavedQueryFlyout', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('should render the flyout with correct tabs and content', async () => {
render(
<OpenSavedQueryFlyout
savedQueryService={mockSavedQueryService}
onClose={mockOnClose}
onQueryOpen={mockOnQueryOpen}
handleQueryDelete={mockHandleQueryDelete}
queryStringManager={queryStringManagerMock.createSetupContract()}
/>
);

const savedQueriesTextElements = screen.getAllByText('Saved queries');

expect(savedQueriesTextElements).toHaveLength(2);

await waitFor(() => screen.getByPlaceholderText('Search'));

await waitFor(() => screen.getByText('Saved Query 1'));
await waitFor(() => screen.getByText('Saved Query 2'));

const openQueryButton = screen.getByText('Open query');

fireEvent.change(screen.getByPlaceholderText('Search'), { target: { value: 'Saved Query 1' } });

await waitFor(() => screen.getByText('Saved Query 1'));
expect(screen.queryByText('Saved Query 2')).not.toBeInTheDocument();

fireEvent.click(screen.getByText('Saved Query 1'));

expect(openQueryButton).toBeEnabled();
});

it('should filter saved queries based on search input', async () => {
render(
<OpenSavedQueryFlyout
savedQueryService={mockSavedQueryService}
onClose={mockOnClose}
onQueryOpen={mockOnQueryOpen}
handleQueryDelete={mockHandleQueryDelete}
queryStringManager={queryStringManagerMock.createSetupContract()}
/>
);

await waitFor(() => screen.getByText('Saved Query 1'));
await waitFor(() => screen.getByText('Saved Query 2'));

const searchBar = screen.getByPlaceholderText('Search');
fireEvent.change(searchBar, { target: { value: 'Saved Query 1' } });

expect(screen.getByText('Saved Query 1')).toBeInTheDocument();
expect(screen.queryByText('Saved Query 2')).toBeNull();
});

it('should select a query when clicking on it and enable the "Open query" button', async () => {
render(
<OpenSavedQueryFlyout
savedQueryService={mockSavedQueryService}
onClose={mockOnClose}
onQueryOpen={mockOnQueryOpen}
handleQueryDelete={mockHandleQueryDelete}
queryStringManager={queryStringManagerMock.createSetupContract()}
/>
);

await waitFor(() => screen.getByText('Saved Query 1'));

fireEvent.click(screen.getByText('Saved Query 1'));

expect(screen.getByText('Open query')).toBeEnabled();
});

it('should call handleQueryDelete when deleting a query', async () => {
mockHandleQueryDelete.mockResolvedValueOnce();
render(
<OpenSavedQueryFlyout
savedQueryService={mockSavedQueryService}
onClose={mockOnClose}
onQueryOpen={mockOnQueryOpen}
handleQueryDelete={mockHandleQueryDelete}
queryStringManager={queryStringManagerMock.createSetupContract()}
/>
);

await waitFor(() => screen.getByText('Saved Query 1'));

const deleteButtons = screen.getAllByText('Delete');

fireEvent.click(deleteButtons[0]);

await waitFor(() => {
expect(mockHandleQueryDelete).toHaveBeenCalledWith({
id: '1',
attributes: {
description: 'Description for Query 1',
query: {
language: 'sql',
query: 'SELECT * FROM table1',
},
title: 'Saved Query 1',
},
});
});
expect(mockHandleQueryDelete).toHaveBeenCalledTimes(1);
});

it('should handle pagination controls correctly', async () => {
render(
<OpenSavedQueryFlyout
savedQueryService={mockSavedQueryService}
onClose={mockOnClose}
onQueryOpen={mockOnQueryOpen}
handleQueryDelete={mockHandleQueryDelete}
queryStringManager={queryStringManagerMock.createSetupContract()}
/>
);

await waitFor(() => screen.getByText('Saved Query 1'));

const pageSizeButton = await screen.findByText(/10/);
fireEvent.click(pageSizeButton);

expect(mockSavedQueryService.getAllSavedQueries).toHaveBeenCalled();
});
});
Loading

0 comments on commit 25d3de7

Please sign in to comment.