diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.test.ts index 9ff540de13fe1..a178228f4996b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.test.ts @@ -18,6 +18,8 @@ jest.mock('../../app_logic', () => ({ })); import { AppLogic } from '../../app_logic'; +import { EngineTypes } from '../engine/types'; + import { ApiTokenTypes } from './constants'; import { CredentialsLogic } from './credentials_logic'; @@ -61,8 +63,8 @@ describe('CredentialsLogic', () => { const credentialsDetails = { engines: [ - { name: 'engine1', type: 'indexed', language: 'english', result_fields: {} }, - { name: 'engine1', type: 'indexed', language: 'english', result_fields: {} }, + { name: 'engine1', type: EngineTypes.indexed, language: 'english', result_fields: {} }, + { name: 'engine1', type: EngineTypes.indexed, language: 'english', result_fields: {} }, ], }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts index d48b91123255d..bf2fba6344e7a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts @@ -9,6 +9,8 @@ import { LogicMounter, mockHttpValues } from '../../../__mocks__'; import { nextTick } from '@kbn/test/jest'; +import { EngineTypes } from './types'; + import { EngineLogic } from './'; describe('EngineLogic', () => { @@ -17,7 +19,7 @@ describe('EngineLogic', () => { const mockEngineData = { name: 'some-engine', - type: 'default', + type: EngineTypes.default, created_at: 'some date timestamp', language: null, document_count: 1, @@ -213,7 +215,7 @@ describe('EngineLogic', () => { describe('isMetaEngine', () => { it('should be set based on engine.type', () => { - const mockMetaEngine = { ...mockEngineData, type: 'meta' }; + const mockMetaEngine = { ...mockEngineData, type: EngineTypes.meta }; mount({ engine: mockMetaEngine }); expect(EngineLogic.values).toEqual({ diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts index 664a3006cfa2c..aa4a978da0550 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts @@ -11,7 +11,7 @@ import { HttpLogic } from '../../../shared/http'; import { IIndexingStatus } from '../../../shared/types'; -import { EngineDetails } from './types'; +import { EngineDetails, EngineTypes } from './types'; interface EngineValues { dataLoading: boolean; @@ -78,7 +78,7 @@ export const EngineLogic = kea>({ ], }, selectors: ({ selectors }) => ({ - isMetaEngine: [() => [selectors.engine], (engine) => engine?.type === 'meta'], + isMetaEngine: [() => [selectors.engine], (engine) => engine?.type === EngineTypes.meta], isSampleEngine: [() => [selectors.engine], (engine) => !!engine?.sample], hasSchemaConflicts: [ () => [selectors.engine], diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/types.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/types.ts index b50e8eb555dc9..0cfef4320825c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/types.ts @@ -8,9 +8,14 @@ import { Schema, SchemaConflicts, IIndexingStatus } from '../../../shared/types'; import { ApiToken } from '../credentials/types'; +export enum EngineTypes { + default, + indexed, + meta, +} export interface Engine { name: string; - type: string; + type: EngineTypes; language: string | null; result_fields: { [key: string]: ResultField; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/constants.ts index e0e36afa8e0c4..1955084393e57 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/constants.ts @@ -23,6 +23,17 @@ export const CREATE_AN_ENGINE_BUTTON_LABEL = i18n.translate( } ); +export const DELETE_ENGINE_MESSAGE = (engineName: string) => + i18n.translate( + 'xpack.enterpriseSearch.appSearch.enginesOverview.table.action.delete.successMessage', + { + defaultMessage: 'Successfully deleted "{engineName}"', + values: { + engineName, + }, + } + ); + export const CREATE_A_META_ENGINE_BUTTON_LABEL = i18n.translate( 'xpack.enterpriseSearch.appSearch.engines.createAMetaEngineButton.ButtonLabel', { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.test.ts index 8be4f471b79a6..e4776f7a75df4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.test.ts @@ -5,19 +5,20 @@ * 2.0. */ -import { LogicMounter, mockHttpValues } from '../../../__mocks__'; +import { LogicMounter, mockHttpValues, mockFlashMessageHelpers } from '../../../__mocks__'; import { nextTick } from '@kbn/test/jest'; import { DEFAULT_META } from '../../../shared/constants'; -import { EngineDetails } from '../engine/types'; +import { EngineDetails, EngineTypes } from '../engine/types'; import { EnginesLogic } from './'; describe('EnginesLogic', () => { const { mount } = new LogicMounter(EnginesLogic); const { http } = mockHttpValues; + const { flashAPIErrors, setSuccessMessage } = mockFlashMessageHelpers; const DEFAULT_VALUES = { dataLoading: true, @@ -123,6 +124,30 @@ describe('EnginesLogic', () => { }); describe('listeners', () => { + describe('deleteEngine', () => { + it('calls the engine API endpoint then onDeleteEngineSuccess', async () => { + http.delete.mockReturnValueOnce(Promise.resolve({})); + mount(); + jest.spyOn(EnginesLogic.actions, 'onDeleteEngineSuccess'); + + EnginesLogic.actions.deleteEngine(MOCK_ENGINE); + await nextTick(); + + expect(http.delete).toHaveBeenCalledWith('/api/app_search/engines/hello-world'); + expect(EnginesLogic.actions.onDeleteEngineSuccess).toHaveBeenCalledWith(MOCK_ENGINE); + }); + + it('calls flashAPIErrors on API Error', async () => { + http.delete.mockReturnValueOnce(Promise.reject()); + mount(); + + EnginesLogic.actions.deleteEngine(MOCK_ENGINE); + await nextTick(); + + expect(flashAPIErrors).toHaveBeenCalledTimes(1); + }); + }); + describe('loadEngines', () => { it('should call the engines API endpoint and set state based on the results', async () => { http.get.mockReturnValueOnce(Promise.resolve(MOCK_ENGINES_API_RESPONSE)); @@ -164,6 +189,34 @@ describe('EnginesLogic', () => { ); }); }); + + describe('onDeleteEngineSuccess', () => { + beforeEach(() => { + mount(); + }); + + it('should call setSuccessMessage', () => { + EnginesLogic.actions.onDeleteEngineSuccess(MOCK_ENGINE); + + expect(setSuccessMessage).toHaveBeenCalled(); + }); + + it('should call loadEngines if engine.type === default', () => { + jest.spyOn(EnginesLogic.actions, 'loadEngines'); + + EnginesLogic.actions.onDeleteEngineSuccess({ ...MOCK_ENGINE, type: EngineTypes.default }); + + expect(EnginesLogic.actions.loadEngines).toHaveBeenCalled(); + }); + + it('should call loadMetaEngines if engine.type === meta', () => { + jest.spyOn(EnginesLogic.actions, 'loadMetaEngines'); + + EnginesLogic.actions.onDeleteEngineSuccess({ ...MOCK_ENGINE, type: EngineTypes.meta }); + + expect(EnginesLogic.actions.loadMetaEngines).toHaveBeenCalled(); + }); + }); }); describe('selectors', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.ts index 558bf666a51b1..282731fda3bd2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_logic.ts @@ -9,10 +9,13 @@ import { kea, MakeLogicType } from 'kea'; import { Meta } from '../../../../../common/types'; import { DEFAULT_META } from '../../../shared/constants'; +import { flashAPIErrors, setSuccessMessage } from '../../../shared/flash_messages'; import { HttpLogic } from '../../../shared/http'; import { updateMetaPageIndex } from '../../../shared/table_pagination'; -import { EngineDetails } from '../engine/types'; +import { EngineDetails, EngineTypes } from '../engine/types'; + +import { DELETE_ENGINE_MESSAGE } from './constants'; interface EnginesValues { dataLoading: boolean; @@ -29,6 +32,8 @@ interface EnginesAPIResponse { meta: Meta; } interface EnginesActions { + deleteEngine(engine: EngineDetails): { engine: EngineDetails }; + onDeleteEngineSuccess(engine: EngineDetails): { engine: EngineDetails }; onEnginesLoad({ results, meta }: EnginesAPIResponse): EnginesAPIResponse; onMetaEnginesLoad({ results, meta }: EnginesAPIResponse): EnginesAPIResponse; onEnginesPagination(page: number): { page: number }; @@ -40,6 +45,8 @@ interface EnginesActions { export const EnginesLogic = kea>({ path: ['enterprise_search', 'app_search', 'engines_logic'], actions: { + deleteEngine: (engine) => ({ engine }), + onDeleteEngineSuccess: (engine) => ({ engine }), onEnginesLoad: ({ results, meta }) => ({ results, meta }), onMetaEnginesLoad: ({ results, meta }) => ({ results, meta }), onEnginesPagination: (page) => ({ page }), @@ -96,6 +103,20 @@ export const EnginesLogic = kea>({ ], }, listeners: ({ actions, values }) => ({ + deleteEngine: async ({ engine }) => { + const { http } = HttpLogic.values; + let response; + + try { + response = await http.delete(`/api/app_search/engines/${engine.name}`); + } catch (e) { + flashAPIErrors(e); + } + + if (response) { + actions.onDeleteEngineSuccess(engine); + } + }, loadEngines: async () => { const { http } = HttpLogic.values; const { enginesMeta } = values; @@ -122,5 +143,13 @@ export const EnginesLogic = kea>({ }); actions.onMetaEnginesLoad(response); }, + onDeleteEngineSuccess: async ({ engine }) => { + setSuccessMessage(DELETE_ENGINE_MESSAGE(engine.name)); + if ([EngineTypes.default, EngineTypes.indexed].includes(engine.type)) { + actions.loadEngines(); + } else if (engine.type === EngineTypes.meta) { + actions.loadMetaEngines(); + } + }, }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx index ca70e323bd3e7..8297fb490ee04 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx @@ -55,9 +55,13 @@ export const EnginesOverview: React.FC = () => { metaEnginesLoading, } = useValues(EnginesLogic); - const { loadEngines, loadMetaEngines, onEnginesPagination, onMetaEnginesPagination } = useActions( - EnginesLogic - ); + const { + deleteEngine, + loadEngines, + loadMetaEngines, + onEnginesPagination, + onMetaEnginesPagination, + } = useActions(EnginesLogic); useEffect(() => { loadEngines(); @@ -106,6 +110,7 @@ export const EnginesOverview: React.FC = () => { hidePerPageOptions: true, }} onChange={handlePageChange(onEnginesPagination)} + onDeleteEngine={deleteEngine} /> @@ -155,6 +160,7 @@ export const EnginesOverview: React.FC = () => { /> } onChange={handlePageChange(onMetaEnginesPagination)} + onDeleteEngine={deleteEngine} /> diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx index 51c65e1478d13..66f9c3c3c473d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx @@ -6,20 +6,27 @@ */ import '../../../__mocks__/enterprise_search_url.mock'; -import { mockTelemetryActions, mountWithIntl } from '../../../__mocks__'; +import { mockTelemetryActions, mountWithIntl, setMockValues } from '../../../__mocks__'; import React from 'react'; -import { EuiBasicTable, EuiPagination, EuiButtonEmpty } from '@elastic/eui'; +import { ReactWrapper, shallow } from 'enzyme'; +import { EuiBasicTable, EuiPagination, EuiButtonEmpty, EuiIcon, EuiTableRow } from '@elastic/eui'; + +import { KibanaLogic } from '../../../shared/kibana'; import { EuiLinkTo } from '../../../shared/react_router_helpers'; +import { TelemetryLogic } from '../../../shared/telemetry'; import { EngineDetails } from '../engine/types'; +import { EnginesLogic } from './engines_logic'; import { EnginesTable } from './engines_table'; describe('EnginesTable', () => { const onChange = jest.fn(); + const onDeleteEngine = jest.fn(); + const data = [ { name: 'test-engine', @@ -41,11 +48,22 @@ describe('EnginesTable', () => { loading: false, pagination, onChange, + onDeleteEngine, }; describe('basic table', () => { - const wrapper = mountWithIntl(); - const table = wrapper.find(EuiBasicTable); + let wrapper: ReactWrapper; + let table: ReactWrapper; + + beforeAll(() => { + jest.clearAllMocks(); + setMockValues({ + // LicensingLogic + hasPlatinumLicense: false, + }); + wrapper = mountWithIntl(); + table = wrapper.find(EuiBasicTable); + }); it('renders', () => { expect(table).toHaveLength(1); @@ -83,7 +101,13 @@ describe('EnginesTable', () => { describe('loading', () => { it('passes the loading prop', () => { + jest.clearAllMocks(); + setMockValues({ + // LicensingLogic + hasPlatinumLicense: false, + }); const wrapper = mountWithIntl(); + expect(wrapper.find(EuiBasicTable).prop('loading')).toEqual(true); }); }); @@ -96,6 +120,14 @@ describe('EnginesTable', () => { }); describe('language field', () => { + beforeAll(() => { + jest.clearAllMocks(); + setMockValues({ + // LicensingLogic + hasPlatinumLicense: false, + }); + }); + it('renders language when available', () => { const wrapper = mountWithIntl( { expect(tableContent).not.toContain('Universal'); }); }); + + describe('actions', () => { + it('will hide the action buttons if the user does not have a platinum license', () => { + jest.clearAllMocks(); + setMockValues({ + // LicensingLogic + hasPlatinumLicense: false, + }); + const wrapper = shallow(); + const tableRow = wrapper.find(EuiTableRow).first(); + + expect(tableRow.find(EuiIcon)).toHaveLength(0); + }); + + describe('when user has a platinum license', () => { + let wrapper: ReactWrapper; + let tableRow: ReactWrapper; + let actions: ReactWrapper; + + beforeEach(() => { + jest.clearAllMocks(); + setMockValues({ + // LicensingLogic + hasPlatinumLicense: true, + }); + wrapper = mountWithIntl(); + tableRow = wrapper.find(EuiTableRow).first(); + actions = tableRow.find(EuiIcon); + EnginesLogic.mount(); + }); + + it('renders a manage action', () => { + jest.spyOn(TelemetryLogic.actions, 'sendAppSearchTelemetry'); + jest.spyOn(KibanaLogic.values, 'navigateToUrl'); + actions.at(0).simulate('click'); + + expect(TelemetryLogic.actions.sendAppSearchTelemetry).toHaveBeenCalled(); + expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith('/engines/test-engine'); + }); + + describe('delete action', () => { + it('shows the user a confirm message when the action is clicked', () => { + jest.spyOn(global, 'confirm' as any).mockReturnValueOnce(true); + actions.at(1).simulate('click'); + expect(global.confirm).toHaveBeenCalled(); + }); + + it('clicking the action and confirming deletes the engine', () => { + jest.spyOn(global, 'confirm' as any).mockReturnValueOnce(true); + jest.spyOn(EnginesLogic.actions, 'deleteEngine'); + + actions.at(1).simulate('click'); + + expect(onDeleteEngine).toHaveBeenCalled(); + }); + + it('clicking the action and not confirming does not delete the engine', () => { + jest.spyOn(global, 'confirm' as any).mockReturnValueOnce(false); + jest.spyOn(EnginesLogic.actions, 'deleteEngine'); + + actions.at(1).simulate('click'); + + expect(onDeleteEngine).toHaveBeenCalledTimes(0); + }); + }); + }); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx index f542d12318244..e0c5823503445 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx @@ -7,12 +7,19 @@ import React, { ReactNode } from 'react'; -import { useActions } from 'kea'; +import { useActions, useValues } from 'kea'; -import { EuiBasicTable, EuiBasicTableColumn, CriteriaWithPagination } from '@elastic/eui'; +import { + EuiBasicTable, + EuiBasicTableColumn, + CriteriaWithPagination, + EuiTableActionsColumnType, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage, FormattedNumber } from '@kbn/i18n/react'; +import { FormattedNumber } from '@kbn/i18n/react'; +import { KibanaLogic } from '../../../shared/kibana'; +import { LicensingLogic } from '../../../shared/licensing'; import { EuiLinkTo } from '../../../shared/react_router_helpers'; import { TelemetryLogic } from '../../../shared/telemetry'; import { UNIVERSAL_LANGUAGE } from '../../constants'; @@ -32,6 +39,7 @@ interface EnginesTableProps { hidePerPageOptions: boolean; }; onChange(criteria: CriteriaWithPagination): void; + onDeleteEngine(engine: EngineDetails): void; } export const EnginesTable: React.FC = ({ @@ -40,17 +48,19 @@ export const EnginesTable: React.FC = ({ noItemsMessage, pagination, onChange, + onDeleteEngine, }) => { const { sendAppSearchTelemetry } = useActions(TelemetryLogic); + const { navigateToUrl } = useValues(KibanaLogic); + const { hasPlatinumLicense } = useValues(LicensingLogic); - const engineLinkProps = (engineName: string) => ({ - to: generateEncodedPath(ENGINE_PATH, { engineName }), - onClick: () => - sendAppSearchTelemetry({ - action: 'clicked', - metric: 'engine_table_link', - }), - }); + const generteEncodedEnginePath = (engineName: string) => + generateEncodedPath(ENGINE_PATH, { engineName }); + const sendEngineTableLinkClickTelemetry = () => + sendAppSearchTelemetry({ + action: 'clicked', + metric: 'engine_table_link', + }); const columns: Array> = [ { @@ -59,7 +69,11 @@ export const EnginesTable: React.FC = ({ defaultMessage: 'Name', }), render: (name: string) => ( - + {name} ), @@ -121,28 +135,74 @@ export const EnginesTable: React.FC = ({ render: (number: number) => , truncateText: true, }, - { - field: 'name', - name: i18n.translate( - 'xpack.enterpriseSearch.appSearch.enginesOverview.table.column.actions', - { - defaultMessage: 'Actions', - } - ), - dataType: 'string', - render: (name: string) => ( - - - - ), - align: 'right', - width: '100px', - }, ]; + const actionsColumn: EuiTableActionsColumnType = { + name: i18n.translate('xpack.enterpriseSearch.appSearch.enginesOverview.table.column.actions', { + defaultMessage: 'Actions', + }), + actions: [ + { + name: i18n.translate( + 'xpack.enterpriseSearch.appSearch.enginesOverview.table.action.manage', + { + defaultMessage: 'Manage', + } + ), + description: i18n.translate( + 'xpack.enterpriseSearch.appSearch.enginesOverview.table.action.manage.buttonDescription', + { + defaultMessage: 'Manage this engine', + } + ), + type: 'icon', + icon: 'eye', + onClick: (engineDetails) => { + sendEngineTableLinkClickTelemetry(); + navigateToUrl(generteEncodedEnginePath(engineDetails.name)); + }, + }, + { + name: i18n.translate( + 'xpack.enterpriseSearch.appSearch.enginesOverview.table.action.delete.buttonLabel', + { + defaultMessage: 'Delete', + } + ), + description: i18n.translate( + 'xpack.enterpriseSearch.appSearch.enginesOverview.table.action.delete.buttonDescription', + { + defaultMessage: 'Delete this engine', + } + ), + type: 'icon', + icon: 'trash', + onClick: (engine) => { + if ( + window.confirm( + i18n.translate( + 'xpack.enterpriseSearch.appSearch.enginesOverview.table.action.delete.confirmationPopupMessage', + { + defaultMessage: + 'Are you sure you want to permanently delete "{engineName}" and all of its content?', + values: { + engineName: engine.name, + }, + } + ) + ) + ) { + onDeleteEngine(engine); + } + }, + }, + ], + }; + + if (hasPlatinumLicense) { + columns.push(actionsColumn); + } + return ( { }); }); + describe('DELETE /api/app_search/engines/{name}', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'delete', + path: '/api/app_search/engines/{name}', + }); + + registerEnginesRoutes({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request to enterprise search', () => { + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/as/engines/:name', + }); + }); + + it('validates correctly with name', () => { + const request = { params: { name: 'test-engine' } }; + mockRouter.shouldValidate(request); + }); + + it('fails validation without name', () => { + const request = { params: {} }; + mockRouter.shouldThrow(request); + }); + + it('fails validation with a non-string name', () => { + const request = { params: { name: 1 } }; + mockRouter.shouldThrow(request); + }); + }); + describe('GET /api/app_search/engines/{name}/overview', () => { let mockRouter: MockRouter; diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts index 766be196e70e7..77b055add7d79 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts @@ -69,6 +69,19 @@ export function registerEnginesRoutes({ path: '/as/engines/:name/details', }) ); + router.delete( + { + path: '/api/app_search/engines/{name}', + validate: { + params: schema.object({ + name: schema.string(), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/as/engines/:name', + }) + ); router.get( { path: '/api/app_search/engines/{name}/overview', diff --git a/x-pack/test/functional_enterprise_search/page_objects/app_search.ts b/x-pack/test/functional_enterprise_search/page_objects/app_search.ts index 755e0d0db7307..8c02cdb705272 100644 --- a/x-pack/test/functional_enterprise_search/page_objects/app_search.ts +++ b/x-pack/test/functional_enterprise_search/page_objects/app_search.ts @@ -20,12 +20,12 @@ export function AppSearchPageProvider({ getService, getPageObjects }: FtrProvide async getEngineLinks(): Promise { const engines = await testSubjects.find('appSearchEngines'); - return await testSubjects.findAllDescendant('engineNameLink', engines); + return await testSubjects.findAllDescendant('EngineNameLink', engines); }, async getMetaEngineLinks(): Promise { const metaEngines = await testSubjects.find('appSearchMetaEngines'); - return await testSubjects.findAllDescendant('engineNameLink', metaEngines); + return await testSubjects.findAllDescendant('EngineNameLink', metaEngines); }, }; }