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
@@ -0,0 +1,33 @@
/*
* 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 { mockHttpValues } from '../../../__mocks__/kea_logic';

import { nextTick } from '@kbn/test-jest-helpers';

import { recreateCrawlerConnector } from './recreate_crawler_connector_api_logic';

describe('CreateCrawlerIndexApiLogic', () => {
const { http } = mockHttpValues;
beforeEach(() => {
jest.clearAllMocks();
});
describe('createCrawlerIndex', () => {
it('calls correct api', async () => {
const indexName = 'elastic-co-crawler';
http.post.mockReturnValue(Promise.resolve({ connector_id: 'connectorId' }));

const result = recreateCrawlerConnector({ indexName });
await nextTick();

expect(http.post).toHaveBeenCalledWith(
'/internal/enterprise_search/indices/elastic-co-crawler/crawler/connector'
);
await expect(result).resolves.toEqual({ connector_id: 'connectorId' });
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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 { Actions, createApiLogic } from '../../../shared/api_logic/create_api_logic';
import { HttpLogic } from '../../../shared/http';

export interface RecreateCrawlerConnectorArgs {
indexName: string;
}

export interface RecreateCrawlerConnectorResponse {
created: string; // the name of the newly created index
}

export const recreateCrawlerConnector = async ({ indexName }: RecreateCrawlerConnectorArgs) => {
const route = `/internal/enterprise_search/indices/${indexName}/crawler/connector`;

return await HttpLogic.values.http.post<RecreateCrawlerConnectorResponse>(route);
};

export const RecreateCrawlerConnectorApiLogic = createApiLogic(
['recreate_crawler_connector_api_logic'],
recreateCrawlerConnector
);

export type RecreateCrawlerConnectorActions = Actions<
RecreateCrawlerConnectorArgs,
RecreateCrawlerConnectorResponse
>;
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { i18n } from '@kbn/i18n';

import { createApiLogic } from '../../../shared/api_logic/create_api_logic';
import { Actions, createApiLogic } from '../../../shared/api_logic/create_api_logic';
import { HttpLogic } from '../../../shared/http';

export interface DeleteIndexApiLogicArgs {
Expand Down Expand Up @@ -36,3 +36,5 @@ export const DeleteIndexApiLogic = createApiLogic(['delete_index_api_logic'], de
},
}),
});

export type DeleteIndexApiActions = Actions<DeleteIndexApiLogicArgs, DeleteIndexApiLogicValues>;
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { SyncsContextMenu } from './syncs_context_menu';
export const getHeaderActions = (indexData?: ElasticsearchIndexWithIngestion) => {
const ingestionMethod = getIngestionMethod(indexData);
return [
...(isCrawlerIndex(indexData) ? [<CrawlerStatusIndicator />] : []),
...(isCrawlerIndex(indexData) && indexData.connector ? [<CrawlerStatusIndicator />] : []),
...(isConnectorIndex(indexData) ? [<SyncsContextMenu />] : []),
<SearchEnginesPopover
indexName={indexData?.name}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* 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 { useActions, useValues } from 'kea';

import { EuiButton, EuiPageTemplate } from '@elastic/eui';

import { i18n } from '@kbn/i18n';

import { Status } from '../../../../../../common/types/api';

import { RecreateCrawlerConnectorApiLogic } from '../../../api/crawler/recreate_crawler_connector_api_logic';
import { DeleteIndexModal } from '../../search_indices/delete_index_modal';
import { IndicesLogic } from '../../search_indices/indices_logic';
import { IndexViewLogic } from '../index_view_logic';

import { NoConnectorRecordLogic } from './no_connector_record_logic';

export const NoConnectorRecord: React.FC = () => {
const { indexName } = useValues(IndexViewLogic);
const { isDeleteLoading } = useValues(IndicesLogic);
const { openDeleteModal } = useActions(IndicesLogic);
const { makeRequest } = useActions(RecreateCrawlerConnectorApiLogic);
const { status } = useValues(RecreateCrawlerConnectorApiLogic);
NoConnectorRecordLogic.mount();
const buttonsDisabled = status === Status.LOADING || isDeleteLoading;

return (
<>
<DeleteIndexModal />

<EuiPageTemplate.EmptyPrompt
iconType="alert"
color="danger"
title={
<h2>
{i18n.translate(
'xpack.enterpriseSearch.content.searchIndex.noCrawlerConnectorFound.title',
{
defaultMessage: "This index's connector configuration has been removed",
}
)}
</h2>
}
body={
<p>
{i18n.translate(
'xpack.enterpriseSearch.content.searchIndex.noCrawlerConnectorFound.description',
{
defaultMessage:
'We could not find a connector configuration for this crawler index. The record should be recreated, or the index should be deleted.',
}
)}
</p>
}
actions={[
<EuiButton
color="danger"
disabled={buttonsDisabled}
isLoading={status === Status.LOADING}
onClick={() => makeRequest({ indexName })}
>
{i18n.translate(
'xpack.enterpriseSearch.content.searchIndex.noCrawlerConnectorFound.recreateConnectorRecord',
{
defaultMessage: 'Recreate connector record',
}
)}
</EuiButton>,
<EuiButton
color="danger"
disabled={buttonsDisabled}
isLoading={isDeleteLoading}
fill
onClick={() => openDeleteModal(indexName)}
>
{i18n.translate(
'xpack.enterpriseSearch.content.searchIndex.noCrawlerConnectorFound.deleteIndex',
{
defaultMessage: 'Delete index',
}
)}
</EuiButton>,
]}
/>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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 { LogicMounter } from '../../../../__mocks__/kea_logic';

import { KibanaLogic } from '../../../../shared/kibana';

import { RecreateCrawlerConnectorApiLogic } from '../../../api/crawler/recreate_crawler_connector_api_logic';
import { DeleteIndexApiLogic } from '../../../api/index/delete_index_api_logic';
import { SEARCH_INDICES_PATH } from '../../../routes';

import { NoConnectorRecordLogic } from './no_connector_record_logic';

describe('NoConnectorRecordLogic', () => {
const { mount: deleteMount } = new LogicMounter(DeleteIndexApiLogic);
const { mount: recreateMount } = new LogicMounter(RecreateCrawlerConnectorApiLogic);
const { mount } = new LogicMounter(NoConnectorRecordLogic);
beforeEach(() => {
deleteMount();
recreateMount();
mount();
});
it('should redirect to search indices on delete', () => {
KibanaLogic.values.navigateToUrl = jest.fn();
DeleteIndexApiLogic.actions.apiSuccess({} as any);
expect(KibanaLogic.values.navigateToUrl).toHaveBeenCalledWith(SEARCH_INDICES_PATH);
});
it('should fetch index on recreate', () => {
NoConnectorRecordLogic.actions.fetchIndex = jest.fn();
RecreateCrawlerConnectorApiLogic.actions.apiSuccess({} as any);
expect(NoConnectorRecordLogic.actions.fetchIndex).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -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 { kea, MakeLogicType } from 'kea';

import { KibanaLogic } from '../../../../shared/kibana';

import {
RecreateCrawlerConnectorActions,
RecreateCrawlerConnectorApiLogic,
} from '../../../api/crawler/recreate_crawler_connector_api_logic';
import {
DeleteIndexApiActions,
DeleteIndexApiLogic,
} from '../../../api/index/delete_index_api_logic';
import { SEARCH_INDICES_PATH } from '../../../routes';
import { IndexViewActions, IndexViewLogic } from '../index_view_logic';

type NoConnectorRecordActions = RecreateCrawlerConnectorActions['apiSuccess'] & {
deleteSuccess: DeleteIndexApiActions['apiSuccess'];
fetchIndex: IndexViewActions['fetchIndex'];
};

export const NoConnectorRecordLogic = kea<MakeLogicType<{}, NoConnectorRecordActions>>({
connect: {
actions: [
RecreateCrawlerConnectorApiLogic,
['apiSuccess'],
IndexViewLogic,
['fetchIndex'],
DeleteIndexApiLogic,
['apiSuccess as deleteSuccess'],
],
},
listeners: ({ actions }) => ({
apiSuccess: () => {
actions.fetchIndex();
},
deleteSuccess: () => {
KibanaLogic.values.navigateToUrl(SEARCH_INDICES_PATH);
},
}),
path: ['enterprise_search', 'content', 'no_connector_record'],
});
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { AutomaticCrawlScheduler } from './crawler/automatic_crawl_scheduler/aut
import { CrawlCustomSettingsFlyout } from './crawler/crawl_custom_settings_flyout/crawl_custom_settings_flyout';
import { CrawlerConfiguration } from './crawler/crawler_configuration/crawler_configuration';
import { SearchIndexDomainManagement } from './crawler/domain_management/domain_management';
import { NoConnectorRecord } from './crawler/no_connector_record';
import { SearchIndexDocuments } from './documents';
import { SearchIndexIndexMappings } from './index_mappings';
import { IndexNameLogic } from './index_name_logic';
Expand Down Expand Up @@ -224,12 +225,16 @@ export const SearchIndex: React.FC = () => {
rightSideItems: getHeaderActions(index),
}}
>
<>
{indexName === index?.name && (
<EuiTabbedContent tabs={tabs} selectedTab={selectedTab} onTabClick={onTabClick} />
)}
{isCrawlerIndex(index) && <CrawlCustomSettingsFlyout />}
</>
{isCrawlerIndex(index) && !index.connector ? (
<NoConnectorRecord />
) : (
<>
{indexName === index?.name && (
<EuiTabbedContent tabs={tabs} selectedTab={selectedTab} onTabClick={onTabClick} />
)}
{isCrawlerIndex(index) && <CrawlCustomSettingsFlyout />}
</>
)}
</EnterpriseSearchContentPageTemplate>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ describe('IndicesLogic', () => {
describe('openDeleteModal', () => {
it('should set deleteIndexName and set isDeleteModalVisible to true', () => {
IndicesLogic.actions.fetchIndexDetails = jest.fn();
IndicesLogic.actions.openDeleteModal(connectorIndex);
IndicesLogic.actions.openDeleteModal(connectorIndex.name);
expect(IndicesLogic.values).toEqual({
...DEFAULT_VALUES,
deleteModalIndexName: 'connector',
Expand All @@ -98,7 +98,7 @@ describe('IndicesLogic', () => {
});
describe('closeDeleteModal', () => {
it('should set deleteIndexName to empty and set isDeleteModalVisible to false', () => {
IndicesLogic.actions.openDeleteModal(connectorIndex);
IndicesLogic.actions.openDeleteModal(connectorIndex.name);
IndicesLogic.actions.fetchIndexDetails = jest.fn();
IndicesLogic.actions.closeDeleteModal();
expect(IndicesLogic.values).toEqual({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export interface IndicesActions {
}): { meta: Meta; returnHiddenIndices: boolean; searchQuery?: string };
makeRequest: typeof FetchIndicesAPILogic.actions.makeRequest;
onPaginate(newPageIndex: number): { newPageIndex: number };
openDeleteModal(index: ElasticsearchViewIndex): { index: ElasticsearchViewIndex };
openDeleteModal(indexName: string): { indexName: string };
setIsFirstRequest(): void;
}
export interface IndicesValues {
Expand Down Expand Up @@ -102,7 +102,7 @@ export const IndicesLogic = kea<MakeLogicType<IndicesValues, IndicesActions>>({
searchQuery,
}),
onPaginate: (newPageIndex) => ({ newPageIndex }),
openDeleteModal: (index) => ({ index }),
openDeleteModal: (indexName) => ({ indexName }),
setIsFirstRequest: true,
},
connect: {
Expand Down Expand Up @@ -137,8 +137,8 @@ export const IndicesLogic = kea<MakeLogicType<IndicesValues, IndicesActions>>({
await breakpoint(150);
actions.makeRequest(input);
},
openDeleteModal: ({ index }) => {
actions.fetchIndexDetails({ indexName: index.name });
openDeleteModal: ({ indexName }) => {
actions.fetchIndexDetails({ indexName });
},
}),
path: ['enterprise_search', 'content', 'indices_logic'],
Expand All @@ -147,7 +147,7 @@ export const IndicesLogic = kea<MakeLogicType<IndicesValues, IndicesActions>>({
'',
{
closeDeleteModal: () => '',
openDeleteModal: (_, { index: { name } }) => name,
openDeleteModal: (_, { indexName }) => indexName,
},
],
isDeleteModalVisible: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ interface IndicesTableProps {
isLoading?: boolean;
meta: Meta;
onChange: (criteria: CriteriaWithPagination<ElasticsearchViewIndex>) => void;
onDelete: (index: ElasticsearchViewIndex) => void;
onDelete: (indexName: string) => void;
}

export const IndicesTable: React.FC<IndicesTableProps> = ({
Expand Down Expand Up @@ -175,7 +175,7 @@ export const IndicesTable: React.FC<IndicesTableProps> = ({
},
}
),
onClick: (index) => onDelete(index),
onClick: (index) => onDelete(index.name),
type: 'icon',
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export function getIngestionStatus(index?: ElasticsearchIndexWithIngestion): Ing
if (!index || isApiIndex(index)) {
return IngestionStatus.CONNECTED;
}
if (isConnectorIndex(index) || isCrawlerIndex(index)) {
if (isConnectorIndex(index) || (isCrawlerIndex(index) && index.connector)) {
if (
index.connector.last_seen &&
moment(index.connector.last_seen).isBefore(moment().subtract(30, 'minutes'))
Expand Down
Loading