[App Search] Synonyms set up (#97187)

* Add barebones Synonyms view

* Update Synonyms route+nav link

* Add server API routes

- will be used in upcoming PRs, may change

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Constance 2021-04-19 10:35:22 -07:00 committed by GitHub
parent 08b5f0db2f
commit 283e2ca798
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 358 additions and 5 deletions

View file

@ -211,8 +211,7 @@ export const EngineNav: React.FC = () => {
)}
{canManageEngineSynonyms && (
<SideNavLink
isExternal
to={getAppSearchUrl(generateEnginePath(ENGINE_SYNONYMS_PATH))}
to={generateEnginePath(ENGINE_SYNONYMS_PATH)}
data-test-subj="EngineSynonymsLink"
>
{SYNONYMS_TITLE}

View file

@ -22,6 +22,7 @@ import { CurationsRouter } from '../curations';
import { EngineOverview } from '../engine_overview';
import { RelevanceTuning } from '../relevance_tuning';
import { ResultSettings } from '../result_settings';
import { Synonyms } from '../synonyms';
import { EngineRouter } from './engine_router';
@ -100,6 +101,13 @@ describe('EngineRouter', () => {
expect(wrapper.find(AnalyticsRouter)).toHaveLength(1);
});
it('renders a synonyms view', () => {
setMockValues({ ...values, myRole: { canManageEngineSynonyms: true } });
const wrapper = shallow(<EngineRouter />);
expect(wrapper.find(Synonyms)).toHaveLength(1);
});
it('renders a curations view', () => {
setMockValues({ ...values, myRole: { canManageEngineCurations: true } });
const wrapper = shallow(<EngineRouter />);

View file

@ -27,7 +27,7 @@ import {
// ENGINE_CRAWLER_PATH,
// META_ENGINE_SOURCE_ENGINES_PATH,
ENGINE_RELEVANCE_TUNING_PATH,
// ENGINE_SYNONYMS_PATH,
ENGINE_SYNONYMS_PATH,
ENGINE_CURATIONS_PATH,
ENGINE_RESULT_SETTINGS_PATH,
// ENGINE_SEARCH_UI_PATH,
@ -39,8 +39,8 @@ import { CurationsRouter } from '../curations';
import { DocumentDetail, Documents } from '../documents';
import { EngineOverview } from '../engine_overview';
import { RelevanceTuning } from '../relevance_tuning';
import { ResultSettings } from '../result_settings';
import { Synonyms } from '../synonyms';
import { EngineLogic, getEngineBreadcrumbs } from './';
@ -53,7 +53,7 @@ export const EngineRouter: React.FC = () => {
// canViewEngineCrawler,
// canViewMetaEngineSourceEngines,
canManageEngineRelevanceTuning,
// canManageEngineSynonyms,
canManageEngineSynonyms,
canManageEngineCurations,
canManageEngineResultSettings,
// canManageEngineSearchUi,
@ -107,6 +107,11 @@ export const EngineRouter: React.FC = () => {
<RelevanceTuning />
</Route>
)}
{canManageEngineSynonyms && (
<Route path={ENGINE_SYNONYMS_PATH}>
<Synonyms />
</Route>
)}
{canManageEngineResultSettings && (
<Route path={ENGINE_RESULT_SETTINGS_PATH}>
<ResultSettings />

View file

@ -6,3 +6,4 @@
*/
export { SYNONYMS_TITLE } from './constants';
export { Synonyms } from './synonyms';

View file

@ -0,0 +1,21 @@
/*
* 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 '../../__mocks__/engine_logic.mock';
import React from 'react';
import { shallow } from 'enzyme';
import { Synonyms } from './';
describe('Synonyms', () => {
it('renders', () => {
shallow(<Synonyms />);
// TODO: Check for Synonym cards, Synonym modal
});
});

View file

@ -0,0 +1,27 @@
/*
* 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 { EuiPageHeader, EuiPageContentBody } from '@elastic/eui';
import { FlashMessages } from '../../../shared/flash_messages';
import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
import { getEngineBreadcrumbs } from '../engine';
import { SYNONYMS_TITLE } from './constants';
export const Synonyms: React.FC = () => {
return (
<>
<SetPageChrome trail={getEngineBreadcrumbs([SYNONYMS_TITLE])} />
<EuiPageHeader pageTitle={SYNONYMS_TITLE} />
<FlashMessages />
<EuiPageContentBody>TODO</EuiPageContentBody>
</>
);
};

View file

@ -0,0 +1,207 @@
/*
* 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 { MockRouter, mockRequestHandler, mockDependencies } from '../../__mocks__';
import { registerSynonymsRoutes } from './synonyms';
describe('synonyms routes', () => {
describe('GET /api/app_search/engines/{engineName}/synonyms', () => {
let mockRouter: MockRouter;
beforeEach(() => {
jest.clearAllMocks();
mockRouter = new MockRouter({
method: 'get',
path: '/api/app_search/engines/{engineName}/synonyms',
});
registerSynonymsRoutes({
...mockDependencies,
router: mockRouter.router,
});
});
it('creates a request handler', () => {
expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({
path: '/as/engines/:engineName/synonyms/collection',
});
});
describe('validates', () => {
it('with pagination query params', () => {
const request = {
query: {
'page[current]': 1,
'page[size]': 10,
},
};
mockRouter.shouldValidate(request);
});
it('missing query params', () => {
const request = { query: {} };
mockRouter.shouldThrow(request);
});
});
});
describe('POST /api/app_search/engines/{engineName}/synonyms', () => {
let mockRouter: MockRouter;
beforeEach(() => {
jest.clearAllMocks();
mockRouter = new MockRouter({
method: 'post',
path: '/api/app_search/engines/{engineName}/synonyms',
});
registerSynonymsRoutes({
...mockDependencies,
router: mockRouter.router,
});
});
it('creates a request handler', () => {
expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({
path: '/as/engines/:engineName/synonyms/collection',
});
});
describe('validates', () => {
it('with synonyms', () => {
const request = {
body: {
synonyms: ['a', 'b', 'c'],
},
};
mockRouter.shouldValidate(request);
});
it('empty synonyms array', () => {
const request = {
body: {
queries: [],
},
};
mockRouter.shouldThrow(request);
});
it('only one synonym', () => {
const request = {
body: {
queries: ['a'],
},
};
mockRouter.shouldThrow(request);
});
it('empty synonym strings', () => {
const request = {
body: {
queries: ['', '', ''],
},
};
mockRouter.shouldThrow(request);
});
it('missing synonyms', () => {
const request = { body: {} };
mockRouter.shouldThrow(request);
});
});
});
describe('PUT /api/app_search/engines/{engineName}/synonyms/{synonymId}', () => {
let mockRouter: MockRouter;
beforeEach(() => {
jest.clearAllMocks();
mockRouter = new MockRouter({
method: 'put',
path: '/api/app_search/engines/{engineName}/synonyms/{synonymId}',
});
registerSynonymsRoutes({
...mockDependencies,
router: mockRouter.router,
});
});
it('creates a request handler', () => {
expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({
path: '/as/engines/:engineName/synonyms/:synonymId',
});
});
describe('validates', () => {
it('with synonyms', () => {
const request = {
body: {
synonyms: ['a', 'b', 'c'],
},
};
mockRouter.shouldValidate(request);
});
it('empty synonyms array', () => {
const request = {
body: {
queries: [],
},
};
mockRouter.shouldThrow(request);
});
it('only one synonym', () => {
const request = {
body: {
queries: ['a'],
},
};
mockRouter.shouldThrow(request);
});
it('empty synonym strings', () => {
const request = {
body: {
queries: ['', '', ''],
},
};
mockRouter.shouldThrow(request);
});
it('missing synonyms', () => {
const request = { body: {} };
mockRouter.shouldThrow(request);
});
});
});
describe('DELETE /api/app_search/engines/{engineName}/synonyms/{synonymId}', () => {
let mockRouter: MockRouter;
beforeEach(() => {
jest.clearAllMocks();
mockRouter = new MockRouter({
method: 'delete',
path: '/api/app_search/engines/{engineName}/synonyms/{synonymId}',
});
registerSynonymsRoutes({
...mockDependencies,
router: mockRouter.router,
});
});
it('creates a request handler', () => {
expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({
path: '/as/engines/:engineName/synonyms/:synonymId',
});
});
});
});

View file

@ -0,0 +1,85 @@
/*
* 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 synonymsSchema = schema.arrayOf(schema.string({ minLength: 1 }), { minSize: 2 });
export function registerSynonymsRoutes({
router,
enterpriseSearchRequestHandler,
}: RouteDependencies) {
router.get(
{
path: '/api/app_search/engines/{engineName}/synonyms',
validate: {
params: schema.object({
engineName: schema.string(),
}),
query: schema.object({
'page[current]': schema.number(),
'page[size]': schema.number(),
}),
},
},
enterpriseSearchRequestHandler.createRequest({
path: '/as/engines/:engineName/synonyms/collection',
})
);
router.post(
{
path: '/api/app_search/engines/{engineName}/synonyms',
validate: {
params: schema.object({
engineName: schema.string(),
}),
body: schema.object({
synonyms: synonymsSchema,
}),
},
},
enterpriseSearchRequestHandler.createRequest({
path: '/as/engines/:engineName/synonyms/collection',
})
);
router.put(
{
path: '/api/app_search/engines/{engineName}/synonyms/{synonymId}',
validate: {
params: schema.object({
engineName: schema.string(),
synonymId: schema.string(),
}),
body: schema.object({
synonyms: synonymsSchema,
}),
},
},
enterpriseSearchRequestHandler.createRequest({
path: '/as/engines/:engineName/synonyms/:synonymId',
})
);
router.delete(
{
path: '/api/app_search/engines/{engineName}/synonyms/{synonymId}',
validate: {
params: schema.object({
engineName: schema.string(),
synonymId: schema.string(),
}),
},
},
enterpriseSearchRequestHandler.createRequest({
path: '/as/engines/:engineName/synonyms/:synonymId',
})
);
}