mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[App Search] Wired up action buttons for suggestion detail view (#114183)
This commit is contained in:
parent
9bb8f2246c
commit
a5a8bb29d6
9 changed files with 464 additions and 82 deletions
|
@ -45,6 +45,7 @@ const MOCK_RESPONSE: SuggestionsAPIResponse = {
|
|||
updated_at: '2021-07-08T14:35:50Z',
|
||||
promoted: ['1', '2'],
|
||||
status: 'applied',
|
||||
operation: 'create',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -14,6 +14,7 @@ export interface CurationSuggestion {
|
|||
promoted: string[];
|
||||
status: 'pending' | 'applied' | 'automated' | 'rejected' | 'disabled';
|
||||
curation_id?: string;
|
||||
operation: 'create' | 'update' | 'delete';
|
||||
override_curation_id?: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { setMockActions } from '../../../../../__mocks__/kea_logic';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
|
@ -12,18 +14,22 @@ import { shallow } from 'enzyme';
|
|||
import { CurationActionBar } from './curation_action_bar';
|
||||
|
||||
describe('CurationActionBar', () => {
|
||||
const handleAcceptClick = jest.fn();
|
||||
const handleRejectClick = jest.fn();
|
||||
const actions = {
|
||||
acceptSuggestion: jest.fn(),
|
||||
rejectSuggestion: jest.fn(),
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
setMockActions(actions);
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(
|
||||
<CurationActionBar onAcceptClick={handleAcceptClick} onRejectClick={handleRejectClick} />
|
||||
);
|
||||
const wrapper = shallow(<CurationActionBar />);
|
||||
|
||||
wrapper.find('[data-test-subj="rejectButton"]').simulate('click');
|
||||
expect(handleRejectClick).toHaveBeenCalled();
|
||||
expect(actions.rejectSuggestion).toHaveBeenCalled();
|
||||
|
||||
wrapper.find('[data-test-subj="acceptButton"]').simulate('click');
|
||||
expect(handleAcceptClick).toHaveBeenCalled();
|
||||
expect(actions.acceptSuggestion).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,17 +7,17 @@
|
|||
|
||||
import React from 'react';
|
||||
|
||||
import { useActions } from 'kea';
|
||||
|
||||
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { CurationActionsPopover } from './curation_actions_popover';
|
||||
import { CurationSuggestionLogic } from './curation_suggestion_logic';
|
||||
|
||||
interface Props {
|
||||
onAcceptClick: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||
onRejectClick: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||
}
|
||||
export const CurationActionBar: React.FC = () => {
|
||||
const { acceptSuggestion, rejectSuggestion } = useActions(CurationSuggestionLogic);
|
||||
|
||||
export const CurationActionBar: React.FC<Props> = ({ onAcceptClick, onRejectClick }) => {
|
||||
return (
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
|
@ -41,7 +41,7 @@ export const CurationActionBar: React.FC<Props> = ({ onAcceptClick, onRejectClic
|
|||
color="danger"
|
||||
iconType="crossInACircleFilled"
|
||||
data-test-subj="rejectButton"
|
||||
onClick={onRejectClick}
|
||||
onClick={rejectSuggestion}
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.rejectButtonLabel',
|
||||
|
@ -55,7 +55,7 @@ export const CurationActionBar: React.FC<Props> = ({ onAcceptClick, onRejectClic
|
|||
color="success"
|
||||
iconType="checkInCircleFilled"
|
||||
data-test-subj="acceptButton"
|
||||
onClick={onAcceptClick}
|
||||
onClick={acceptSuggestion}
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.acceptButtonLabel',
|
||||
|
@ -64,12 +64,7 @@ export const CurationActionBar: React.FC<Props> = ({ onAcceptClick, onRejectClic
|
|||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<CurationActionsPopover
|
||||
onAccept={() => {}}
|
||||
onAutomate={() => {}}
|
||||
onReject={() => {}}
|
||||
onTurnOff={() => {}}
|
||||
/>
|
||||
<CurationActionsPopover />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { setMockActions } from '../../../../../__mocks__/kea_logic';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
|
@ -12,48 +14,40 @@ import { shallow } from 'enzyme';
|
|||
import { CurationActionsPopover } from './curation_actions_popover';
|
||||
|
||||
describe('CurationActionsPopover', () => {
|
||||
const handleAccept = jest.fn();
|
||||
const handleAutomate = jest.fn();
|
||||
const handleReject = jest.fn();
|
||||
const handleTurnOff = jest.fn();
|
||||
const actions = {
|
||||
acceptSuggestion: jest.fn(),
|
||||
acceptAndAutomateSuggestion: jest.fn(),
|
||||
rejectSuggestion: jest.fn(),
|
||||
rejectAndDisableSuggestion: jest.fn(),
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
setMockActions(actions);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(
|
||||
<CurationActionsPopover
|
||||
onAccept={handleAccept}
|
||||
onAutomate={handleAutomate}
|
||||
onReject={handleReject}
|
||||
onTurnOff={handleTurnOff}
|
||||
/>
|
||||
);
|
||||
const wrapper = shallow(<CurationActionsPopover />);
|
||||
expect(wrapper.isEmptyRender()).toBe(false);
|
||||
|
||||
wrapper.find('[data-test-subj="acceptButton"]').simulate('click');
|
||||
expect(handleAccept).toHaveBeenCalled();
|
||||
expect(actions.acceptSuggestion).toHaveBeenCalled();
|
||||
|
||||
wrapper.find('[data-test-subj="automateButton"]').simulate('click');
|
||||
expect(handleAutomate).toHaveBeenCalled();
|
||||
expect(actions.acceptAndAutomateSuggestion).toHaveBeenCalled();
|
||||
|
||||
wrapper.find('[data-test-subj="rejectButton"]').simulate('click');
|
||||
expect(handleReject).toHaveBeenCalled();
|
||||
expect(actions.rejectSuggestion).toHaveBeenCalled();
|
||||
|
||||
wrapper.find('[data-test-subj="turnoffButton"]').simulate('click');
|
||||
expect(handleTurnOff).toHaveBeenCalled();
|
||||
expect(actions.rejectAndDisableSuggestion).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('can open and close', () => {
|
||||
const wrapper = shallow(
|
||||
<CurationActionsPopover
|
||||
onAccept={handleAccept}
|
||||
onAutomate={handleAutomate}
|
||||
onReject={handleReject}
|
||||
onTurnOff={handleTurnOff}
|
||||
/>
|
||||
);
|
||||
const wrapper = shallow(<CurationActionsPopover />);
|
||||
|
||||
expect(wrapper.prop('isOpen')).toBe(false);
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { useActions } from 'kea';
|
||||
|
||||
import {
|
||||
EuiButtonIcon,
|
||||
EuiListGroup,
|
||||
|
@ -16,20 +18,16 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
interface Props {
|
||||
onAccept: () => void;
|
||||
onAutomate: () => void;
|
||||
onReject: () => void;
|
||||
onTurnOff: () => void;
|
||||
}
|
||||
import { CurationSuggestionLogic } from './curation_suggestion_logic';
|
||||
|
||||
export const CurationActionsPopover: React.FC<Props> = ({
|
||||
onAccept,
|
||||
onAutomate,
|
||||
onReject,
|
||||
onTurnOff,
|
||||
}) => {
|
||||
export const CurationActionsPopover: React.FC = () => {
|
||||
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
||||
const {
|
||||
acceptSuggestion,
|
||||
acceptAndAutomateSuggestion,
|
||||
rejectSuggestion,
|
||||
rejectAndDisableSuggestion,
|
||||
} = useActions(CurationSuggestionLogic);
|
||||
|
||||
const onButtonClick = () => setIsPopoverOpen(!isPopoverOpen);
|
||||
const closePopover = () => setIsPopoverOpen(false);
|
||||
|
@ -63,7 +61,7 @@ export const CurationActionsPopover: React.FC<Props> = ({
|
|||
'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.actionsAcceptButtonLabel',
|
||||
{ defaultMessage: 'Accept this suggestion' }
|
||||
)}
|
||||
onClick={onAccept}
|
||||
onClick={acceptSuggestion}
|
||||
data-test-subj="acceptButton"
|
||||
/>
|
||||
<EuiListGroupItem
|
||||
|
@ -73,7 +71,7 @@ export const CurationActionsPopover: React.FC<Props> = ({
|
|||
'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.actionsAutomateButtonLabel',
|
||||
{ defaultMessage: 'Automate - always accept new suggestions for this query' }
|
||||
)}
|
||||
onClick={onAutomate}
|
||||
onClick={acceptAndAutomateSuggestion}
|
||||
data-test-subj="automateButton"
|
||||
/>
|
||||
<EuiListGroupItem
|
||||
|
@ -83,7 +81,7 @@ export const CurationActionsPopover: React.FC<Props> = ({
|
|||
'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.actionsRejectButtonLabel',
|
||||
{ defaultMessage: 'Reject this suggestion' }
|
||||
)}
|
||||
onClick={onReject}
|
||||
onClick={rejectSuggestion}
|
||||
data-test-subj="rejectButton"
|
||||
/>
|
||||
<EuiListGroupItem
|
||||
|
@ -93,7 +91,7 @@ export const CurationActionsPopover: React.FC<Props> = ({
|
|||
'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.actionsTurnOffButtonLabel',
|
||||
{ defaultMessage: 'Reject and turn off suggestions for this query' }
|
||||
)}
|
||||
onClick={onTurnOff}
|
||||
onClick={rejectAndDisableSuggestion}
|
||||
data-test-subj="turnoffButton"
|
||||
/>
|
||||
</EuiListGroup>
|
||||
|
|
|
@ -65,10 +65,7 @@ export const CurationSuggestion: React.FC = () => {
|
|||
pageTitle: suggestionQuery,
|
||||
}}
|
||||
>
|
||||
<CurationActionBar
|
||||
onAcceptClick={() => alert('Accepted')}
|
||||
onRejectClick={() => alert('Rejected')}
|
||||
/>
|
||||
<CurationActionBar />
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
LogicMounter,
|
||||
mockFlashMessageHelpers,
|
||||
mockHttpValues,
|
||||
mockKibanaValues,
|
||||
} from '../../../../../__mocks__/kea_logic';
|
||||
|
||||
import { set } from 'lodash/fp';
|
||||
|
@ -32,7 +33,8 @@ const suggestion: CurationSuggestion = {
|
|||
query: 'foo',
|
||||
updated_at: '2021-07-08T14:35:50Z',
|
||||
promoted: ['1', '2', '3'],
|
||||
status: 'applied',
|
||||
status: 'pending',
|
||||
operation: 'create',
|
||||
};
|
||||
|
||||
const curation = {
|
||||
|
@ -115,13 +117,51 @@ const MOCK_DOCUMENTS_RESPONSE = {
|
|||
|
||||
describe('CurationSuggestionLogic', () => {
|
||||
const { mount } = new LogicMounter(CurationSuggestionLogic);
|
||||
const { flashAPIErrors } = mockFlashMessageHelpers;
|
||||
const { flashAPIErrors, setQueuedErrorMessage } = mockFlashMessageHelpers;
|
||||
const { navigateToUrl } = mockKibanaValues;
|
||||
|
||||
const mountLogic = (props: object = {}) => {
|
||||
mount(props, { query: 'foo-query' });
|
||||
};
|
||||
|
||||
const { http } = mockHttpValues;
|
||||
|
||||
const itHandlesInlineErrors = (callback: () => void) => {
|
||||
it('handles inline errors', async () => {
|
||||
http.put.mockReturnValueOnce(
|
||||
Promise.resolve({
|
||||
results: [
|
||||
{
|
||||
error: 'error',
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
mountLogic({
|
||||
suggestion,
|
||||
});
|
||||
|
||||
callback();
|
||||
await nextTick();
|
||||
|
||||
expect(flashAPIErrors).toHaveBeenCalledWith('error');
|
||||
});
|
||||
};
|
||||
|
||||
const itHandlesErrors = (httpMethod: any, callback: () => void) => {
|
||||
it('handles errors', async () => {
|
||||
httpMethod.mockReturnValueOnce(Promise.reject('error'));
|
||||
mountLogic({
|
||||
suggestion,
|
||||
});
|
||||
|
||||
callback();
|
||||
await nextTick();
|
||||
|
||||
expect(flashAPIErrors).toHaveBeenCalledWith('error');
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
@ -207,7 +247,8 @@ describe('CurationSuggestionLogic', () => {
|
|||
query: 'foo',
|
||||
updated_at: '2021-07-08T14:35:50Z',
|
||||
promoted: ['1', '2', '3'],
|
||||
status: 'applied',
|
||||
status: 'pending',
|
||||
operation: 'create',
|
||||
},
|
||||
// Note that these were re-ordered to match the 'promoted' list above, and since document
|
||||
// 3 was not found it is not included in this list
|
||||
|
@ -243,9 +284,7 @@ describe('CurationSuggestionLogic', () => {
|
|||
);
|
||||
http.post.mockReturnValueOnce(Promise.resolve(MOCK_DOCUMENTS_RESPONSE));
|
||||
http.get.mockReturnValueOnce(Promise.resolve(curation));
|
||||
mountLogic({
|
||||
suggestion: set('curation_id', 'cur-6155e69c7a2f2e4f756303fd', suggestion),
|
||||
});
|
||||
mountLogic();
|
||||
jest.spyOn(CurationSuggestionLogic.actions, 'onSuggestionLoaded');
|
||||
|
||||
CurationSuggestionLogic.actions.loadSuggestion();
|
||||
|
@ -255,7 +294,6 @@ describe('CurationSuggestionLogic', () => {
|
|||
'/internal/app_search/engines/some-engine/curations/cur-6155e69c7a2f2e4f756303fd',
|
||||
{ query: { skip_record_analytics: 'true' } }
|
||||
);
|
||||
await nextTick();
|
||||
|
||||
expect(CurationSuggestionLogic.actions.onSuggestionLoaded).toHaveBeenCalledWith({
|
||||
suggestion: expect.any(Object),
|
||||
|
@ -264,14 +302,204 @@ describe('CurationSuggestionLogic', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('handles errors', async () => {
|
||||
http.post.mockReturnValueOnce(Promise.reject('error'));
|
||||
mount();
|
||||
|
||||
// This could happen if a user applies a suggestion and then navigates back to a detail page via
|
||||
// the back button, etc. The suggestion still exists, it's just not in a "pending" state
|
||||
// so we can show it.ga
|
||||
it('will redirect if the suggestion is not found', async () => {
|
||||
http.post.mockReturnValueOnce(Promise.resolve(set('results', [], MOCK_RESPONSE)));
|
||||
mountLogic();
|
||||
CurationSuggestionLogic.actions.loadSuggestion();
|
||||
await nextTick();
|
||||
expect(setQueuedErrorMessage).toHaveBeenCalled();
|
||||
expect(navigateToUrl).toHaveBeenCalledWith('/engines/some-engine/curations');
|
||||
});
|
||||
|
||||
expect(flashAPIErrors).toHaveBeenCalledWith('error');
|
||||
itHandlesErrors(http.post, () => {
|
||||
CurationSuggestionLogic.actions.loadSuggestion();
|
||||
});
|
||||
});
|
||||
|
||||
describe('acceptSuggestion', () => {
|
||||
it('will make an http call to apply the suggestion, and then navigate to that detail page', async () => {
|
||||
http.put.mockReturnValueOnce(
|
||||
Promise.resolve({
|
||||
results: [
|
||||
{
|
||||
...suggestion,
|
||||
status: 'accepted',
|
||||
curation_id: 'cur-6155e69c7a2f2e4f756303fd',
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
mountLogic({
|
||||
suggestion,
|
||||
});
|
||||
|
||||
CurationSuggestionLogic.actions.acceptSuggestion();
|
||||
await nextTick();
|
||||
|
||||
expect(http.put).toHaveBeenCalledWith(
|
||||
'/internal/app_search/engines/some-engine/search_relevance_suggestions',
|
||||
{
|
||||
body: JSON.stringify([
|
||||
{
|
||||
query: 'foo',
|
||||
type: 'curation',
|
||||
status: 'applied',
|
||||
},
|
||||
]),
|
||||
}
|
||||
);
|
||||
|
||||
expect(navigateToUrl).toHaveBeenCalledWith(
|
||||
'/engines/some-engine/curations/cur-6155e69c7a2f2e4f756303fd'
|
||||
);
|
||||
});
|
||||
|
||||
itHandlesErrors(http.put, () => {
|
||||
CurationSuggestionLogic.actions.acceptSuggestion();
|
||||
});
|
||||
|
||||
itHandlesInlineErrors(() => {
|
||||
CurationSuggestionLogic.actions.acceptSuggestion();
|
||||
});
|
||||
});
|
||||
|
||||
describe('acceptAndAutomateSuggestion', () => {
|
||||
it('will make an http call to apply the suggestion, and then navigate to that detail page', async () => {
|
||||
http.put.mockReturnValueOnce(
|
||||
Promise.resolve({
|
||||
results: [
|
||||
{
|
||||
...suggestion,
|
||||
status: 'accepted',
|
||||
curation_id: 'cur-6155e69c7a2f2e4f756303fd',
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
mountLogic({
|
||||
suggestion,
|
||||
});
|
||||
|
||||
CurationSuggestionLogic.actions.acceptAndAutomateSuggestion();
|
||||
await nextTick();
|
||||
|
||||
expect(http.put).toHaveBeenCalledWith(
|
||||
'/internal/app_search/engines/some-engine/search_relevance_suggestions',
|
||||
{
|
||||
body: JSON.stringify([
|
||||
{
|
||||
query: 'foo',
|
||||
type: 'curation',
|
||||
status: 'automated',
|
||||
},
|
||||
]),
|
||||
}
|
||||
);
|
||||
|
||||
expect(navigateToUrl).toHaveBeenCalledWith(
|
||||
'/engines/some-engine/curations/cur-6155e69c7a2f2e4f756303fd'
|
||||
);
|
||||
});
|
||||
|
||||
itHandlesErrors(http.put, () => {
|
||||
CurationSuggestionLogic.actions.acceptAndAutomateSuggestion();
|
||||
});
|
||||
|
||||
itHandlesInlineErrors(() => {
|
||||
CurationSuggestionLogic.actions.acceptAndAutomateSuggestion();
|
||||
});
|
||||
});
|
||||
|
||||
describe('rejectSuggestion', () => {
|
||||
it('will make an http call to apply the suggestion, and then navigate back the curations page', async () => {
|
||||
http.put.mockReturnValueOnce(
|
||||
Promise.resolve({
|
||||
results: [
|
||||
{
|
||||
...suggestion,
|
||||
status: 'rejected',
|
||||
curation_id: 'cur-6155e69c7a2f2e4f756303fd',
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
mountLogic({
|
||||
suggestion,
|
||||
});
|
||||
|
||||
CurationSuggestionLogic.actions.rejectSuggestion();
|
||||
await nextTick();
|
||||
|
||||
expect(http.put).toHaveBeenCalledWith(
|
||||
'/internal/app_search/engines/some-engine/search_relevance_suggestions',
|
||||
{
|
||||
body: JSON.stringify([
|
||||
{
|
||||
query: 'foo',
|
||||
type: 'curation',
|
||||
status: 'rejected',
|
||||
},
|
||||
]),
|
||||
}
|
||||
);
|
||||
|
||||
expect(navigateToUrl).toHaveBeenCalledWith('/engines/some-engine/curations');
|
||||
});
|
||||
|
||||
itHandlesErrors(http.put, () => {
|
||||
CurationSuggestionLogic.actions.rejectSuggestion();
|
||||
});
|
||||
|
||||
itHandlesInlineErrors(() => {
|
||||
CurationSuggestionLogic.actions.rejectSuggestion();
|
||||
});
|
||||
});
|
||||
|
||||
describe('rejectAndDisableSuggestion', () => {
|
||||
it('will make an http call to apply the suggestion, and then navigate back the curations page', async () => {
|
||||
http.put.mockReturnValueOnce(
|
||||
Promise.resolve({
|
||||
results: [
|
||||
{
|
||||
...suggestion,
|
||||
status: 'disabled',
|
||||
curation_id: 'cur-6155e69c7a2f2e4f756303fd',
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
mountLogic({
|
||||
suggestion,
|
||||
});
|
||||
|
||||
CurationSuggestionLogic.actions.rejectAndDisableSuggestion();
|
||||
await nextTick();
|
||||
|
||||
expect(http.put).toHaveBeenCalledWith(
|
||||
'/internal/app_search/engines/some-engine/search_relevance_suggestions',
|
||||
{
|
||||
body: JSON.stringify([
|
||||
{
|
||||
query: 'foo',
|
||||
type: 'curation',
|
||||
status: 'disabled',
|
||||
},
|
||||
]),
|
||||
}
|
||||
);
|
||||
|
||||
expect(navigateToUrl).toHaveBeenCalledWith('/engines/some-engine/curations');
|
||||
});
|
||||
|
||||
itHandlesErrors(http.put, () => {
|
||||
CurationSuggestionLogic.actions.rejectAndDisableSuggestion();
|
||||
});
|
||||
|
||||
itHandlesInlineErrors(() => {
|
||||
CurationSuggestionLogic.actions.rejectAndDisableSuggestion();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,12 +8,23 @@
|
|||
import { kea, MakeLogicType } from 'kea';
|
||||
import { HttpSetup } from 'kibana/public';
|
||||
|
||||
import { flashAPIErrors } from '../../../../../shared/flash_messages';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import {
|
||||
flashAPIErrors,
|
||||
setQueuedErrorMessage,
|
||||
setQueuedSuccessMessage,
|
||||
} from '../../../../../shared/flash_messages';
|
||||
import { HttpLogic } from '../../../../../shared/http';
|
||||
import { EngineLogic } from '../../../engine';
|
||||
import { KibanaLogic } from '../../../../../shared/kibana';
|
||||
import { ENGINE_CURATIONS_PATH, ENGINE_CURATION_PATH } from '../../../../routes';
|
||||
import { EngineLogic, generateEnginePath } from '../../../engine';
|
||||
import { Result } from '../../../result/types';
|
||||
import { Curation, CurationSuggestion } from '../../types';
|
||||
|
||||
interface Error {
|
||||
error: string;
|
||||
}
|
||||
interface CurationSuggestionValues {
|
||||
dataLoading: boolean;
|
||||
suggestion: CurationSuggestion | null;
|
||||
|
@ -36,6 +47,10 @@ interface CurationSuggestionActions {
|
|||
suggestedPromotedDocuments: Result[];
|
||||
curation: Curation;
|
||||
};
|
||||
acceptSuggestion(): void;
|
||||
acceptAndAutomateSuggestion(): void;
|
||||
rejectSuggestion(): void;
|
||||
rejectAndDisableSuggestion(): void;
|
||||
}
|
||||
|
||||
interface CurationSuggestionProps {
|
||||
|
@ -53,6 +68,10 @@ export const CurationSuggestionLogic = kea<
|
|||
suggestedPromotedDocuments,
|
||||
curation,
|
||||
}),
|
||||
acceptSuggestion: true,
|
||||
acceptAndAutomateSuggestion: true,
|
||||
rejectSuggestion: true,
|
||||
rejectAndDisableSuggestion: true,
|
||||
}),
|
||||
reducers: () => ({
|
||||
dataLoading: [
|
||||
|
@ -81,13 +100,14 @@ export const CurationSuggestionLogic = kea<
|
|||
},
|
||||
],
|
||||
}),
|
||||
listeners: ({ actions, props }) => ({
|
||||
listeners: ({ actions, values, props }) => ({
|
||||
loadSuggestion: async () => {
|
||||
const { http } = HttpLogic.values;
|
||||
const { engineName } = EngineLogic.values;
|
||||
|
||||
try {
|
||||
const suggestion = await getSuggestions(http, engineName, props.query);
|
||||
const suggestion = await getSuggestion(http, engineName, props.query);
|
||||
if (!suggestion) return;
|
||||
const promotedIds: string[] = suggestion.promoted;
|
||||
const documentDetailsResopnse = getDocumentDetails(http, engineName, promotedIds);
|
||||
|
||||
|
@ -116,14 +136,144 @@ export const CurationSuggestionLogic = kea<
|
|||
flashAPIErrors(e);
|
||||
}
|
||||
},
|
||||
acceptSuggestion: async () => {
|
||||
const { http } = HttpLogic.values;
|
||||
const { engineName } = EngineLogic.values;
|
||||
const { suggestion } = values;
|
||||
|
||||
try {
|
||||
const updatedSuggestion = await updateSuggestion(
|
||||
http,
|
||||
engineName,
|
||||
suggestion!.query,
|
||||
'applied'
|
||||
);
|
||||
|
||||
setQueuedSuccessMessage(
|
||||
i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.successfullyAppliedMessage',
|
||||
{ defaultMessage: 'Suggestion was succefully applied.' }
|
||||
)
|
||||
);
|
||||
KibanaLogic.values.navigateToUrl(
|
||||
generateEnginePath(ENGINE_CURATION_PATH, {
|
||||
curationId: updatedSuggestion.curation_id,
|
||||
})
|
||||
);
|
||||
} catch (e) {
|
||||
flashAPIErrors(e);
|
||||
}
|
||||
},
|
||||
acceptAndAutomateSuggestion: async () => {
|
||||
const { http } = HttpLogic.values;
|
||||
const { engineName } = EngineLogic.values;
|
||||
const { suggestion } = values;
|
||||
|
||||
try {
|
||||
const updatedSuggestion = await updateSuggestion(
|
||||
http,
|
||||
engineName,
|
||||
suggestion!.query,
|
||||
'automated'
|
||||
);
|
||||
|
||||
setQueuedSuccessMessage(
|
||||
i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.successfullyAutomatedMessage',
|
||||
{
|
||||
defaultMessage:
|
||||
'Suggestion was succefully applied and all future suggestions for the query "{query}" will be automatically applied.',
|
||||
values: { query: suggestion!.query },
|
||||
}
|
||||
)
|
||||
);
|
||||
KibanaLogic.values.navigateToUrl(
|
||||
generateEnginePath(ENGINE_CURATION_PATH, {
|
||||
curationId: updatedSuggestion.curation_id,
|
||||
})
|
||||
);
|
||||
} catch (e) {
|
||||
flashAPIErrors(e);
|
||||
}
|
||||
},
|
||||
rejectSuggestion: async () => {
|
||||
const { http } = HttpLogic.values;
|
||||
const { engineName } = EngineLogic.values;
|
||||
const { suggestion } = values;
|
||||
|
||||
try {
|
||||
await updateSuggestion(http, engineName, suggestion!.query, 'rejected');
|
||||
|
||||
setQueuedSuccessMessage(
|
||||
i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.successfullyRejectedMessage',
|
||||
{
|
||||
defaultMessage: 'Suggestion was succefully rejected.',
|
||||
}
|
||||
)
|
||||
);
|
||||
KibanaLogic.values.navigateToUrl(generateEnginePath(ENGINE_CURATIONS_PATH));
|
||||
} catch (e) {
|
||||
flashAPIErrors(e);
|
||||
}
|
||||
},
|
||||
rejectAndDisableSuggestion: async () => {
|
||||
const { http } = HttpLogic.values;
|
||||
const { engineName } = EngineLogic.values;
|
||||
const { suggestion } = values;
|
||||
|
||||
try {
|
||||
await updateSuggestion(http, engineName, suggestion!.query, 'disabled');
|
||||
|
||||
setQueuedSuccessMessage(
|
||||
i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.successfullyDisabledMessage',
|
||||
{
|
||||
defaultMessage:
|
||||
'Suggestion was succefully rejected and you will no longer receive suggestions for the query "{query}".',
|
||||
values: { query: suggestion!.query },
|
||||
}
|
||||
)
|
||||
);
|
||||
KibanaLogic.values.navigateToUrl(generateEnginePath(ENGINE_CURATIONS_PATH));
|
||||
} catch (e) {
|
||||
flashAPIErrors(e);
|
||||
}
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const getSuggestions = async (
|
||||
const updateSuggestion = async (
|
||||
http: HttpSetup,
|
||||
engineName: string,
|
||||
query: string,
|
||||
status: string
|
||||
) => {
|
||||
const response = await http.put<{ results: Array<CurationSuggestion | Error> }>(
|
||||
`/internal/app_search/engines/${engineName}/search_relevance_suggestions`,
|
||||
{
|
||||
body: JSON.stringify([
|
||||
{
|
||||
query,
|
||||
type: 'curation',
|
||||
status,
|
||||
},
|
||||
]),
|
||||
}
|
||||
);
|
||||
|
||||
if (response.results[0].hasOwnProperty('error')) {
|
||||
throw (response.results[0] as Error).error;
|
||||
}
|
||||
|
||||
return response.results[0] as CurationSuggestion;
|
||||
};
|
||||
|
||||
const getSuggestion = async (
|
||||
http: HttpSetup,
|
||||
engineName: string,
|
||||
query: string
|
||||
): Promise<CurationSuggestion> => {
|
||||
): Promise<CurationSuggestion | undefined> => {
|
||||
const response = await http.post(
|
||||
`/internal/app_search/engines/${engineName}/search_relevance_suggestions/${query}`,
|
||||
{
|
||||
|
@ -140,6 +290,18 @@ const getSuggestions = async (
|
|||
}
|
||||
);
|
||||
|
||||
if (response.results.length < 1) {
|
||||
const message = i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.notFoundError',
|
||||
{
|
||||
defaultMessage: 'Could not find suggestion, it may have already been applied or rejected.',
|
||||
}
|
||||
);
|
||||
setQueuedErrorMessage(message);
|
||||
KibanaLogic.values.navigateToUrl(generateEnginePath(ENGINE_CURATIONS_PATH));
|
||||
return;
|
||||
}
|
||||
|
||||
const suggestion = response.results[0] as CurationSuggestion;
|
||||
return suggestion;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue