[App Search] Remaining Result Settings logic and routes (#94947)

This commit is contained in:
Jason Stoltzfus 2021-03-22 15:58:32 -04:00 committed by GitHub
parent b4ff1f65dc
commit 19aeb99dc3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 862 additions and 14 deletions

View file

@ -9,6 +9,8 @@ import { i18n } from '@kbn/i18n';
import { FieldResultSetting } from './types';
export const DEFAULT_SNIPPET_SIZE = 100;
export const RESULT_SETTINGS_TITLE = i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.resultSettings.title',
{ defaultMessage: 'Result Settings' }

View file

@ -5,7 +5,13 @@
* 2.0.
*/
import { LogicMounter } from '../../../__mocks__';
import { LogicMounter, mockFlashMessageHelpers, mockHttpValues } from '../../../__mocks__';
import { mockEngineValues } from '../../__mocks__';
import { omit } from 'lodash';
import { nextTick } from '@kbn/test/jest';
import { Schema, SchemaConflicts, SchemaTypes } from '../../../shared/types';
@ -29,13 +35,27 @@ describe('ResultSettingsLogic', () => {
schemaConflicts: {},
};
const SELECTORS = {
reducedServerResultFields: {},
resultFieldsAtDefaultSettings: true,
resultFieldsEmpty: true,
stagedUpdates: false,
};
// Values without selectors
const resultSettingLogicValues = () => omit(ResultSettingsLogic.values, Object.keys(SELECTORS));
beforeEach(() => {
jest.clearAllMocks();
mockEngineValues.engineName = 'test-engine';
});
it('has expected default values', () => {
mount();
expect(ResultSettingsLogic.values).toEqual(DEFAULT_VALUES);
expect(ResultSettingsLogic.values).toEqual({
...DEFAULT_VALUES,
...SELECTORS,
});
});
describe('actions', () => {
@ -71,7 +91,7 @@ describe('ResultSettingsLogic', () => {
schemaConflicts
);
expect(ResultSettingsLogic.values).toEqual({
expect(resultSettingLogicValues()).toEqual({
...DEFAULT_VALUES,
dataLoading: false,
saving: false,
@ -173,7 +193,7 @@ describe('ResultSettingsLogic', () => {
ResultSettingsLogic.actions.openConfirmSaveModal();
expect(ResultSettingsLogic.values).toEqual({
expect(resultSettingLogicValues()).toEqual({
...DEFAULT_VALUES,
openModal: OpenModal.ConfirmSaveModal,
});
@ -186,7 +206,7 @@ describe('ResultSettingsLogic', () => {
ResultSettingsLogic.actions.openConfirmResetModal();
expect(ResultSettingsLogic.values).toEqual({
expect(resultSettingLogicValues()).toEqual({
...DEFAULT_VALUES,
openModal: OpenModal.ConfirmResetModal,
});
@ -200,7 +220,7 @@ describe('ResultSettingsLogic', () => {
ResultSettingsLogic.actions.closeModals();
expect(ResultSettingsLogic.values).toEqual({
expect(resultSettingLogicValues()).toEqual({
...DEFAULT_VALUES,
openModal: OpenModal.None,
});
@ -230,7 +250,7 @@ describe('ResultSettingsLogic', () => {
ResultSettingsLogic.actions.clearAllFields();
expect(ResultSettingsLogic.values).toEqual({
expect(resultSettingLogicValues()).toEqual({
...DEFAULT_VALUES,
nonTextResultFields: {
foo: {},
@ -275,7 +295,7 @@ describe('ResultSettingsLogic', () => {
ResultSettingsLogic.actions.resetAllFields();
expect(ResultSettingsLogic.values).toEqual({
expect(resultSettingLogicValues()).toEqual({
...DEFAULT_VALUES,
nonTextResultFields: {
bar: { raw: true, snippet: false, snippetFallback: false },
@ -303,7 +323,7 @@ describe('ResultSettingsLogic', () => {
ResultSettingsLogic.actions.resetAllFields();
expect(ResultSettingsLogic.values).toEqual({
expect(resultSettingLogicValues()).toEqual({
...DEFAULT_VALUES,
openModal: OpenModal.None,
});
@ -339,7 +359,7 @@ describe('ResultSettingsLogic', () => {
snippetFallback: false,
});
expect(ResultSettingsLogic.values).toEqual({
expect(resultSettingLogicValues()).toEqual({
...DEFAULT_VALUES,
// the settings for foo are updated below for any *ResultFields state in which they appear
nonTextResultFields: {
@ -372,7 +392,7 @@ describe('ResultSettingsLogic', () => {
});
// 'baz' does not exist in state, so nothing is updated
expect(ResultSettingsLogic.values).toEqual({
expect(resultSettingLogicValues()).toEqual({
...DEFAULT_VALUES,
...initialValues,
});
@ -387,7 +407,7 @@ describe('ResultSettingsLogic', () => {
ResultSettingsLogic.actions.saving();
expect(ResultSettingsLogic.values).toEqual({
expect(resultSettingLogicValues()).toEqual({
...DEFAULT_VALUES,
saving: true,
openModal: OpenModal.None,
@ -395,4 +415,453 @@ describe('ResultSettingsLogic', () => {
});
});
});
describe('selectors', () => {
describe('resultFieldsAtDefaultSettings', () => {
it('should return true if all fields are at their default settings', () => {
mount({
resultFields: {
foo: { raw: true, snippet: false, snippetFallback: false },
bar: { raw: true, snippet: false, snippetFallback: false },
},
});
expect(ResultSettingsLogic.values.resultFieldsAtDefaultSettings).toEqual(true);
});
it('should return false otherwise', () => {
mount({
resultFields: {
foo: { raw: true, snippet: false, snippetFallback: false },
bar: { raw: true, snippet: true, snippetFallback: false },
},
});
expect(ResultSettingsLogic.values.resultFieldsAtDefaultSettings).toEqual(false);
});
});
describe('resultFieldsEmpty', () => {
it('should return true if all fields are empty', () => {
mount({
resultFields: {
foo: {},
bar: {},
},
});
expect(ResultSettingsLogic.values.resultFieldsEmpty).toEqual(true);
});
it('should return false otherwise', () => {
mount({
resultFields: {
foo: {},
bar: { raw: true, snippet: true, snippetFallback: false },
},
});
expect(ResultSettingsLogic.values.resultFieldsEmpty).toEqual(false);
});
});
describe('stagedUpdates', () => {
it('should return true if changes have been made since the last save', () => {
mount({
lastSavedResultFields: {
foo: {},
bar: { raw: true, snippet: true, snippetFallback: false },
},
resultFields: {
foo: { raw: false, snippet: true, snippetFallback: true },
bar: { raw: true, snippet: true, snippetFallback: false },
},
});
// resultFields is different than lastSavedResultsFields, which happens if changes
// have been made since the last save, which is represented by lastSavedResultFields
expect(ResultSettingsLogic.values.stagedUpdates).toEqual(true);
});
it('should return false otherwise', () => {
mount({
lastSavedResultFields: {
foo: { raw: false, snippet: true, snippetFallback: true },
bar: { raw: true, snippet: true, snippetFallback: false },
},
resultFields: {
foo: { raw: false, snippet: true, snippetFallback: true },
bar: { raw: true, snippet: true, snippetFallback: false },
},
});
expect(ResultSettingsLogic.values.stagedUpdates).toEqual(false);
});
});
describe('reducedServerResultFields', () => {
it('filters out fields that do not have any settings', () => {
mount({
serverResultFields: {
foo: { raw: { size: 5 } },
bar: {},
},
});
expect(ResultSettingsLogic.values.reducedServerResultFields).toEqual({
// bar was filtered out because it has neither raw nor snippet data set
foo: { raw: { size: 5 } },
});
});
});
});
describe('listeners', () => {
const { http } = mockHttpValues;
const { flashAPIErrors } = mockFlashMessageHelpers;
const serverFieldResultSettings = {
foo: {
raw: {},
},
bar: {
raw: {},
},
};
const schema = {
foo: 'text',
bar: 'number',
};
const schemaConflicts = {
baz: {
text: ['test'],
number: ['test2'],
},
};
describe('clearRawSizeForField', () => {
it('should remove the raw size set on a field', () => {
mount({
resultFields: {
foo: { raw: true, rawSize: 5, snippet: false },
bar: { raw: true, rawSize: 5, snippet: false },
},
});
jest.spyOn(ResultSettingsLogic.actions, 'updateField');
ResultSettingsLogic.actions.clearRawSizeForField('foo');
expect(ResultSettingsLogic.actions.updateField).toHaveBeenCalledWith('foo', {
raw: true,
snippet: false,
});
});
});
describe('clearSnippetSizeForField', () => {
it('should remove the snippet size set on a field', () => {
mount({
resultFields: {
foo: { raw: false, snippet: true, snippetSize: 5 },
bar: { raw: true, rawSize: 5, snippet: false },
},
});
jest.spyOn(ResultSettingsLogic.actions, 'updateField');
ResultSettingsLogic.actions.clearSnippetSizeForField('foo');
expect(ResultSettingsLogic.actions.updateField).toHaveBeenCalledWith('foo', {
raw: false,
snippet: true,
});
});
});
describe('toggleRawForField', () => {
it('should toggle the raw value on for a field', () => {
mount({
resultFields: {
foo: { raw: false, snippet: true, snippetSize: 5 },
bar: { raw: false, snippet: false },
},
});
jest.spyOn(ResultSettingsLogic.actions, 'updateField');
ResultSettingsLogic.actions.toggleRawForField('bar');
expect(ResultSettingsLogic.actions.updateField).toHaveBeenCalledWith('bar', {
raw: true,
snippet: false,
});
});
it('should maintain rawSize if it was set prior', () => {
mount({
resultFields: {
foo: { raw: false, snippet: true, snippetSize: 5 },
bar: { raw: false, rawSize: 10, snippet: false },
},
});
jest.spyOn(ResultSettingsLogic.actions, 'updateField');
ResultSettingsLogic.actions.toggleRawForField('bar');
expect(ResultSettingsLogic.actions.updateField).toHaveBeenCalledWith('bar', {
raw: true,
rawSize: 10,
snippet: false,
});
});
it('should remove rawSize value when toggling off', () => {
mount({
resultFields: {
foo: { raw: false, snippet: true, snippetSize: 5 },
bar: { raw: true, rawSize: 5, snippet: false },
},
});
jest.spyOn(ResultSettingsLogic.actions, 'updateField');
ResultSettingsLogic.actions.toggleRawForField('bar');
expect(ResultSettingsLogic.actions.updateField).toHaveBeenCalledWith('bar', {
raw: false,
snippet: false,
});
});
it('should still work if the object is empty', () => {
mount({
resultFields: {
foo: { raw: false, snippet: true, snippetSize: 5 },
bar: {},
},
});
jest.spyOn(ResultSettingsLogic.actions, 'updateField');
ResultSettingsLogic.actions.toggleRawForField('bar');
expect(ResultSettingsLogic.actions.updateField).toHaveBeenCalledWith('bar', {
raw: true,
});
});
});
describe('toggleSnippetForField', () => {
it('should toggle the raw value on for a field, always setting the snippet size to 100', () => {
mount({
resultFields: {
foo: { raw: false, snippet: true, snippetSize: 5 },
bar: { raw: false, snippet: false },
},
});
jest.spyOn(ResultSettingsLogic.actions, 'updateField');
ResultSettingsLogic.actions.toggleSnippetForField('bar');
expect(ResultSettingsLogic.actions.updateField).toHaveBeenCalledWith('bar', {
raw: false,
snippet: true,
snippetSize: 100,
});
});
it('should remove rawSize value when toggling off', () => {
mount({
resultFields: {
foo: { raw: false, snippet: true, snippetSize: 5 },
bar: { raw: false, snippet: true, snippetSize: 5 },
},
});
jest.spyOn(ResultSettingsLogic.actions, 'updateField');
ResultSettingsLogic.actions.toggleSnippetForField('bar');
expect(ResultSettingsLogic.actions.updateField).toHaveBeenCalledWith('bar', {
raw: false,
snippet: false,
});
});
it('should still work if the object is empty', () => {
mount({
resultFields: {
foo: { raw: false, snippet: true, snippetSize: 5 },
bar: {},
},
});
jest.spyOn(ResultSettingsLogic.actions, 'updateField');
ResultSettingsLogic.actions.toggleSnippetForField('bar');
expect(ResultSettingsLogic.actions.updateField).toHaveBeenCalledWith('bar', {
snippet: true,
snippetSize: 100,
});
});
});
describe('toggleSnippetFallbackForField', () => {
it('should toggle the snippetFallback value for a field', () => {
mount({
resultFields: {
foo: { raw: false, snippet: true, snippetSize: 5, snippetFallback: true },
bar: { raw: false, snippet: false },
},
});
jest.spyOn(ResultSettingsLogic.actions, 'updateField');
ResultSettingsLogic.actions.toggleSnippetFallbackForField('foo');
expect(ResultSettingsLogic.actions.updateField).toHaveBeenCalledWith('foo', {
raw: false,
snippet: true,
snippetSize: 5,
snippetFallback: false,
});
});
});
describe('updateRawSizeForField', () => {
it('should update the rawSize value for a field', () => {
mount({
resultFields: {
foo: { raw: false, snippet: true, snippetSize: 5, snippetFallback: true },
bar: { raw: true, rawSize: 5, snippet: false },
},
});
jest.spyOn(ResultSettingsLogic.actions, 'updateField');
ResultSettingsLogic.actions.updateRawSizeForField('bar', 7);
expect(ResultSettingsLogic.actions.updateField).toHaveBeenCalledWith('bar', {
raw: true,
rawSize: 7,
snippet: false,
});
});
});
describe('updateSnippetSizeForField', () => {
it('should update the snippetSize value for a field', () => {
mount({
resultFields: {
foo: { raw: false, snippet: true, snippetSize: 5, snippetFallback: true },
bar: { raw: true, rawSize: 5, snippet: false },
},
});
jest.spyOn(ResultSettingsLogic.actions, 'updateField');
ResultSettingsLogic.actions.updateSnippetSizeForField('foo', 7);
expect(ResultSettingsLogic.actions.updateField).toHaveBeenCalledWith('foo', {
raw: false,
snippet: true,
snippetSize: 7,
snippetFallback: true,
});
});
});
describe('initializeResultSettingsData', () => {
it('should remove the snippet size set on a field', () => {
mount({
resultFields: {
foo: { raw: false, snippet: true, snippetSize: 5 },
bar: { raw: true, rawSize: 5, snippet: false },
},
});
jest.spyOn(ResultSettingsLogic.actions, 'updateField');
ResultSettingsLogic.actions.clearSnippetSizeForField('foo');
expect(ResultSettingsLogic.actions.updateField).toHaveBeenCalledWith('foo', {
raw: false,
snippet: true,
});
});
});
describe('initializeResultFields', () => {
it('should make an API call and set state based on the response', async () => {
mount();
http.get.mockReturnValueOnce(
Promise.resolve({
searchSettings: {
result_fields: serverFieldResultSettings,
},
schema,
schemaConflicts,
})
);
jest.spyOn(ResultSettingsLogic.actions, 'initializeResultFields');
ResultSettingsLogic.actions.initializeResultSettingsData();
await nextTick();
expect(http.get).toHaveBeenCalledWith(
'/api/app_search/engines/test-engine/result_settings/details'
);
expect(ResultSettingsLogic.actions.initializeResultFields).toHaveBeenCalledWith(
serverFieldResultSettings,
schema,
schemaConflicts
);
});
it('handles errors', async () => {
mount();
http.get.mockReturnValueOnce(Promise.reject('error'));
ResultSettingsLogic.actions.initializeResultSettingsData();
await nextTick();
expect(flashAPIErrors).toHaveBeenCalledWith('error');
});
});
describe('saveResultSettings', () => {
it('should make an API call to update result settings and update state accordingly', async () => {
mount({
schema,
});
http.put.mockReturnValueOnce(
Promise.resolve({
result_fields: serverFieldResultSettings,
})
);
jest.spyOn(ResultSettingsLogic.actions, 'saving');
jest.spyOn(ResultSettingsLogic.actions, 'initializeResultFields');
ResultSettingsLogic.actions.saveResultSettings(serverFieldResultSettings);
expect(ResultSettingsLogic.actions.saving).toHaveBeenCalled();
await nextTick();
expect(http.put).toHaveBeenCalledWith(
'/api/app_search/engines/test-engine/result_settings',
{
body: JSON.stringify({
result_fields: serverFieldResultSettings,
}),
}
);
expect(ResultSettingsLogic.actions.initializeResultFields).toHaveBeenCalledWith(
serverFieldResultSettings,
schema
);
});
it('handles errors', async () => {
mount();
http.put.mockReturnValueOnce(Promise.reject('error'));
ResultSettingsLogic.actions.saveResultSettings(serverFieldResultSettings);
await nextTick();
expect(flashAPIErrors).toHaveBeenCalledWith('error');
});
});
});
});

View file

@ -6,9 +6,16 @@
*/
import { kea, MakeLogicType } from 'kea';
import { omit, isEqual } from 'lodash';
import { i18n } from '@kbn/i18n';
import { flashAPIErrors, setSuccessMessage } from '../../../shared/flash_messages';
import { HttpLogic } from '../../../shared/http';
import { Schema, SchemaConflicts } from '../../../shared/types';
import { EngineLogic } from '../engine';
import { DEFAULT_SNIPPET_SIZE } from './constants';
import {
FieldResultSetting,
FieldResultSettingObject,
@ -17,6 +24,8 @@ import {
} from './types';
import {
areFieldsAtDefaultSettings,
areFieldsEmpty,
clearAllFields,
clearAllServerFields,
convertServerResultFieldsToResultFields,
@ -46,9 +55,21 @@ interface ResultSettingsActions {
resetAllFields(): void;
updateField(
fieldName: string,
settings: FieldResultSetting
settings: FieldResultSetting | {}
): { fieldName: string; settings: FieldResultSetting };
saving(): void;
// Listeners
clearRawSizeForField(fieldName: string): { fieldName: string };
clearSnippetSizeForField(fieldName: string): { fieldName: string };
toggleRawForField(fieldName: string): { fieldName: string };
toggleSnippetForField(fieldName: string): { fieldName: string };
toggleSnippetFallbackForField(fieldName: string): { fieldName: string };
updateRawSizeForField(fieldName: string, size: number): { fieldName: string; size: number };
updateSnippetSizeForField(fieldName: string, size: number): { fieldName: string; size: number };
initializeResultSettingsData(): void;
saveResultSettings(
resultFields: ServerFieldResultSettingObject
): { resultFields: ServerFieldResultSettingObject };
}
interface ResultSettingsValues {
@ -62,6 +83,11 @@ interface ResultSettingsValues {
lastSavedResultFields: FieldResultSettingObject;
schema: Schema;
schemaConflicts: SchemaConflicts;
// Selectors
resultFieldsAtDefaultSettings: boolean;
resultFieldsEmpty: boolean;
stagedUpdates: true;
reducedServerResultFields: ServerFieldResultSettingObject;
}
export const ResultSettingsLogic = kea<MakeLogicType<ResultSettingsValues, ResultSettingsActions>>({
@ -90,6 +116,15 @@ export const ResultSettingsLogic = kea<MakeLogicType<ResultSettingsValues, Resul
resetAllFields: () => true,
updateField: (fieldName, settings) => ({ fieldName, settings }),
saving: () => true,
clearRawSizeForField: (fieldName) => ({ fieldName }),
clearSnippetSizeForField: (fieldName) => ({ fieldName }),
toggleRawForField: (fieldName) => ({ fieldName }),
toggleSnippetForField: (fieldName) => ({ fieldName }),
toggleSnippetFallbackForField: (fieldName) => ({ fieldName }),
updateRawSizeForField: (fieldName, size) => ({ fieldName, size }),
updateSnippetSizeForField: (fieldName, size) => ({ fieldName, size }),
initializeResultSettingsData: () => true,
saveResultSettings: (resultFields) => ({ resultFields }),
}),
reducers: () => ({
dataLoading: [
@ -187,4 +222,122 @@ export const ResultSettingsLogic = kea<MakeLogicType<ResultSettingsValues, Resul
},
],
}),
selectors: ({ selectors }) => ({
resultFieldsAtDefaultSettings: [
() => [selectors.resultFields],
(resultFields) => areFieldsAtDefaultSettings(resultFields),
],
resultFieldsEmpty: [
() => [selectors.resultFields],
(resultFields) => areFieldsEmpty(resultFields),
],
stagedUpdates: [
() => [selectors.lastSavedResultFields, selectors.resultFields],
(lastSavedResultFields, resultFields) => !isEqual(lastSavedResultFields, resultFields),
],
reducedServerResultFields: [
() => [selectors.serverResultFields],
(serverResultFields: ServerFieldResultSettingObject) =>
Object.entries(serverResultFields).reduce(
(acc: ServerFieldResultSettingObject, [fieldName, resultSetting]) => {
if (resultSetting.raw || resultSetting.snippet) {
acc[fieldName] = resultSetting;
}
return acc;
},
{}
),
],
}),
listeners: ({ actions, values }) => ({
clearRawSizeForField: ({ fieldName }) => {
actions.updateField(fieldName, omit(values.resultFields[fieldName], ['rawSize']));
},
clearSnippetSizeForField: ({ fieldName }) => {
actions.updateField(fieldName, omit(values.resultFields[fieldName], ['snippetSize']));
},
toggleRawForField: ({ fieldName }) => {
// We cast this because it could be an empty object, which we can still treat as a FieldResultSetting safely
const field = values.resultFields[fieldName] as FieldResultSetting;
const raw = !field.raw;
actions.updateField(fieldName, {
...omit(field, ['rawSize']),
raw,
...(raw ? { rawSize: field.rawSize } : {}),
});
},
toggleSnippetForField: ({ fieldName }) => {
// We cast this because it could be an empty object, which we can still treat as a FieldResultSetting safely
const field = values.resultFields[fieldName] as FieldResultSetting;
const snippet = !field.snippet;
actions.updateField(fieldName, {
...omit(field, ['snippetSize']),
snippet,
...(snippet ? { snippetSize: DEFAULT_SNIPPET_SIZE } : {}),
});
},
toggleSnippetFallbackForField: ({ fieldName }) => {
// We cast this because it could be an empty object, which we can still treat as a FieldResultSetting safely
const field = values.resultFields[fieldName] as FieldResultSetting;
actions.updateField(fieldName, {
...field,
snippetFallback: !field.snippetFallback,
});
},
updateRawSizeForField: ({ fieldName, size }) => {
actions.updateField(fieldName, { ...values.resultFields[fieldName], rawSize: size });
},
updateSnippetSizeForField: ({ fieldName, size }) => {
actions.updateField(fieldName, { ...values.resultFields[fieldName], snippetSize: size });
},
initializeResultSettingsData: async () => {
const { http } = HttpLogic.values;
const { engineName } = EngineLogic.values;
const url = `/api/app_search/engines/${engineName}/result_settings/details`;
try {
const {
schema,
schemaConflicts,
searchSettings: { result_fields: serverFieldResultSettings },
} = await http.get(url);
actions.initializeResultFields(serverFieldResultSettings, schema, schemaConflicts);
} catch (e) {
flashAPIErrors(e);
}
},
saveResultSettings: async ({ resultFields }) => {
actions.saving();
const { http } = HttpLogic.values;
const { engineName } = EngineLogic.values;
const url = `/api/app_search/engines/${engineName}/result_settings`;
actions.saving();
let response;
try {
response = await http.put(url, {
body: JSON.stringify({
result_fields: resultFields,
}),
});
} catch (e) {
flashAPIErrors(e);
}
actions.initializeResultFields(response.result_fields, values.schema);
setSuccessMessage(
i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.resultSettings.saveSuccessMessage',
{
defaultMessage: 'Result settings have been saved successfully.',
}
)
);
},
}),
});

View file

@ -34,4 +34,4 @@ export interface FieldResultSetting {
snippetFallback: boolean;
}
export type FieldResultSettingObject = Record<string, FieldResultSetting>;
export type FieldResultSettingObject = Record<string, FieldResultSetting | {}>;

View file

@ -8,6 +8,8 @@
import { SchemaTypes } from '../../../shared/types';
import {
areFieldsAtDefaultSettings,
areFieldsEmpty,
convertServerResultFieldsToResultFields,
convertToServerFieldResultSetting,
clearAllServerFields,
@ -172,3 +174,68 @@ describe('splitResultFields', () => {
});
});
});
describe('areFieldsEmpty', () => {
it('should return true if all fields are empty objects', () => {
expect(
areFieldsEmpty({
foo: {},
bar: {},
})
).toBe(true);
});
it('should return false otherwise', () => {
expect(
areFieldsEmpty({
foo: {
raw: true,
rawSize: 5,
snippet: false,
snippetFallback: false,
},
bar: {
raw: true,
rawSize: 5,
snippet: false,
snippetFallback: false,
},
})
).toBe(false);
});
});
describe('areFieldsAtDefaultSettings', () => {
it('will return true if all settings for all fields are at their defaults', () => {
expect(
areFieldsAtDefaultSettings({
foo: {
raw: true,
snippet: false,
snippetFallback: false,
},
bar: {
raw: true,
snippet: false,
snippetFallback: false,
},
})
).toEqual(true);
});
it('will return false otherwise', () => {
expect(
areFieldsAtDefaultSettings({
foo: {
raw: true,
snippet: false,
snippetFallback: false,
},
bar: {
raw: false,
snippet: true,
snippetFallback: true,
},
})
).toEqual(false);
});
});

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { isEqual, isEmpty } from 'lodash';
import { Schema } from '../../../shared/types';
import { DEFAULT_FIELD_SETTINGS, DISABLED_FIELD_SETTINGS } from './constants';
@ -115,3 +117,17 @@ export const splitResultFields = (resultFields: FieldResultSettingObject, schema
return { textResultFields, nonTextResultFields };
};
export const areFieldsEmpty = (fields: FieldResultSettingObject) => {
const anyNonEmptyField = Object.values(fields).find((resultSettings) => {
return !isEmpty(resultSettings);
});
return !anyNonEmptyField;
};
export const areFieldsAtDefaultSettings = (fields: FieldResultSettingObject) => {
const anyNonDefaultSettingsValue = Object.values(fields).find((resultSettings) => {
return !isEqual(resultSettings, DEFAULT_FIELD_SETTINGS);
});
return !anyNonDefaultSettingsValue;
};

View file

@ -12,6 +12,7 @@ import { registerCredentialsRoutes } from './credentials';
import { registerCurationsRoutes } from './curations';
import { registerDocumentsRoutes, registerDocumentRoutes } from './documents';
import { registerEnginesRoutes } from './engines';
import { registerResultSettingsRoutes } from './result_settings';
import { registerRoleMappingsRoutes } from './role_mappings';
import { registerSearchSettingsRoutes } from './search_settings';
import { registerSettingsRoutes } from './settings';
@ -26,4 +27,5 @@ export const registerAppSearchRoutes = (dependencies: RouteDependencies) => {
registerCurationsRoutes(dependencies);
registerSearchSettingsRoutes(dependencies);
registerRoleMappingsRoutes(dependencies);
registerResultSettingsRoutes(dependencies);
};

View file

@ -0,0 +1,91 @@
/*
* 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 { mockDependencies, mockRequestHandler, MockRouter } from '../../__mocks__';
import { registerResultSettingsRoutes } from './result_settings';
const resultFields = {
id: {
raw: {},
},
hp: {
raw: {},
},
name: {
raw: {},
},
};
describe('result settings routes', () => {
describe('GET /api/app_search/engines/{name}/result_settings/details', () => {
const mockRouter = new MockRouter({
method: 'get',
path: '/api/app_search/engines/{engineName}/result_settings/details',
});
beforeEach(() => {
registerResultSettingsRoutes({
...mockDependencies,
router: mockRouter.router,
});
});
it('creates a request to enterprise search', () => {
mockRouter.callRoute({
params: { engineName: 'some-engine' },
});
expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({
path: '/as/engines/:engineName/result_settings/details',
});
});
});
describe('PUT /api/app_search/engines/{name}/result_settings', () => {
const mockRouter = new MockRouter({
method: 'put',
path: '/api/app_search/engines/{engineName}/result_settings',
});
beforeEach(() => {
registerResultSettingsRoutes({
...mockDependencies,
router: mockRouter.router,
});
});
it('creates a request to enterprise search', () => {
mockRouter.callRoute({
params: { engineName: 'some-engine' },
body: {
result_settings: resultFields,
},
});
expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({
path: '/as/engines/:engineName/result_settings',
});
});
describe('validates', () => {
it('correctly', () => {
const request = {
body: {
result_fields: resultFields,
},
};
mockRouter.shouldValidate(request);
});
it('missing required fields', () => {
const request = { body: {} };
mockRouter.shouldThrow(request);
});
});
});
});

View file

@ -0,0 +1,48 @@
/*
* 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 { schema } from '@kbn/config-schema';
import { RouteDependencies } from '../../plugin';
const resultFields = schema.recordOf(schema.string(), schema.object({}, { unknowns: 'allow' }));
export function registerResultSettingsRoutes({
router,
enterpriseSearchRequestHandler,
}: RouteDependencies) {
router.get(
{
path: '/api/app_search/engines/{engineName}/result_settings/details',
validate: {
params: schema.object({
engineName: schema.string(),
}),
},
},
enterpriseSearchRequestHandler.createRequest({
path: '/as/engines/:engineName/result_settings/details',
})
);
router.put(
{
path: '/api/app_search/engines/{engineName}/result_settings',
validate: {
params: schema.object({
engineName: schema.string(),
}),
body: schema.object({
result_fields: resultFields,
}),
},
},
enterpriseSearchRequestHandler.createRequest({
path: '/as/engines/:engineName/result_settings',
})
);
}