[Search] Create connectors page (#167804)

## Summary

This adds the ability to create a connector to Serverless Search and
edit its name, description and service type.

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Sander Philipse 2023-10-04 14:05:52 +02:00 committed by GitHub
parent f377164948
commit f0125245ed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 744 additions and 25 deletions

View file

@ -21,8 +21,9 @@ export const createConnector = async (
isNative: boolean;
language: string | null;
name?: string;
pipeline: IngestPipelineParams;
pipeline?: IngestPipelineParams;
serviceType?: string | null;
instant_response?: boolean;
}
): Promise<Connector> => {
const document = createConnectorDocument({
@ -33,7 +34,7 @@ export const createConnector = async (
const result = await client.index({
document,
index: CURRENT_CONNECTORS_INDEX,
refresh: 'wait_for',
refresh: input.instant_response ? false : 'wait_for',
});
return { ...document, id: result._id };

View file

@ -6,6 +6,7 @@
* Side Public License, v 1.
*/
import { WriteResponseBase } from '@elastic/elasticsearch/lib/api/types';
import { ElasticsearchClient } from '@kbn/core/server';
import { i18n } from '@kbn/i18n';
@ -17,7 +18,7 @@ export const updateConnectorNameAndDescription = async (
client: ElasticsearchClient,
connectorId: string,
connectorUpdates: Partial<Pick<Connector, 'name' | 'description'>>
) => {
): Promise<WriteResponseBase> => {
const connectorResult = await client.get<ConnectorDocument>({
id: connectorId,
index: CONNECTORS_INDEX,

View file

@ -15,6 +15,14 @@ export const CANCEL_LABEL: string = i18n.translate('xpack.serverlessSearch.cance
defaultMessage: 'Cancel',
});
export const EDIT_LABEL: string = i18n.translate('xpack.serverlessSearch.edit', {
defaultMessage: 'Edit',
});
export const SAVE_LABEL: string = i18n.translate('xpack.serverlessSearch.save', {
defaultMessage: 'Save',
});
export const BACK_LABEL: string = i18n.translate('xpack.serverlessSearch.back', {
defaultMessage: 'Back',
});
@ -45,3 +53,7 @@ export const INVALID_JSON_ERROR: string = i18n.translate(
defaultMessage: 'Invalid JSON',
}
);
export const CONNECTOR_LABEL: string = i18n.translate('xpack.serverlessSearch.connector', {
defaultMessage: 'Connector',
});

View file

@ -0,0 +1,119 @@
/*
* 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 {
EuiButton,
EuiButtonIcon,
EuiFlexGroup,
EuiFlexItem,
EuiPageTemplate,
EuiSpacer,
EuiText,
} from '@elastic/eui';
import { useQuery } from '@tanstack/react-query';
import { Connector } from '@kbn/search-connectors';
import React from 'react';
import { useParams } from 'react-router-dom';
import { i18n } from '@kbn/i18n';
import { CONNECTOR_LABEL } from '../../../../common/i18n_string';
import { useKibanaServices } from '../../hooks/use_kibana';
import { BASE_CONNECTORS_PATH } from '../connectors_router';
import { EditName } from './edit_name';
import { EditServiceType } from './edit_service_type';
import { EditDescription } from './edit_description';
export const EditConnector: React.FC = () => {
const { id } = useParams<{ id: string }>();
const {
application: { navigateToUrl },
http,
} = useKibanaServices();
const { data, isLoading, refetch } = useQuery({
queryKey: [`fetchConnector${id}`],
queryFn: () =>
http.fetch<{ connector: Connector }>(`/internal/serverless_search/connector/${id}`),
});
if (isLoading) {
<EuiPageTemplate offset={0} grow restrictWidth data-test-subj="svlSearchEditConnectorsPage">
<EuiPageTemplate.EmptyPrompt
title={
<h1>
{i18n.translate('xpack.serverlessSearch.connectors.loading', {
defaultMessage: 'Loading',
})}
</h1>
}
/>
</EuiPageTemplate>;
}
if (!data?.connector) {
return (
<EuiPageTemplate offset={0} grow restrictWidth data-test-subj="svlSearchEditConnectorsPage">
<EuiPageTemplate.EmptyPrompt
title={
<h1>
{i18n.translate('xpack.serverlessSearch.connectors.notFound', {
defaultMessage: 'Could not find a connector with id {id}',
values: { id },
})}
</h1>
}
actions={
<EuiButton color="primary" fill onClick={() => navigateToUrl(BASE_CONNECTORS_PATH)}>
{i18n.translate('xpack.serverlessSearch.connectors.goBack', {
defaultMessage: 'Go back',
})}
</EuiButton>
}
/>
</EuiPageTemplate>
);
}
const { connector } = data;
return (
<EuiPageTemplate offset={0} grow restrictWidth data-test-subj="svlSearchEditConnectorsPage">
<EuiPageTemplate.Section grow={false}>
<EuiText size="s">{CONNECTOR_LABEL}</EuiText>
<EuiFlexGroup direction="row" justifyContent="spaceBetween">
<EuiFlexItem>
<EditName connectorId={id} name={connector.name} onSuccess={refetch} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<span>
<EuiButtonIcon
aria-label={i18n.translate('xpack.serverlessSearch.connectors.openMenuLabel', {
defaultMessage: 'Open menu',
})}
iconType="boxesVertical"
/>
</span>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageTemplate.Section>
<EuiPageTemplate.Section>
<EuiFlexGroup direction="row">
<EuiFlexItem>
<EditServiceType
connectorId={id}
serviceType={connector.service_type ?? ''}
onSuccess={() => refetch()}
/>
<EuiSpacer />
<EditDescription
connectorId={id}
description={connector.description ?? ''}
onSuccess={refetch}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageTemplate.Section>
</EuiPageTemplate>
);
};

View file

@ -0,0 +1,134 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { css } from '@emotion/react';
import React, { useEffect, useState } from 'react';
import {
EuiFlexItem,
EuiFlexGroup,
EuiFieldText,
EuiForm,
EuiButton,
EuiSpacer,
EuiFormRow,
EuiText,
EuiButtonEmpty,
} from '@elastic/eui';
import { useMutation } from '@tanstack/react-query';
import { CANCEL_LABEL, EDIT_LABEL, SAVE_LABEL } from '../../../../common/i18n_string';
import { useKibanaServices } from '../../hooks/use_kibana';
interface EditDescriptionProps {
connectorId: string;
description: string;
onSuccess: () => void;
}
export const EditDescription: React.FC<EditDescriptionProps> = ({
connectorId,
description,
onSuccess,
}) => {
const [isEditing, setIsEditing] = useState(false);
const [newDescription, setNewDescription] = useState(description);
const { http } = useKibanaServices();
useEffect(() => setNewDescription(description), [description]);
const { isLoading, isSuccess, mutate } = useMutation({
mutationFn: async (inputDescription: string) => {
const body = { Description: inputDescription };
const result = await http.post(
`/internal/serverless_search/connectors/${connectorId}/description`,
{
body: JSON.stringify(body),
}
);
return result;
},
});
useEffect(() => {
if (isSuccess) {
setIsEditing(false);
onSuccess();
}
}, [isSuccess, onSuccess]);
return (
<EuiFlexGroup direction="row">
<EuiForm>
<EuiFlexItem grow={false}>
<EuiFormRow
helpText={i18n.translate('xpack.serverlessSearch.connectors.descriptionHelpText', {
defaultMessage: 'Optional description for your connector.',
})}
label={i18n.translate('xpack.serverlessSearch.connectors.descriptionLabel', {
defaultMessage: 'Description',
})}
labelAppend={
<EuiButtonEmpty size="xs" onClick={() => setIsEditing(true)}>
{EDIT_LABEL}
</EuiButtonEmpty>
}
>
{isEditing ? (
<EuiFieldText
onChange={(event) => setNewDescription(event.target.value)}
value={newDescription}
/>
) : (
<EuiText>{description}</EuiText>
)}
</EuiFormRow>
</EuiFlexItem>
{isEditing && (
<>
<EuiSpacer size="s" />
<EuiFlexGroup direction="row" justifyContent="center" alignItems="center">
<EuiFlexItem
grow={false}
css={css`
justify-content: center;
`}
>
<EuiButton
color="primary"
fill
onClick={() => mutate(newDescription)}
type="submit"
size="s"
isLoading={isLoading}
>
{SAVE_LABEL}
</EuiButton>
</EuiFlexItem>
<EuiFlexItem
grow={false}
css={css`
justify-content: center;
`}
>
<EuiButton
size="s"
isLoading={isLoading}
onClick={() => {
setNewDescription(description);
setIsEditing(false);
}}
>
{CANCEL_LABEL}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</>
)}
</EuiForm>
</EuiFlexGroup>
);
};

View file

@ -0,0 +1,132 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { css } from '@emotion/react';
import React, { useEffect, useState } from 'react';
import {
EuiFlexItem,
EuiFlexGroup,
EuiTitle,
EuiButtonIcon,
EuiFieldText,
EuiForm,
EuiButton,
EuiFormLabel,
EuiSpacer,
} from '@elastic/eui';
import { useMutation } from '@tanstack/react-query';
import { CANCEL_LABEL, CONNECTOR_LABEL, SAVE_LABEL } from '../../../../common/i18n_string';
import { useKibanaServices } from '../../hooks/use_kibana';
interface EditNameProps {
connectorId: string;
name: string;
onSuccess: () => void;
}
export const EditName: React.FC<EditNameProps> = ({ connectorId, name, onSuccess }) => {
const [isEditing, setIsEditing] = useState(false);
const [newName, setNewName] = useState(name);
const { http } = useKibanaServices();
useEffect(() => setNewName(name), [name]);
const { isLoading, isSuccess, mutate } = useMutation({
mutationFn: async (inputName: string) => {
const body = { name: inputName };
const result = await http.post(`/internal/serverless_search/connectors/${connectorId}/name`, {
body: JSON.stringify(body),
});
return result;
},
});
useEffect(() => {
if (isSuccess) {
setIsEditing(false);
onSuccess();
}
}, [isSuccess, onSuccess]);
return (
<EuiFlexGroup direction="row">
{!isEditing ? (
<>
<EuiFlexItem grow={false}>
<EuiTitle>
<h1>{name || CONNECTOR_LABEL}</h1>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem
grow={false}
css={css`
justify-content: center;
`}
>
<EuiButtonIcon
color="text"
iconType="pencil"
aria-label={i18n.translate('xpack.serverlessSearch.connectors.editNameLabel', {
defaultMessage: 'Edit connector name',
})}
onClick={() => setIsEditing(true)}
/>
</EuiFlexItem>
</>
) : (
<EuiForm>
<EuiFlexItem grow={false}>
<EuiFormLabel>
{i18n.translate('xpack.serverlessSearch.connectors.nameLabel', {
defaultMessage: 'Name',
})}
</EuiFormLabel>
<EuiFieldText onChange={(event) => setNewName(event.target.value)} value={newName} />
</EuiFlexItem>
<EuiSpacer />
<EuiFlexGroup direction="row" justifyContent="center" alignItems="center">
<EuiFlexItem
grow={false}
css={css`
justify-content: center;
`}
>
<EuiButton
color="primary"
fill
type="submit"
onClick={() => mutate(newName)}
size="s"
isLoading={isLoading}
>
{SAVE_LABEL}
</EuiButton>
</EuiFlexItem>
<EuiFlexItem
grow={false}
css={css`
justify-content: center;
`}
>
<EuiButton
size="s"
isLoading={isLoading}
onClick={() => {
setNewName(name);
setIsEditing(false);
}}
>
{CANCEL_LABEL}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiForm>
)}
</EuiFlexGroup>
);
};

View file

@ -0,0 +1,88 @@
/*
* 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 { i18n } from '@kbn/i18n';
import React, { useEffect } from 'react';
import {
EuiFlexItem,
EuiFlexGroup,
EuiForm,
EuiFormLabel,
EuiIcon,
EuiSuperSelect,
} from '@elastic/eui';
import { useMutation } from '@tanstack/react-query';
import { useKibanaServices } from '../../hooks/use_kibana';
import { useConnectorTypes } from '../../hooks/api/use_connector_types';
interface EditServiceTypeProps {
connectorId: string;
serviceType: string;
onSuccess: () => void;
}
export const EditServiceType: React.FC<EditServiceTypeProps> = ({
connectorId,
serviceType,
onSuccess,
}) => {
const { http } = useKibanaServices();
const { data: connectorTypes } = useConnectorTypes();
const options =
connectorTypes?.connectors.map((connectorType) => ({
inputDisplay: (
<EuiFlexGroup direction="row" alignItems="center">
<EuiFlexItem grow={false}>
<EuiIcon
size="l"
title={connectorType.name}
id={connectorType.serviceType}
type={connectorType.iconPath}
/>
</EuiFlexItem>
<EuiFlexItem>{connectorType.name}</EuiFlexItem>
</EuiFlexGroup>
),
value: connectorType.serviceType,
})) || [];
const { isLoading, isSuccess, mutate } = useMutation({
mutationFn: async (inputServiceType: string) => {
const body = { service_type: inputServiceType };
const result = await http.post(
`/internal/serverless_search/connectors/${connectorId}/service_type`,
{
body: JSON.stringify(body),
}
);
return result;
},
});
useEffect(() => {
if (isSuccess) {
onSuccess();
}
}, [isSuccess, onSuccess]);
return (
<EuiForm>
<EuiFormLabel>
{i18n.translate('xpack.serverlessSearch.connectors.serviceTypeLabel', {
defaultMessage: 'Connector type',
})}
</EuiFormLabel>
<EuiSuperSelect
isLoading={isLoading}
onChange={(event) => mutate(event)}
options={options}
valueOfSelected={serviceType ?? ''}
/>
</EuiForm>
);
};

View file

@ -17,18 +17,23 @@ import {
EuiTitle,
EuiToolTip,
} from '@elastic/eui';
import { Connector, ConnectorServerSideDefinition } from '@kbn/search-connectors';
import { Connector } from '@kbn/search-connectors';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { useQuery } from '@tanstack/react-query';
import React from 'react';
import { useMutation, useQuery } from '@tanstack/react-query';
import React, { useEffect } from 'react';
import { LEARN_MORE_LABEL } from '../../../common/i18n_string';
import { PLUGIN_ID } from '../../../common';
import { useKibanaServices } from '../hooks/use_kibana';
import { CREATE_CONNECTOR_PATH } from './connectors_router';
import { useConnectorTypes } from '../hooks/api/use_connector_types';
export const ConnectorsOverview = () => {
const { http } = useKibanaServices();
const {
application: { navigateToUrl },
http,
} = useKibanaServices();
const assetBasePath = http.basePath.prepend(`/plugins/${PLUGIN_ID}/assets`);
const connectorsPath = assetBasePath + '/connectors.svg';
@ -39,19 +44,35 @@ export const ConnectorsOverview = () => {
http.fetch<{ connectors: Connector[] }>('/internal/serverless_search/connectors'),
});
const { data: connectorTypes } = useQuery({
queryKey: ['fetchConnectorTypes'],
queryFn: () =>
http.fetch<{ connectors: ConnectorServerSideDefinition[] }>(
'/internal/serverless_search/connector_types'
),
const { data: connectorTypes } = useConnectorTypes();
const {
data: connector,
isLoading,
isSuccess,
mutate,
} = useMutation({
mutationFn: async () => {
const result = await http.post<{ connector: Connector }>(
'/internal/serverless_search/connectors'
);
return result.connector;
},
});
useEffect(() => {
navigateToUrl(`${CREATE_CONNECTOR_PATH}/${connector?.id}`);
}, [connector, isSuccess, navigateToUrl]);
const createConnector = () => mutate();
return (
<EuiPageTemplate offset={0} grow restrictWidth data-test-subj="svlSearchConnectorsPage">
<EuiPageTemplate.Header
pageTitle={i18n.translate('xpack.serverlessSearch.connectors.title', {
defaultMessage: 'Connectors',
})}
restrictWidth
rightSideItems={[
<EuiFlexGroup direction="row" alignItems="flexStart">
<EuiFlexItem>
@ -79,7 +100,12 @@ export const ConnectorsOverview = () => {
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<EuiButton fill iconType="plusInCircleFilled">
<EuiButton
isLoading={isLoading}
fill
iconType="plusInCircleFilled"
onClick={() => createConnector()}
>
{i18n.translate('xpack.serverlessSearch.connectors.createConnector', {
defaultMessage: 'Create connector',
})}
@ -107,7 +133,7 @@ export const ConnectorsOverview = () => {
{(data?.connectors || []).length > 0 ? (
<></>
) : (
<EuiPageTemplate.Section restrictWidth color="subdued">
<EuiPageTemplate.Section grow restrictWidth color="subdued">
<EuiFlexGroup alignItems="center" direction="column">
<EuiFlexItem>
<EuiPanel paddingSize="l" hasShadow={false} hasBorder>
@ -248,7 +274,12 @@ export const ConnectorsOverview = () => {
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem>
<EuiButton fill iconType="plusInCircleFilled">
<EuiButton
isLoading={isLoading}
fill
iconType="plusInCircleFilled"
onClick={() => createConnector()}
>
{i18n.translate('xpack.serverlessSearch.connectorsEmpty.createConnector', {
defaultMessage: 'Create connector',
})}
@ -269,7 +300,7 @@ export const ConnectorsOverview = () => {
<EuiFlexItem>
<EuiFlexGroup gutterSize="s">
{connectorTypes?.connectors.map((connectorType) => (
<EuiFlexItem>
<EuiFlexItem key={connectorType.name}>
<EuiToolTip content={connectorType.name}>
<EuiIcon
size="l"

View file

@ -0,0 +1,28 @@
/*
* 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 { Route, Routes } from '@kbn/shared-ux-router';
import React from 'react';
import { EditConnector } from './connectors/edit_connector';
import { ConnectorsOverview } from './connectors_overview';
export const BASE_CONNECTORS_PATH = 'connectors';
export const CREATE_CONNECTOR_SLUG = `create_connector`;
export const CREATE_CONNECTOR_PATH = `${BASE_CONNECTORS_PATH}/${CREATE_CONNECTOR_SLUG}`;
export const ConnectorsRouter: React.FC = () => {
return (
<Routes>
<Route path={`/${CREATE_CONNECTOR_SLUG}/:id`}>
<EditConnector />
</Route>
<Route exact path="/">
<ConnectorsOverview />
</Route>
</Routes>
);
};

View file

@ -16,6 +16,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import ReactDOM from 'react-dom';
import React from 'react';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { Router } from '@kbn/shared-ux-router';
import { ServerlessSearchContext } from './hooks/use_kibana';
export async function renderApp(
@ -23,7 +24,7 @@ export async function renderApp(
core: CoreStart,
services: ServerlessSearchContext
) {
const { ConnectorsOverview } = await import('./components/connectors_overview');
const { ConnectorsRouter } = await import('./components/connectors_router');
const queryClient = new QueryClient();
ReactDOM.render(
<KibanaThemeProvider theme={core.theme}>
@ -31,7 +32,9 @@ export async function renderApp(
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools initialIsOpen={false} />
<I18nProvider>
<ConnectorsOverview />
<Router history={services.history}>
<ConnectorsRouter />
</Router>
</I18nProvider>
</QueryClientProvider>
</KibanaContextProvider>

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 { ConnectorServerSideDefinition } from '@kbn/search-connectors';
import { useQuery } from '@tanstack/react-query';
import { useKibanaServices } from '../use_kibana';
export const useConnectorTypes = () => {
const { http } = useKibanaServices();
return useQuery({
queryKey: ['fetchConnectorTypes'],
queryFn: () =>
http.fetch<{ connectors: ConnectorServerSideDefinition[] }>(
'/internal/serverless_search/connector_types'
),
});
};

View file

@ -6,13 +6,14 @@
*/
import { CloudStart } from '@kbn/cloud-plugin/public';
import type { CoreStart } from '@kbn/core/public';
import type { AppMountParameters, CoreStart } from '@kbn/core/public';
import type { SharePluginStart } from '@kbn/share-plugin/public';
import { useKibana as useKibanaBase } from '@kbn/kibana-react-plugin/public';
import { AuthenticatedUser } from '@kbn/security-plugin/common';
export interface ServerlessSearchContext {
cloud: CloudStart;
history: AppMountParameters['history'];
share: SharePluginStart;
user?: AuthenticatedUser;
}

View file

@ -10,3 +10,4 @@ export const MANAGEMENT_API_KEYS = '/app/management/security/api_keys';
// Server Routes
export const CREATE_API_KEY_PATH = '/internal/security/api_key';
export const FETCH_INDICES_PATH = '/internal/serverless_search/indices';
export const CREATE_CONNECTOR_PATH = '/internal/connectors';

View file

@ -45,7 +45,7 @@ export class ServerlessSearchPlugin
euiIconType: 'logoElastic',
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
appRoute: '/app/elasticsearch',
async mount({ element }: AppMountParameters) {
async mount({ element, history }: AppMountParameters) {
const { renderApp } = await import('./application/elasticsearch');
const [coreStart, services] = await core.getStartServices();
const { security } = services;
@ -58,7 +58,7 @@ export class ServerlessSearchPlugin
user = undefined;
}
return await renderApp(element, coreStart, { user, ...services });
return await renderApp(element, coreStart, { history, user, ...services });
},
});
@ -71,12 +71,12 @@ export class ServerlessSearchPlugin
euiIconType: 'logoElastic',
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
searchable: false,
async mount({ element }: AppMountParameters) {
async mount({ element, history }: AppMountParameters) {
const { renderApp } = await import('./application/connectors');
const [coreStart, services] = await core.getStartServices();
docLinks.setDocLinks(coreStart.docLinks.links);
return await renderApp(element, coreStart, { ...services });
return await renderApp(element, coreStart, { history, ...services });
},
});

View file

@ -5,7 +5,15 @@
* 2.0.
*/
import { CONNECTOR_DEFINITIONS, fetchConnectors } from '@kbn/search-connectors';
import { schema } from '@kbn/config-schema';
import {
CONNECTOR_DEFINITIONS,
createConnector,
fetchConnectorById,
fetchConnectors,
updateConnectorNameAndDescription,
updateConnectorServiceType,
} from '@kbn/search-connectors';
import { RouteDependencies } from '../plugin';
export const registerConnectorsRoutes = ({ http, router }: RouteDependencies) => {
@ -27,6 +35,30 @@ export const registerConnectorsRoutes = ({ http, router }: RouteDependencies) =>
}
);
router.get(
{
path: '/internal/serverless_search/connector/{connectorId}',
validate: {
params: schema.object({
connectorId: schema.string(),
}),
},
},
async (context, request, response) => {
const { client } = (await context.core).elasticsearch;
const result = await fetchConnectorById(client.asCurrentUser, request.params.connectorId);
return result
? response.ok({
body: {
connector: result.value,
},
headers: { 'content-type': 'application/json' },
})
: response.notFound();
}
);
router.get(
{
path: '/internal/serverless_search/connector_types',
@ -50,4 +82,118 @@ export const registerConnectorsRoutes = ({ http, router }: RouteDependencies) =>
});
}
);
router.post(
{
path: '/internal/serverless_search/connectors',
validate: {},
},
async (context, request, response) => {
const { client } = (await context.core).elasticsearch;
const connector = await createConnector(client.asCurrentUser, {
indexName: null,
instant_response: true,
isNative: false,
language: null,
});
return response.ok({
body: {
connector,
},
headers: { 'content-type': 'application/json' },
});
}
);
router.post(
{
path: '/internal/serverless_search/connectors/{connectorId}/name',
validate: {
body: schema.object({
name: schema.string(),
}),
params: schema.object({
connectorId: schema.string(),
}),
},
},
async (context, request, response) => {
const { client } = (await context.core).elasticsearch;
const result = await updateConnectorNameAndDescription(
client.asCurrentUser,
request.params.connectorId,
{
name: request.body.name,
}
);
return response.ok({
body: {
result,
},
headers: { 'content-type': 'application/json' },
});
}
);
router.post(
{
path: '/internal/serverless_search/connectors/{connectorId}/description',
validate: {
body: schema.object({
description: schema.string(),
}),
params: schema.object({
connectorId: schema.string(),
}),
},
},
async (context, request, response) => {
const { client } = (await context.core).elasticsearch;
const result = await updateConnectorNameAndDescription(
client.asCurrentUser,
request.params.connectorId,
{
description: request.body.description,
}
);
return response.ok({
body: {
result,
},
headers: { 'content-type': 'application/json' },
});
}
);
router.post(
{
path: '/internal/serverless_search/connectors/{connectorId}/service_type',
validate: {
body: schema.object({
service_type: schema.string(),
}),
params: schema.object({
connectorId: schema.string(),
}),
},
},
async (context, request, response) => {
const { client } = (await context.core).elasticsearch;
const result = await updateConnectorServiceType(
client.asCurrentUser,
request.params.connectorId,
request.body.service_type
);
return response.ok({
body: {
result,
},
headers: { 'content-type': 'application/json' },
});
}
);
};

View file

@ -34,5 +34,6 @@
"@kbn/core-lifecycle-browser",
"@kbn/react-kibana-context-theme",
"@kbn/search-connectors",
"@kbn/shared-ux-router",
]
}