Skip to content

Commit

Permalink
[Enterprise Search] Enable converting native connector to custom (#15…
Browse files Browse the repository at this point in the history
…5853)

## Summary

This adds the ability to convert a native connector to a customized
connector.

---------

Co-authored-by: Liam Thompson <[email protected]>
(cherry picked from commit e44087a)
  • Loading branch information
sphilipse committed Apr 27, 2023
1 parent 497dbb5 commit 58b8fc5
Show file tree
Hide file tree
Showing 10 changed files with 320 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/kbn-doc-links/src/get_doc_links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => {
apiKeys: `${KIBANA_DOCS}api-keys.html`,
behavioralAnalytics: `${ENTERPRISE_SEARCH_DOCS}analytics-overview.html`,
behavioralAnalyticsEvents: `${ENTERPRISE_SEARCH_DOCS}analytics-events.html`,
buildConnector: `{$ENTERPRISE_SEARCH_DOCS}build-connector.html`,
bulkApi: `${ELASTICSEARCH_DOCS}docs-bulk.html`,
configuration: `${ENTERPRISE_SEARCH_DOCS}configuration.html`,
connectors: `${ENTERPRISE_SEARCH_DOCS}connectors.html`,
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-doc-links/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export interface DocLinks {
readonly apiKeys: string;
readonly behavioralAnalytics: string;
readonly behavioralAnalyticsEvents: string;
readonly buildConnector: string;
readonly bulkApi: string;
readonly configuration: string;
readonly connectors: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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 { convertConnector } from './convert_connector_api_logic';

describe('ConvertConnectorApilogic', () => {
const { http } = mockHttpValues;
beforeEach(() => {
jest.clearAllMocks();
});
describe('convertConnector', () => {
it('calls correct api', async () => {
const promise = Promise.resolve('result');
http.put.mockReturnValue(promise);
const result = convertConnector({ connectorId: 'connectorId1' });
await nextTick();
expect(http.put).toHaveBeenCalledWith(
'/internal/enterprise_search/connectors/connectorId1/native',
{ body: JSON.stringify({ is_native: false }) }
);
await expect(result).resolves.toEqual('result');
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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 { createApiLogic } from '../../../shared/api_logic/create_api_logic';
import { HttpLogic } from '../../../shared/http';

export interface ConvertConnectorApiLogicArgs {
connectorId: string;
}

export interface ConvertConnectorApiLogicResponse {
updated: boolean;
}

export const convertConnector = async ({
connectorId,
}: ConvertConnectorApiLogicArgs): Promise<ConvertConnectorApiLogicResponse> => {
const route = `/internal/enterprise_search/connectors/${connectorId}/native`;

return await HttpLogic.values.http.put<{ updated: boolean }>(route, {
body: JSON.stringify({ is_native: false }),
});
};

export const ConvertConnectorApiLogic = createApiLogic(
['convert_connector_api_logic'],
convertConnector
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* 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 {
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
EuiTitle,
EuiSpacer,
EuiText,
EuiLink,
EuiButton,
EuiConfirmModal,
} from '@elastic/eui';

import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';

import { CANCEL_BUTTON_LABEL } from '../../../../../shared/constants';

import { docLinks } from '../../../../../shared/doc_links';

import { ConvertConnectorLogic } from './convert_connector_logic';

export const ConvertConnector: React.FC = () => {
const { convertConnector, hideModal, showModal } = useActions(ConvertConnectorLogic);
const { isLoading, isModalVisible } = useValues(ConvertConnectorLogic);

return (
<>
{isModalVisible && (
<EuiConfirmModal
onCancel={() => hideModal()}
onConfirm={() => convertConnector()}
title={i18n.translate(
'xpack.enterpriseSearch.content.engine.indices.convertInfexConfirm.title',
{ defaultMessage: 'Sure you want to convert your connector?' }
)}
buttonColor="danger"
cancelButtonText={CANCEL_BUTTON_LABEL}
confirmButtonText={i18n.translate(
'xpack.enterpriseSearch.content.engine.indices.convertIndexConfirm.text',
{
defaultMessage: 'Yes',
}
)}
isLoading={isLoading}
defaultFocusedButton="confirm"
maxWidth
>
<EuiText>
<p>
{i18n.translate(
'xpack.enterpriseSearch.content.engine.indices.convertIndexConfirm.description',
{
defaultMessage:
"Once you convert a native connector to a self-managed connector client this can't be undone.",
}
)}
</p>
</EuiText>
</EuiConfirmModal>
)}
<EuiFlexGroup direction="row" alignItems="center" gutterSize="xs">
<EuiFlexItem grow={false}>
<EuiIcon type="wrench" />
</EuiFlexItem>
<EuiFlexItem>
<EuiTitle size="xxs">
<h4>
{i18n.translate(
'xpack.enterpriseSearch.content.indices.configurationConnector.nativeConnector.convertConnector.title',
{
defaultMessage: 'Customize your connector',
}
)}
</h4>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="s" />
<EuiText size="s">
<FormattedMessage
id="xpack.enterpriseSearch.content.indices.configurationConnector.nativeConnector.convertConnector.description"
defaultMessage="Want to customize this native connector? Convert it to a {link}, to be self-managed on your own infrastructure."
values={{
link: (
<EuiLink href={docLinks.buildConnector} target="_blank">
{i18n.translate(
'xpack.enterpriseSearch.content.indices.configurationConnector.nativeConnector.convertConnector.linkTitle',
{ defaultMessage: 'connector client' }
)}
</EuiLink>
),
}}
/>
<EuiSpacer size="s" />
<EuiButton onClick={() => showModal()}>
{i18n.translate(
'xpack.enterpriseSearch.content.indices.configurationConnector.nativeConnector.convertConnector.buttonTitle',
{ defaultMessage: 'Convert connector' }
)}
</EuiButton>
</EuiText>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* 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.
*/
/*
* 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 { Status } from '../../../../../../../common/types/api';
import { Actions } from '../../../../../shared/api_logic/create_api_logic';
import {
ConvertConnectorApiLogic,
ConvertConnectorApiLogicArgs,
ConvertConnectorApiLogicResponse,
} from '../../../../api/connector/convert_connector_api_logic';
import { IndexViewActions, IndexViewLogic } from '../../index_view_logic';

interface ConvertConnectorValues {
connectorId: typeof IndexViewLogic.values.connectorId;
isLoading: boolean;
isModalVisible: boolean;
status: Status;
}

type ConvertConnectorActions = Pick<
Actions<ConvertConnectorApiLogicArgs, ConvertConnectorApiLogicResponse>,
'apiError' | 'apiSuccess' | 'makeRequest'
> & {
convertConnector(): void;
fetchIndex(): IndexViewActions['fetchIndex'];
hideModal(): void;
showModal(): void;
};

export const ConvertConnectorLogic = kea<
MakeLogicType<ConvertConnectorValues, ConvertConnectorActions>
>({
actions: {
convertConnector: () => true,
deleteDomain: () => true,
hideModal: () => true,
showModal: () => true,
},
connect: {
actions: [
ConvertConnectorApiLogic,
['apiError', 'apiSuccess', 'makeRequest'],
IndexViewLogic,
['fetchIndex'],
],
values: [ConvertConnectorApiLogic, ['status'], IndexViewLogic, ['connectorId']],
},
listeners: ({ actions, values }) => ({
convertConnector: () => {
if (values.connectorId) {
actions.makeRequest({ connectorId: values.connectorId });
}
},
}),
path: ['enterprise_search', 'convert_connector_modal'],
reducers: {
isModalVisible: [
false,
{
apiError: () => false,
apiSuccess: () => false,
hideModal: () => false,
showModal: () => true,
},
],
},
selectors: ({ selectors }) => ({
isLoading: [() => [selectors.status], (status: Status) => status === Status.LOADING],
}),
});
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { IndexViewLogic } from '../../index_view_logic';
import { ConnectorNameAndDescription } from '../connector_name_and_description/connector_name_and_description';
import { NATIVE_CONNECTORS } from '../constants';

import { ConvertConnector } from './convert_connector';
import { NativeConnectorAdvancedConfiguration } from './native_connector_advanced_configuration';
import { NativeConnectorConfigurationConfig } from './native_connector_configuration_config';
import { ResearchConfiguration } from './research_configuration';
Expand Down Expand Up @@ -203,6 +204,11 @@ export const NativeConnectorConfiguration: React.FC = () => {
</EuiText>
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiPanel hasBorder hasShadow={false}>
<ConvertConnector />
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class DocLinks {
public appSearchWebCrawlerReference: string;
public behavioralAnalytics: string;
public behavioralAnalyticsEvents: string;
public buildConnector: string;
public bulkApi: string;
public clientsGoIndex: string;
public clientsGuide: string;
Expand Down Expand Up @@ -164,6 +165,7 @@ class DocLinks {
this.appSearchWebCrawlerReference = '';
this.behavioralAnalytics = '';
this.behavioralAnalyticsEvents = '';
this.buildConnector = '';
this.bulkApi = '';
this.clientsGoIndex = '';
this.clientsGuide = '';
Expand Down Expand Up @@ -294,6 +296,7 @@ class DocLinks {
this.appSearchWebCrawlerReference = docLinks.links.appSearch.webCrawlerReference;
this.behavioralAnalytics = docLinks.links.enterpriseSearch.behavioralAnalytics;
this.behavioralAnalyticsEvents = docLinks.links.enterpriseSearch.behavioralAnalyticsEvents;
this.buildConnector = docLinks.links.enterpriseSearch.buildConnector;
this.bulkApi = docLinks.links.enterpriseSearch.bulkApi;
this.clientsGoIndex = docLinks.links.clients.goIndex;
this.clientsGuide = docLinks.links.clients.guide;
Expand Down
Original file line number Diff line number Diff line change
@@ -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 { IScopedClusterClient } from '@kbn/core-elasticsearch-server';

import { CONNECTORS_INDEX } from '../..';
import { Connector } from '../../../common/types/connectors';

export const putUpdateNative = async (
client: IScopedClusterClient,
connectorId: string,
isNative: boolean
) => {
const result = await client.asCurrentUser.update<Connector>({
doc: {
is_native: isNative,
},
id: connectorId,
index: CONNECTORS_INDEX,
});

return result;
};
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { fetchSyncJobsByConnectorId } from '../../lib/connectors/fetch_sync_jobs
import { cancelSyncs } from '../../lib/connectors/post_cancel_syncs';
import { updateFiltering } from '../../lib/connectors/put_update_filtering';
import { updateFilteringDraft } from '../../lib/connectors/put_update_filtering_draft';
import { putUpdateNative } from '../../lib/connectors/put_update_native';
import { startConnectorSync } from '../../lib/connectors/start_sync';
import { updateConnectorConfiguration } from '../../lib/connectors/update_connector_configuration';
import { updateConnectorNameAndDescription } from '../../lib/connectors/update_connector_name_and_description';
Expand Down Expand Up @@ -383,4 +384,24 @@ export function registerConnectorRoutes({ router, log }: RouteDependencies) {
return result ? response.ok({ body: result }) : response.conflict();
})
);
router.put(
{
path: '/internal/enterprise_search/connectors/{connectorId}/native',
validate: {
body: schema.object({
is_native: schema.boolean(),
}),
params: schema.object({
connectorId: schema.string(),
}),
},
},
elasticsearchErrorHandler(log, async (context, request, response) => {
const { client } = (await context.core).elasticsearch;
const connectorId = decodeURIComponent(request.params.connectorId);
const { is_native } = request.body;
const result = await putUpdateNative(client, connectorId, is_native);
return result ? response.ok({ body: result }) : response.conflict();
})
);
}

0 comments on commit 58b8fc5

Please sign in to comment.