Skip to content

Commit

Permalink
[App Search] Set up Curations routes & complete 'Edit Query' action i…
Browse files Browse the repository at this point in the history
…n Analytics tables (elastic#91052) (elastic#91528)

* Set up Curations routes

* Update EngineRouter/Nav with Curations

* Set up Curations find_or_create API

* [bug] Fix view action not working correctly for "" query

* Add Edit query action
- to call find_or_create curation API & navigate to curation page

+ fix copy string, only just noticed this :doh:

* Add/update unit tests for action column

- Refactor out into a single shared test helper file that both AnalyticsTable and RecentQueriesTable simply calls & runs (instead of copying and pasting the same tests twice into 2 diff files)
- note: test file can't be `.test.tsx` or Jest tries to automatically run it, which we don't want

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Constance <constancecchen@users.noreply.github.com>
  • Loading branch information
kibanamachine and Constance authored Feb 16, 2021
1 parent 04eca96 commit 944476b
Show file tree
Hide file tree
Showing 14 changed files with 297 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
* 2.0.
*/

import { mountWithIntl, mockKibanaValues } from '../../../../../__mocks__';
import { mountWithIntl } from '../../../../../__mocks__';
import '../../../../__mocks__/engine_logic.mock';

import React from 'react';

import { EuiBasicTable, EuiBadge, EuiEmptyPrompt } from '@elastic/eui';

import { runActionColumnTests } from './shared_columns_tests';

import { AnalyticsTable } from './';

describe('AnalyticsTable', () => {
const { navigateToUrl } = mockKibanaValues;

const items = [
{
key: 'some search',
Expand Down Expand Up @@ -69,18 +69,9 @@ describe('AnalyticsTable', () => {
expect(tableContent).toContain('0');
});

it('renders an action column', () => {
describe('renders an action column', () => {
const wrapper = mountWithIntl(<AnalyticsTable items={items} />);
const viewQuery = wrapper.find('[data-test-subj="AnalyticsTableViewQueryButton"]').first();
const editQuery = wrapper.find('[data-test-subj="AnalyticsTableEditQueryButton"]').first();

viewQuery.simulate('click');
expect(navigateToUrl).toHaveBeenCalledWith(
'/engines/some-engine/analytics/query_detail/some%20search'
);

editQuery.simulate('click');
// TODO
runActionColumnTests(wrapper);
});

it('renders an empty prompt if no items are passed', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
* 2.0.
*/

import { mountWithIntl, mockKibanaValues } from '../../../../../__mocks__';
import { mountWithIntl } from '../../../../../__mocks__';
import '../../../../__mocks__/engine_logic.mock';

import React from 'react';

import { EuiBasicTable, EuiBadge, EuiEmptyPrompt } from '@elastic/eui';

import { runActionColumnTests } from './shared_columns_tests';

import { RecentQueriesTable } from './';

describe('RecentQueriesTable', () => {
const { navigateToUrl } = mockKibanaValues;

const items = [
{
query_string: 'some search',
Expand Down Expand Up @@ -63,18 +63,9 @@ describe('RecentQueriesTable', () => {
expect(tableContent).toContain('3');
});

it('renders an action column', () => {
describe('renders an action column', () => {
const wrapper = mountWithIntl(<RecentQueriesTable items={items} />);
const viewQuery = wrapper.find('[data-test-subj="AnalyticsTableViewQueryButton"]').first();
const editQuery = wrapper.find('[data-test-subj="AnalyticsTableEditQueryButton"]').first();

viewQuery.simulate('click');
expect(navigateToUrl).toHaveBeenCalledWith(
'/engines/some-engine/analytics/query_detail/some%20search'
);

editQuery.simulate('click');
// TODO
runActionColumnTests(wrapper);
});

it('renders an empty prompt if no items are passed', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import React from 'react';

import { i18n } from '@kbn/i18n';

import { flashAPIErrors } from '../../../../../shared/flash_messages';
import { HttpLogic } from '../../../../../shared/http';
import { KibanaLogic } from '../../../../../shared/kibana';
import { EuiLinkTo } from '../../../../../shared/react_router_helpers';
import { ENGINE_ANALYTICS_QUERY_DETAIL_PATH } from '../../../../routes';
import { generateEnginePath } from '../../../engine';
import { ENGINE_ANALYTICS_QUERY_DETAIL_PATH, ENGINE_CURATION_PATH } from '../../../../routes';
import { generateEnginePath, EngineLogic } from '../../../engine';
import { Query, RecentQuery } from '../../types';

import { InlineTagsList } from './inline_tags_list';
Expand Down Expand Up @@ -63,7 +65,7 @@ export const ACTIONS_COLUMN = {
onClick: (item: Query | RecentQuery) => {
const { navigateToUrl } = KibanaLogic.values;

const query = (item as Query).key || (item as RecentQuery).query_string;
const query = (item as Query).key || (item as RecentQuery).query_string || '""';
navigateToUrl(generateEnginePath(ENGINE_ANALYTICS_QUERY_DETAIL_PATH, { query }));
},
'data-test-subj': 'AnalyticsTableViewQueryButton',
Expand All @@ -74,12 +76,25 @@ export const ACTIONS_COLUMN = {
}),
description: i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.analytics.table.editTooltip',
{ defaultMessage: 'Edit query analytics' }
{ defaultMessage: 'Edit query' }
),
type: 'icon',
icon: 'pencil',
onClick: () => {
// TODO: CurationsLogic
onClick: async (item: Query | RecentQuery) => {
const { http } = HttpLogic.values;
const { navigateToUrl } = KibanaLogic.values;
const { engineName } = EngineLogic.values;

try {
const query = (item as Query).key || (item as RecentQuery).query_string || '""';
const response = await http.get(
`/api/app_search/engines/${engineName}/curations/find_or_create`,
{ query: { query } }
);
navigateToUrl(generateEnginePath(ENGINE_CURATION_PATH, { curationId: response.id }));
} catch (e) {
flashAPIErrors(e);
}
},
'data-test-subj': 'AnalyticsTableEditQueryButton',
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import {
mockHttpValues,
mockKibanaValues,
mockFlashMessageHelpers,
} from '../../../../../__mocks__';
import '../../../../__mocks__/engine_logic.mock';

import { ReactWrapper } from 'enzyme';

import { nextTick } from '@kbn/test/jest';

export const runActionColumnTests = (wrapper: ReactWrapper) => {
const { http } = mockHttpValues;
const { navigateToUrl } = mockKibanaValues;
const { flashAPIErrors } = mockFlashMessageHelpers;

beforeEach(() => {
jest.clearAllMocks();
});

describe('view action', () => {
it('navigates to the query detail view', () => {
wrapper.find('[data-test-subj="AnalyticsTableViewQueryButton"]').first().simulate('click');

expect(navigateToUrl).toHaveBeenCalledWith(
'/engines/some-engine/analytics/query_detail/some%20search'
);
});

it('falls back to "" for the empty query', () => {
wrapper.find('[data-test-subj="AnalyticsTableViewQueryButton"]').last().simulate('click');
expect(navigateToUrl).toHaveBeenCalledWith(
'/engines/some-engine/analytics/query_detail/%22%22'
);
});
});

describe('edit action', () => {
it('calls the find_or_create curation API, then navigates the user to the curation', async () => {
http.get.mockReturnValue(Promise.resolve({ id: 'cur-123456789' }));
wrapper.find('[data-test-subj="AnalyticsTableEditQueryButton"]').first().simulate('click');
await nextTick();

expect(http.get).toHaveBeenCalledWith(
'/api/app_search/engines/some-engine/curations/find_or_create',
{
query: { query: 'some search' },
}
);
expect(navigateToUrl).toHaveBeenCalledWith('/engines/some-engine/curations/cur-123456789');
});

it('falls back to "" for the empty query', async () => {
http.get.mockReturnValue(Promise.resolve({ id: 'cur-987654321' }));
wrapper.find('[data-test-subj="AnalyticsTableEditQueryButton"]').last().simulate('click');
await nextTick();

expect(http.get).toHaveBeenCalledWith(
'/api/app_search/engines/some-engine/curations/find_or_create',
{
query: { query: '""' },
}
);
expect(navigateToUrl).toHaveBeenCalledWith('/engines/some-engine/curations/cur-987654321');
});

it('handles API errors', async () => {
http.get.mockReturnValue(Promise.reject());
wrapper.find('[data-test-subj="AnalyticsTableEditQueryButton"]').first().simulate('click');
await nextTick();

expect(flashAPIErrors).toHaveBeenCalled();
});
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { Route, Switch } from 'react-router-dom';

import { shallow } from 'enzyme';

import { CurationsRouter } from './';

describe('CurationsRouter', () => {
it('renders', () => {
const wrapper = shallow(<CurationsRouter engineBreadcrumb={['Engines', 'some-engine']} />);

expect(wrapper.find(Switch)).toHaveLength(1);
expect(wrapper.find(Route)).toHaveLength(5);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { Route, Switch } from 'react-router-dom';

import { APP_SEARCH_PLUGIN } from '../../../../../common/constants';
import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
import { BreadcrumbTrail } from '../../../shared/kibana_chrome/generate_breadcrumbs';
import { NotFound } from '../../../shared/not_found';
import {
ENGINE_CURATIONS_PATH,
ENGINE_CURATIONS_NEW_PATH,
ENGINE_CURATION_PATH,
ENGINE_CURATION_ADD_RESULT_PATH,
} from '../../routes';

import { CURATIONS_TITLE } from './constants';

interface Props {
engineBreadcrumb: BreadcrumbTrail;
}
export const CurationsRouter: React.FC<Props> = ({ engineBreadcrumb }) => {
const CURATIONS_BREADCRUMB = [...engineBreadcrumb, CURATIONS_TITLE];

return (
<Switch>
<Route exact path={ENGINE_CURATIONS_PATH}>
<SetPageChrome trail={CURATIONS_BREADCRUMB} />
TODO: Curations overview
</Route>
<Route exact path={ENGINE_CURATIONS_NEW_PATH}>
<SetPageChrome trail={[...CURATIONS_BREADCRUMB, 'Create a curation']} />
TODO: Curation creation view
</Route>
<Route exact path={ENGINE_CURATION_PATH}>
<SetPageChrome trail={[...CURATIONS_BREADCRUMB, 'curation queries']} />
TODO: Curation view (+ show a NotFound view if ID is invalid)
</Route>
<Route exact path={ENGINE_CURATION_ADD_RESULT_PATH}>
<SetPageChrome
trail={[...CURATIONS_BREADCRUMB, 'curation queries', 'add result manually']}
/>
TODO: Curation Add Result view
</Route>
<Route>
<NotFound breadcrumbs={CURATIONS_BREADCRUMB} product={APP_SEARCH_PLUGIN} />
</Route>
</Switch>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
*/

export { CURATIONS_TITLE } from './constants';
export { CurationsRouter } from './curations_router';
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,8 @@ export const EngineNav: React.FC = () => {
)}
{canManageEngineCurations && (
<SideNavLink
isExternal
to={getAppSearchUrl(generateEnginePath(ENGINE_CURATIONS_PATH))}
to={generateEnginePath(ENGINE_CURATIONS_PATH)}
shouldShowActiveForSubroutes
data-test-subj="EngineCurationsLink"
>
{CURATIONS_TITLE}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { shallow } from 'enzyme';

import { Loading } from '../../../shared/loading';
import { AnalyticsRouter } from '../analytics';
import { CurationsRouter } from '../curations';
import { EngineOverview } from '../engine_overview';
import { RelevanceTuning } from '../relevance_tuning';

Expand Down Expand Up @@ -97,7 +98,14 @@ describe('EngineRouter', () => {
expect(wrapper.find(AnalyticsRouter)).toHaveLength(1);
});

it('renders an relevance tuning view', () => {
it('renders a curations view', () => {
setMockValues({ ...values, myRole: { canManageEngineCurations: true } });
const wrapper = shallow(<EngineRouter />);

expect(wrapper.find(CurationsRouter)).toHaveLength(1);
});

it('renders a relevance tuning view', () => {
setMockValues({ ...values, myRole: { canManageEngineRelevanceTuning: true } });
const wrapper = shallow(<EngineRouter />);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ import {
// META_ENGINE_SOURCE_ENGINES_PATH,
ENGINE_RELEVANCE_TUNING_PATH,
// ENGINE_SYNONYMS_PATH,
// ENGINE_CURATIONS_PATH,
ENGINE_CURATIONS_PATH,
// ENGINE_RESULT_SETTINGS_PATH,
// ENGINE_SEARCH_UI_PATH,
// ENGINE_API_LOGS_PATH,
} from '../../routes';
import { AnalyticsRouter } from '../analytics';
import { CurationsRouter } from '../curations';
import { DocumentDetail, Documents } from '../documents';
import { OVERVIEW_TITLE } from '../engine_overview';
import { EngineOverview } from '../engine_overview';
Expand All @@ -46,13 +47,13 @@ export const EngineRouter: React.FC = () => {
const {
myRole: {
canViewEngineAnalytics,
canManageEngineRelevanceTuning,
// canViewEngineDocuments,
// canViewEngineSchema,
// canViewEngineCrawler,
// canViewMetaEngineSourceEngines,
canManageEngineRelevanceTuning,
// canManageEngineSynonyms,
// canManageEngineCurations,
canManageEngineCurations,
// canManageEngineResultSettings,
// canManageEngineSearchUi,
// canViewEngineApiLogs,
Expand Down Expand Up @@ -97,6 +98,11 @@ export const EngineRouter: React.FC = () => {
<Route path={ENGINE_DOCUMENTS_PATH}>
<Documents engineBreadcrumb={engineBreadcrumb} />
</Route>
{canManageEngineCurations && (
<Route path={ENGINE_CURATIONS_PATH}>
<CurationsRouter engineBreadcrumb={engineBreadcrumb} />
</Route>
)}
{canManageEngineRelevanceTuning && (
<Route path={ENGINE_RELEVANCE_TUNING_PATH}>
<RelevanceTuning engineBreadcrumb={engineBreadcrumb} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@ export const META_ENGINE_SOURCE_ENGINES_PATH = `${ENGINE_PATH}/engines`;

export const ENGINE_RELEVANCE_TUNING_PATH = `${ENGINE_PATH}/relevance_tuning`;
export const ENGINE_SYNONYMS_PATH = `${ENGINE_PATH}/synonyms`;
export const ENGINE_CURATIONS_PATH = `${ENGINE_PATH}/curations`;
// TODO: Curations sub-pages
export const ENGINE_RESULT_SETTINGS_PATH = `${ENGINE_PATH}/result-settings`;

export const ENGINE_CURATIONS_PATH = `${ENGINE_PATH}/curations`;
export const ENGINE_CURATIONS_NEW_PATH = `${ENGINE_CURATIONS_PATH}/new`;
export const ENGINE_CURATION_PATH = `${ENGINE_CURATIONS_PATH}/:curationId`;
export const ENGINE_CURATION_ADD_RESULT_PATH = `${ENGINE_CURATIONS_PATH}/:curationId/add_result`;

export const ENGINE_SEARCH_UI_PATH = `${ENGINE_PATH}/reference_application/new`;
export const ENGINE_API_LOGS_PATH = `${ENGINE_PATH}/api-logs`;
Loading

0 comments on commit 944476b

Please sign in to comment.