Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface CurationSuggestion {
updated_at: string;
promoted: string[];
status: 'pending' | 'applied' | 'automated' | 'rejected' | 'disabled';
curation_id?: string;
override_curation_id?: string;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export const CurationResultPanel: React.FC<Props> = ({ variant, results }) => {
>
{results.length > 0 ? (
results.map((result) => (
<EuiFlexItem grow={false} key={result.id.raw}>
<EuiFlexItem key={result.id.raw} style={{ width: '100%' }}>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather this was in a SCSS file but 🤷

<Result
result={result}
isMetaEngine={isMetaEngine}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ describe('CurationSuggestion', () => {
},
},
],
curation: {
promoted: [
{
id: '4',
foo: 'foo',
},
],
},
isMetaEngine: true,
engine: {
schema: {},
Expand Down Expand Up @@ -88,6 +96,27 @@ describe('CurationSuggestion', () => {
expect(actions.loadSuggestion).toHaveBeenCalled();
});

it('shows existing promoted documents', () => {
const wrapper = shallow(<CurationSuggestion />);
const suggestedResultsPanel = wrapper.find(CurationResultPanel).at(0);
// gets populated from 'curation' in state, and converted to results format (i.e, has raw properties, etc.)
expect(suggestedResultsPanel.prop('results')).toEqual([
{
id: {
raw: '4',
snippet: null,
},
foo: {
raw: 'foo',
snippet: null,
},
_meta: {
id: '4',
},
},
]);
});

it('shows suggested promoted documents', () => {
const wrapper = shallow(<CurationSuggestion />);
const suggestedResultsPanel = wrapper.find(CurationResultPanel).at(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { EngineLogic } from '../../../engine';
import { AppSearchPageTemplate } from '../../../layout';
import { Result } from '../../../result';
import { Result as ResultType } from '../../../result/types';
import { convertToResultFormat } from '../../curation/results';
import { getCurationsBreadcrumbs } from '../../utils';

import { CurationActionBar } from './curation_action_bar';
Expand All @@ -35,14 +36,15 @@ import { DATA } from './temp_data';

export const CurationSuggestion: React.FC = () => {
const { query } = useDecodedParams();
const { engine, isMetaEngine } = useValues(EngineLogic);
const curationSuggestionLogic = CurationSuggestionLogic({ query });
const { loadSuggestion } = useActions(curationSuggestionLogic);
const { engine, isMetaEngine } = useValues(EngineLogic);
const { suggestion, suggestedPromotedDocuments, dataLoading } =
const { suggestion, suggestedPromotedDocuments, curation, dataLoading } =
useValues(curationSuggestionLogic);
const [showOrganicResults, setShowOrganicResults] = useState(false);
const currentOrganicResults = [...DATA].splice(5, 4);
const proposedOrganicResults = [...DATA].splice(2, 4);
const existingCurationResults = curation ? curation.promoted.map(convertToResultFormat) : [];

const suggestionQuery = suggestion?.query || '';

Expand Down Expand Up @@ -79,7 +81,7 @@ export const CurationSuggestion: React.FC = () => {
</h2>
</EuiTitle>
<EuiSpacer size="s" />
<CurationResultPanel variant="current" results={[...DATA].splice(0, 3)} />
<CurationResultPanel variant="current" results={existingCurationResults} />
</EuiFlexItem>
<EuiFlexItem>
<EuiTitle size="xxs">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
mockHttpValues,
} from '../../../../../__mocks__/kea_logic';

import { set } from 'lodash/fp';

import '../../../../__mocks__/engine_logic.mock';

import { nextTick } from '@kbn/test/jest';
Expand All @@ -23,6 +25,7 @@ const DEFAULT_VALUES = {
dataLoading: true,
suggestion: null,
suggestedPromotedDocuments: [],
curation: null,
};

const suggestion: CurationSuggestion = {
Expand All @@ -32,6 +35,19 @@ const suggestion: CurationSuggestion = {
status: 'applied',
};

const curation = {
id: 'cur-6155e69c7a2f2e4f756303fd',
queries: ['foo'],
promoted: [
{
id: '5',
},
],
hidden: [],
last_updated: 'September 30, 2021 at 04:32PM',
organic: [],
};

const suggestedPromotedDocuments = [
{
id: {
Expand Down Expand Up @@ -117,16 +133,18 @@ describe('CurationSuggestionLogic', () => {

describe('actions', () => {
describe('onSuggestionLoaded', () => {
it('should save the loaded suggestion and promoted documents associated with that suggestion and set dataLoading to false', () => {
it('should save provided state and set dataLoading to false', () => {
mountLogic();
CurationSuggestionLogic.actions.onSuggestionLoaded({
suggestion,
suggestedPromotedDocuments,
curation,
});
expect(CurationSuggestionLogic.values).toEqual({
...DEFAULT_VALUES,
suggestion,
suggestedPromotedDocuments,
curation,
dataLoading: false,
});
});
Expand All @@ -146,7 +164,7 @@ describe('CurationSuggestionLogic', () => {
});
});

it('should make an API call and trigger onSuggestionLoaded', async () => {
it('should make API calls to fetch data and trigger onSuggestionLoaded', async () => {
http.post.mockReturnValueOnce(Promise.resolve(MOCK_RESPONSE));
http.post.mockReturnValueOnce(Promise.resolve(MOCK_DOCUMENTS_RESPONSE));
mountLogic();
Expand Down Expand Up @@ -213,6 +231,36 @@ describe('CurationSuggestionLogic', () => {
},
},
],
curation: null,
});
});

it('will also fetch curation details if the suggestion has a curation_id', async () => {
http.post.mockReturnValueOnce(
Promise.resolve(
set('results[0].curation_id', 'cur-6155e69c7a2f2e4f756303fd', MOCK_RESPONSE)
)
);
http.post.mockReturnValueOnce(Promise.resolve(MOCK_DOCUMENTS_RESPONSE));
http.get.mockReturnValueOnce(Promise.resolve(curation));
mountLogic({
suggestion: set('curation_id', 'cur-6155e69c7a2f2e4f756303fd', suggestion),
});
jest.spyOn(CurationSuggestionLogic.actions, 'onSuggestionLoaded');

CurationSuggestionLogic.actions.loadSuggestion();
await nextTick();

expect(http.get).toHaveBeenCalledWith(
'/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),
suggestedPromotedDocuments: expect.any(Object),
curation,
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,35 @@
*/

import { kea, MakeLogicType } from 'kea';
import { HttpSetup } from 'kibana/public';

import { flashAPIErrors } from '../../../../../shared/flash_messages';
import { HttpLogic } from '../../../../../shared/http';
import { EngineLogic } from '../../../engine';
import { Result } from '../../../result/types';
import { CurationSuggestion } from '../../types';
import { Curation, CurationSuggestion } from '../../types';

interface CurationSuggestionValues {
dataLoading: boolean;
suggestion: CurationSuggestion | null;
suggestedPromotedDocuments: Result[];
curation: Curation | null;
}

interface CurationSuggestionActions {
loadSuggestion(): void;
onSuggestionLoaded({
suggestion,
suggestedPromotedDocuments,
curation,
}: {
suggestion: CurationSuggestion;
suggestedPromotedDocuments: Result[];
curation: Curation;
}): {
suggestion: CurationSuggestion;
suggestedPromotedDocuments: Result[];
curation: Curation;
};
}

Expand All @@ -43,9 +48,10 @@ export const CurationSuggestionLogic = kea<
path: ['enterprise_search', 'app_search', 'curations', 'suggestion_logic'],
actions: () => ({
loadSuggestion: true,
onSuggestionLoaded: ({ suggestion, suggestedPromotedDocuments }) => ({
onSuggestionLoaded: ({ suggestion, suggestedPromotedDocuments, curation }) => ({
suggestion,
suggestedPromotedDocuments,
curation,
}),
}),
reducers: () => ({
Expand All @@ -68,64 +74,92 @@ export const CurationSuggestionLogic = kea<
onSuggestionLoaded: (_, { suggestedPromotedDocuments }) => suggestedPromotedDocuments,
},
],
curation: [
null,
{
onSuggestionLoaded: (_, { curation }) => curation,
},
],
}),
listeners: ({ actions, props }) => ({
loadSuggestion: async () => {
const { http } = HttpLogic.values;
const { engineName } = EngineLogic.values;

try {
const response = await http.post(
`/internal/app_search/engines/${engineName}/search_relevance_suggestions/${props.query}`,
{
body: JSON.stringify({
page: {
current: 1,
size: 1,
},
filters: {
status: ['pending'],
type: 'curation',
},
}),
}
);
const suggestion = await getSuggestions(http, engineName, props.query);
const promotedIds: string[] = suggestion.promoted;
const documentDetailsResopnse = getDocumentDetails(http, engineName, promotedIds);

const suggestion = response.results[0];
let promises = [documentDetailsResopnse];
if (suggestion.curation_id) {
promises = [...promises, getCuration(http, engineName, suggestion.curation_id)];
}

const searchResponse = await http.post(
`/internal/app_search/engines/${engineName}/search`,
{
query: { query: '' },
body: JSON.stringify({
page: {
size: 100,
},
filters: {
id: suggestion.promoted,
},
}),
}
);
const [documentDetails, curation] = await Promise.all(promises);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great solution!


// Filter out docs that were not found and maintain promoted order
const promotedIds: string[] = suggestion.promoted;
const documentDetails = searchResponse.results;
const suggestedPromotedDocuments = promotedIds.reduce((acc: Result[], id: string) => {
const found = documentDetails.find(
const found = documentDetails.results.find(
(documentDetail: Result) => documentDetail.id.raw === id
);
if (!found) return acc;
return [...acc, found];
}, []);

actions.onSuggestionLoaded({
suggestion: suggestion as CurationSuggestion,
suggestion,
suggestedPromotedDocuments,
curation: curation || null,
});
} catch (e) {
flashAPIErrors(e);
}
},
}),
});

const getSuggestions = async (
http: HttpSetup,
engineName: string,
query: string
): Promise<CurationSuggestion> => {
const response = await http.post(
`/internal/app_search/engines/${engineName}/search_relevance_suggestions/${query}`,
{
body: JSON.stringify({
page: {
current: 1,
size: 1,
},
filters: {
status: ['pending'],
type: 'curation',
},
}),
}
);

const suggestion = response.results[0] as CurationSuggestion;
return suggestion;
};

const getDocumentDetails = async (http: HttpSetup, engineName: string, documentIds: string[]) => {
return http.post(`/internal/app_search/engines/${engineName}/search`, {
query: { query: '' },
body: JSON.stringify({
page: {
size: 100,
},
filters: {
id: documentIds,
},
}),
});
};

const getCuration = async (http: HttpSetup, engineName: string, curationId: string) => {
return http.get(`/internal/app_search/engines/${engineName}/curations/${curationId}`, {
query: { skip_record_analytics: 'true' },
});
};
Comment on lines +147 to +165
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth testing these?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah, there's no logic to them, I just pulled them out for readability.